SpringCloud负载均衡

负载均衡

负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。

负载均衡(Load Balance)其意思就是分摊到多个操作单元上进行执行,例如Web服务器FTP服务器企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。

然而在SpringBoot中,负载均衡的概念被微小化,即同时有多个微服务提供者给一个消费者提高同样的操作,我们需要在选择谁提高服务来实现大流量情况下,避免服务器宕机的可能。

环境搭建

创建一个服务消费者两个服务提供者:并注册到服务中心。具体搭建过程参考如下:

https://www.cnblogs.com/Rampant/p/14775726.html

在配置环境是需要注意,如果项目中需要使用ribbon,在spring2.4.X 已经中spring-cloud-starter-netflix-eureka-client已经把ribbon剔除了,想要使用ribbon请自行导入包

搭建完成后如下所示:

访问服务注册中心如下:http://localhost:7001/

编写业务代码

1、首先,我们要实现负载均衡,必需将服务提供者集群。修改application.yml的服务名一致即可,修改如下

# 修改服务名一样即可对于集群的服务
spring:
  application:
    name: cloud-provider-Load-Balance

2、编写服务提供者业务,编写一个即可,另外一个复制即可

package com.wyx.cloud.controller;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class ProviderController {

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/getInfo")
    public String getPort(){
        String msg = "获取信息成功,提供服务的服务端口为:"+serverPort;
        return msg;
    }
}

3、编写服务消费者业务,采用 RestTemplate

package com.wyx.cloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@Slf4j
public class ConsumerController {

    @Resource
    RestTemplate restTemplate;

    private final String PROVIDER_URL = "http://CLOUD-PROVIDER-LOAD-BALANCE";

    @GetMapping("/consumer/getInfo")
    public String getInfo(){

        return restTemplate.getForObject(PROVIDER_URL+"/getInfo",String.class);

    }
}

4、配置默认负载均衡

package com.wyx.cloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationConfig {

    @Bean
    @LoadBalanced  // 选择不同的服务提供者,不然会因为不知道走哪一台服务提供者而报错
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

5、运行测试:http://localhost/consumer/getInfo 多次点击,发现默认的负载均衡就是 轮询

LoadBalance

LoadBalance 是 Spring Cloud 提供了自己的客户端负载均衡器抽象和实现。对于负载均衡机制,ReactiveLoadBalancer添加了接口,并为其提供了轮询随机的实现。为了让实例从反应中选择ServiceInstanceListSupplier 。目前,我们支持基于服务发现的实现,ServiceInstanceListSupplier

老版本中集成了Ribbon的默认的负载均衡(轮询),也是和上面的一样配置,他提供了很多负载均衡算法

1、随机策略——RandomRule

2、轮询策略——RoundRobinRule
注:Ribbon默认策略

3、重试策略——RetryRule

4、最低并发策略——BestAvailableRule

5、可用过滤策略——AvailabilityFilteringRule
过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值)

性能仅次于最低并发策略。

6、响应时间加权策略——WeightedResponseTimeRule
每隔30秒计算一次服务器响应时间,以响应时间作为权重,响应时间越短的服务器被选中的概率越大。

7、区域权衡策略——ZoneAvoidanceRule

Ribbon的负载均衡策略使用建议
一般情况下,推荐使用最低并发策略,这个性能比默认的轮询策略高很多。

在上面的示例中,默认就是使用了负载均衡的轮询方式,当然我们也可以切换为随机的方式,只需要修改如下即可

1、创建一个配置类,并将负载均衡随机类注入容器

package com.wyx.cloud.config;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

@Configuration
public class CustomLoadBalancerClientConfiguration {
    // 官方写好的随机类
    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                                            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RandomLoadBalancer(loadBalancerClientFactory
                .getLazyProvider(name, ServiceInstanceListSupplier.class),
                name);
    }
}
// 和ribbon不一样,这里必须配置在SpringBoot能扫描的包,及其子包下

老版本替换负载均衡

// 编写一个类注入负载均衡算法类,这里很新版本不一样,该类必须不能让springboot扫描到,不能放在同级,或者子包下面
@Configuration
public class RibbonConfiguration {
  @Bean
  public IRule ribbonRule() {
    // 负载均衡规则,改为随机,可以new的类,参考上面提供的类名,官方写好的的类,也可以自定义负载均衡类
    return new RandomRule();
  }
}

老版本下,将使用该注解将其注入到负载均衡即可

/**
 * name 的 值代表需要负载均衡的服务名,切记服务名不能使用下划线。
 * configuration 代表我们添加的轮询配置类,这里使用的是我们上一步配置的类。
 */
@RibbonClient(name = "user", configuration = RibbonConfiguration.class)

测试即可

2、使用注解替换我们要对那个服务的调用进行轮询

package com.wyx.cloud.config;


import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 在类上添加该注解,可以是主启动类,也可以是配置类,都可以
 @LoadBalancerClient(value = "CLOUD-PROVIDER-LOAD-BALANCE", configuration = CustomLoadBalancerClientConfiguration.class)
 // value 的 值代表需要负载均衡的服务名,切记服务名不能使用下划线。
 // configuration 代表我们添加的轮询配置类,这里使用的是我们上一步配置的类。
*/
@Configuration
@LoadBalancerClient(value = "CLOUD-PROVIDER-LOAD-BALANCE", configuration = CustomLoadBalancerClientConfiguration.class)
public class ApplicationConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

当然除了官方提供的配置类,我们也可以通过自定义的接口实现自己的负载均衡,我们自需要写一个负载均衡的配置类即可。下面我们就来写一个。

实现自定义负载均衡,我们实现官方给的ReactorServiceInstanceLoadBalancer接口即可

package com.wyx.cloud.config;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Random;

// 自定义一个类实现官方提供的负载均衡的算法
public class MyLoadBalance implements ReactorServiceInstanceLoadBalancer {

    // 服务列表
    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
    private int count = 0;

    // serviceInstanceListSupplierProvider 是官方读取读取服务中心服务提供者的实例,并用列表返回。
    // 我们自需要将它使用构造器方法注入,便可获取读取的实例
    // 构造器
    public MyLoadBalance(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider) {
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
    }


    // 重写方法 即可实现自己需要的负载均衡负载均衡
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable();
        return supplier.get().next().map(this::getInstanceResponse);
    }


    private Response<ServiceInstance> getInstanceResponse(
            List<ServiceInstance> instances) {
        if (instances.isEmpty()) {
            return new EmptyResponse();
        }

        // 随机获取一个实例,并返回
        int size = instances.size();
        Random random = new Random();
        ServiceInstance instance = instances.get(random.nextInt(size));
        return new DefaultResponse(instance);
    }
}

2、将自己编写的类注入容器

package com.wyx.cloud.config;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CustomLoadBalancerClientConfiguration {

    @Bean
    public ReactorServiceInstanceLoadBalancer MyLoadBalance(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider) {
        return new MyLoadBalance(serviceInstanceListSupplierProvider);
    }
}

3、使用注解,将负载均衡设置为自己注入的bean

// 使用方法和上面一样,不在重述
@LoadBalancerClient(value = "CLOUD-PROVIDER-LOAD-BALANCE", configuration = CustomLoadBalancerClientConfiguration.class)

OpenFeign

OpenFeign为微服务架构下服务之间的调用提供了解决方案,OpenFeign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用OpenFeign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。

简单理解,openfeign就是利用声明式,模板化的Http客户端。

声明式:即将服务提供者中提供的方法利用接口将其声明,有什么方法

模板化:采用定义的接口经行调用。

使用流程

1、首先将我们搭建的环境还原

2、编写服务提供者,和上面一样具体参考上面即可

3、服务消费者,修改服务消费者的pom.xml文件

<!--添加 openfeign 的启动依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

4、主程序上添加注解自动开启openfeign服务

@EnableFeignClients

5、将服务提供者的提供的服务抽象成接口

package com.wyx.cloud.service;


import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;

@Service  // 注入容器
@FeignClient("CLOUD-PROVIDER-LOAD-BALANCE") // 需要提供服务的服务注册名,也是服务提供者的spring.application.name 
public interface ProviderInterface {
	
    // 提供的接口方法,注入容器即可
    @Bean
    @GetMapping("/getInfo")
    public String getPort();

}

6、服务调用,采用接口调用即可,如下

package com.wyx.cloud.controller;

import com.wyx.cloud.service.ProviderInterface;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class ConsumerController {

    @Resource
    ProviderInterface providerInterface;

    @GetMapping("/consumer/getInfo")
    public String getInfo(){
        return providerInterface.getPort();
    }
}

更多使用,可以参考官网:https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/#spring-cloud-feign

超时控制

​ 超时控制,指feign客户端远程调用服务提供者,默认情况下,feign客户端只等待一秒(集成ribbon的老版本,新版本不使用Ribbon的版本一直等待),如果因为网络或者服务器宕机的原因,造成服务不可以,就返回错误信息,但是大多数情况下,1秒的响应时间过于短,我们需要自定义一下配置。(相对于老版本),

但是对于新版本,一直无限等待也会加重网络拥塞,我们也需要设置一定的值,

只需要在application.yaml下添加如下配置即可

# 集成ribbon的老版本,使用这个配置
#设置feign 客户端超时时间(openFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
  ConnectTimeout: 5000


# 新版本修改以下默认配置
feign:
  client:
    config:
      default:
        // 连接超时的时间
        connectTimeout: 1000
        // 从发送信息到接收响应的时间
        readTimeout: 1000

更多配置参考官网的自定义配置

日志打印

Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign中Http请求的细节。

1、配置日志打印功能,注入日志打印的依赖

package com.wyx.cloud.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {

    /*
    NONE:默认的,不显示任何日志。
	BASIC:仅记录请求方法、URL、响应状态码及执行时间。
	HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息。
	FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。
    */
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

2、添加 application.yml

logging:
  level:
    com.wyx.cloud.service: debug

测试即可

posted @ 2021-06-11 15:42  橘子有点甜  阅读(1534)  评论(0编辑  收藏  举报