A lightweight powerful flow control component enabling reliability and monitoring for microservices. (轻量级的流量控制、熔断降级 Java 库). 中文文档
雪崩效应又称cascading failure(级联故障),指基础服务故障导致上层服务故障并且故障像雪球一样越滚越大。
<!--sentinel--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sentinel</artifactId> </dependency> 复制代码
无 复制代码
无 复制代码
打开所有端点查看是否生效
# actuator management: endpoints: web: exposure: # 生产不能全部放开且需配置安全措施 include: '*' 复制代码
出现以下内容证明整合成功了
下载我们pom文件定义的相同版本的Sentinel。 下载地址
#启动 java -jar XXX.jar 复制代码
spring: cloud: sentinel: transport: # 指定sentinel控制台地址 dashboard: localhost:8080 复制代码
流控规则
直接
关联:关联资源达到阈值,限流自己
链路:指定链路上的流量
// 定义一个common服务 package com.virgo.user.service; import com.virgo.entity.TblCar; /** * @author zhaozha * @date 2019/10/15 下午4:27 */ public interface CommonService { TblCar common(); } package com.virgo.user.service; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.virgo.entity.TblCar; import org.springframework.stereotype.Service; /** * @author zhaozha * @date 2019/10/15 下午4:28 */ @Service public class CommonServiceImpl implements CommonService { @Override @SentinelResource("common") public TblCar common() { return TblCar.builder().id(1L).build(); } } // controller层同时调用这个服务 @GetMapping("/test/a") public String testA() { commonServiceImpl.common(); return "testA"; } @GetMapping("/test/b") public String testB() { commonServiceImpl.common(); return "testB"; } 复制代码
降级规则
热点规则
系统规则
授权规则
微服务注册/心跳到控制台,控制台通过api获取/推送消息
spring: cloud: sentinel: transport: 控制台地址 dashboard: 和控制台通信的ip client-ip: 和控制通信端口(默认8719,如果已经占用,+1直到找到) port: 心跳毫秒 heartbeat-interval-ms: 复制代码
配置项 | 默认值 | 描述 |
---|---|---|
server.port | 8080 | 指定端口 |
csp.sentinel.dashboard.server | localhost:8080 | 指定地址 |
project.name | - | - |
sentinel.dashboard.auth.username[1.6] | sentinel | Dashboard登陆账号 |
sentinel.dashboard.auth.password[1.6] | sentinel | Dashboard登陆密码 |
server.servlet.session.timeout[1.6] | 30分钟 | 登陆session过期时间(7200:7200秒/60m:60分钟) |
@GetMapping("/test/sentinel/api") public String testSentinelApi(@RequestParam(required = false) String a) { String resourceName = "test-sentinel-api"; ContextUtil.enter(resourceName, "test"); Entry entry = null; try { entry = SphU.entry(resourceName); if (StringUtils.isBlank(a)) { throw new IllegalArgumentException("参数不可为空"); } return a; } catch (BlockException e) { return "限流/降级了"; } catch (IllegalArgumentException e) { Tracer.trace(e); return "参数非法"; } finally { if (entry != null) { entry.exit(); } ContextUtil.exit(); } } 复制代码
通过@SentinelResource简化代码
package com.virgo.user.sentinel; import com.alibaba.csp.sentinel.slots.block.BlockException; /** * @author zhaozha * @date 2019/10/16 下午3:11 */ public class ControllerBlockHandlerClass { /** * 处理限流/降级 * * @param a * @param e * @return */ public static String block(String a, BlockException e) { return "降级/限流了"; } } @GetMapping("/test/sentinel/api") @SentinelResource( value = "test-sentinel-api", blockHandler = "block", blockHandlerClass = com.virgo.user.sentinel.ControllerBlockHandlerClass.class ) public String testSentinelApi(@RequestParam(required = false) String a) { if (StringUtils.isBlank(a)) { throw new IllegalArgumentException("参数不可为空"); } return a; } 复制代码
@Bean @LoadBalanced @SentinelRestTemplate public RestTemplate restTemplate(){ return new RestTemplate(); } 复制代码
resttemplate: sentinel: enabled: 复制代码
feign: #feign整合sentinel sentinel enable: true 复制代码
自定义返回报文
package com.virgo.user.feignclient; import com.virgo.entity.TblCar; import com.virgo.user.configuration.GlobalFeignConfiguration; import com.virgo.user.feignclient.fallback.LockCenterFeignClientFallback; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; /** * @author zhaozha * @date 2019/10/11 下午12:52 */ @FeignClient(name = "lock-center", configuration = GlobalFeignConfiguration.class,fallback = LockCenterFeignClientFallback.class) public interface LockCenterFeignClient { @GetMapping("/lock/test/{id}") TblCar findById(@PathVariable(value="id") Long id); } package com.virgo.user.feignclient.fallback; import com.virgo.entity.TblCar; import com.virgo.user.feignclient.LockCenterFeignClient; import org.springframework.stereotype.Component; /** * @author zhaozha * @date 2019/10/16 下午3:36 */ @Component public class LockCenterFeignClientFallback implements LockCenterFeignClient { @Override public TblCar findById(Long id) { return TblCar.builder().level(1).build(); } } 复制代码
package com.virgo.user.feignclient; import com.virgo.entity.TblCar; import com.virgo.user.configuration.GlobalFeignConfiguration; import com.virgo.user.feignclient.fallbackFactory.LockCenterFeignClientFallbackFactory; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; /** * @author zhaozha * @date 2019/10/11 下午12:52 */ @FeignClient(name = "lock-center", configuration = GlobalFeignConfiguration.class,fallbackFactory = LockCenterFeignClientFallbackFactory.class) public interface LockCenterFeignClient { @GetMapping("/lock/test/{id}") TblCar findById(@PathVariable(value="id") Long id); } package com.virgo.user.feignclient.fallbackFactory; import com.virgo.entity.TblCar; import com.virgo.user.feignclient.LockCenterFeignClient; import feign.hystrix.FallbackFactory; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * @author zhaozha * @date 2019/10/16 下午4:02 */ @Component @Slf4j public class LockCenterFeignClientFallbackFactory implements FallbackFactory<LockCenterFeignClient> { @Override public LockCenterFeignClient create(Throwable throwable) { return new LockCenterFeignClient() { @Override public TblCar findById(Long id) { log.info("限流/降级",throwable); return TblCar.builder().level(1).build(); } }; } } 复制代码
使用方式 | 使用方式 | 使用方法 |
---|---|---|
编码方式 | API | try...catch...finally |
注解方式 | @SentinelResource | blockHandler/fallback |
RestTemplate | @SentinelRestTemplate | blockHandler/fallback |
Feign | feign.sentinel.enabled | fallback/fallbackFactory |
阿里云服务(AHAS)
官方文档
package com.virgo.user.sentinel; import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; import com.alibaba.csp.sentinel.slots.block.flow.FlowException; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException; import com.alibaba.csp.sentinel.slots.system.SystemBlockException; import com.fasterxml.jackson.databind.ObjectMapper; import com.virgo.dto.CommonResult; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author zhaozha * @date 2019/10/17 上午9:49 */ @Component public class VirgoUrlBlockHandler implements UrlBlockHandler { @Override public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException { // 统一返回对象 CommonResult commonResult = null; // 流控 if(e instanceof FlowException){ commonResult = CommonResult.builder().status(100).msg("流控异常").build(); } // 降级 else if(e instanceof DegradeException){ commonResult = CommonResult.builder().status(101).msg("降级异常").build(); } // 热点 else if(e instanceof ParamFlowException){ commonResult = CommonResult.builder().status(102).msg("热点异常").build(); } // 系统 else if(e instanceof SystemBlockException){ commonResult = CommonResult.builder().status(102).msg("系统异常").build(); } // 授权 else if (e instanceof AuthorityException){ commonResult = CommonResult.builder().status(102).msg("授权异常").build(); } httpServletResponse.setStatus(500); httpServletResponse.setCharacterEncoding("utf-8"); httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8"); httpServletResponse.setContentType("application/json;charset=utf-8"); // spring mvc自带的json操作工具,叫jackson new ObjectMapper() .writeValue( httpServletResponse.getWriter(), commonResult ); } } 复制代码
package com.virgo.user.sentinel; import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; /** * @author zhaozha * @date 2019/10/17 上午11:51 */ @Component public class MyRequestOriginParser implements RequestOriginParser { @Override public String parseOrigin(HttpServletRequest httpServletRequest) { // 从请求参数中获取名为 origin 的参数并返回 // 如果获取不到origin参数,那么就抛异常 // todo 改成header String origin = httpServletRequest.getParameter("origin"); if (StringUtils.isBlank(origin)) { throw new IllegalArgumentException("origin must be specified"); } return origin; } } 复制代码
package com.virgo.user.sentinel; import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner; import org.apache.commons.lang.math.NumberUtils; import org.springframework.stereotype.Component; import java.util.Arrays; /** * @author zhaozha * @date 2019/10/17 下午12:05 */ @Component public class VirgoUrlCleaner implements UrlCleaner { @Override public String clean(String originUrl) { // 让 /shares/1 与 /shares/2 的返回值相同 // 返回/shares/{number} // todo 多参数 String[] split = originUrl.split("/"); return Arrays.stream(split) .map(string -> { if (NumberUtils.isNumber(string)) { return "{number}"; } return string; }) .reduce((a, b) -> a + "/" + b) .orElse(""); } } 复制代码