1. spring cloud . spring boot . alibaba . sentinel 版本
代码语言:txt复制 <spring-boot.version>2.3.5.RELEASE</spring-boot.version>代码语言:txt复制 <spring-cloud.version>Hoxton.SR8</spring-cloud.version>代码语言:txt复制 <spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version>代码语言:txt复制 sentinel-dashboard 1.8.02. 项目依赖
代码语言:txt复制 <dependency>代码语言:txt复制 <groupId>com.alibaba.cloud</groupId>代码语言:txt复制 <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>代码语言:txt复制 </dependency>3. sentinel 官网地址
https://github.com/alibaba/Sentinel
4. 配置微服务间统一限流降级处理
代码语言:txt复制 1. 配置服务间调用开关及发生降级后异常处理
@AutoConfigureBefore(SentinelFeignAutoConfiguration.class)
public class SentinelAutoConfiguration {代码语言:txt复制 @Bean代码语言:txt复制 @Scope("prototype")代码语言:txt复制 @ConditionalOnMissingBean代码语言:txt复制 @ConditionalOnProperty(name = "feign.sentinel.enabled")代码语言:txt复制 public Feign.Builder feignSentinelBuilder() {代码语言:txt复制 return xxxxxSentinelFeign.builder();代码语言:txt复制 }代码语言:txt复制 @Bean代码语言:txt复制 @ConditionalOnMissingBean代码语言:txt复制 public BlockExceptionHandler blockExceptionHandler() {代码语言:txt复制 return new xxxxxUrlBlockHandler();代码语言:txt复制 }代码语言:txt复制}代码语言:txt复制2. 重写 {@link com.alibaba.cloud.sentinel.feign.SentinelFeign} 支持自动降级注入代码语言:txt复制public final class xxxxxSentinelFeign {代码语言:txt复制 private xxxxxSentinelFeign() {代码语言:txt复制 }代码语言:txt复制 public static xxxxSentinelFeign.Builder builder() {代码语言:txt复制 return new xxxxxSentinelFeign.Builder();代码语言:txt复制 }代码语言:txt复制 public static final class Builder extends Feign.Builder implements ApplicationContextAware {代码语言:txt复制 private Contract contract = new Contract.Default();代码语言:txt复制 private ApplicationContext applicationContext;代码语言:txt复制 private FeignContext feignContext;代码语言:txt复制 @Override代码语言:txt复制 public Feign.Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) {代码语言:txt复制 throw new UnsupportedOperationException();代码语言:txt复制 }代码语言:txt复制 @Override代码语言:txt复制 public xxxxxSentinelFeign.Builder contract(Contract contract) {代码语言:txt复制 this.contract = contract;代码语言:txt复制 return this;代码语言:txt复制 }代码语言:txt复制 @Override代码语言:txt复制 public Feign build() {代码语言:txt复制 super.invocationHandlerFactory(new InvocationHandlerFactory() {代码语言:txt复制 @Override代码语言:txt复制 public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {代码语言:txt复制 // using reflect get fallback and fallbackFactory properties from代码语言:txt复制 // FeignClientFactoryBean because FeignClientFactoryBean is a package代码语言:txt复制 // level class, we can not use it in our package代码语言:txt复制 Object feignClientFactoryBean = xxxxSentinelFeign.Builder.this.applicationContext代码语言:txt复制 .getBean("&" target.type().getName());代码语言:txt复制 Class fallback = (Class) getFieldValue(feignClientFactoryBean, "fallback");代码语言:txt复制 Class fallbackFactory = (Class) getFieldValue(feignClientFactoryBean, "fallbackFactory");代码语言:txt复制 String beanName = (String) getFieldValue(feignClientFactoryBean, "contextId");代码语言:txt复制 if (!StringUtils.hasText(beanName)) {代码语言:txt复制 beanName = (String) getFieldValue(feignClientFactoryBean, "name");代码语言:txt复制 }代码语言:txt复制 Object fallbackInstance;代码语言:txt复制 FallbackFactory fallbackFactoryInstance;代码语言:txt复制 // check fallback and fallbackFactory properties代码语言:txt复制 if (void.class != fallback) {代码语言:txt复制 fallbackInstance = getFromContext(beanName, "fallback", fallback, target.type());代码语言:txt复制 return new xxxxSentinelInvocationHandler(target, dispatch,代码语言:txt复制 new FallbackFactory.Default(fallbackInstance));代码语言:txt复制 }代码语言:txt复制 if (void.class != fallbackFactory) {代码语言:txt复制 fallbackFactoryInstance = (FallbackFactory) getFromContext(beanName, "fallbackFactory",代码语言:txt复制 fallbackFactory, FallbackFactory.class);代码语言:txt复制 return new xxxxSentinelInvocationHandler(target, dispatch, fallbackFactoryInstance);代码语言:txt复制 }代码语言:txt复制 return new xxxxxSentinelInvocationHandler(target, dispatch);代码语言:txt复制 }代码语言:txt复制 private Object getFromContext(String name, String type, Class fallbackType, Class targetType) {代码语言:txt复制 Object fallbackInstance = feignContext.getInstance(name, fallbackType);代码语言:txt复制 if (fallbackInstance == null) {代码语言:txt复制 throw new IllegalStateException(String.format(代码语言:txt复制 "No %s instance of type %s found for feign client %s", type, fallbackType, name));代码语言:txt复制 }代码语言:txt复制 if (!targetType.isAssignableFrom(fallbackType)) {代码语言:txt复制 throw new IllegalStateException(String.format(代码语言:txt复制 "Incompatible %s instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",代码语言:txt复制 type, fallbackType, targetType, name));代码语言:txt复制 }代码语言:txt复制 return fallbackInstance;代码语言:txt复制 }代码语言:txt复制 });代码语言:txt复制 super.contract(new SentinelContractHolder(contract));代码语言:txt复制 return super.build();代码语言:txt复制 }代码语言:txt复制 private Object getFieldValue(Object instance, String fieldName) {代码语言:txt复制 Field field = ReflectionUtils.findField(instance.getClass(), fieldName);代码语言:txt复制 field.setAccessible(true);代码语言:txt复制 try {代码语言:txt复制 return field.get(instance);代码语言:txt复制 }代码语言:txt复制 catch (IllegalAccessException e) {代码语言:txt复制 // ignore代码语言:txt复制 }代码语言:txt复制 return null;代码语言:txt复制 }代码语言:txt复制 @Override代码语言:txt复制 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {代码语言:txt复制 this.applicationContext = applicationContext;代码语言:txt复制 feignContext = this.applicationContext.getBean(FeignContext.class);代码语言:txt复制 }代码语言:txt复制 }代码语言:txt复制}代码语言:txt复制3. 降级后异常处理
public class xxxxxSentinelInvocationHandler implements InvocationHandler {代码语言:txt复制 public static final String EQUALS = "equals";代码语言:txt复制 public static final String HASH_CODE = "hashCode";代码语言:txt复制 public static final String TO_STRING = "toString";代码语言:txt复制 private final Target<?> target;代码语言:txt复制 private final Map<Method, InvocationHandlerFactory.MethodHandler> dispatch;代码语言:txt复制 private FallbackFactory fallbackFactory;代码语言:txt复制 private Map<Method, Method> fallbackMethodMap;代码语言:txt复制 xxxxxxSentinelInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch,代码语言:txt复制 FallbackFactory fallbackFactory) {代码语言:txt复制 this.target = checkNotNull(target, "target");代码语言:txt复制 this.dispatch = checkNotNull(dispatch, "dispatch");代码语言:txt复制 this.fallbackFactory = fallbackFactory;代码语言:txt复制 this.fallbackMethodMap = toFallbackMethod(dispatch);代码语言:txt复制 }代码语言:txt复制 xxxxxxSentinelInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {代码语言:txt复制 this.target = checkNotNull(target, "target");代码语言:txt复制 this.dispatch = checkNotNull(dispatch, "dispatch");代码语言:txt复制 }代码语言:txt复制 @Override代码语言:txt复制 public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {代码语言:txt复制 if (EQUALS.equals(method.getName())) {代码语言:txt复制 try {代码语言:txt复制 Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;代码语言:txt复制 return equals(otherHandler);代码语言:txt复制 }代码语言:txt复制 catch (IllegalArgumentException e) {代码语言:txt复制 return false;代码语言:txt复制 }代码语言:txt复制 }代码语言:txt复制 else if (HASH_CODE.equals(method.getName())) {代码语言:txt复制 return hashCode();代码语言:txt复制 }代码语言:txt复制 else if (TO_STRING.equals(method.getName())) {代码语言:txt复制 return toString();代码语言:txt复制 }代码语言:txt复制 Object result;代码语言:txt复制 InvocationHandlerFactory.MethodHandler methodHandler = this.dispatch.get(method);代码语言:txt复制 // only handle by HardCodedTarget代码语言:txt复制 if (target instanceof Target.HardCodedTarget) {代码语言:txt复制 Target.HardCodedTarget hardCodedTarget = (Target.HardCodedTarget) target;代码语言:txt复制 MethodMetadata methodMetadata = SentinelContractHolder.METADATA_MAP代码语言:txt复制 .get(hardCodedTarget.type().getName() Feign.configKey(hardCodedTarget.type(), method));代码语言:txt复制 // resource default is HttpMethod:protocol://url代码语言:txt复制 if (methodMetadata == null) {代码语言:txt复制 result = methodHandler.invoke(args);代码语言:txt复制 }代码语言:txt复制 else {代码语言:txt复制 String resourceName = methodMetadata.template().method().toUpperCase() ":" hardCodedTarget.url()代码语言:txt复制 methodMetadata.template().path();代码语言:txt复制 Entry entry = null;代码语言:txt复制 try {代码语言:txt复制 ContextUtil.enter(resourceName);代码语言:txt复制 entry = SphU.entry(resourceName, EntryType.OUT, 1, args);代码语言:txt复制 result = methodHandler.invoke(args);代码语言:txt复制 }代码语言:txt复制 catch (Throwable ex) {代码语言:txt复制 // fallback handle代码语言:txt复制 if (!BlockException.isBlockException(ex)) {代码语言:txt复制 Tracer.trace(ex);代码语言:txt复制 }代码语言:txt复制 if (fallbackFactory != null) {代码语言:txt复制 try {代码语言:txt复制 Object fallbackResult = fallbackMethodMap.get(method).invoke(fallbackFactory.create(ex),代码语言:txt复制 args);代码语言:txt复制 return fallbackResult;代码语言:txt复制 }代码语言:txt复制 catch (IllegalAccessException e) {代码语言:txt复制 // shouldn't happen as method is public due to being an代码语言:txt复制 // interface代码语言:txt复制 throw new AssertionError(e);代码语言:txt复制 }代码语言:txt复制 catch (InvocationTargetException e) {代码语言:txt复制 throw new AssertionError(e.getCause());代码语言:txt复制 }代码语言:txt复制 }else {代码语言:txt复制 // 若是XResponse类型 执行自动降级返回 XResponse代码语言:txt复制 if (XResponse.class == method.getReturnType()) {代码语言:txt复制 log.error("feign 服务间调用异常", ex);代码语言:txt复制 //不能返回 XResponse , 涉及业务处理。 直接抛出异常,在全局异常中处理代码语言:txt复制// return XResponse.builder()代码语言:txt复制// .data(Boolean.FALSE)代码语言:txt复制// .msg("服务请求超时,请稍后重试")代码语言:txt复制// .httpStatus(HttpStatus.SERVICE_UNAVAILABLE)代码语言:txt复制// .build();代码语言:txt复制 throw new FeginServierException("系统繁忙,请稍后重试");代码语言:txt复制 }else {代码语言:txt复制 throw ex;代码语言:txt复制 }代码语言:txt复制 }代码语言:txt复制 }代码语言:txt复制 finally {代码语言:txt复制 if (entry != null) {代码语言:txt复制 entry.exit(1, args);代码语言:txt复制 }代码语言:txt复制 ContextUtil.exit();代码语言:txt复制 }代码语言:txt复制 }代码语言:txt复制 }代码语言:txt复制 else {代码语言:txt复制 // other target type using default strategy代码语言:txt复制 result = methodHandler.invoke(args);代码语言:txt复制 }代码语言:txt复制 return result;代码语言:txt复制 }代码语言:txt复制 @Override代码语言:txt复制 public boolean equals(Object obj) {代码语言:txt复制 if (obj instanceof SentinelInvocationHandler) {代码语言:txt复制 xxxxSentinelInvocationHandler other = (xxxxSentinelInvocationHandler) obj;代码语言:txt复制 return target.equals(other.target);代码语言:txt复制 }代码语言:txt复制 return false;代码语言:txt复制 }代码语言:txt复制 @Override代码语言:txt复制 public int hashCode() {代码语言:txt复制 return target.hashCode();代码语言:txt复制 }代码语言:txt复制 @Override代码语言:txt复制 public String toString() {代码语言:txt复制 return target.toString();代码语言:txt复制 }代码语言:txt复制 static Map<Method, Method> toFallbackMethod(Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {代码语言:txt复制 Map<Method, Method> result = new LinkedHashMap<>();代码语言:txt复制 for (Method method : dispatch.keySet()) {代码语言:txt复制 method.setAccessible(true);代码语言:txt复制 result.put(method, method);代码语言:txt复制 }代码语言:txt复制 return result;代码语言:txt复制 }代码语言:txt复制}代码语言:txt复制4. 服务间流控降级自定义返回
public class xxxxxUrlBlockHandler implements BlockExceptionHandler {代码语言:txt复制 @Override代码语言:txt复制 public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {代码语言:txt复制 log.error("sentinel 降级 资源名称{}", e.getRule().getResource(), e);代码语言:txt复制 response.setContentType(ContentType.JSON.toString());代码语言:txt复制 response.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());代码语言:txt复制 response.setCharacterEncoding("UTF-8");代码语言:txt复制 response.getWriter().print(JSONUtil.toJsonStr(XResponse.builder()代码语言:txt复制 .data(Boolean.FALSE)代码语言:txt复制 .msg("服务请求超时,请稍后重试")代码语言:txt复制 .httpStatus(HttpStatus.SERVICE_UNAVAILABLE)代码语言:txt复制 .build()代码语言:txt复制 .toResponseEntity()));代码语言:txt复制 }代码语言:txt复制}5. 网关集成流控
#######将上面4步操作集成公共的依赖, 网关与各个微服务集成 xxx-common-sentinel##########
代码语言:txt复制 <!--断路器网关依赖 -->代码语言:txt复制 <dependency>代码语言:txt复制 <groupId>com.alibaba.cloud</groupId>代码语言:txt复制 <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>代码语言:txt复制 </dependency>代码语言:txt复制 <dependency>代码语言:txt复制 <groupId>com.xxxx</groupId>代码语言:txt复制 <artifactId>xxxx-common-sentinel</artifactId>代码语言:txt复制 <version>1.0.0</version>代码语言:txt复制 </dependency>- 5.1 网关配置限流降级
@Configuration代码语言:txt复制public class GatewayConfiguration {代码语言:txt复制 @PostConstruct代码语言:txt复制 public void doInit() {代码语言:txt复制 GatewayCallbackManager.setBlockHandler(new GateWayBlockRequestHandler());代码语言:txt复制 }代码语言:txt复制}- 5.2 服务返回 一般返回自定义的返回体 sentinel 不能根据自定义的返回体进行异常降级 需自已处理
@Component代码语言:txt复制public class GatewayBlockFilter implements GlobalFilter, Ordered {代码语言:txt复制 @Override代码语言:txt复制 public int getOrder() {代码语言:txt复制 return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;代码语言:txt复制 }代码语言:txt复制 @Override代码语言:txt复制 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {代码语言:txt复制 ServerHttpResponse originalResponse = exchange.getResponse();代码语言:txt复制 ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {代码语言:txt复制 @Override代码语言:txt复制 public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {代码语言:txt复制 // get match route id代码语言:txt复制 Route route = (Route) exchange.getAttributes().get(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);代码语言:txt复制 String id = route.getId();代码语言:txt复制 // 500 error -> degrade代码语言:txt复制 if (originalResponse.getRawStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR.value() ) {代码语言:txt复制 Entry entry = null;代码语言:txt复制 try {代码语言:txt复制 // 0 token代码语言:txt复制 entry = SphU.entry(id, EntryType.OUT);代码语言:txt复制 Tracer.trace(new RuntimeException("INTERNAL_SERVER_ERROR"));代码语言:txt复制 } catch (BlockException e) {代码语言:txt复制 log.error(e.getMessage(), e);代码语言:txt复制 } finally {代码语言:txt复制 if (entry != null){代码语言:txt复制 entry.close();代码语言:txt复制 }代码语言:txt复制 }代码语言:txt复制 }代码语言:txt复制 return super.writeWith(body);代码语言:txt复制 }代码语言:txt复制 };代码语言:txt复制 // replace response with decorator代码语言:txt复制 return chain.filter(exchange.mutate().response(decoratedResponse).build());代码语言:txt复制 }代码语言:txt复制}6. sentinel 规则持久化 - 控制台(1.8.0)
- 6.1 在sentinel 官网下最新控制台源码
image.png
- 6.2 将控制台源码拿到自己项目中部署
image.png
- 6.3 修改持久化规则,在源码的test 目录中找到 nacos , 将nacos 目录拷贝到 源码的rule 目录下,完善流控降级的规则 。
image.png
image.png
- 6.4 在自己微服务中添加sentinel对nacos 依赖 ( 可以添加到上面第四步 xxx-common-sentinel 公共的依赖里面 )
<dependency>代码语言:txt复制 <groupId>com.alibaba.csp</groupId>代码语言:txt复制 <artifactId>sentinel-datasource-nacos</artifactId>代码语言:txt复制 </dependency>- 6.5 在网关配置里面将 sentinel 拉去配置的地址配置 (nacos)
image.png
- 6.6 在所有服务依赖的配置文件中添加 其他规则配置拉取地址, 如在 application-dev.yml中
image.png
- 6.7 默认网关启动在sentinel 控制台会跟普通服务显示一样 需要添加启动项
@SpringCloudApplication代码语言:txt复制public class XXXGatewayApplication {代码语言:txt复制 public static void main(String[] args) {代码语言:txt复制 System.setProperty("csp.sentinel.app.type", "1");代码语言:txt复制 SpringApplication.run(XXXGatewayApplication.class, args);代码语言:txt复制 }代码语言:txt复制}


