前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Cloud(三)负载均衡,服务熔断,服务降级,服务限流

Spring Cloud(三)负载均衡,服务熔断,服务降级,服务限流

作者头像
HcodeBlogger
发布2020-07-14 11:03:30
1.8K0
发布2020-07-14 11:03:30
举报
文章被收录于专栏:Hcode网站Hcode网站

Ribbon实现负载均衡

负载均衡,英文名称为Load Balance,其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器等,从而协同完成工作任务。

  1. 导入pom依赖

单独导入

代码语言:javascript
复制
COPY  <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>2.2.1.RELEASE</version>
  </dependency>

整合依赖spring-cloud-starter-netflix-eureka-client里面也包含了ribbon的依赖

代码语言:javascript
复制
COPY  <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.2.1.RELEASE</version>
  </dependency>
  1. 在主启动类所在包的上一级创建一个包,用来创建自定义负载均衡策略类
在这里插入图片描述
在这里插入图片描述

书写自定义负载均衡策略类,先继承AbstractLoadBalancerRule抽象类,该类是IRule接口的一个抽象实现类,继承它,然后自定义自己的负载均衡策略,注入spring容器即可覆盖原有的轮询策略。

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
COPYpublic class HcodeRule extends AbstractLoadBalancerRule {
    //每个服务,访问5次,然后循环所有服务
    private AtomicInteger total = new AtomicInteger(); //服务被调用的次数
    private AtomicInteger currentIndex = new AtomicInteger(); //当前被调用服务的下标
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        Server server = null;

        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            //获得活着的服务
            List<Server> upList = lb.getReachableServers();
            //获得全部服务
            List<Server> allList = lb.getAllServers();

            int serverCount = allList.size();
            if (serverCount == ) {

                return null;
            }
//            //生成区间随机数
//            int index = chooseRandomInt(serverCount);
//            server = upList.get(index); //从或者的服务中随机获取一个
            /*============================================*/
            if(total.get()<){
               server = upList.get(currentIndex.get());
                getAndIncrement(total);
            }else{
                total.set();
                getAndIncrement(currentIndex);
                if(currentIndex.get()>=upList.size()){
                    currentIndex.set();
                }
                server = upList.get(currentIndex.get());
                getAndIncrement(total);
            }

            /*==============================================*/
            if (server == null) {
                Thread.yield();
                continue;
            }

            if (server.isAlive()) {
                return (server);
            }

            // Shouldn't actually happen.. but must be transient or a bug.
            server = null;
            Thread.yield();
        }

        return server;

    }
    //自旋锁防止高并发情况数据不一致性
    private final int getAndIncrement(AtomicInteger atomicInteger){
        int current;
        int next;
        do {
            current = atomicInteger.get();
            next = current >=  ?  : current + ;
        }while (!atomicInteger.compareAndSet(current,next));  //第一个参数是期望值,第二个参数是修改值是
        return next;
    }

    protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
    }
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        // TODO Auto-generated method stub

    }
    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }


}

再将该策略类注入IOC容器,当然如果不想写自定义策略类,那就使用原有的IRule的实现类,将其注入IOC容器即可.下面两种二选一

代码语言:javascript
复制
COPY@Configuration
public class MyRule {

    @Bean  //使用自定义的
    public IRule HcodeRule(){
        return new HcodeRule();
    }
    //============================================================
    //AvailabilityFilteringRule : 先过滤掉出现故障的服务器,对剩下的服务进行轮询
    //RoundRobinRule 轮询 默认设置
    //RandomRule 随机
    //WeightedResponseTimeRule 权重
    //RetryRule:先按照轮询获取,若获取服务失败,则在指定时间内进行重试

    //随机访问,只要将实现类注入到ICO容器,即可覆盖
    @Bean //使用原有的实现类
    public IRule myRule(){
        return new RandomRule();
    }

}
  1. 主启动类上加上注解,让消费端服务能去使用自定义的负载均衡策略
代码语言:javascript
复制
COPY@SpringBootApplication
@EnableEurekaClient
//在微服务启动的时候就能去加载我们自定义的负载均衡Ribbon类,name为微服务提供者注册到注册中心的服务名
@RibbonClient(name="SPRINGCLOUD-PROVIDER-DEPT",configuration = MyRule.class)
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class,args);
    }
}

相关概念理解

  1. 服务熔断:当某个服务提供者出现问题(出现运行时无法处理的异常或者某些操作时间过长),卡死了,不能让用户一直等待,需要调用备用的响应方法向调用方返回特定响应,防止服务链路某一微服务模块卡死,导致整体服务出现雪崩。(发生熔断一般是某个微服务模块,也就是微服务提供者)
  2. 服务降级:服务降级一般也是有多种情况,访问量过大,微服务出现异常,或者微服务响应超时等等,一般服务降级触发熔断都在消费者端设置,当某个服务不可用时,直接返回备用响应。
  3. 服务限流:也是服务降级的一种方式,限流,比如秒杀场景,不能访问用户瞬间都访问服务器,限制一次只可以有多少请求。

Hystrix 实现服务熔断和服务降级

  1. 微服务提供者导入pom依赖

单独导入依赖 org.springframework.cloud:spring-cloud-starter-hystrix:2.2.1.RELEASE

代码语言:javascript
复制
COPY<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.1.RELEASE</version>
</dependency>

使用eureka整合客户端pom就不用再导入单独的hystrix

代码语言:javascript
复制
COPY        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
  1. 微服务提供者的controller层
代码语言:javascript
复制
COPY@RestController
@DefaultProperties(defaultFallback = "Global_FallbackMethod") //没有配置专属熔断器方法,就走全局熔断器
public class TestController {

    @HystrixCommand //没加特定方法,走全局
    @PostMapping("/global")
    public String test1(){
        return "(●'◡'●)全局熔断器方法未触发";
    }

    //全局响应Fallback方法 服务降级触发熔断
    public Object Global_FallbackMethod(){
        return "┭┮﹏┭┮全局熔断器方法触发了,呜呜呜!";
    }



    //断路器
    @HystrixCommand( fallbackMethod = "CircuitBreaker_fallback" , commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),// 是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),// 请求次数
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), //时间窗口期,失败后经过多久尝试恢复
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60")  //失败率到达多少后跳闸
    })
    @GetMapping("/local")
    public String test2(){
        return "(●'◡'●)特定断路器方法未触发~";
    }

    public String CircuitBreaker_fallback(){ //参数可以跟原接口方法参数一致
        return "┭┮﹏┭┮特定限制方法熔断器触发了,呜呜呜~";
    }
}

3. 微服务提供者的主启动类加上开启断路器的注解

代码语言:javascript
复制
COPY@EnableCircuitBreaker //添加对服务熔断的支持
  1. 客户端导入的所需依赖跟提供者差不多,使用openfeign进行服务调用通信。
代码语言:javascript
复制
COPY<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

然后配置yml,让feign支持hystrix

代码语言:javascript
复制
COPYfeign:
  hystrix:
    enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。

然后再服务层接口加上注解。

代码语言:javascript
复制
COPY@FeignClient(value = "hystrix-test-provider",fallback = DeptFallbackService.class) //服务名,备用的响应类
@Component
public interface testService {

    @GetMapping("/global")
    public String test1();
}

兜底用的备用响应类

代码语言:javascript
复制
COPY@Component // 服务降级
public class DeptFallbackService implements testService{
    public String test1() {
        return "┭┮﹏┭┮该服务被降级了,目前无法使用";
    }
}

最后,主启动类添加@EnableHystrix

代码语言:javascript
复制
COPY@EnableHystrix

Sentinel实现服务熔断,降级,限流

老样子,hystrix也是停止更新了,所以换新的才是王道~况且阿里巴巴的sentinel就是继承hystrix,两者挺像的。 - 官网下载:https://github.com/alibaba/Sentinel/releases - 特性:

在这里插入图片描述
在这里插入图片描述

- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。 - 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

下载好jar包sentinel-dashboard-1.7.0.jar后,直接命令行运行即可~ 注意:该boot服务默认为8080端口号。启动后,访问http://localhost:8080,账号密码都是sentinel。

在这里插入图片描述
在这里插入图片描述
服务提供者
  1. 导入pom
代码语言:javascript
复制
COPY        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

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

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

2. 配置yml

代码语言:javascript
复制
COPYserver:
  port: 

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848  # nacos注册中心的地址
    sentinel:
      transport:
        dashboard: localhost:8080 # sentinel的地址
        port:   #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口

# 暴露监控
management:
  endpoints:
    web:
      exposure:
        include: '*'

3. 主启动类添加注解

代码语言:javascript
复制
COPY@EnableDiscoveryClient
@SpringBootApplication

4. 控制层针对服务限流的备用方法,至于限流方式,就直接再sentinel控制台直接添加即可,@SentinelResource的value参数就是对应的资源名,里面也可以加fallback调用断路器备用响应方法,使用跟@HystrixCommand差不多。

代码语言:javascript
复制
COPY@RestController
@SentinelResource(fallbackClass = "Global_FallbackMethod")
public class TestController {

    @SentinelResource(value = "test",blockHandlerClass = CustomerBlockHandle.class,blockHandler ="deal_test")
    @GetMapping("/test")
    public String test(@RequestParam(value = "p") String p){
        return "(●'◡'●)没有被限流";
    }

//    public String test(String p,BlockException blockException){
//        return "-------------deal list ┭┮﹏┭┮";
//    }

     //全局响应Fallback方法 服务降级触发熔断
    public Object Global_FallbackMethod(){
        return "┭┮﹏┭┮全局熔断器方法触发了,呜呜呜!";
    }
}

统一处理限流的备用方法类

代码语言:javascript
复制
COPYpublic class CustomerBlockHandle {
    public static String deal_test(String p, BlockException blockException){
        return "┭┮﹏┭┮被限流了";
    }
}
  1. 启动后,再sentinel控制台并没有发现该服务,但是nacos却已经发现该服务成功注册了,是因为需要发送一次请求,网页访问该服务,sentinel才能监控到(Sentinel采用的是懒加载)。
在这里插入图片描述
在这里插入图片描述

微服务消费者(使用feign方式)

  1. 导入pom
代码语言:javascript
复制
COPY        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

2. 配置yml

代码语言:javascript
复制
COPYserver:
  port: 


spring:
  application:
    name: nacos-test-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 

service-url:
  nacos-user-service: http://cloudalibaba-sentinel-service

#对Feign的支持
feign:
  sentinel:
    enabled: true

3. 配置接口以及接口的服务降级备用响应的实现类

代码语言:javascript
复制
COPY@FeignClient(value = "cloudalibaba-sentinel-service",fallback = DeptFallbackService.class)
@Component
public interface TestService {

    @GetMapping("/test")
    public String test();
}
代码语言:javascript
复制
COPY@Component // 服务降级
public class DeptFallbackService implements DeptService {
    public String test() {
        return "┭┮﹏┭┮服务被降级了,不能使用~";
    }
}

4. controller层服务调用

代码语言:javascript
复制
COPY@Autowired
 private TestService testService ;

@GetMapping(value = "/consumer/test")
public String test(){
    return testService.test();
}
  1. 主启动类
代码语言:javascript
复制
COPY@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class test80
{
    public static void main(String[] args) {
        SpringApplication.run(test80.class, args);
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Ribbon实现负载均衡
  • 相关概念理解
    • Hystrix 实现服务熔断和服务降级
      • Sentinel实现服务熔断,降级,限流
        • 服务提供者
      • 微服务消费者(使用feign方式)
      相关产品与服务
      负载均衡
      负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档