SpringCloud - Ribbon服务调用

SpringCloud - Ribbon负载均衡

0. Ribbon概述

① LB负载均衡(Load Balance)是什么?

负载均衡的表现就是将用户的请求分摊到多个服务器上,从而达到高可用的目的。常见的负载均衡软件有:Nginx、LVS、硬件F5等。

② 目前主流的负载方案分为以下两种:

  • 集中式负载均衡,在消费者和服务提供方中间使用独立的代理方式进行负载,有硬件的(比如 F5),也有软件的(比如 Nginx)。
  • 客户端自己做负载均衡,根据自己的请求情况做负载,Ribbon 就属于客户端自己做负载。

③ Ribbon负载均衡 VS Nginx负载均衡:

  • Nginx是服务器端负载均衡 ,客户端的请求都发给Nginx,Nginx实现分发,将请求发送到不同的服务器上。

  • Ribbon是客户端负载均衡 ,在调用微服务接口的时候,会在注册中心拿到注册信息服务列表缓存到本地JVM,从而在本地实现RPC远程服务调用技术。

④ 集中式LB和进程内LB:

  • 集中式LoadBalancer:在服务的消费方和提供方之间使用的独立LoadBalancer设备(可以是硬件,如F5,也可以是软件,如Nginx),由该设施负责把请求通过某种策略转发至服务的提供方。

  • 进程内LoadBalancer:将LoadBalancer集成到消费方,消费者从服务注册中心获取到可用地址,自己再从这些地址中,选择要访问的服务器(如这个微服务器名称下有多个生产者的微服务,端口8001、8002、8003,进行轮循)。Ribbon就属于进程内LoadBalancer,它只是一个类库,集成消费者进程,消费者通过它获取服务提供方的地址。

Spring Cloud Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于 Netflix Ribbon 实现。通过 Spring Cloud 的封装,可以让我们轻松地将面向服务的 REST 模版请求自动转换成客户端负载均衡的服务调用。

Spring Cloud Ribbon 虽然只是一个工具类框架,它不像服务注册中心、配置中心、API 网关那样需要独立部署,但是它几乎存在于每一个 Spring Cloud 构建的微服务和基础设施中。因为微服务间的调用,API 网关的请求转发等内容,实际上都是通过 Ribbon 来实现的。

⑤ ribbon其实就是一个负载均衡的客户端组件,他可以和其他所需请求的客户端组件结合使用,和Eureka结合只是其中的一个实例。ribbon其实就是负载均衡加RestTemplate

ribbon模块:

名 称说 明
ribbon-loadbalancer负载均衡模块,可独立使用,也可以和别的模块一起使用。
Ribbon内置的负载均衡算法都实现在其中。
ribbon-eureka基于 Eureka 封装的模块,能够快速、方便地集成 Eureka。
ribbon-transport基于 Netty 实现多协议的支持,比如 HTTP、Tcp、Udp 等。
ribbon-httpclient基于 Apache HttpClient 封装的 REST 客户端,集成了负载均衡模块,可以直接在项目中使用来调用接口。
ribbon-exampleRibbon 使用代码示例,通过这些示例能够让你的学习事半功倍。
ribbon-core一些比较核心且具有通用性的代码,客户端 API 的一些配置和其他 API 的定义。

⑥ Ribbon在工作时分为两步:

第一步先选择EurekaServer,它优先选择在同一个区域内负载较少的Server

第二步再根据用户指定的策略,再从server取到的服务注册列表中选择一个地址

其中Ribbon提供了多中策略:比如轮询,随机和根据时间响应加权。

在这里插入图片描述

1. Ribbon的使用

① Euraka的最新版已经集成了ribbon:

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

② 也可以手动的导入ribbon:放到order消费者模块中,因为只有order访问pay生产者时需要负载均衡

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

③ RestTemplate类:

spring-cloud-starter-netflix-eureka-client坐标下,是引入了spring-cloud-starter-netflix-ribbon的,所以,我们仅仅只需要添加一个@LoadBalanced就可以实现负载均衡。

RestTemplate常见的方法有getForObject()、getForEntity()、postForObject()、postForEntity()方法。其中

  • ForObject()方法返回对象为响应体中数据转换成的对象,基本理解为JSON。

  • ForEntity()方法返回对象是ResponseEntity对象,包含了响应中的信息,比如响应头,响应状态码,响应体等。

④ 运行环境和测试:

将cloud-eureka-server7001、cloud-eureka-server7002的配置文件改成集群模式,让7001和7002互相注册,将cloud-provider-payment8001、cloud-provider-payment8002的配置文件改成集群模式,将cloud-consumer-order80的配置文件改成集群模式。

在cloud-consumer-order80模块中的OrderController里,添加两个方法,分别调用getForEntity()和postForEntity()方法。

@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult getPaymentById2(@PathVariable("id") Long id) {
    ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
    //打印响应状态码
    System.out.println("status code=" + entity.getStatusCode());
    //打印响应头
    System.out.println("headers=" + entity.getHeaders());
    //打印响应体
    if (entity.getStatusCode().is2xxSuccessful()) {
        return entity.getBody();
    } else {
        return new CommonResult(404, "查找失败");
    }
}
@GetMapping("/consumer/payment/create2")
public CommonResult create2(Payment payment) {
    return restTemplate.postForEntity(PAYMENT_URL + "/payment/create", payment, CommonResult.class).getBody();
}

先启动Eureka注册中心,再启动两个生产者,最后启动消费者,浏览器发送请求http://localhost//consumer/payment/getForEntity/1来调用ForEntity()方法进行测试。在浏览器端,可以看到port的值,不断在8001和8002之前进行切换。

2. 替换Ribbon的负载均衡算法

IRule接口,Riboon使用该接口,根据特定算法从所有服务中,选择一个服务,Rule接口有7个实现类,每个实现类代表一个负载均衡算法。

在这里插入图片描述

① 官方文档说明:如果想要替换Ribbon的轮询算法,这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了,也就是不能放在主启动类所在的包及子包下,因此新建一个包并定义一个配置类将轮询算法换为随机算法:

package com.atguigu.myrule;

@Configuration
public class MySelfRule {
    @Bean
    public IRule myRule(){
        return new RandomRule();//定义为随机算法
    }
}

② 在80主启动类上加一个注解(使用指定的算法)

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)
//name是服务提供者名称(地址),表明负载均衡用在服务提供者上
//configuration = MySelfRule.class是调用自定义的负载均衡配置类(使用自定义的随机算法)
public class OrderMain80 {
    public static void main(String[] args) {
       SpringApplication.run(OrderMain80.class,args);
    }
}

表示访问CLOUD_PAYMENT_SERVICE的服务时,使用我们自定义的负载均衡算法

3. 手写轮询算法

轮询算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次重启后rest接口从1计数。

① payment服务提供者模块(8001,8002)的Controller层加入一个方法返回服务的端口号:

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

@GetMapping(value = "payment/lb")
public String getPaymentLB(){
return serverPort;
}

② 修改order 80消费者模块:

在配置类config中去掉@LoadBalanced注解(自带的负载均衡机制)

@Configuration
public class ApplicationContextConfig {
    @Bean
    //@LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

在80模块中自定义接口并编写实现类:

package com.atguigu.springcloud.lb;

public interface LoadBalance {
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}
public class MyLB implements LoadBalance{

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    //获取下一个要调用的服务的id
    public final int getAndIncrement(){
        int current;
        int next;
        do{
            current = this.atomicInteger.get();
            next = current >= 2147483647 ? 0: current+1;
        }while (!this.atomicInteger.compareAndSet(current,next));
        System.out.println("next : "+next);
        return next;
    }

    //根据所有的服务,选择一个本次调用的服务
    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
        //拿到id后,进行取余运算,得到真正要调用的服务
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}

在Controller层方法添加自定义的负载均衡算法:

@RestController
@Slf4j
public class OrderController {
    
    //将访问地址改为服务名称
    private static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private DiscoveryClient discoveryClient;

    @Resource
    private LoadBalance loadBalance;

       @GetMapping(value = "/consumer/payment/lb")
    public String getPaymentLB(){
        //拿到 CLOUD-PAYMENT-SERVICE下的所有服务
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        if(instances==null || instances.size()<=0){
           return null;
        }
        ServiceInstance serviceInstance = loadBalance.instances(instances);
        return restTemplate.getForObject(PAYMENT_URL+"/payment/lb",String.class);
    }
}

浏览器访问http://localhost/consumer/payment/lb,发现8001和8002轮询访问。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我一直在流浪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值