Spring Cloud - 远程调用和负载均衡_编了个程的博客-程序员秘密

点击↑上方↑蓝色“编了个程”关注我~

每周至少一篇原创文章

这是本公众号的第 9 篇原创文章

负载均衡

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

  • 服务端负载均衡

  • 客户端负载均衡

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

传统负载均衡

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

客户端负载均衡

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的所有负载均衡策略,查看各实现类顶部的注释可以看到它的具体策略:

负载均衡器
  • 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注解:

@SpringBootApplication
@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端口,不断刷新,发现已经实现了负载均衡。

点击“阅读原文”查看源码地址。

关于作者

微信公众号:编了个程(blgcheng)

个人网站:https://yasinshaw.com

笔名Yasin,一个有深度,有态度,有温度的程序员。工作之余分享编程技术和生活,如果喜欢我的文章,可以顺手关注一下公众号,也欢迎转发分享给你的朋友~

推荐阅读

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yasinshaw/article/details/106168898

智能推荐

eosjs 文档(API接口)_weixin_33979363的博客-程序员秘密

API接口接口AbiProviderAuthorityProviderAuthorityProviderArgsBinaryAbiCachedAbiSignatureProviderSignatureProviderArgsAbiProvider实现类JsonRpc属性getRawAbigetRawAbi:...

C++ Builder 10 安装GLScene库_SeniorZ的博客-程序员秘密

背景:在老版本的Borland C++ Builder 6.0中有一个名为 TOpenGL的组件,可以进行3D图形显示与绘制的功能。但是在新版本的C++ Builder 10(当前最新版本为10.3.2,我使用的是10.2.3)中,该组件并没有做适配,因为实在是太老了 适合BCB6.0的OpenGL组件下载地址:http://www.hellix.com/Alan/Computing/本文...

华为网络设备SSH方式登录配置_ssh首次认证功能启用ac_abc768047936abc的博客-程序员秘密

HCNA学习记录配置通过SSH登录路由器:Server端配置: 1. 生成RSA主机密钥对: rsa local-key-pair create 2.进入VTY模式 user-interface vty 0 4 3.设置用户验证方式为AAA authentication-mode aaa 4.指定VT...

CentOS 7.7 安装redis-5.0.7_centos7安装redis-5.0.7.tar_C_SF_C的博客-程序员秘密

CentOS 7.7 安装redis-5.0.7参考文献:https://blog.csdn.net/lingfeian/article/details/93755467下载安装包并解压,移动到安装目录wget http://download.redis.io/releases/redis-5.0.7.tar.gztar -zxvf redis-5.0.7.tar.gzsudo mv r...

java上传图片格式限制_Java springboot 中上传图片文件的1Md的限制_瘦瘦的搬砖工的博客-程序员秘密

在配置文件中修改了spring.servlet.multipart.max-file-size=3.40spring.servlet.multipart.max-request-size=3M遇到下面的错误:Description:Failed to bind properties under 'spring.servlet.multipart.max-file-size' to org.spri...

newspaper安装_weixin_30480075的博客-程序员秘密

安装系统是centos6.5 32位Python环境 Python2.7由于直接pip安装时,lmlx安装有问题1. 安装lmlxhttp://mdba.cn/?p=86yum install libxml* -yyum install libxslt* -ywget http://lxml.de/files/lxml-3.1.2.tgztar xzvf lx...

随便推点

Random类_weixin_30466039的博客-程序员秘密

一:定义:专门用来生成伪随机数,有两个构造器,一个构造器使用默认的种子(以当前时间作为种子),一个需要程序员显式传入一个long型整数的种子二.使用方法 1 import java.util.Arrays; 2 import java.util.Random; 3 /** 4 * 一.Arrays类:此类包含用来操作数组(比如排序和搜索)的各种方法 5 * 二...

后台获得数据_diaoxiexi3244的博客-程序员秘密

后台获得数据 1 <body> 2 3 <form action = "do_post.jsp" method = "post"> 4 5 用户名:<input type = "...

Java_IO_CommonsIO_listFiles方法获取文件夹子孙集_Chill_Lyn_的博客-程序员秘密

package cn.CommonsIO;import java.io.File;import java.util.Collection;import org.apache.commons.io.FileUtils;import org.apache.commons.io.filefilter.DirectoryFileFilter;import org.apache.commons...

AUTOSAR MCAL CAN Driver 接口函数_can_pdutype在哪里定义_olddddd的博客-程序员秘密

1. 单控制器服务接口函数(1) Can_Initvoid Can_Init(const Can_ConfigType* Config)初始化函数,输入为CAN模块配置结构体指针,在MCU启动时调用,初始化CAN模块的时钟、波特率、引脚、接收发送邮箱等一系列寄存器。完毕后将CAN控制器的状态由CAN_CS_UNINIT转换为CAN_CS_STOPPED。(2)Can_SetBaudrateStd_ReturnType Can_SetBaudrate(uint8 Controller,uint16

java中Exception的细节_iteye_18227的博客-程序员秘密

[size=x-large]一、异常的丢失[/size] 任何一个Java程序员应该都不会不知道Java中的Exception机制。下面是总结的一些在开发中不是太过于重要的关于Exception的细节。有时候就是因为不注意这些细节而导致一些不易发现的问题。之前看过一个blog http://blog.csdn.net/hguisu/article/details/615563...

从零到一的 React 学习与实践资料索引_weixin_34216036的博客-程序员秘密

React 学习与实践资料索引Overview: 概览Principle: 设计理念Case Study: 案例Book: 书籍Course & Conf: 视频教程与会议Resource: 其他资源集锦Tutorial: 入门教程Concept: 概念Coding: 基础编码Compone...

推荐文章

热门文章

相关标签