Microservice + springcloud + springcloud alibaba study notes [Use of Ribbon] (4/9)

1. Ribbon load balancing

1.1 Introduction to Ribbon

Spring Cloud Ribbon is a set of client load balancing tools based on Netflix Ribbon.

Simply put, Ribbon is an open source project released by Netflix. Its main function is to provide client-side software load balancing algorithms and service calls. The Ribbon client component provides a series of comprehensive configuration items such as connection timeout, retry, etc. Simply put, it is to list all machines behind Load Balancer (LB for short) in the configuration file, and Ribbon will automatically help you connect these machines based on certain rules (such as simple polling, random connection, etc.). We can easily use Ribbon to implement a custom load balancing algorithm

Note: Ribbon is currently in maintenance and basically not ready to update

1.2 Ribbon function

insert image description here

  • Centralized LB

That is, an independent LB facility (which can be hardware, such as F5, or software, such as Nginx) is used between the service consumer and the provider, and the facility is responsible for forwarding the access request to the service provider through a certain strategy

  • In-process LB

Integrating LB logic into the consumer, the consumer learns which addresses are available from the service registry, and then selects a suitable server from these addresses.

Ribbon is an in-process LB. It is just a class library integrated in the consumer process, and the consumer obtains the address of the service provider through it.

The difference between the two:

  1. Simply put, it is to evenly distribute user requests to multiple services, so as to achieve HA (high availability) of the system.
    Common load balancing software includes Nginx, LVS, hardware F5, etc.

  2. The difference between Ribbon local load balancing client and Nginx server load balancing

​ Nginx is a server load balancer, all client requests will be handed over to Nginx, and then Nginx will forward the request. That is, load balancing is implemented by the server.

​ Ribbon local load balancing, when calling the microservice interface, it will obtain the registration information service list from the registration center and cache it locally in the JVM, so as to realize the RPC remote service calling technology locally

Ribbon is load balancing + RestTemplate

Ribbon is actually a client component for software to achieve load balancing. It can be used in combination with other clients that require requests. Combining with Eureka is just one example

insert image description here

Ribbon is divided into two steps when working:

  1. The first step is to choose EurekaSever, which gives priority to servers with less load in the same area.
  2. The second step is to select an address in the service registration list obtained from the server according to the policy specified by the user.
    Among them, Ribbon provides a variety of strategies: such as polling, random and weighted according to response time.

1.3 Using the Ribbon:

  1. By default, when we use the new version of eureka, it integrates ribbon by default:
        <!-- 服务注册中心的客户端端 eureka-client -->
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

Ribbon is integrated in this > spring-cloud-starter-netflix-eureka-client

insert image description here
We can also manually introduce the ribbon and put it in the order module, becauseOnly when order access payLoad balancing is required.

	//ribbon依赖
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
      <version>2.2.1.RELEASE</version>
      <scope>compile</scope>
    </dependency>
  1. RestTemplate extension description

== Demo of getForObject/getForEntity methods ==

  • Demonstration of getForObject method

The object returned by getForObject is the object converted from the data in the response body, which can basically be understood as JSON

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

insert image description here

  • Demonstration of getForEntity method

The object returned by getForEntity is the ResponseEntity object, which contains some important information in the response, such as response header, response status code, response body, etc.

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

insert image description here

1.3.1 Ribbon common load balancing algorithm

IRule: select a service to be accessed from the service list according to a specific algorithm

The Rule interface has 7 implementation classes, and each implementation class represents a load balancing algorithm

Inheritance diagram:

Implement class load balancing algorithm

insert image description here
insert image description here

1.3.2 Using the Ribbon

Replacement on the load balancing algorithm

Configuration Notes:

The official documentation clearly warns:

Our custom algorithm configuration classCannot be placed under the current package and its subpackages scanned by @ComponentScan, otherwise the configuration class we customized will be shared by all Ribbon clients, and the purpose of special customization cannot be achieved

​ To put it bluntly, it cannot be placed in the package where the main startup class is located and the subpackages of the package where it is located

  1. Create a package at the same level as springcloud, named myribbonrule

sp

  1. Create a configuration class and specify a load balancing algorithm

Custom ribbon load balancing configuration class:

package com.tigerhhzz.myribbonrule;

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

/**
 * @author tigerhhzz
 * @date 2023/4/10 11:24
 */
@Configuration
public class MyselfRibbonRule {
    
    

    @Bean
    public IRule myRule() {
    
    

        return new RandomRule();   //定义为随机
    }
}

The default polling algorithm is replaced by a random algorithm

  1. Add an annotation @RibbonClient to the main startup class
package com.tigerhhzz.springcloud;

import com.tigerhhzz.myribbonrule.MyselfRibbonRule;
import lombok.extern.slf4j.Slf4j;
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;

@Slf4j
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PROVIDER-SERVICE",configuration = MyselfRibbonRule.class)
public class OrderMain80{
    
    
    public static void main(String[] args){
    
    
        SpringApplication.run(OrderMain80.class,args);
        log.info("OrderMain80启动成功~~~~~~~~~~~~~~~~~~~");
    }
}

//name:代表哪个服务提供者要使用我们配置的算法
//configuretion:指定我们的算法配置类

Indicates that when accessing the service of CLOUD-PROVIDER-SERVICE, use our custom load balancing algorithm

1.3.3 Polling algorithm principle of ribbon

insert image description here

1.3.4 Handwriting a load balancing polling algorithm
  1. Transformation module (8001,8002), the controller method

Add a method to the controller method of the module (8001,8002) to return the current node port

    @GetMapping("/payment/lb")
    public String getPaymentLB(){
    
    
        return serverPort;
    }
  1. Modify the order80 module

Remove the @LoadBalanced annotation

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

/**
 * @author tigerhhzz
 * @date 2023/4/8 22:52
 */
@Configuration
public class ApplicationContextConfig {
    
    

    @Bean
    //@LoadBalanced   //注释掉这个注解
    public RestTemplate getRestTemplate(){
    
    
        return new RestTemplate();
        /*
        RestTemplate提供了多种便捷访问远程http服务的方法,
        是一种简单便捷的访问restful服务模板类,是spring提供的用于rest服务的客户端模板工具集
        */
    }
}
  1. Create a new custom ribbon load balancing interface
package com.tigerhhzz.myloadbanlance;

import org.springframework.cloud.client.ServiceInstance;

import java.util.List;

/**
 * @author tigerhhzz
 * @date 2023/4/10 17:24
 */
public interface myloadbanlance {
    
    
    ServiceInstance instance(List<ServiceInstance> instances);
}

  1. Custom ribbon load balancing interface implementation class
package com.tigerhhzz.myloadbanlance;

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

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

/**
 * @author tigerhhzz
 * @date 2023/4/10 17:25
 */
@Component
public class myloadbanlanceImpl implements myloadbanlance{
    
    


    private AtomicInteger atomicInteger = new AtomicInteger(0);

    //获取下一个要调用的服务id
    public final int getIncrement() {
    
    
        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 instance(List<ServiceInstance> instances) {
    
    
        //拿到id,进行取余得到真正要调用服务的下标
        int index = getIncrement() % instances.size();
        return instances.get(index);
    }
}

  1. Add a test interface @GetMapping("payment/lb") to the controller of the 80 module
package com.tigerhhzz.springcloud.controller;

import com.tigerhhzz.myloadbanlance.myloadbanlance;
import com.tigerhhzz.springcloud.entities.CommonResult;
import com.tigerhhzz.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

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

@RestController
@Slf4j
@RequestMapping("comsumer")
public class OrderController {
    
    

    //远程调用的 地址
    //public static final String PAYMENY_URL = "http://localhost:8001";

    //远程调用的 地址
    public static final String PAYMENT_URL = "http://cloud-provider-service";

    @Resource
    private RestTemplate restTemplate;
    @Resource
    private myloadbanlance myloadbanlance;
    @Resource
    private DiscoveryClient discoveryClient;

    @PostMapping("payment/create")
    public CommonResult<Payment> create (@RequestBody Payment payment){
    
    

        return restTemplate.postForObject(PAYMENT_URL + "/payment/create",//请求地址
                                          payment,//请求参数
                                          CommonResult.class);//返回类型
    }

    @GetMapping("payment/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id")Long id){
    
    
        return restTemplate.getForObject(PAYMENT_URL + "/payment/" + id,//请求地址
                                         CommonResult.class);//返回类型
    }

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

    @GetMapping("getForEntity/{id}")
    public CommonResult<Payment> getPaymentById2(@PathVariable("id") Long id){
    
    
        ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/"+id, CommonResult.class);
        if(entity.getStatusCode().is2xxSuccessful()){
    
    
            return entity.getBody();
        }else{
    
    
            return new CommonResult(444,"操作失败");
        }
    }

    @GetMapping("payment/lb")
    public String getPaymentLB() {
    
    
        List<ServiceInstance> instances = discoveryClient.getInstances("cloud-provider-service");
        if (instances == null || instances.size() == 0) {
    
    
            return null;
        }
        ServiceInstance serviceInstance = myloadbanlance.instance(instances);
        URI uri = serviceInstance.getUri();
        System.out.println(uri+"/payment/lb");
        return restTemplate.getForObject(uri+"/payment/lb",String.class);
//        return uri+"/payment/lb";
    }


}

1.3.5 Start the service and test

Note: The handwritten custom polling algorithm package must be placed in the same directory as the startup class, otherwise, after spring starts, the custom polling algorithm class written by itself cannot be scanned

insert image description here

insert image description here

Guess you like

Origin blog.csdn.net/weixin_43025151/article/details/130058955