This article has participated in the activity of "New person creation Ceremony", and started the road of digging gold creation together.

1, Robbon

1.1 overview of the Ribbon

What is the Ribbon?

  • SpringCloud-RibbonIs based onNetflix RibbonImplementation of a set of client load balancing tools.
  • In a nutshell,RibbonisNetflixOpen source project, the main function is to provide client-side software load balancing algorithm and service invocation.RibbonThe client component provides a comprehensive set of configurations such as connection timeout, recovery, and so on. Simply put, it is listed in the configuration fileLoad Balancer(hereinafter referred to asLB) All the machines in the back,RibbonWill automatically help you to connect to these machines based on certain rules (such as simple polling, random connections, etc.). We’re easy to useRibbonImplement a customized load balancing algorithm.
  • In a word, the load-balancing +RestTemplate call.

(2) Ribbon’s official website

  • The website address
  • It has also entered maintenance mode

(3) Load balancing (LB)

  • Load balancing is to evenly distribute user requests to multiple services to achieve system load balancingHA(High availability). Common load balancing by softwarenginx,LVS,F5.
  1. Centralized LB: is the use of independence between the consumer and provider of the serviceLBFacilities (can be hardware, e.gF5Can also be software, such asnginx), which is responsible for forwarding access requests to service providers through certain policies.
  2. The in-process LBWill:LBLogic is integrated into the consumer, who learns from the service registry what addresses are available and then selects a suitable server from those addresses.RibbonIt’s in the processLBIt is simply a library that integrates with the consumer process to get the address of the service provider.

(4) Ribbon local load balancing client and Nginx server load balancing

  1. NginxIt’s a server load balancer. All requests from the client are handed overnginxImplement the forward request. That is, load balancing is implemented by the server.
  2. RibbonThe local load balancer, when invoking the microservice interface, is cached after the registry retrieves the list of registration informationJVMLocal, and thus implemented locallyRPCRemote service invocation technology.

1.2 Ribbon Load Balancing Demo

(1) Architecture description

  • RibbonIs a soft load balancing client component that can be used in conjunction with other clients that require it, andeurekaCombining is just one example.

The Ribbon works in two steps

  1. The first step is to chooseEurekaServer, he preferentially selects those with less load in the same areaserver.
  2. Step 2 According to the user specified policy, in theserverSelect an address from the service registry you get. Among themRibbonSeveral strategies are provided: such as polling, randomization, and weighting by response time.

(2) POM file

  • So in introducingEurekaIntegration is included in the integration packageRibbonthejarThe package.
  • So what we implemented earlier8001 and 8002Alternate access is called load balancing.

(3) RestTemplate description

  1. getForObject: Returns the object to which the data in the response body is convertedJson.
  2. getForEntity: Returns the object asResponseEntityObject that contains some important information in the response, such as the response header, response status code, and response body.
  3. postForObject
  4. postForEntity

1.3 Ribbon core component IRule

1. Main load rules

  1. RoundRobinRule: polling
  2. RandomRule: random
  3. RetryRule: according to the firstRoundRobinRuleIf obtaining the service fails, a retry is performed within a specified period of time
  4. WeightedResponseTimeRule :RoundRobinRuleThe faster the response speed, the greater the weight of the instance selection, the easier to be selected
  5. BestAvailableRule : Filters out the services that are in the circuit breaker tripping state due to multiple access failures and selects the service with the least concurrency
  6. AvailabilityFilteringRule : Filter out the faulty instances and then select the instances with smaller concurrency
  7. ZoneAvoidanceRule: Default rule, compound judgmentserverArea performance andserverAvailability select server

2. How do I replace load rules

  1. Modify the configuration under the Cloud-consumer-Order80 package.

  2. Our custom configuration classes cannot be placed in the current and sub-packages that @ComponentScan scans, otherwise our custom configuration classes will be shared by all Ribbon clients and will not be specialized.

  3. Create a new myrule subpackage under the com.xiao package.

  1. inmyruleCreate a new one under the packageMySelfRuleThe configuration class
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MySelfRule {

    @Bean
    public IRule getRandomRule(a){
        return new RandomRule(); // Create a random access load rule}}Copy the code
  1. Modify the main startup class as follows:
import com.xiao.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;

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)
public class OrderMain80 {
    public static void main(String[] args) { SpringApplication.run(OrderMain80.class,args); }}Copy the code
  1. The test results
  • The result is random access in our newly configured fashion.

1.4. Ribbon Load Balancing Algorithm

1.4.1 Principle of polling algorithm

  • Load balancing algorithm:restInterface requests % Total number of server clusters = actual server location subscript called after each service restartrestInterface count from1Start.

1.4.2 RoundRobinRule source code

import com.netflix.client.config.IClientConfig;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RoundRobinRule extends AbstractLoadBalancerRule {
    private AtomicInteger nextServerCyclicCounter;
    private static final boolean AVAILABLE_ONLY_SERVERS = true;
    private static final boolean ALL_SERVERS = false;
    private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);

    public RoundRobinRule(a) {
        this.nextServerCyclicCounter = new AtomicInteger(0);
    }

    public RoundRobinRule(ILoadBalancer lb) {
        this(a);this.setLoadBalancer(lb);
    }

    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        } else {
            Server server = null;
            int count = 0;

            while(true) {
                if (server == null && count++ < 10) {
                	// Get the service provider in up state
                    List<Server> reachableServers = lb.getReachableServers();
                    // Get all service providers
                    List<Server> allServers = lb.getAllServers();
                    int upCount = reachableServers.size();
                    int serverCount = allServers.size();
                    
                    if(upCount ! =0&& serverCount ! =0) {
                    	// Fetch the relevant service provider from the subscript obtained by the module
                        int nextServerIndex = this.incrementAndGetModulo(serverCount);
                        server = (Server)allServers.get(nextServerIndex);

                        if (server == null) {
                            Thread.yield();
                        } else {
                            if (server.isAlive() && server.isReadyToServe()) {
                                return server;
                            }

                            server = null;
                        }
                        continue;
                    }

                    log.warn("No up servers available from load balancer: " + lb);
                    return null;
                }

                if (count >= 10) {
                    log.warn("No available alive servers after 10 tries from load balancer: " + lb);
                }

                returnserver; }}}private int incrementAndGetModulo(int modulo) {
        int current;
        int next;
        do {
        	// Add first and then take the module
            current = this.nextServerCyclicCounter.get();
            next = (current + 1) % modulo;
            // CAS returns true if it succeeds, otherwise it keeps spinning
        } while(!this.nextServerCyclicCounter.compareAndSet(current, next));

        returnnext; }}Copy the code

1.4.3 Handwritten polling algorithm

1. Modify the Controller of the payment module

Add the following

@GetMapping(value = "/payment/lb")
public String getPaymentLB(a){
      return ServerPort;
}
Copy the code

2. ApplicationContextConfig removes the @loadBalanced annotation

3. The LoadBalancer interface

import org.springframework.cloud.client.ServiceInstance;

import java.util.List;

public interface LoadBalancer {
    // Collect the total number of servers that can provide services and put it in the list
    ServiceInstance instances(List<ServiceInstance> serviceInstances);

}

Copy the code

4. Write MyLB classes

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

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

@Component
public class MyLB implements LoadBalancer {

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    / / coordinates
    private final int getAndIncrement(a){
        int current;
        int next;
        do {
            current = this.atomicInteger.get();
            next = current >= 2147483647 ? 0 : current + 1;
        }while (!this.atomicInteger.compareAndSet(current,next));  // The first argument is the expected value, and the second argument is the modified value
        System.out.println("******* number of visits, number next:"+next);
        return next;
    }

    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances) {  // Get a list of machines
        int index = getAndIncrement() % serviceInstances.size(); // Get the server subscript position
        returnserviceInstances.get(index); }}Copy the code

Modify the OrderController class

import com.xiao.cloud.entities.CommonResult;
import com.xiao.cloud.entities.Payment;
import com.xiao.cloud.lb.LoadBalancer;
import lombok.extern.slf4j.Slf4j;
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.RestController;
import org.springframework.web.client.RestTemplate;

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

@RestController
@Slf4j
public class OrderController {

    // public static final String PAYMENT_URL = "http://localhost:8001";
    public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private LoadBalancer loadBalancer;

    @Resource
    private DiscoveryClient discoveryClient;

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

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

    @GetMapping("/consumer/payment/getForEntity/{id}")
    public CommonResult<Payment> getPayment2(@PathVariable("id") Long id){
        ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
        if (entity.getStatusCode().is2xxSuccessful()){
            // log.info(entity.getStatusCode()+"\t"+entity.getHeaders());
            return entity.getBody();
        }else {
            return new CommonResult<>(444."Operation failed"); }}@GetMapping(value = "/consumer/payment/lb")
    public String getPaymentLB(a){
        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); }}Copy the code

6. Test results

  • Finally, in the8001and8002Polling access between the two.
  • The console output is as follows

7. Package structure diagram