SpringCloud(第八章 Ribbon负载均衡服务调用)

一、概述

1.是什么

1.Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。
2.简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。

2.官网资料

https://github.com/Netflix/ribbon/wiki/Getting-Started

1.Ribbon目前也进入维护模式
在这里插入图片描述
2.未来替换方案
在这里插入图片描述

3.能干嘛

1.LB(负载均衡)

1.负载均衡(Load Balance)是什么

1.简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。
2.常见的负载均衡有软件Nginx,LVS,硬件F5等。

2.Ribbon本地负载均衡客户端VS Nginx服务端负载均衡区别

1.Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后nginx实现转发请求。即负载均衡是由服务端实现的。
2.Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地RPC远程服务调用技术。

1.集中式LB

即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方;

2.进程内LB

1.将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择一个合适的服务器。
2.Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取服务提供方的地址。

2.前面我们讲解过了80通过轮询负载访问8001/8002
3.一句话

负载均衡+RestTemplate调用

二、Ribbon负载均衡演示

1.架构说明

在这里插入图片描述

Ribbon在工作时分为两步
1.第一步先选择EurekaServer,它优先选择在同一区域内负载较少的server。
2.第二步再根据用户指定的策略,再从server取到的服务注册列表中选择一个地址。
3.其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。

总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。

2.POM

之前写样例时没有引入spring-cloud-starter-ribbon也可以使用ribbon,
在这里插入图片描述
猜测spring-cloud-starter-netflix-eureka-client自带了spring-cloud-starter-ribbon引用,证明如下:可以看到了spring-cloud-starter-netflix-eureka-client确实引入了Ribbon。
在这里插入图片描述

3.二说RestTemplate的使用

1.官网:
https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html
在这里插入图片描述

2.getForObject方法/getForEntity方法

1.返回对象为响应体中数据转化成的对象,基本上可以理解为Json
在这里插入图片描述
2.返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等
在这里插入图片描述

3.postForObject/postForEntity
在这里插入图片描述

4.GET请求方法

 
<T> T getForObject(String url, Class<T> responseType, Object... uriVariables);
 
<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables);
 
<T> T getForObject(URI url, Class<T> responseType);
 
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables);
 
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables);
 
<T> ResponseEntity<T> getForEntity(URI var1, Class<T> responseType);

5.POST请求方法

 
<T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables);
 
<T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables);
 
<T> T postForObject(URI url, @Nullable Object request, Class<T> responseType);
 
<T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables);
 
<T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables);
 
<T> ResponseEntity<T> postForEntity(URI url, @Nullable Object request, Class<T> responseType);

三、Ribbon核心组件IRule

(1)IRule:根据特定算法中从服务列表中选取一个要访问的服务

1.IRule的实现类
在这里插入图片描述
2.Ribbon默认自带的负载均衡算法(默认使用轮询)
在这里插入图片描述

(2)如何替换

1.修改cloud-consumer-order80
2.修改配置细节

官方文档明确给出了警告:
这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端锁共享,达不到特殊化定制的目的了。
在这里插入图片描述
3.新建package
com.itcast.myrule
在这里插入图片描述

4.上面包下新建MySelfRule规则类

package com.itcast.myrule;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author henry
 * @version 1.0
 * @date 2021/9/15 0015 20:52
 */
@Configuration
public class MySelfRule
{
    @Bean
    public IRule myRule()
    {
        return new RandomRule();//定义为随机
    }
}

5.主启动类添加@RibbonClient

package com.itcast.springcloud;

import com.itcast.myrule.MySelfRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

/**
 * @author henry
 * @version 1.0
 * @date 2021/8/28 0028 21:11
 * 在启动该微服务的时候就能去加载我们的自定义Ribbon配置类,从而使配置生效,形如:
 */
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration= MySelfRule.class)
public class OrderMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class,args);
    }
}

6.测试

http://localhost/consumer/payment/get/31

四、Ribbon负载均衡算法

1.原理

在这里插入图片描述

2.源码

3.手写

(1)7001/7002集群启动
(2)8001/8002微服务Controller改造

 
package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

/**
 * @auther zzyy
 * @create 2020-01-27 21:17
 */
@RestController
@Slf4j
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;

    @Resource
    private PaymentService paymentService;

    @Resource
    private DiscoveryClient discoveryClient;

    @PostMapping(value = "/payment/create")
    public CommonResult create(@RequestBody Payment payment)
    {
        int result = paymentService.create(payment);
        log.info("*****插入操作返回结果:" + result);

        if(result > 0)
        {
            return new CommonResult(200,"插入成功,返回结果"+result+"\t 服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(444,"插入失败",null);
        }
    }

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id)
    {
        Payment payment = paymentService.getPaymentById(id);
        log.info("*****查询结果:{}",payment);
        if (payment != null) {
            return new CommonResult(200,"查询成功"+"\t 服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(444,"没有对应记录,查询ID: "+id,null);
        }
    }

    @GetMapping(value = "/payment/discovery")
    public Object discovery()
    {
        List<String> services = discoveryClient.getServices();
        for (String element : services) {
            System.out.println(element);
        }

        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance element : instances) {
            System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
                    + element.getUri());
        }
        return this.discoveryClient;
    }

    //手写负载均衡新添代码
    @GetMapping(value = "/payment/lb")
    public String getPaymentLB()
    {
        return serverPort;
    }
}

(3)80订单微服务改造
1.ApplicationContextBean去掉注解@LoadBalanced

 
package com.atguigu.springcloud.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;

/**
 * @auther zzyy
 * @create 2020-01-28 16:22
 */
@Configuration
public class ApplicationContextBean
{
    @Bean
    //@LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}

2.LoadBalancer接口

 
package com.atguigu.springcloud.lb;

import org.springframework.cloud.client.ServiceInstance;

import java.util.List;

/**
 * @auther zzyy
 * @create 2020-02-02 17:57
 */
public interface LoadBalancer
{
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}

3.MyLB

package com.atguigu.springcloud.lb;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @auther zzyy
 * @create 2020-02-02 17:57
 */
@Component
public class MyLB implements LoadBalancer
{
    private AtomicInteger atomicInteger = new AtomicInteger(0);

    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)
    {
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}

4.OrderController

 
package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.lb.LoadBalancer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.net.URI;
import java.util.List;

/**
 * @auther zzyy
 * @create 2020-01-28 16:22
 */
@RestController
public class OrderController
{
    //public static final String PAYMENT_SRV = "http://localhost:8001";
    public static final String PAYMENT_SRV = "http://CLOUD-PAYMENT-SERVICE";

    @Resource
    private RestTemplate restTemplate;
    //可以获取注册中心上的服务列表
    @Resource
    private DiscoveryClient discoveryClient;
    @Resource
    private LoadBalancer loadBalancer;

    @GetMapping("/consumer/payment/create")
    public CommonResult<Payment> create(Payment payment)
    {
        return restTemplate.postForObject(PAYMENT_SRV+"/payment/create",payment,CommonResult.class);
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult<Payment> getPayment(@PathVariable("id") Long id)
    {
        return restTemplate.getForObject(PAYMENT_SRV+"/payment/get/"+id,CommonResult.class);
    }

    @GetMapping("/consumer/payment/getForEntity/{id}")
    public CommonResult<Payment> getPayment2(@PathVariable("id") Long id)
    {
        ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_SRV+"/payment/get/"+id, CommonResult.class);
        if(entity.getStatusCode().is2xxSuccessful()){
            return entity.getBody();
        }else {
            return new CommonResult(444, "操作失败");
        }
    }
 
 
    @Resource
    private LoadBalancer loadBalancer;

    @GetMapping("/consumer/payment/lb")
    public String getPaymentLB()
    {
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");

        if(instances == null || instances.size()<=0) {
            return null;
        }
        ServiceInstance serviceInstance = loadBalancer.instances(instances);
        URI uri = serviceInstance.getUri();

        return restTemplate.getForObject(uri+"/payment/lb",String.class);
    }
}

5.测试

http://localhost/consumer/payment/lb

ps:尚硅谷SpringCloud学习笔记

SpringCloud(第一章 零基础理论入门)
SpringCloud(第二章 从2.2.x和H版开始说起)
SpringCloud(第三章 关于Cloud各种组件的停更/升级/替换)
SpringCloud(第四章 服务架构编码构建)
SpringCloud(第五章 Eureka服务注册与发现)
SpringCloud(第六章 Zookeeper服务注册与发现)
SpringCloud(第七章 Consul服务注册与发现)
SpringCloud(第八章 Ribbon负载均衡服务调用)
SpringCloud(第九章 OpenFeign服务接口调用)
SpringCloud(第十章 Hystrix断路器)
SpringCloud(第十一章 Gateway新一代网关)
SpringCloud(第十二章 SpringCloud Config 分布式配置中心)
SpringCloud(第十三章 SpringCloud Bus 消息总线)
SpringCloud(第十四章 SpringCloud Stream 消息驱动)
SpringCloud(第十五章 SpringCloud Sleuth 分布式请求链路跟踪)
SpringCloud(第十六章 SpringCloud Alibaba 入门简介)
SpringCloud(第十七章 SpringCloud Alibaba Nacos服务注册和配置中心)
SpringCloud(第十八章 SpringCloud Alibaba Sentinel实现熔断与限流)
SpringCloud(第十九章 SpringCloud Alibaba Seata处理分布式事务)
SpringCloud(第二十章 SpringCloud之雪花算法)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值