zoukankan      html  css  js  c++  java
  • Sentinel流控与熔断

    参考: https://thinkwon.blog.csdn.net/article/details/103770879

    项目结构

    com.guo     
    ├── guo-sentinel             // sentinel限流熔断学习
    │       └── guo-sentinel-base                         // [9204]消费端,限流、熔断在这里体现
    │       └── guo-sentinel-provider                     // [9205]接口提供端

    安装Sentinel控制台

    Sentinel控制台是一个轻量级的控制台应用,它可用于实时查看单机资源监控及集群资源汇总,并提供了一系列的规则管理功能,如流控规则、降级规则、热点规则等。

    • 下载</br> 可以从https://github.com/alibaba/Sentinel/releases下载sentinel-dashboard-$version.jar包。我这里下载的是 sentinel-dashboard-1.8.0.jar 版本。可以从百度云盘下载 提取码:0708

    • 启动控制台

      java -Dserver.port=8718 -Dcsp.sentinel.dashboard.server=localhost:8718 -Dproject.name=sentinel-dashboard -Dcsp.sentinel.api.port=8719 -jar D:gyinstall微服务sentinel-dashboard-1.8.0.jar

      其中-Dserver.port=8718用于指定Sentinel控制台端口为8718,D:gyinstall微服务sentinel-dashboard-1.8.0.jar为下载的包路径地址。

    • 打开控制台</br> Sentinel提供了一个可视化的操作平台,安装好之后,在浏览器中输入(http://localhost:8718 (opens new window))就可以访问了,默认的用户名和密码都是sentinel(我使用的是1.8.0版本)

    sentinel控制台

     

    限流

    添加依赖

    <!-- 在guo-sentinel导入依赖 -->
    <dependencyManagement>
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>${spring-cloud.alibaba}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    </dependencyManagement>
    <!-- 在guo-sentinel-base添加依赖 -->
    <dependencies>
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>${spring-cloud.alibaba}</version>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    </dependencies>

    添加bootstrap.yml配置

    server:
    port: 9100
    address: localhost
     
     
    spring:
    application:
      name: guo-sentinel-base
    cloud:
      sentinel:
        transport:
          dashboard: localhost:8718   #sentinel控制台的请求地址

    按资源名称限流

    • 在guo-sentinel-base编写需要限流的资源接口


    import java.util.HashMap;
    import java.util.Map;

    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;

    import com.alibaba.csp.sentinel.annotation.SentinelResource;
    import com.alibaba.csp.sentinel.slots.block.BlockException;
    import com.guo.sentinel.handle.CustomBlockHandler;

    import lombok.extern.slf4j.Slf4j;

    @Slf4j
    @RestController
    @RequestMapping("/rateLimit")
    public class RateLimitController {

       /**
        * 按资源名称限流,需要指定限流处理逻辑
        *
        * @return
        */
       @GetMapping("/byResource")
       @SentinelResource(value = "byResource", blockHandler = "handleException")
       public Map<String,Object> byResource() {
      log.info("/rateLimit/byResource");
           Map<String,Object> result = new HashMap<>();
           result.put("name","按资源名称限流");
           result.put("code",200);
           return result ;
      }

       /**
        * 按url限流,有默认的限流处理逻辑
        *
        * @return
        */
       @GetMapping("byUrl")
       @SentinelResource(value = "byUrl", blockHandler = "handleException")
       public Map<String,Object> byUrl() {
      log.info("/rateLimit/byResource");
           Map<String,Object> result = new HashMap<>();
           result.put("name","按url限流");
           result.put("code",200);
           return result ;
      }

       public Map<String,Object> handleException(BlockException exception) {
           Map<String,Object> result = new HashMap<>();
           result.put("name",exception.getClass().getCanonicalName());
           result.put("code",200);
           return result ;
      }

       @GetMapping("/customBlockHandler")
       @SentinelResource(value = "customBlockHandler", blockHandler = "handleException", blockHandlerClass = CustomBlockHandler.class)
       public Map<String,Object> blockHandler() {
           Map<String,Object> result = new HashMap<>();
           result.put("name","限流成功");
           result.put("code",200);
           return result ;
      }


    }
    • 在项目guo-sentinel-provider编写远程测试接口

    controller


    import java.util.List;
    import java.util.Map;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;

    import com.guo.sentinel.service.UserService;

    @RestController
    @RequestMapping("/user")
    public class UserController {

       @Autowired
       private UserService userService;

       @PostMapping("/insert")
       public Map<String,Object> insert(@RequestBody Map<String,Object> user) {
           return userService.insert(user);
      }

       @GetMapping("/{id}")
       public Map<String,Object> getUser(@PathVariable Long id) {
           return userService.getUser(id);
      }

       @GetMapping("/listUsersByIds")
       public Map<String,Object> listUsersByIds(@RequestParam List<Long> ids) {
           return userService.listUsersByIds(ids);
      }

       @GetMapping("/getByUsername")
       public Map<String,Object> getByUsername(@RequestParam String username) {
           return userService.getByUsername(username);
      }

       @PostMapping("/update")
       public Map<String,Object> update(@RequestBody Map<String,Object> user) {
           return userService.update(user);
      }

       @PostMapping("/delete/{id}")
       public Map<String,Object> delete(@PathVariable Long id) {
           return userService.delete(id);
      }

    }

    service

    import java.util.List;
    import java.util.Map;

    public interface UserService {

       Map<String,Object> insert(Map<String,Object> user);

       Map<String,Object> getUser(Long id);

       Map<String,Object> listUsersByIds(List<Long> ids);

       Map<String,Object> getByUsername(String username);

       Map<String,Object> update(Map<String,Object> user);

       Map<String,Object> delete(Long id);

    }

    service.impl

    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;

    import org.springframework.stereotype.Service;

    import com.guo.sentinel.service.UserService;

    import lombok.extern.slf4j.Slf4j;

    @Slf4j
    @Service
    public class UserServiceImpl implements UserService {

    @Override
    public Map<String, Object> insert(Map<String, Object> user) {
    log.info("新增:{}", user);
    return null;
    }

    @Override
    public Map<String, Object> getUser(Long id) {
    log.info("获取:{}", id);
    if(Long.valueOf("1").equals(id)) {
               throw new RuntimeException("remote func is fail");
          }

           Map<String,Object> result = new HashMap<>();
           result.put("reqData",id);
           result.put("code","200");

           return result ;
    }

    @Override
    public Map<String, Object> listUsersByIds(List<Long> ids) {
    log.info("获取集合:{}", ids);
    return null;
    }

    @Override
    public Map<String, Object> getByUsername(String username) {
    log.info("根据用户名获取:{}", username);
    if(1==1) throw new RuntimeException("模拟异常");
    return null;
    }

    @Override
    public Map<String, Object> update(Map<String, Object> user) {
    log.info("更新:{}", user);
    return null;
    }

    @Override
    public Map<String, Object> delete(Long id) {
    log.info("删除:{}", id);
    return null;
    }

    }
    • 在nacos配置guo-sentinel-base-dev.yml中添加远程调用接口地址

    service-url:
    user-service: http://localhost:9205

     

    • 在sentinel控制台给资源添加限流规则 image-20210709175218305

    资源名称必须与流量控制定义的资源名称一致

    image-20210709175308824

     

     

    • 快速访问接口发现资源被限流, 设置1QPS,连续访问2次以上发现,远程服务并未被调用,直接走了限流逻辑 image-20210709175337627

    ##按url限流

    • 在sentinel控制台给资源添加限流规则

    image-20210709175359003

    资源名称必须与流量控制定义的资源访问全路径一致

    image-20210709175447627

     

     

    自定义限流逻辑

    用blockHandlerClass = CustomBlockHandler.class指定限流逻辑
    import com.alibaba.csp.sentinel.slots.block.BlockException;

    import java.util.HashMap;
    import java.util.Map;

    public class CustomBlockHandler {

       public static Map<String,Object> handleException(BlockException exception) {

           Map<String,Object> result = new HashMap<>();
           result.put("name","自定义限流信息");
           result.put("code",200);
           return result ;
      }

    }

    熔断

    • 在guo-sentinel-base编写需要熔断的资源接口


    import com.alibaba.csp.sentinel.annotation.SentinelResource;

    import lombok.extern.slf4j.Slf4j;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;

    import java.util.HashMap;
    import java.util.Map;


    @Slf4j
    @RestController
    @RequestMapping("/breaker")
    public class CircleBreakerController {

       private static final Logger LOGGER = LoggerFactory.getLogger(CircleBreakerController.class);

       @Autowired
       private RestTemplate restTemplate;

       @Value("${service-url.user-service}")
       private String userServiceUrl;

       @GetMapping("/fallback/{id}")
        @SentinelResource(value = "fallback", fallback = "handleFallback")
       public Map<String,Object> fallback(@PathVariable Long id) {
           Map<String,Object> forObject = restTemplate.getForObject(userServiceUrl + "/user/{1}", Map.class, id);
           System.out.println(forObject);
           return forObject;
      }

       @GetMapping("/fallbackException/{id}")
       @SentinelResource(value = "fallbackException", fallback = "handleFallback2", exceptionsToIgnore = {NullPointerException.class})
       public Map<String,Object> fallbackException(@PathVariable Long id) {
      log.info("/breaker/fallbackException");
           if (id == 1) {
               throw new IndexOutOfBoundsException();
          } else if (id == 2) {
               throw new NullPointerException();
          }

           return restTemplate.getForObject(userServiceUrl + "/user/{1}", Map.class, id);
      }

       public Map<String,Object> handleFallback(Long id) {
           Map<String,Object> result = new HashMap<>();
           result.put("name","服务熔断");
           result.put("code",200);
           return result ;
      }

       public Map<String,Object> handleFallback2(Long id, Throwable e) {
           LOGGER.error("handleFallback2 id:{},throwable class:{}", id, e.getClass());
           Map<String,Object> result = new HashMap<>();
           result.put("name","服务熔断");
           result.put("code",200);

           return result ;
      }
    }

    在feign接口上的限流与熔断

    guo-sentinel-base添加feign接口相关依赖

    <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>

    guo-sentinel-base中nacos添加配置项

    # 打开sentinel对feign的支持
    feign:
    sentinel:
      enabled: true

    在应用启动类上添加启用feign的注解@EnableFeignClients

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;

    @EnableFeignClients
    @EnableDiscoveryClient
    @SpringBootApplication
    public class SentinelBaseApplication {

       public static void main(String[] args) {
           SpringApplication.run(SentinelBaseApplication.class, args);
      }
    }

    创建feign接口

    controller


    import java.util.List;
    import java.util.Map;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;

    import com.guo.sentinel.service.UserService;

    @RestController
    @RequestMapping("/user")
    public class UserFeignController {

       @Autowired
       private UserService userService;

       @PostMapping("/insert")
       public Map<String,Object> insert(@RequestBody Map<String,Object> user) {
           return userService.insert(user);
      }

       @GetMapping("/{id}")
       public Map<String,Object> getUser(@PathVariable Long id) {
           return userService.getUser(id);
      }

       @GetMapping("/listUsersByIds")
       public Map<String,Object> listUsersByIds(@RequestParam List<Long> ids) {
           return userService.listUsersByIds(ids);
      }

       @GetMapping("/getByUsername")
       public Map<String,Object> getByUsername(@RequestParam String username) {
           return userService.getByUsername(username);
      }

       @PostMapping("/update")
       public Map<String,Object> update(@RequestBody Map<String,Object> user) {
           return userService.update(user);
      }

       @PostMapping("/delete/{id}")
       public Map<String,Object> delete(@PathVariable Long id) {
           return userService.delete(id);
      }

    }

    service

    import java.util.List;
    import java.util.Map;

    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestParam;

    import com.guo.sentinel.service.impl.UserFallbackService;

    // 注意value = "guo-sentinel-provider"值与服务提供方的服务注册名一致
    @FeignClient(value = "guo-sentinel-provider", fallback = UserFallbackService.class)
    public interface UserService {

       @PostMapping("/user/insert")
       Map<String,Object> insert(@RequestBody Map<String,Object> user);

       @GetMapping("/user/{id}")
       Map<String,Object> getUser(@PathVariable(value = "id") Long id);

       @GetMapping("/user/listUsersByIds")
       Map<String,Object> listUsersByIds(@RequestParam(value = "ids") List<Long> ids);

       @GetMapping("/user/getByUsername")
       Map<String,Object> getByUsername(@RequestParam(value = "username") String username);

       @PostMapping("/user/update")
       Map<String,Object> update(@RequestBody Map<String,Object> user);

       @PostMapping("/user/delete/{id}")
       Map<String,Object> delete(@PathVariable(value = "id") Long id);

    }

    fallback熔断逻辑UserFallbackService.java


    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;

    import org.springframework.stereotype.Service;

    import com.guo.sentinel.service.UserService;

    @Service
    public class UserFallbackService implements UserService {

    @Override
    public Map<String, Object> insert(Map<String, Object> user) {
    return this.installFallBack();
    }

    @Override
    public Map<String, Object> getUser(Long id) {
    return this.installFallBack();
    }

    @Override
    public Map<String, Object> listUsersByIds(List<Long> ids) {
    return this.installFallBack();
    }

    @Override
    public Map<String, Object> getByUsername(String username) {
    return this.installFallBack();
    }

    @Override
    public Map<String, Object> update(Map<String, Object> user) {
    return this.installFallBack();
    }

    @Override
    public Map<String, Object> delete(Long id) {
    return this.installFallBack();
    }

    private Map<String, Object> installFallBack() {
    Map<String, Object> r = new HashMap<>();
    r.put("code", 500);
    r.put("msg", "调用失败,服务被降级");
    return r;
    }

    }

    在sentinel控制台配置熔断逻辑

    image-20210712093022290

     

    统一限流处理

    参考: https://zhuanlan.zhihu.com/p/150058613

     

    使用nacos存储限流、熔断规则

    默认情况下,当我们在Sentinel控制台中配置规则时,控制台推送规则方式是通过API将规则推送至客户端并直接更新到内存中。一旦我们重启应用,规则将消失。下面我们介绍下如何将配置规则进行持久化,以存储到Nacos为例。

    先在guo-sentinel-base pom.xml中添加相关依赖:

    <dependency>
       <groupId>com.alibaba.csp</groupId>
       <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>

    修改bootstrap.yml添加规则配置

    server:
    port: 9204
     
    spring:
    application:
      name: guo-sentinel-base
    profiles:
      active: dev    
    cloud:
      sentinel:
        transport:
          # 配置Sentinel dashborad地址
          dashboard: http://localhost:8718
          port: 8719
        # 添加Nacos数据源配置
        datasource:
          - nacos:
              server-addr: localhost:8848
              # guo-sentinel-base-flow-rules.json
              data-id: ${spring.application.name}-flow-rules
              group-id: DEFAULT_GROUP
              data-type: json
              rule-type: flow
      nacos:
        discovery:
          server-addr: localhost:8848
        config:
          server-addr: localhost:8848
          file-extension: yml
          ext-config:
          # guo-sentinel-base-dev.yml
            - data-id: ${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
              group: DEFAULT_GROUP
              refresh: true
               

    在nacos中新建配置guo-sentinel-base-flow-rules.json

    [
      {
           "resource": "/rateLimit/byUrl",
           "limitApp": "default",
           "grade": 1,
           "count": 1,
           "strategy": 0,
           "controlBehavior": 0,
           "clusterMode": false
      }
    ]

    限流相关参数解释:

    • resource:资源名称;

    • limitApp:来源应用;

    • grade:阈值类型,0表示线程数,1表示QPS;

    • count:单机阈值;

    • strategy:流控模式,0表示直接,1表示关联,2表示链路;

    • controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;

    • clusterMode:是否集群。

    熔断相关参数解释

    • resource:资源名,即规则的作用对象

    • grade:熔断策略,支持慢调用比例/异常比例/异常数策略,默认为 慢调用比例

    • count:慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值

    • timeWindow:熔断时长,单位为 s

    • minRequestAmount:熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入),默认为5

    • statIntervalMs:统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入),默认为1000ms

    • slowRatioThreshold:慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)

     

    代码地址

    https://gitee.com/laiba_yun/test20210702

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    导入excel表格的数据--->到mysql中
    git 恢复丢失的文件-- 不提交入口文件
    putty 中使用git
    mysql 登录中用户管理
    putty 窗口管理
    navicat导出表结构-->导入powerdesigner
    mysql导出数据表结构,必须退出mysql命令.重新使用msyqldump命令
    服务器上死活不执行新增的代码-----你需要清除程序缓存了
    把json数据 [ { } ] 转为数组
    从尾到头打印链表
  • 原文地址:https://www.cnblogs.com/yun965861480/p/15000693.html
Copyright © 2011-2022 走看看