# microservice **Repository Path**: hands0meking/microservice ## Basic Information - **Project Name**: microservice - **Description**: Spring cloud 微服务模板 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-03-04 - **Last Updated**: 2021-06-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 1 先构建父类模板 定义好使用的spring boot 的版本,spring cloud 的版本 ## 1.1 添加pom ```xml 4.0.0 org.hands0meking microservice pom 1.0-SNAPSHOT Finchley.SR4 2.0.4.RELEASE org.springframework.cloud spring-cloud-dependencies ${spring.cloud-version} pom import org.springframework.boot spring-boot-dependencies ${spring.boot-version} pom import ``` ## 1.2 添加本地映射 修改host文件 ,路径:C:\Windows\System32\drivers\etc ``` 127.0.0.1 gaoqj.erake7000.com 127.0.0.1 gaoqj.erake7001.com 127.0.0.1 gaoqj.erake7003.com ``` # 2 添加注册中心组件 eureka > Spring cloud 的官网:https://docs.spring.io/spring-cloud-netflix/docs/2.2.7.RELEASE/reference/html/ > > 参考项目: > > ``` > microservice-eureka7000 > microservice-eureka7001 > microservice-eureka7003 > ``` ## 2.1 引入依赖 ```xml org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-starter-netflix-eureka-server ``` ## 2.2 加入配置 ```yaml server: port: 7000 eureka: client: service-url: defaultZone: http://gaoqj.erake7001.com:7001/eureka/ register-with-eureka: false # 是否向注册中心注册自己,缺省:true;一般情况下,Eureka服务端是不需要再注册自己的 fetch-registry: false # 是否从Eureka获取注册信息,缺省:true;一般情况下,Eureka服务端是不需要的 server: enable-self-preservation: false # 是否允许开启自我保护模式,缺省:true;当Eureka服务器在短时间内丢失过多客户端时,自我保护模式可使服务端不再删除失去连接的客户端 peer-eureka-nodes-update-interval-ms: 10000 # Peer节点更新间隔,单位:毫秒 eviction-interval-timer-in-ms: 60000 # Eureka服务器清理无效节点的时间间隔,单位:毫秒,缺省:60000,即60秒 spring: application: name: euraka-registry # 应用名称,将会显示在Eureka界面的应用名称列 ``` ## 2.3 添加注解 ```java @SpringBootApplication @EnableEurekaServer public class Eureka7000Application { public static void main(String[] args) { SpringApplication.run(Eureka7000Application.class, args); } } ``` ## 2.4 添加第二个、第三个注册中心 注册中心不可能只有一个,必须HA,所以必须还有第二个、第三个... 这个时候,只需要修改 2.2 加入配置 的端口号,跟注册的地址就可以了 ```yaml server: port: 7001 eureka: client: service-url: defaultZone: http://gaoqj.erake7000.com:7000/eureka/ register-with-eureka: false # 是否向注册中心注册自己,缺省:true;一般情况下,Eureka服务端是不需要再注册自己的 fetch-registry: false # 是否从Eureka获取注册信息,缺省:true;一般情况下,Eureka服务端是不需要的 server: enable-self-preservation: false # 是否允许开启自我保护模式,缺省:true;当Eureka服务器在短时间内丢失过多客户端时,自我保护模式可使服务端不再删除失去连接的客户端 # peer-eureka-nodes-update-interval-ms: 10000 # Peer节点更新间隔,单位:毫秒 # eviction-interval-timer-in-ms: 60000 # Eureka服务器清理无效节点的时间间隔,单位:毫秒,缺省:60000,即60秒 spring: application: name: euraka-registry # 应用名称,将会显示在Eureka界面的应用名称列 ``` # 3 引入一个微服务,注册到注册中心 参考项目: ``` microservice-server1-8011 ``` ## 3.1 引入依赖 ```xml org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-starter-netflix-eureka-client ``` ## 3.2 加入配置 ```yaml server: port: 8011 spring: application: name: server1 # 应用名称,将会显示在Eureka界面的应用名称列 eureka: client: service-url: defaultZone: http://gaoqj.erake7000.com:7000/eureka/,http://gaoqj.erake7001.com:7001/eureka/,http://gaoqj.erake7003.com:7003/eureka/ register-with-eureka: true # 是否向注册中心注册自己,缺省:true;一般情况下,Eureka服务端是不需要再注册自己的 fetch-registry: true # 是否从Eureka获取注册信息,缺省:true;一般情况下,Eureka服务端是不需要的 ``` ## 3.3 启动类添加注解 ```java @SpringBootApplication @EnableEurekaClient public class Server18011Application { public static void main(String[] args) { SpringApplication.run(Server18011Application.class, args); } } ``` # 4 使用 RestTemplate ,并整合 Ribbon(客户端负载均衡) > Spring cloud 的官网:https://docs.spring.io/spring-cloud-netflix/docs/2.2.7.RELEASE/reference/html/ > > 参考项目: > > ``` > microservice-client1-9011 > ``` ## 4.1 添加依赖 ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-client ``` 如果是使用 spring-cloud-starter-netflix-eureka-client 的话,里面已经添加有了 Ribbon 的依赖,如下图 ![image-20210304163256217](README/images/image-20210304163256217.png) ## 4.2 配置RestTemplate ```java @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } ``` ## 4.3 使用案例 SERVER2 : 对应的微服务的 实例名称 ```java @Autowired private RestTemplate restTemplate; private String url = "http://SERVER2/getPort"; @GetMapping("getPort") public String getServerPort(){ ResponseEntity response = restTemplate.getForEntity(url, String.class); System.out.println(response); return response.getBody(); } ``` ## 4.4 默认配置 com.netflix.client.config.DefaultClientConfigImpl 默认的配置类:org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration ![image-20210305113157141](README/images/image-20210305113157141.png) 可以使用自定义配置: .ribbon.* ![image-20210305103130548](README/images/image-20210305103130548.png) ## 4.5 自定义负载规则 ### 4.5.1 创建一个配置类 > PS: > > - 1)必须是配置类 > > - 2)必须在Springboot的可扫描范围之外 > > eg > > ```java > @Configuration > public class MyIRuleConfig { > @Bean > public IRule myIRule(){ > return new RandomRule(); > } > } > ``` > > ### 4.5.2 在启动类上使用@RibbonClient 将配置扫入 ```java @RibbonClient(name = "server2",configuration = MyIRuleConfig.class) ``` name:代表是的注册到注册中心的实例名称 # 5 使用 openfeign > Spring cloud 的官网:https://docs.spring.io/spring-cloud-netflix/docs/2.2.7.RELEASE/reference/html/ > > 参考项目: > > ``` > microservice-openFein-8031 > ``` ## 5.1 引入依赖 > ```xml > > > org.springframework.cloud > spring-cloud-starter-openfeign > > ``` ## 5.2 配置类添加启动注解 @EnableFeignClients ```java @SpringBootApplication @EnableEurekaClient @EnableFeignClients public class OpenFein8031Application { public static void main(String[] args) { SpringApplication.run(OpenFein8031Application.class, args); } } ``` ## 5.3 创建服务client > @FeignClient("order") ==> 的order 是对应服务提供方在 eureka 的实例名称 > @GetMapping("/getPort") > String info(); > > ==> 跟controller 是一样的 ```java @FeignClient("order") @Component public interface OrderClient { @GetMapping("/getPort") String info(); } ``` ## 5.4 超时时间限制 默认情况下,超时的时间为 1s; ### 如果需要所有的都配置超时时间为5s,则: ```yaml ribbon: ReadTimeout: 5000 ConnectTimeout: 5000 ``` ### 如果需要针对某个配置超时时间,则使用.ribbon.*的方案 比如,配置 OrderService的实例超时时间为4s ```yaml OrderService: ribbon: ReadTimeout: 4000 ConnectTimeout: 4000 ``` ## 5.5 日志级别的配置 > https://docs.spring.io/spring-cloud-openfeign/docs/2.2.7.RELEASE/reference/html/#feign-logging ## 5.6 自动装配的 类 ``` org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\ org.springframework.cloud.openfeign.FeignAutoConfiguration,\ org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\ org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration ``` ## 5.7 每次调用,将header 也带过去 > 默认情况下,使用feign 调用的时候,header会遗漏 ### 5.7.1 创建 Interceptor ```java public class OpenFeignInterceptor implements RequestInterceptor { private final Logger logger = LoggerFactory.getLogger(OpenFeignInterceptor.class); @Override public void apply(RequestTemplate requestTemplate) { logger.info(">>>>>>>>> OpenFeignInterceptor ....,{}",requestTemplate); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder .getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); Enumeration headerNames = request.getHeaderNames(); if (headerNames != null) { while (headerNames.hasMoreElements()) { String name = headerNames.nextElement(); String values = request.getHeader(name); System.out.println("++++> "+name +" "+values); requestTemplate.header(name, values); } } Enumeration bodyNames = request.getParameterNames(); StringBuffer body =new StringBuffer(); if (bodyNames != null) { while (bodyNames.hasMoreElements()) { String name = bodyNames.nextElement(); String values = request.getParameter(name); body.append(name).append("=").append(values).append("&"); } } if(body.length()!=0) { body.deleteCharAt(body.length()-1); requestTemplate.body(body.toString()); logger.info("feign interceptor body:{}",body.toString()); } } } ``` ### 5.7.2 配置 Interceptor ```yaml feign: client: config: default: requestInterceptors: - org.handsomeking.mincroservice.openfein8031.config.OpenFeignInterceptor ``` # 6 服务降级、服务熔断 Hystrix > 参考项目: > > ``` > microservice-hystrix-8051 > ``` > > https://github.com/Netflix/Hystrix > > 服务降级: > > > > 发生原因: > > > > > > - 请求超时 > > > - 服务熔断导致的服务降级 > > > - 服务异常 > > 服务熔断: > > 服务限流: > > 配置文件:HystrixCommandProperties、HystrixPropertiesManager ## 服务降级 ### 6.1 引入依赖 ```xml org.springframework.cloud spring-cloud-starter-netflix-hystrix ``` ### 6.2 启动类引入启动注解 ```java @EnableCircuitBreaker ``` ### 6.3 controller使用 #### 6.3.1 基本使用 @RestController @RequestMapping("/hystrix") public class IndexController { ```java @RestController @RequestMapping("/hystrix") public class IndexController { @GetMapping public String normal(){ return "normal O(∩_∩)O哈哈~"; } @GetMapping("/timeout") @HystrixCommand(fallbackMethod = "timeoutFallback",commandProperties = { @HystrixProperty(name= HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS,value = "3000") }) public String timeout(){ try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } return "normal"; } @GetMapping("/error") @HystrixCommand(fallbackMethod = "errorFallback") public String error(){ System.out.println(10/0); return "normal"; } public String timeoutFallback(){ return "IndexController .... timeoutFallback o(╥﹏╥)o "; } public String errorFallback(){ return "IndexController .... errorFallback o(╥﹏╥)o"; } } ``` #### 6.3.2 类层使用全局 ```java @RestController @RequestMapping("/hystrix/default") @DefaultProperties(defaultFallback = "defaultFallBack", commandProperties = { @HystrixProperty(name= HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS,value = "3000") } ) public class UseDefaultController { @GetMapping("/test1") public String test1(){ return "ok"; } @GetMapping("/test2") @HystrixCommand public String test2(){ try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } return "使用默认的兜底方法"; } @GetMapping("/test3") @HystrixCommand(fallbackMethod = "custFallBack", commandProperties = { @HystrixProperty(name= HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_TIMEOUT_IN_MILLISECONDS,value = "6000") }) public String test3(){ try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } return "自定义的 fallback!"; } @GetMapping("/test4") @HystrixCommand public String test4(){ System.out.println(1/0); return "使用默认的兜底方法"; } public String defaultFallBack(){ return "这个是controller层的default fallback!"; } public String custFallBack(){ return "这个是controller层的自定义的 fallback!"; } } ``` ## 6.4 整合openfein 使用 ### 6.4.1 引入依赖 ```xml org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.cloud spring-cloud-starter-openfeign ``` ### 6.4.2 加入配置 ```yaml feign: hystrix: enabled: true ``` ### 6.4.3 添加client 调用微服务 ```java @Component @FeignClient(value = "SERVER2",fallback = Service2ClientFallback.class) public interface Service2Client { @GetMapping("/timeout") String timeout(); @GetMapping("/discovery") Object index(); } ``` ### 6.4.4 编写fallback 类 注意:加入 @Component 放到spring容器中 ```java @Component public class Service2ClientFallback implements Service2Client { @Override public String timeout() { return "超时啦...... o(╥﹏╥)o"; } @Override public Object index() { return "程序异常..... o(╥﹏╥)o"; } } ``` ## 熔断 > https://martinfowler.com/bliki/CircuitBreaker.html > > ![image-20210325105821792](README/images/image-20210325105821792.png) > > CircuitBreakerController >```java > @GetMapping("/timeout") > @HystrixCommand(fallbackMethod = "fallbackTimeout",commandProperties = { > @HystrixProperty(name= HystrixPropertiesManager.CIRCUIT_BREAKER_ENABLED,value = "true"),//是否打开熔断,默认true > @HystrixProperty(name= HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,value = "10000"),//窗口期,默认5s > @HystrixProperty(name= HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,value = "50"),//出问题的阈值,默认50% > @HystrixProperty(name= HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,value = "3")//请求数量阈值,默认20 > }) > public String timeout(String name){ > try { > TimeUnit.SECONDS.sleep(10); > } catch (InterruptedException e) { > e.printStackTrace(); > } > return "正常超时返回~~~"; > } >``` > 熔断的 ## hystrix-dashboard > https://docs.spring.io/spring-cloud-netflix/docs/2.2.7.RELEASE/reference/html/#circuit-breaker-hystrix-dashboard > > **只能观察被hystrix修饰的接口,即@HystrixCommand 修饰的接口** ### 1 dashboard 服务添加pom依赖 ```xml org.springframework.cloud spring-cloud-starter-netflix-hystrix-dashboard ``` ### 2 dashboard 服务配置类添加@EnableHystrixDashboard注解 ### 3 运行dashboard 服务,在浏览器上输入url > /hystrix > > ![image-20210325145218733](README/images/image-20210325145218733.png) ### 4 在被观察的服务上,添加hystrix.stream ```java @Bean public ServletRegistrationBean getServlet(){ HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean registration = new ServletRegistrationBean(streamServlet); registration.setLoadOnStartup(1); registration.addUrlMappings("/hystrix.stream"); registration.setName("hystrixMetricsStreamServlet"); return registration; } ``` ### 在dashboard上输入观察的服务 > PS: 如果页面一直loading,则先调一下降级的接口 > > 如:http://localhost:8051/hystrix.stream > > ![image-20210325145532741](README/images/image-20210325145532741.png) # 7 zuul > https://docs.spring.io/spring-cloud-netflix/docs/2.2.7.RELEASE/reference/html/#router-and-filter-zuul # 8 gateway > https://spring.io/projects/spring-cloud-gateway > > https://docs.spring.io/spring-cloud-gateway/docs/2.2.4.RELEASE/reference/html/ > > 取代zuul的理由 > > - zuul1 停更 > - zuul2 还在开发 > - gateway支持spring5 ,Spring Boot 2 , Project Reactor > - geteway 使用的底层是netty,zuul 基于阻塞IO API,servlet2.5 > - 动态路由:动态路由任何请求属性 > - 支持断言、过滤器 > - 集成hystrix > - 支持限流 > - 支持重写 ## 添加依赖 ```xml org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-gateway ``` ## 引入配置 ```yaml server: port: 9527 spring: application: name: microserve-gateway # 应用名称,将会显示在Eureka界面的应用名称列 cloud: gateway: routes: - id: microserve-server1 #只需要保证唯一即可 uri: http://localhost:8011/ predicates: - After=2021-01-20T17:42:47.789-07:00[America/Denver] - Path=/config/{port} order: 0 - id: microserve-server21 #只需要保证唯一即可 uri: http://localhost:8022 predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver] order: 3 - id: microserve-server22 #只需要保证唯一即可 uri: http://localhost:8021 predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver] order: 2 eureka: client: service-url: # defaultZone: http://gaoqj.erake7000.com:7000/eureka/,http://gaoqj.erake7001.com:7001/eureka/,http://gaoqj.erake7003.com:7003/eureka/ defaultZone: http://gaoqj.erake7000.com:7000/eureka/ register-with-eureka: true # 是否向注册中心注册自己,缺省:true;一般情况下,Eureka服务端是不需要再注册自己的 fetch-registry: true # 是否从Eureka获取注册信息,缺省:true;一般情况下,Eureka服务端是不需要的 ``` uri:代理的地址 order : 越小,越先执行,默认为0 predicates: 必填,常见的有11种,看官方文档: https://docs.spring.io/spring-cloud-gateway/docs/2.2.7.RELEASE/reference/html/#the-path-route-predicate-factory ![image-20210421114915187](README/images/image-20210421114915187.png) ## 全局自定义过滤器 > MyFilter , MyFilter2 > > 其中,@Order ,值越小,优先权越高 ## 超时配置 ```yaml spring: cloud: gateway: httpclient: connect-timeout: 11000 response-timeout: 11000 ``` ## LoadBanlance: > ```properties > spring.cloud.gateway.discovery.locator.enabled=true ##开启lb > ``` > > uri: lb://SERVER2 ==> SERVER2 示例名称 ```yaml spring: application: name: microserve-gateway # 应用名称,将会显示在Eureka界面的应用名称列 cloud: gateway: httpclient: connect-timeout: 11000 response-timeout: 11000 discovery: locator: enabled: true #开启lb routes: - id: microserve-server1 #只需要保证唯一即可 uri: lb://SERVER1 predicates: - After=2021-01-20T17:42:47.789-07:00[America/Denver] - Path=/config/* order: 0 - id: microserve-server22 #只需要保证唯一即可 uri: lb://SERVER2 predicates: - After=2017-01-20T17:42:47.789-07:00[America/Denver] - Path=/server2/* order: 2 ``` # 9 stream > https://docs.spring.io/spring-cloud-stream-binder-rabbit/docs/3.0.10.RELEASE/reference/html/spring-cloud-stream-binder-rabbit.html > > # 10 nacos > 文档:https://nacos.io/zh-cn/docs/what-is-nacos.html > > springcloud alibaba : https://spring.io/projects/spring-cloud-alibaba > > 1.3.1下载路径 https://java-share.cn:8443/document/nacos/nacos-server-1.3.1.tar.gz > > ![image-20210604101955832](README/images/image-20210604101955832.png) ## 父pom加入总依赖 > ```xml > > com.alibaba.cloud > spring-cloud-alibaba-dependencies > 2.0.4.RELEASE > pom > import > > ``` ## 启动nacos服务 ## 服务注册 ### 加入依赖 > ```xml > > com.alibaba.cloud > spring-cloud-starter-alibaba-nacos-discovery > > ``` ### 配置服务中心 >```yaml >server: > port: 8080 > > >spring: > application: > name: nacos-client-80 > cloud: > nacos: > discovery: > server-addr: 192.168.234.200:8848 > >``` ### 启动应用后,可以看到服务列表 > ![image-20210604093656551](README/images/image-20210604093656551.png) ### 更换namespace > 修改配置文件,添加namespaceid即可 > > ```yaml > server: > port: 10102 > spring: > application: > name: nacos-config-10102 > profiles: > active: dev,db > cloud: > nacos: > discovery: > server-addr: 192.168.234.200:8848 > namespace: 72e5f92c-01d1-4748-9f90-2b1ea9592029 > ``` > > ## 配置中心 ### 加入依赖 > ```xml > > com.alibaba.cloud > spring-cloud-starter-alibaba-nacos-discovery > > > com.alibaba.cloud > spring-cloud-starter-alibaba-nacos-config > > ``` ### 加入配置 > 创建 bootstrap.yml 文件 > > ```yaml > server: > port: 10102 > spring: > application: > name: nacos-config-10102 > profiles: > active: dev,db > cloud: > nacos: > discovery: > server-addr: 192.168.234.200:8848 > config: > server-addr: 192.168.234.200:8848 > file-extension: yaml > > ``` > > 根据nacos的dataId的命名规则 > > ![image-20210604103301214](README/images/image-20210604103301214.png) > > 会读取2个配置中心的文件 > > - nacos-config-10102-dev.yaml > - nacos-config-10102-db.yaml > > 所以创建dataId如下: > > ![image-20210604103349103](README/images/image-20210604103349103.png) ### 更换namespace > namespace_id: 72e5f92c-01d1-4748-9f90-2b1ea9592029 > > ![image-20210604104111852](README/images/image-20210604104111852.png) > > 修改配置,添加 namespace id > > > ```yaml > > server: > > port: 10102 > > spring: > > application: > > name: nacos-config-10102 > > profiles: > > active: dev,db > > cloud: > > nacos: > > discovery: > > server-addr: 192.168.234.200:8848 > > config: > > server-addr: 192.168.234.200:8848 > > file-extension: yaml > > namespace: 72e5f92c-01d1-4748-9f90-2b1ea9592029 > > ``` ## 配置持久化 > ![image-20210604110113674](README/images/image-20210604110113674.png) ## 配置集群 > https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html > > ![image-20210604175801862](README/images/image-20210604175801862.png) ### 数据库统一持久化 > 将集群配置到同一个高可用的db中 ### 配置nacos的启动配置文件 > 在nacos的解压目录nacos/的conf目录下,有配置文件cluster.conf,请每行配置成ip:port。(请配置3个或3个以上节点) > > ```plain > # ip:port > 192.168.234.200:8801 > 192.168.234.200:8802 > 192.168.234.200:8803 > ``` > > 注意:这个ip不能是localhost/127.0.0.1 ### 配置VIP,nginx > 部分nginx配置 > > ``` > http { > include /usr/local/sbin/conf/mime.types; > default_type application/octet-stream; > sendfile on; > keepalive_timeout 65; > > upstream nacos { > server 192.168.234.200:8801; > server 192.168.234.200:8802; > server 192.168.234.200:8803; > } > > server { > listen 80; > server_name 192.168.234.200; > > location /king { > root /; > autoindex on; > } > location / { > proxy_pass http://nacos; > } > > error_page 500 502 503 504 /50x.html; > location = /50x.html { > root html; > } > } > > ``` ### 启动集群后,登录nacos,看到集群节点 > ![image-20210607100721361](README/images/image-20210607100721361.png) # 11 alibaba - sentinel > https://github.com/alibaba/Sentinel/ > > 下载地址:https://github.com/alibaba/Sentinel/releases > > https://java-share.cn:8443/document/alibaba_sentinel/sentinel-dashboard-1.8.0.jar ## 是什么 > 引用官网的说法: > > ## Sentinel 是什么? > > 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 > > Sentinel 具有以下特征: > > - **丰富的应用场景**:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。 > - **完备的实时监控**:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。 > - **广泛的开源生态**:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。 > - **完善的 SPI 扩展点**:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。 > > Sentinel 的主要特性: > > ![image-20210609110246457](README/images/image-20210609110246457.png) ## 加入依赖 ```xml com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.csp sentinel-datasource-nacos com.alibaba.cloud spring-cloud-starter-alibaba-sentinel ``` ## 添加配置 > ```yaml > spring: > application: > name: nacos-sentinel > cloud: > nacos: > discovery: > server-addr: 192.168.234.200:80 # 集群 > sentinel: > transport: > dashboard: 192.168.234.200:8080 > ``` ## sentinel 的使用 > https://sentinelguard.io/zh-cn/ > > https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8 ![image-20210609114637545](README/images/image-20210609114637545.png) ### 流控 > https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6 ![image-20210615161824053](README/images/image-20210615161824053.png) #### 资源名 > 接口的uri > > 不包含 server.servlet.context-path 配置的上下文前缀 #### 针对来源 > 流控规则中的 `limitApp` 字段用于根据调用来源进行流量控制。该字段的值有以下三种选项,分别对应不同的场景: > > - `default`:表示不区分调用者,来自任何调用者的请求都将进行限流统计。如果这个资源名的调用总和超过了这条规则定义的阈值,则触发限流。 > - `{some_origin_name}`:表示针对特定的调用者,只有来自这个调用者的请求才会进行流量控制。例如 `NodeA` 配置了一条针对调用者`caller1`的规则,那么当且仅当来自 `caller1` 对 `NodeA` 的请求才会触发流量控制。 > - `other`:表示针对除 `{some_origin_name}` 以外的其余调用方的流量进行流量控制。例如,资源`NodeA`配置了一条针对调用者 `caller1` 的限流规则,同时又配置了一条调用者为 `other` 的规则,那么任意来自非 `caller1` 对 `NodeA` 的调用,都不能超过 `other` 这条规则定义的阈值。 > > 同一个资源名可以配置多条规则,规则的生效顺序为:**{some_origin_name} > other > default** #### 阈值类型 > 流量控制主要有两种统计类型,一种是统计并发线程数,另外一种则是统计 QPS > > 并发数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。**并发数控制通常在调用端进行配置。** #### 单机阈值 > 一台机器的流控阈值 #### 流控模式 > 直接:流控是通过改资源进行流控 > > 关联:流控能力是根据另一个资源进行流控 #### 流控效果 > **直接拒绝**(`RuleConstant.CONTROL_BEHAVIOR_DEFAULT`)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出`FlowException`。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。具体的例子参见 [FlowQpsDemo](https://github.com/alibaba/Sentinel/blob/master/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/flow/FlowQpsDemo.java)。 > > Warm Up(`RuleConstant.CONTROL_BEHAVIOR_WARM_UP`)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。详细文档可以参考 [流量控制 - Warm Up 文档](https://github.com/alibaba/Sentinel/wiki/限流---冷启动),具体的例子可以参见 [WarmUpFlowDemo](https://github.com/alibaba/Sentinel/blob/master/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/flow/WarmUpFlowDemo.java)。 > > 通常冷启动的过程系统允许通过的 QPS 曲线如下图所示: > > ![image](README/images/68292392-b5b0aa00-00c6-11ea-86e1-ecacff8aab51.png) > > #### 匀速排队 > > 匀速排队(`RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER`)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。详细文档可以参考 [流量控制 - 匀速器模式](https://github.com/alibaba/Sentinel/wiki/流量控制-匀速排队模式),具体的例子可以参见 [PaceFlowDemo](https://github.com/alibaba/Sentinel/blob/master/sentinel-demo/sentinel-demo-basic/src/main/java/com/alibaba/csp/sentinel/demo/flow/PaceFlowDemo.java)。 > > 该方式的作用如下图所示: > > ![image](README/images/68292442-d4af3c00-00c6-11ea-8251-d0977366d9b4.png) > > 这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。 > > > 注意:匀速排队模式暂时不支持 QPS > 1000 的场景。 ### 降级 > ![image-20210615165146608](README/images/image-20210615165146608.png) ### 热点 ### 授权 ## 整合fein使用 ### 添加fein依赖 > ```pom > > org.springframework.cloud > spring-cloud-starter-openfeign > > ``` ## sentinel总体监控 > 版本为:2.X的方案 ### 加入pom > ```xml > > org.springframework.boot > spring-boot-starter-actuator > > ``` ### 加入配置 > ```yaml > management: > endpoints: > web: > exposure: > include: "*" > ``` ### 访问地址 > localhost:8084/actuator/sentinel > > ![image-20210616104705979](README/images/image-20210616104705979.png) ## 注解的支持 @SentinelResource ### 加入依赖 > ```xml > > com.alibaba.csp > sentinel-annotation-aspectj > x.y.z > > ``` ### 注解解析 > **注意:注解方式埋点不支持 private 方法。** > > `@SentinelResource` 用于定义资源,并提供可选的异常处理和 fallback 配置项。 `@SentinelResource` 注解包含以下属性: > > - `value`:资源名称,必需项(不能为空) > - `entryType`:entry 类型,可选项(默认为 `EntryType.OUT`) > - `blockHandler` / `blockHandlerClass`: `blockHandler` 对应处理 `BlockException` 的函数名称,可选项。blockHandler 函数访问范围需要是 `public`,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 `BlockException`。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 `blockHandlerClass` 为对应的类的 `Class` 对象,注意对应的函数必需为 static 函数,否则无法解析。 > - defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求: > - 返回值类型必须与原函数返回值类型一致; > - 方法参数列表需要为空,或者可以额外多一个 `Throwable` 类型的参数用于接收对应的异常。 > - defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 `fallbackClass` 为对应的类的 `Class` 对象,注意对应的函数必需为 static 函数,否则无法解析。 > - `exceptionsToIgnore`(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。 > > 1.8.0 版本开始,`defaultFallback` 支持在类级别进行配置。 > > > 注:1.6.0 之前的版本 fallback 函数只针对降级异常(`DegradeException`)进行处理,**不能针对业务异常进行处理**。 > > 特别地,若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 `BlockException` 时只会进入 `blockHandler` 处理逻辑。若未配置 `blockHandler`、`fallback` 和 `defaultFallback`,则被限流降级时会将 `BlockException` **直接抛出**(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 `UndeclaredThrowableException`)。 > > 示例: > > ``` > public class TestService { > > // 原函数 > @SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback") > public String hello(long s) { > return String.format("Hello at %d", s); > } > > // Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数. > public String helloFallback(long s) { > return String.format("Halooooo %d", s); > } > > // Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致. > public String exceptionHandler(long s, BlockException ex) { > // Do some log here. > ex.printStackTrace(); > return "Oops, error occurred at " + s; > } > > // 这里单独演示 blockHandlerClass 的配置. > // 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 public static 函数. > @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class}) > public void test() { > System.out.println("Test"); > } > } > ``` > > 从 1.4.0 版本开始,注解方式定义资源支持自动统计业务异常,无需手动调用 `Tracer.trace(ex)` 来记录业务异常。Sentinel 1.4.0 以前的版本需要自行调用 `Tracer.trace(ex)` 来记录业务异常。 ## 问题 ### 1 应用启动后,配置就没有了 > 可以再配置文件上初始化 > > ``` > 一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果: > > resource:资源名,即限流规则的作用对象 > count: 限流阈值 > grade: 限流阈值类型(QPS 或并发线程数) > limitApp: 流控针对的调用来源,若为 default 则不区分调用来源 > strategy: 调用关系限流策略 > controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队) > ``` > > microservice-nacos-sentinal-10105 => SentinelConfig ### 2 限流时,全局fackback跟局部变量没有