SpringCloud学习记录(二)负载均衡,服务熔断,服务降级,服务限流

Ribbon实现负载均衡

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

  1. 导入pom依赖

单独导入

  <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-ribbonartifactId>
            <version>2.2.1.RELEASEversion>
  dependency>

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

  <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
            <version>2.2.1.RELEASEversion>
  dependency>
  1. 在主启动类所在包的上一级创建一个包,用来创建自定义负载均衡策略类
    SpringCloud学习记录(二)负载均衡,服务熔断,服务降级,服务限流_第1张图片
    书写自定义负载均衡策略类,先继承AbstractLoadBalancerRule抽象类,该类是IRule接口的一个抽象实现类,继承它,然后自定义自己的负载均衡策略,注入spring容器即可覆盖原有的轮询策略。
    SpringCloud学习记录(二)负载均衡,服务熔断,服务降级,服务限流_第2张图片
public class HcodeRule extends AbstractLoadBalancerRule {
    //每个服务,访问5次,然后循环所有服务
    private AtomicInteger total = new AtomicInteger(0); //服务被调用的次数
    private AtomicInteger currentIndex = new AtomicInteger(0); //当前被调用服务的下标
    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 == 0) {

                return null;
            }
//            //生成区间随机数
//            int index = chooseRandomInt(serverCount);
//            server = upList.get(index); //从或者的服务中随机获取一个
            /*============================================*/
            if(total.get()<5){
               server = upList.get(currentIndex.get());
                getAndIncrement(total);
            }else{
                total.set(0);
                getAndIncrement(currentIndex);
                if(currentIndex.get()>=upList.size()){
                    currentIndex.set(0);
                }
                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 >= 2147483647 ? 0 : current + 1;
        }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容器即可.下面两种二选一

@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. 主启动类上加上注解,让消费端服务能去使用自定义的负载均衡策略
@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

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
    <version>2.2.1.RELEASEversion>
dependency>

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

        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>
  1. 微服务提供者的controller层
@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 "┭┮﹏┭┮特定限制方法熔断器触发了,呜呜呜~";
    }
}
  1. 微服务提供者的主启动类加上开启断路器的注解
@EnableCircuitBreaker //添加对服务熔断的支持
  1. 客户端导入的所需依赖跟提供者差不多,使用openfeign进行服务调用通信。
<dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-openfeignartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>

然后配置yml,让feign支持hystrix

feign:
  hystrix:
    enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。

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

@FeignClient(value = "hystrix-test-provider",fallback = DeptFallbackService.class) //服务名,备用的响应类
@Component
public interface testService {

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

兜底用的备用响应类

@Component // 服务降级
public class DeptFallbackService implements testService{
    public String test1() {
        return "┭┮﹏┭┮该服务被降级了,目前无法使用";
    }
}

最后,主启动类添加@EnableHystrix

@EnableHystrix

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

老样子,hystrix也是停止更新了,所以换新的才是王道~况且阿里巴巴的sentinel就是继承hystrix,两者挺像的。

  • 官网下载:https://github.com/alibaba/Sentinel/releases
  • 特性:
    SpringCloud学习记录(二)负载均衡,服务熔断,服务降级,服务限流_第3张图片
  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

下载好jar包sentinel-dashboard-1.7.0.jar后,直接命令行运行即可~
注意:该boot服务默认为8080端口号。启动后,访问http://localhost:8080,账号密码都是sentinel。
SpringCloud学习记录(二)负载均衡,服务熔断,服务降级,服务限流_第4张图片

服务提供者

  1. 导入pom
        <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>
  1. 配置yml
server:
  port: 8666

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

# 暴露监控
management:
  endpoints:
    web:
      exposure:
        include: '*'
  1. 主启动类添加注解
@EnableDiscoveryClient
@SpringBootApplication

  1. 控制层针对服务限流的备用方法,至于限流方式,就直接再sentinel控制台直接添加即可,@SentinelResource的value参数就是对应的资源名,里面也可以加fallback调用断路器备用响应方法,使用跟@HystrixCommand差不多。
@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 "┭┮﹏┭┮全局熔断器方法触发了,呜呜呜!";
    }
}

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

public class CustomerBlockHandle {
    public static String deal_test(String p, BlockException blockException){
        return "┭┮﹏┭┮被限流了";
    }
}
  1. 启动后,再sentinel控制台并没有发现该服务,但是nacos却已经发现该服务成功注册了,是因为需要发送一次请求,网页访问该服务,sentinel才能监控到(Sentinel采用的是懒加载)。
    SpringCloud学习记录(二)负载均衡,服务熔断,服务降级,服务限流_第5张图片

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

  1. 导入pom
		<dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-alibaba-nacos-discoveryartifactId>
        dependency>
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-alibaba-sentinelartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-openfeignartifactId>
        dependency>
         <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-actuatorartifactId>
        dependency>
 

  1. 配置yml
server:
  port: 80


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

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

#对Feign的支持
feign:
  sentinel:
    enabled: true
  1. 配置接口以及接口的服务降级备用响应的实现类
@FeignClient(value = "cloudalibaba-sentinel-service",fallback = DeptFallbackService.class)
@Component
public interface TestService {

    @GetMapping("/test")
    public String test();
}
@Component // 服务降级
public class DeptFallbackService implements DeptService {
    public String test() {
        return "┭┮﹏┭┮服务被降级了,不能使用~";
    }
}
  1. controller层服务调用
@Autowired
 private TestService testService ;

@GetMapping(value = "/consumer/test")
public String test(){
    return testService.test();
}
  1. 主启动类
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class test80
{
    public static void main(String[] args) {
        SpringApplication.run(test80.class, args);
    }
}
 

你可能感兴趣的:(JAVA)