springcloud:成熟的微服务框架,定位为开发人员提供工具,以快速构建分布式系统
项目搭建流程:
1、新建多模块项目,开发
2、服务注册与发现Eureka,注册服务方便引用
3、利用Fegin实现服务间调用
4、服务调用正常,考虑服务的负载均衡,使用Ribbon
5、服务宕机,容灾,避免大面积服务瘫痪,使用熔断器Hystrix
6、整体系统发布统一ip端口,使用网关Zuul,也可对请求进行过滤器的编写
其他:
1、Session共享机制
2、微服务应用开发重难点环节
3、微服务子模块通过网关访问文件,需配置地址映射
4、缓存的配置类
一、Eureka服务端搭建(注册中心)
一、Eureka服务端搭建(引依赖、加配置、使用注解)
// 新建子模块pom引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
//application.properties加配置
spring.application.name=eureka-server 服务名
server.port=8000 服务端口
eureka.instance.hostname=localhost 主机名称
eureka.client.fetch-registry=false fetch-registry获取注册表,不需要同步其他节点数据
eureka.client.register-with-eureka=false register-with-eureka代表是否将自己注册到eureka-server,默认是true
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/ 服务对外所提供的地址
//启动类加注解@EnableEurekaServer
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
====eureka-server配置完成,访问地址http://localhost:8000/查看服务注册情况
二、Eureka客户端搭建(子模块服务注册)
二、Eureka客户端搭建(引入pom依赖,配置文件编写)
//引入pom依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
//application.properties配置文件修改
//增加服务端注册中心地址
eureka.client.service-url.defaultZone=http://localhost:8000/eureka/
=====先启动server,再启动子模块客户端服务,查看服务端网页注册情况,注册成功
三、利用Fegin实现服务间调用(子模块业务间调用,不经过网关,需要携带网关的Session信息,只用RequestInterceptor请求拦截器)
三、利用Fegin实现服务间调用(pom引入依赖、加配置、引注解)
//引入pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
//启动类加注解@EnableFeignClients,拥有调用Feign的能力
@SpringBootApplication
@EnableFeignClients
public class CoursePriceApplication {
public static void main(String[] args) {
SpringApplication.run(CoursePriceApplication.class, args);
}
}
//代码新增其他模块提供的服务接口用作Feign客户端
@FeignClient(value="course-list") value一般为要调用的子模块的服务名,接口为对外提供的接口,引用的实体可依赖其他模块
public interface CourseListClient {
@GetMapping("/courses")
List<Course> courseList();
}
//使用注解在其他方法内直接调用
//子模块调用携带网关的Session信息
/**
* 描述: Feign请求拦截器
*/
@EnableFeignClients
@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
//通过RequestContextHolder获取到请求
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
return;
}
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
Enumeration<String> values = request.getHeaders(name);
while (values.hasMoreElements()) {
String value = values.nextElement();
requestTemplate.header(name, value);
}
}
}
}
}
四、使用Ribbon实现负载均衡
//四、指定对应服务使用ribbon负载均衡(加配置)
course-list.ribbon.NFLoadBanlancerRuleClassName=com.netflix.loadbalancer.RoundRobinRule
要调用的服务名.ribbon.NFLoadBanlancerRuleClassName=负载均衡策略
五、 使用熔断器Hystrix
//五、使用断路器(加依赖、加配置,加注解,代码引入)
//在调用方加入断路器
//加入pom依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
//application.properties配置文件修改
feign.hystrix.enabled=true 开启断路器配置,默认不开启
//启动类加入断路器注解@EnableCircuitBreaker
@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
public class CoursePriceApplication {
public static void main(String[] args) {
SpringApplication.run(CoursePriceApplication.class, args);
}
}
//编码引入,断路后如何进行处理
//新建断路器实现类,针对要调用的类接口进行断路后处理实现
@Component
public class CourseListClientHystrix implements CourseListClient {
@Override
public List<Course> courseList() {
return Collections.emptyList();
}
}
//之前的Feign客户端进行断路器配置,增加fallback指定发生错误要调用的类
@FeignClient(value = "course-list", fallback = CourseListClientHystrix.class)
@Primary
public interface CourseListClient {
@GetMapping("/courses")
List<Course> courseList();
}
===测试,要调用的服务停止,访问接口不会报错
六、使用网关Zuul实现路由功能
//六、使用网关Zuul实现路由功能(Zuul服务注册,引入依赖,配置路由地址)
//新建独立子模块Zuul
//引入pom依赖(客户端,网关依赖)
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
//新建启动类(加注解@EnableZuulProxy,@SpringCloudApplication)
@EnableZuulProxy
@SpringCloudApplication
public class ZuulGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulGatewayApplication.class, args);
}
}
//application.properties加配置
spring.application.name=course-gateway
server.port=9000
logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}
mybatis.configuration.map-underscore-to-camel-case=true
eureka.client.service-url.defaultZone=http://localhost:8000/eureka/ //服务注册地址
//配置路由地址(可不加使用默认配置,直接ip端口加注册中心子模块服务名+方法名)
zuul.prefix=/imooc //前缀
zuul.routes.course-list.path=/list/** //子模块路径别名
zuul.routes.course-list.service-id=course-list //与path搭配使用,指定子服务名
zuul.routes.course-price.path=/price/** //同上
zuul.routes.course-price.service-id=course-price
//配置完成 使用ip端口加配置前缀+子服务路径别名+方法名
七、使用Zuul网关实现过滤器功能
如下:
/**
* 描述: 前置过滤器
*/
@Component
public class PreRequestFilter extends ZuulFilter {
@Override
public String filterType() {
//请求之前过滤
return FilterConstants.PRE_TYPE;
}
//排序,该值越小,filter越早执行
@Override
public int filterOrder() {
return 5;
}
//开关,表示是否需要执行该filter
@Override
public boolean shouldFilter() {
//return true;
//如:过滤部分请求
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String requestURI = request.getRequestURI();
if (requestURI.contains("images") || requestURI.contains("pay")) {
return false;
}
if (requestURI.contains("cart") || requestURI.contains("order")) {
return true;
}
return false;
}
//filter具体的功能方法
@Override
public Object run() throws ZuulException {
//打印请求链接
//RequestContext currentContext = RequestContext.getCurrentContext();
//System.out.println("URI:" + currentContext.getRequest().getRequestURI());
//return null;
//鉴定用户是否存在
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletRequest request = currentContext.getRequest();
HttpSession session = request.getSession();
User currentUser = (User)session.getAttribute(Constant.IMOOC_MALL_USER);
if (currentUser == null) {
currentContext.setSendZuulResponse(false);
currentContext.setResponseBody("{\n"
+ " \"status\": 10007,\n"
+ " \"msg\": \"NEED_LOGIN\",\n"
+ " \"data\": null\n"
+ "}");
currentContext.setResponseStatusCode(200);
}
return null;
}
}
/**
* 描述: 后置过滤器
*/
@Component
public class PostRequestFilter extends ZuulFilter {
@Override
public String filterType() {
return FilterConstants.POST_TYPE;
}
@Override
public int filterOrder() {
return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext currentContext = RequestContext.getCurrentContext();
int status = currentContext.getResponse().getStatus();
System.out.println("response status:" + status);
return null;
}
}
其他:
一、多模块Session共享机制(使用Redis)
使用redis实现多服务session共享
//修改application.properties配置,增加redis配置
//各业务模块增加session配置
spring.session.store-type=redis session方案指定为redis
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
//网关模块增加redis配置
spring.session.store-type=redis
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
zuul.sensitive-headers= 为空,无需要过滤的headers
zuul.host.connect-timeout-millis=15000 网关超时时间15s
//启动类增加注解@EnableRedisHttpSession
二、微服务应用开发重难点环节
重难点:模块拆分设计、公共模块、Zuul过滤器、Session处理、Feign调用
常见错误:模块粒度不合适、无公共模块、各接口独立校验、session无法共享、HTTP手动调用
三、微服务子模块通过网关访问文件,需配置地址映射
/**
* 描述: 配置地址映射
*/
@Configuration
public class ImoocMallWebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/images/**")
.addResourceLocations("file:" + ProductConstant.FILE_UPLOAD_DIR);
registry.addResourceHandler("swagger-ui.html").addResourceLocations(
"classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations(
"classpath:/META-INF/resources/webjars/");
}
}
四、缓存的配置类
/**
* 描述: 缓存的配置类
*/
@Configuration
@EnableCaching
public class CachingConfig {
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter
.lockingRedisCacheWriter(connectionFactory);
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
cacheConfiguration = cacheConfiguration.entryTtl(Duration.ofSeconds(30));
RedisCacheManager redisCacheManager = new RedisCacheManager(redisCacheWriter,
cacheConfiguration);
return redisCacheManager;
}
}