(二期)23、微服务框架spring cloud(二)
解决方向
- 为网络其请求设置超时时间
- 使用断路器模式
- 统计一段时间内的失败次数来决定正常访问还是直接返回
- 断路器处于半开状态,应许一个请求进入,如果请求正常,就关闭断路器。
- 包裹请求
- 跳闸机制
- 资源隔离
- 监控
- 回退机制
- 自我修复
- 通过HystrixCommand注解
- fallbackMethod回退函数
- 1:Hystrix使用命令模式HystrixCommand(Command)包装依赖调用逻辑,每个命令在单独线程中/信号授权下执行。
- 2:可配置依赖调用超时时间,超时时间一般设为比99.5%平均时间略高即可.当调用超时时,直接返回或执行fallback逻辑。
- 3:为每个依赖提供一个小的线程池(或信号),如果线程池已满调用将被立即拒绝,默认不采用排队.加速失败判定时间。
- 4:依赖调用结果分:成功,失败(抛出异常),超时,线程拒绝,短路。 请求失败(异常,拒绝,超时,短路)时执行fallback(降级)逻辑。
- 5:提供熔断器组件,可以自动运行或手动调用,停止当前依赖一段时间(10秒),熔断器默认错误率阈值为50%,超过将自动运行。
- 6:提供近实时依赖的统计和监控
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
@EnableHystrix
或者
@EnableCircuitBreaker
@HystrixCommand(fallbackMethod = "getUsernameFallback")
@GetMapping("/username")
public Object getUsername() {
return restTemplate.getForObject("http://EUREKA-CLIENT-2/username", String.class);
}
public Object getUsernameFallback() {
return "fall back";
}
默认提示:@HystrixCommand可以和@DefaultProperties配合使用
@hystrixCommand注解参数详解
- https://my.oschina.net/lyyjason/blog/1800102
/**
* Feign的fallback测试类
* 使用@FeignClient的fallback属性指定回退类
*/
@FeignClient(name = "microservice-provider-user", fallback = FeignClientFallback.class)
public interface UserFeignClient {
@GetMapping(value = "/{id}")
User findById(@PathVariable("id") Long id);
}
/**
* 回退类FeignClientFallback需实现Feign Client接口
* FeignClientFallback也可以是public class,没有区别
*/
@Component
class FeignClientFallback implements UserFeignClient {
@Override
public User findById(Long id) {
User user = new User();
user.setId(-1L);
user.setUsername("默认用户");
return user;
}
}
feign:
hystrix:
enabled: true
@FeignClient(name = "eureka-client-2", fallbackFactory = Demo2ClientFallbackFactory.class)
public interface Demo2Client {
@GetMapping("/username")
public String getUsername();
}
@Component
public class Demo2ClientFallbackFactory implements FallbackFactory<Demo2Client> {
public Demo2Client create(Throwable cause) {
return new Demo2Client() {
@Override
public String getUsername() {
System.out.println(cause.toString());
return "hello world fallbackfactory";
}
};
}
}
在实际开发中,并不是所有的请求都要提前预备好服务降级问题,如果我就是要将服务调用失败的信息展示给用户,那么此时就没有必要添加断路器了。
Hystrix断路器模式的状态监控
<!--actuator用于应用监控管理-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management:
endpoint:
health:
show-details: always
#说明断路器已经打开
Hystrix circuit short-circuited and is OPEN
https://www.cnblogs.com/java-synchronized/p/7927726.html
- 线程隔离
- 它将在单独的线程上执行,并发请求受线程池中的线程数量的限制。
- 把执行依赖代码的线程与请求线程(如:jetty线程)分离,请求线程可以自由控制离开的时间(异步过程)。
- 通过线程池大小可以控制并发量,当线程池饱和时可以提前拒绝服务,防止依赖问题扩散。
- 线上建议线程池不要设置过大,否则大量堵塞线程有可能会拖慢服务器。
- 信号量隔离
- 它将在调用线程上执行,开销相对较小,并发请求收到信号量个数的限制。
- 信号隔离也可以用于限制并发访问,防止阻塞扩散, 与线程隔离最大不同在于执行依赖代码的线程依然是请求线程(该线程需要通过信号申请)
- 当n个并发请求去调用一个目标服务接口时,都要获取一个信号量才能真正去调用目标服务接口,但信号量有限,默认是10个,可以使用maxConcurrentRequests参数配置,如果并发请求数多于信号量个数,就有线程需要进入队列排队,但排队队列也有上限,默认是 5,如果排队队列也满,则必定有请求线程会走fallback流程,从而达到限流和防止雪崩的目的。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
# 路由规则配置
zuul:
routes:
api-a:
path: /usernameByFeign
serviceId: eureka-client-1
- PRE:
- ROUTING:
- POST:
ERROR:
- 除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。
public class PreLogFilter extends ZuulFilter {
/**
* pre请求之前
* route用于将请求路由转到微服务
* post路由到微服务以后执行
* error在其他阶段发生错误的时候执行
* @return
*/
@Override
public String filterType() {
return "pre";
}
/**
* 执行顺序
* @return
*/
@Override
public int filterOrder() {
return 0;
}
/**
* true返回一个boolean判断该过滤器是否要执行
* @return
*/
@Override
public boolean shouldFilter() {
return true;
}
/**
* 过滤器执行具体内容
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
System.out.println("---》路由获取所有参数值" + request.getParameterMap().toString());
System.out.println(request.getHeader("cookie"));
return null;
}
}
@Bean
PreLogFilter preLogFilter() {
return new PreLogFilter();
}
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0-jre</version>
</dependency>
import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
/**
* 限流
*/
public class RateLimiterFilter extends ZuulFilter {
//guava的令牌桶算法
private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);
@Override
public String filterType() {
return PRE_TYPE;
}
@Override
public int filterOrder() {
return -5;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run(){
if(!RATE_LIMITER.tryAcquire()) {
throw new RuntimeException("占坑失败~~");
}
System.out.println("-------->没限流");
return null;
}
}
@Bean
RateLimiterFilter rateLimiterFilter() {
return new RateLimiterFilter();
}
- 限流
- 鉴权
- 参数校验
- 统计
- 日志
- https://gitee.com/minull/ace-security
- https://gitee.com/minull/ace-security/wikis/Home