微服务技术系列教程(21) - SpringCloud- 负载均衡器Ribbon

简介: 微服务技术系列教程(21) - SpringCloud- 负载均衡器Ribbon

01 引言

在SpringCloud中Ribbon负载均衡客户端,会从eureka注册中心服务器端上获取服务注册信息列表,缓存到本地,然后在本地实现轮训负载均衡策略

那么Ribbon和Nginx有什么区别呢?

  • Nginx是客户端所有请求统一交给Nginx,由nginx进行实现负载均衡请求转发,属于服务器端负载均衡(服务器端转发)。
  • 在客户端转发:Ribbon是从eureka注册中心服务器端上获取服务注册信息列表,缓存到本地,让后在本地实现轮训负载均衡策略(客户端转发)。
  • Nginx适合于服务器端实现负载均衡 比如Tomcat(服务器端负载均衡)。
  • Ribbon适合与在微服务中RPC远程调用实现本地服务负载均衡,比如Dubbo、SpringCloud中都是采用本地负载均衡(客户端负载均衡)。

Ribbon流程图:

02 DiscoveryClient负载本地负载均衡

1.搭建Eureka-Service-A1(服务提供者)、Eureka-Service-A2(服务提供者)、Eureka-Service-B(服务消费者)和Eureka-Server(注册中心),环境搭建参考:《 SpringCloud- 服务治理Eureka(搭建注册中心)》

2.Eureka-Service-B(服务消费者)负载均衡代码:

@RestController
public class ServiceBController {
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private DiscoveryClient discoveryClient;
    private int requestCount = 1;
    @RequestMapping("/discoveryClient")
    public String discoveryClient() {
        String serviceUrl = getServiceUrl() + "/getMember";
        return "请求地址为 -> " + serviceUrl;
    }
    @RequestMapping("/getServiceUrl")
    private String getServiceUrl() {
        List<ServiceInstance> instances = discoveryClient.getInstances("app-service-a");
        if (instances == null || instances.size() == 0) {
            return null;
        }
        int size = instances.size();
        int index = requestCount % size;
        requestCount++;
        return instances.get(index).getUri().toString();
    }
}

2.启动Eureka注册中心服务(http://localhost:8100/)、服务器消费者(http://localhost:8000/http://localhost:8001/)、服务器提供者(http://localhost:8000/)。

3.登录Eureka注册中心:http://localhost:8100/,可以看到都注册上了。

4.服务消费者消费,浏览器输入:http://localhost:8002/discoveryClient

再次刷新:

可以看出,Eureka消费者客户端通过轮询的方式来实现本地负载均衡。

03 RestTemplate

RestTemplate是Spring用于同步client端的核心类,简化了与http服务的通信,并满足RestFul原则,程序代码可以给它提供URL,并提取结果。能大幅简化了提交表单数据的难度,并且附带了自动转换JSON数据的功能。(说白了,库室HttpClient的封装,像Apache HttpComponents、Netty和OkHttp。)

该类的入口主要是根据HTTP的六个方法制定:

Http Method RestTemplate Methods
GET getForObject、getForEntify
DELETE delete
POST postForLocation、postForObject
PUT put
HEAD headForHeaders
OPTIONS optionsForAllow
ANY exchange、execute

此外,exchange和excute可以通用上述方法。

在内部,RestTemplate默认使用HttpMessageConverter实例将HTTP消息转换成POJO或者从POJO转换成HTTP消息。默认情况下会注册主mime类型的转换器,但也可以通过setMessageConverters注册其他的转换器。

HttpMessageConverter.java源码:

public interface HttpMessageConverter<T> {
        //指示此转换器是否可以读取给定的类。
    boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
        //指示此转换器是否可以写给定的类。
    boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
        //返回List<MediaType>
    List<MediaType> getSupportedMediaTypes();
        //读取一个inputMessage
    T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException;
        //往output message写一个Object
    void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException;
}

在内部,RestTemplate默认使用SimpleClientHttpRequestFactory和DefaultResponseErrorHandler来分别处理HTTP的创建和错误,但也可以通过setRequestFactory和setErrorHandler来覆盖。


演示实例前,先定义POJO:

public class Notice {
    private int status;
    private Object msg;
    private List<DataBean> data;
}
public  class DataBean {
  private int noticeId;
  private String noticeTitle;
  private Object noticeImg;
  private long noticeCreateTime;
  private long noticeUpdateTime;
  private String noticeContent;
}

3.1 Get请求

getForEntity:getForEntity方法的返回值是一个ResponseEntity<T>ResponseEntity<T>是Spring对HTTP请求响应的封装,包括了几个重要的元素,如响应码、contentType、contentLength、响应消息体等。

@Test
public void rtGetEntity(){
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<Notice> entity = restTemplate.getForEntity("http://fantj.top/notice/list/1/5"
                , Notice.class);
        HttpStatus statusCode = entity.getStatusCode();
        System.out.println("statusCode.is2xxSuccessful()"+statusCode.is2xxSuccessful());
        Notice body = entity.getBody();
        System.out.println("entity.getBody()"+body);
        ResponseEntity.BodyBuilder status = ResponseEntity.status(statusCode);
        status.contentLength(100);
        status.body("我在这里添加一句话");
        ResponseEntity<Class<Notice>> body1 = status.body(Notice.class);
        Class<Notice> body2 = body1.getBody();
        System.out.println("body1.toString()"+body1.toString());
    }
statusCode.is2xxSuccessful()true
entity.getBody()Notice{status=200, msg=null, data=[DataBean{noticeId=21, noticeTitle='aaa', ...
body1.toString()<200 OK,class com.waylau.spring.cloud.weather.pojo.Notice,{Content-Length=[100]}>

getForObjet:getForObject函数实际上是对getForEntity函数的进一步封装,如果你只关注返回的消息体的内容,对其他信息都不关注,此时可以使用getForObject,举一个简单的例子,如下:

/**
* 带参数与不带参的get请求
*/
@Test
public void restTemplateGetTest(){
   RestTemplate restTemplate = new RestTemplate();
   Notice notice = restTemplate.getForObject("http://xxx.top/notice/list/1/5", Notice.class);
   //Notice notice = restTemplate.getForObject("http://fantj.top/notice/list/{1}/{2}" , Notice.class,1,5);
   System.out.println(notice);
}

3.2 Post请求

如果你只关注,返回的消息体,可以直接使用postForObject。用法和getForObject一致。

@Test
public void rtPostObject(){
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://47.xxx.xxx.96/register/checkEmail";
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
    MultiValueMap<String, String> map= new LinkedMultiValueMap<>();
    map.add("email", "xxx@qq.com");
    HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
    ResponseEntity<String> response = restTemplate.postForEntity( url, request , String.class );
    System.out.println(response.getBody());
}

3.3 Put请求、Delete请求

跟上面大同小异,具体的看API。参考文章:《Springboot — 用更优雅的方式发HTTP请求(RestTemplate详解)》

最后,先看下代码:

@SpringBootApplication
@EnableEurekaClient
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
    @Bean
    //@LoadBalanced//@LoadBalanced就能让这个RestTemplate在请求时拥有客户端负载均衡的能力(增加@LoadBalanced,就不能使用127.0.0.1,只能使用应用名)
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

需要注意的是:增加@LoadBalanced,就不能使用本机ip例如(127.0.0.1、localhost),只能使用应用名。

04 源码分析

参考:《客户端负载均衡Ribbon之源码解析》

ServerList:可以响应客户端的特定服务的服务器列表。

ServerListFilter:可以动态获得的具有所需特征的候选服务器列表的过滤器。

ServerListUpdater:用于执行动态服务器列表更新。

Rule:负载均衡策略,用于确定从服务器列表返回哪个服务器。

Ping:客户端用于快速检查服务器当时是否处于活动状态。

LoadBalancer:负载均衡器,负责负载均衡调度的管理。

05 负载均衡器重试机制

这里的重试并不是报错以后的重试,而是负载均衡客户端发现远程请求实例不可到达后,去重试其他实例。

Ribbon重试机制配置:

名称 解析
ribbon.OkToRetryOnAllOperations false(是否所有操作都重试)
ribbon.MaxAutoRetriesNextServer 2(重试负载均衡其它的实例最大重试次数,不包括Server)
ribbon.MaxAutoRetries 1(同一台实例最大重试次数,不包括首次调用)
spring.cloud.loadbalancer.retry.enabled true(重试机制开关)

04 总结

代码已上传至Github,有兴趣的同学可以下载来看看:https://github.com/ylw-github/SpringCloud-Ribbon-Demo

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
目录
相关文章
|
2月前
|
算法 Java 微服务
【SpringCloud(1)】初识微服务架构:创建一个简单的微服务;java与Spring与微服务;初入RestTemplate
微服务架构是What?? 微服务架构是一种架构模式,它提出将单一应用程序划分为一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。 每个服务允许在其独立的进程中,服务于服务间采用轻量级的通信机制互相协作(通常是Http协议的RESTful API或RPC协议)。 每个服务都围绕着具体业务进行构建,并且能够被独立的部署到生产环境、类生产环境等。另外应当尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据上下文,选择合适的语言、工具对其进行构建
488 126
|
2月前
|
负载均衡 算法 Java
【SpringCloud(2)】微服务注册中心:Eureka、Zookeeper;CAP分析;服务注册与服务发现;单机/集群部署Eureka;连接注册中心
1. 什么是服务治理? SpringCloud封装了Netfix开发的Eureka模块来实现服务治理 在传统pc的远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册
283 0
|
2月前
|
负载均衡 Java API
《深入理解Spring》Spring Cloud 构建分布式系统的微服务全家桶
Spring Cloud为微服务架构提供一站式解决方案,涵盖服务注册、配置管理、负载均衡、熔断限流等核心功能,助力开发者构建高可用、易扩展的分布式系统,并持续向云原生演进。
|
3月前
|
监控 安全 Java
Spring Cloud 微服务治理技术详解与实践指南
本文档全面介绍 Spring Cloud 微服务治理框架的核心组件、架构设计和实践应用。作为 Spring 生态系统中构建分布式系统的标准工具箱,Spring Cloud 提供了一套完整的微服务解决方案,涵盖服务发现、配置管理、负载均衡、熔断器等关键功能。本文将深入探讨其核心组件的工作原理、集成方式以及在实际项目中的最佳实践,帮助开发者构建高可用、可扩展的分布式系统。
232 1
|
3月前
|
jenkins Java 持续交付
使用 Jenkins 和 Spring Cloud 自动化微服务部署
随着单体应用逐渐被微服务架构取代,企业对快速发布、可扩展性和高可用性的需求日益增长。Jenkins 作为领先的持续集成与部署工具,结合 Spring Cloud 提供的云原生解决方案,能够有效简化微服务的开发、测试与部署流程。本文介绍了如何通过 Jenkins 实现微服务的自动化构建与部署,并结合 Spring Cloud 的配置管理、服务发现等功能,打造高效、稳定的微服务交付流程。
483 0
使用 Jenkins 和 Spring Cloud 自动化微服务部署
|
3月前
|
Kubernetes Java 微服务
Spring Cloud 微服务架构技术解析与实践指南
本文档全面介绍 Spring Cloud 微服务架构的核心组件、设计理念和实现方案。作为构建分布式系统的综合工具箱,Spring Cloud 为微服务架构提供了服务发现、配置管理、负载均衡、熔断器等关键功能的标准化实现。本文将深入探讨其核心组件的工作原理、集成方式以及在实际项目中的最佳实践,帮助开发者构建高可用、可扩展的分布式系统。
457 0
|
负载均衡 算法 Java
除了 Ribbon,Spring Cloud 中还有哪些负载均衡组件?
这些负载均衡组件各有特点,在不同的场景和需求下,可以根据项目的具体情况选择合适的负载均衡组件来实现高效、稳定的服务调用。
1112 61
|
负载均衡 监控 网络协议
SpringCloud之Ribbon使用
通过以上步骤,就可以在Spring Cloud项目中有效地使用Ribbon来实现服务调用的负载均衡,提高系统的可靠性和性能。在实际应用中,根据具体的业务场景和需求选择合适的负载均衡策略,并进行相应的配置和优化,以确保系统的稳定运行。
550 15
|
负载均衡 Java Nacos
SpringCloud基础1——远程调用、Eureka,Nacos注册中心、Ribbon负载均衡
微服务介绍、SpringCloud、服务拆分和远程调用、Eureka注册中心、Ribbon负载均衡、Nacos注册中心
SpringCloud基础1——远程调用、Eureka,Nacos注册中心、Ribbon负载均衡
|
负载均衡 算法 Java
SpringCloud之Ribbon使用
通过 Ribbon,可以非常便捷的在微服务架构中实现请求负载均衡,提升系统的高可用性和伸缩性。在实际使用中,需要根据实际场景选择合适的负载均衡策略,并对其进行适当配置,以达到更佳的负载均衡效果。
602 13

热门文章

最新文章