前提,有了一套微服务架构的服务,然后有一个统一鉴权的服务用户中心 userService
Hystrix的隔离策略有两种:分别是线程隔离和信号量隔离。
如何配置?
直接在yml 中配置:
hystrix.command.default.execution.isolation.strategy:Semaphore
配置:hystrix.command.default.execution.isolation.strategy 隔离策略,默认是Thread, 可选Thread|Semaphore
(1) 什么是线程池隔离?
为每一个服务接口单独开辟一个线程池,保持与其他服务接口线程的隔离,提高该服务接口的独立性和高可用。
(2) 线程池隔离的优点是什么?
(3) 线程池隔离的缺点是什么?
(1) 什么是信号量隔离?
线程池隔离:
1、调用线程和hystrixCommand线程不是同一个线程,并发请求数受到线程池(不是容器tomcat的线程池,而是hystrixCommand所属于线程组的线程池)中的线程数限制,默认是10。
2、这个是默认的隔离机制
3、hystrixCommand线程无法获取到调用线程中的ThreadLocal中的值
信号量隔离:
1、调用线程和hystrixCommand线程是同一个线程,默认最大并发请求数是10
2、调用数度快,开销小,由于和调用线程是处于同一个线程,所以必须确保调用的微服务可用性足够高并且返回快才用
注意:如果发生找不到上下文的运行时异常,可考虑将隔离策略设置为SEMAPHONE。
本文中由于涉及到了http header 传递,所以只能选择 SEMAPHONE, 一般我们只需要在 网关中开启即可;
@Slf4j
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取token
ServerHttpRequest request = exchange.getRequest();
List accessTokens = request.getHeaders().get(HeaderConstant.ACCESS_TOKEN);
// 去鉴权中心,校验token是否合法....省略
boolean flag = true;
// 鉴权成功 放入http header 中
if(flag ){
ServerHttpRequest build = request.mutate().build();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(request.getHeaders());
httpHeaders.add(HeaderConstant.USER_ID, userId);
build = new ServerHttpRequestDecorator(build) {
@Override
public HttpHeaders getHeaders() {
return httpHeaders;
}
};
return chain.filter(exchange.mutate().request(build).build());
// 否则什么也不做,后续会由于获取不到header 中的token 导致请求错误
}else{
return chain.filter(exchange);
}
}
}
通常可以写在公共依赖模块中,然后每个服务去引入即可
@Slf4j
public class UserInterceptor implements HandlerInterceptor {
@Autowired
private UpmsUserFeignClient upmsFeignClient;
@Override
public boolean preHandle(HttpServletRequest httpRequest,
HttpServletResponse response, Object handler) throws Exception {
//获取请求头会话token,thread local中设置用户id,方便后续获取用户id
String userId = httpRequest.getHeader(HeaderConstant.USER_ID);
if (StringUtils.isNotBlank(userId)) {
//获取用户
String tenantId = null;
Map pathVariables = (Map) httpRequest.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
if (pathVariables != null && pathVariables.get("tenant_id") != null ) {
tenantId = (String)pathVariables.get("tenant_id");
if (!checkUserTenant(userId,tenantId)){
throw new MixException(BusinessErrorEnum.AUTH_TENANT_IS_ILLEGAL);
}
}
CurrentUserUtil.setCurrentUser(userId, tenantId);
return true;
} else {
return true;
}
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
// 此次请求结束,及时清理用户信息
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
try {
CurrentUserUtil.removeCurrentUserInfo();
} catch (Exception e) {
log.error("afterCompletion,e="+e.getMessage(),e);
} finally {
CurrentUserUtil.removeCurrentUserInfo();
}
}
}