Spring Cloud负载均衡及远程调用实现详解

作者:编了个程 时间:2021-10-16 01:11:27 

负载均衡

使用微服务后,为了能够承担高并发的压力,同一个服务可能会启动多个实例。这时候消费者就需要负载均衡,把请求分散到各个实例。负载均衡主要有两种设计:

服务端负载均衡客户端负载均衡

对于传统的分布式服务来说,大多使用服务端负载均衡。一般会使用Nginx或者ELB等工具作为负载均衡器,如下图:

Spring Cloud负载均衡及远程调用实现详解

传统负载均衡

而在Spring Cloud中,使用的是「客户端负载均衡」的方式,使用「Ribbon」组件来实现客户端的负载均衡。只要引入了微服务注册中心依赖,就会自动引入Ribbon依赖。客户端负载均衡原理如下图:

Spring Cloud负载均衡及远程调用实现详解

客户端负载均衡

Ribbon的原理

Ribbon利用了RestTemplate的 * (接口是ClientHttpRequestInterceptor)机制,在 * 中实现的负载均衡。负载均衡的基本实现就是利用从「服务注册中心」获取可用的服务地址列表,然后通过一定算法负载,决定使用哪一个服务地址来进行HTTP调用。

详情可以查看LoadBalancerInterceptor这个类,位于org.springframework.cloud.client.loadbalancer包下。

使用Ribbon

首先定义一下生产者「service-user」,我这里使用容器启动了多个生产者实例,每个实例具有不同的id,我的示例代码里面启动了两个实例:

service-user-1
service-user-2

代码:


@RestController
@RequestMapping
public class HelloController {

@Value("${spring.cloud.consul.discovery.instanceId}")
 private String instanceId;

@GetMapping("hello")
 public String hello() {
   return String.format("hello, this is %s", instanceId);
 }
}

然后对于消费者「service-order」,可以使用Java声明式注解的方式来使用Ribbon,只需要在消费者给RestTemplate类型的Bean配上一个@LoadBalanced就可以了。然后再配置一下负载均衡策略:


@Configuration
public class RibbonConfig {

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

@Bean
 public IRule ribbonRule() {
   return new RandomRule(); // 随机负载均衡
 }
}

然后就可以直接使用自动注入的RestTemplate类型的Bean了:


@RestController
@RequestMapping
public class HelloController {

@Autowired
 RestTemplate restTemplate;

@GetMapping("/ribbon/service-user")
 public String ribbonService(){
   return restTemplate.getForObject("http://service-user/hello", String.class);
 }
}

这个时候访问消费者的/ribbon/service-user,刷新几次,就会看到下面两个随机的响应:

hello, this is service-user-2
hello, this is service-user-1

负载均衡策略

查看IRule接口的实现类,可以看到Ribbon的所有负载均衡策略,查看各实现类顶部的注释可以看到它的具体策略:

Spring Cloud负载均衡及远程调用实现详解

负载均衡器

  • RandomRule:随机选择;

  • RoundRobinRule:轮询;

  • WeightedResponseTimeRule:根据每个服务的响应时间设置权重,响应时间越长,所占权重越少;

  • AvailabilityFilteringRule:过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值);

  • ZoneAvoidanceRule:使用CompositePredicate根据区域和可用性过滤服务器的规则;

  • BestAvailableRule:选择一个最小的并发请求的server;

  • RetryRule:在现有的策略基础上,添加重试机制,因为IRule支持「级联」。

远程调用

Spring Cloud提供了OpenFeign组件(以前叫Feign)来进行远程的HTTP调用。它是对RestTemplate的一个封装。

Feign是一个声明式Web Service客户端。使用Feign能让编写Web Service客户端更加简单。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与微服务注册中心和Ribbon组合使用以支持负载均衡。

使用OpenFeign

第一步,引入依赖:


implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

第二步,启动配置,在启动类上添加@EnableFeignClients注解:


@SpringBootApplicationbr/>@EnableFeignClients
public class ServiceOrderApplication {
public static void main(String[] args) {
 SpringApplication.run(ServiceOrderApplication.class, args);
}
}

第三步:声明调用接口,我这里案例是service-order调用service-user的/hello接口,这里的注解就跟Spring MVC的注解一致:

注意Feign在做POST请求的时候有一个小坑,详情参考:SpringCloud Feign Post表单请求。


@FeignClient("service-user")
public interface UserClient {
 @GetMapping("hello")
 String getUserHello();
}

第四步:使用,注解使用@Autowired注解注入就可以了:


@RestController
@RequestMapping
public class HelloController {

@Autowired
 UserClient userClient;

@GetMapping("hello")
 public String hello() {
   return "hello, this is order service";
 }

@GetMapping("userHello")
 public String user() {
   return userClient.getUserHello() + ", this is order-service";
 }
}

是不是看起来非常简单?

OpenFeign集成Ribbon

前面我们提到,OpenFeign底层是对RestTemplate的一个封装,而Ribbon是通过给RestTemplate添加过滤器来实现的,所以OpenFeign天生就自动集成了Ribbon,我们不需要任何额外的配置。

在上述代码中,启动后,可以访问service-order的/userHello端口,不断刷新,发现已经实现了负载均衡。

来源:https://blog.51cto.com/14890694/2518860

标签:Spring,Cloud,负载,均衡,远程,调用
0
投稿

猜你喜欢

  • Android TextView文本控件介绍

    2023-08-29 10:13:53
  • C#操作XML文件步骤

    2021-11-04 21:51:44
  • java中String的一些方法深入解析

    2023-11-25 21:48:56
  • java实现二分法的完整代码

    2023-08-18 22:09:06
  • Android实现的RecyclerView适配器

    2023-07-12 23:02:21
  • SpringBoot2.0解决Long型数据转换成json格式时丢失精度问题

    2022-10-31 16:56:24
  • 如何使用BufferedReader循环读文件

    2022-04-20 17:32:23
  • 如何使用java修改文件所有者及其权限

    2023-11-16 09:35:53
  • Java中的传值与传引用实现过程解析

    2023-02-21 12:59:41
  • c#基数排序Radix sort的实现方法

    2021-07-25 02:02:21
  • C++之try catch 异常处理入门实例

    2021-09-13 04:42:07
  • JPA 加锁机制及@Version版本控制方式

    2022-10-06 10:57:58
  • Spring实现动态切换多数据源的解决方案

    2023-05-21 13:07:15
  • android module解耦组件化总体概述(推荐)

    2021-11-29 19:46:34
  • Java输入输出流的使用详细介绍

    2023-08-01 22:21:22
  • Kotlin中的对象表达式和对象声明的具体使用

    2022-05-31 04:08:18
  • 关于@RequestLine的使用及配置

    2023-09-23 20:52:19
  • Java——对象初始化顺序使用详解

    2023-11-01 01:08:46
  • Java实现画图 给图片底部添加文字标题

    2023-03-28 23:23:06
  • Spring Native打包本地镜像的操作方法(无需通过Graal的maven插件buildtools)

    2023-11-25 04:24:30
  • asp之家 软件编程 m.aspxhome.com