4. Spring cloud: from incomprehension to abandonment, Chapter 3

Spring cloud: from incomprehension to abandonment, Chapter 3

I. Ribbon Load Balance

Reflection

What's the difference between Ribbon, Nginx and Feign


1. Introduction to Ribbon

	1) Ribbon is a set of load balancing tools for the client
	
	2) Load Balance is divided into centralized LB and in-process LB
	Centralized LB: that is, independent LB facilities (such as hardware, such as F5, or software, such as nginx) are used between the consumer and provider of the service, which is responsible for forwarding the access request to the provider of the service through a certain policy;
	In process LB: integrate the LB logic into the consumer, and the consumer will know which addresses are available from the service registry, and then choose a suitable server from these addresses. Ribbon belongs to in-process lb. it is just a class library, integrated in the consumer process, through which the consumer obtains the address of the service provider.

	3) Spring Cloud Ribbon is a set of client-side load balancing tools based on Netflix Ribbon.
	In short, Ribbon is an open-source project released by Netflix. Its main function is to provide the software load balancing algorithm of the client and connect the middle tier services of Netflix. The Ribbon client component provides a series of perfect configuration items, such as connection timeout, Retry, etc. Simply put, list all the machines behind the Load Balancer (LB) in the configuration file. The Ribbon will automatically help you connect these machines based on some rules (such as simple polling, random connection, etc.). It's also easy to use the Ribbon to implement a custom load balancing algorithm.
	


2. Preliminary configuration of Ribbon

①. maven coordinate ②. Startup class @ EnnableXXX

It should be a client-side load balancing tool, so a series of operations are carried out on cloud-consumer-dept-80.

2.1) POM modification

eureka client, Ribbon client and config client (server is included in the server)

<!-- Ribbon Relevant -->
<!--Ribbon client-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!--Eureka client-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<!--config client-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>



2.2) YML modification

New eureka service path

eureka:
  client:
    register-with-eureka: false
    service-url: 
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/


2.3). ConfigBean modification

It should be that consumer accesses service provider through restTemplate, so load balancing should be added in restTemplate

package com.lee.cloud.cfgbean;

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;

//Configuration class
@Configuration
public class ConfigBean {

    //RestTemplate provides a variety of convenient ways to access remote HTTP services
    //It is a simple and convenient access to restful service template class, which is a set of client template tools provided by spring for accessing Rest service
    //Similar to JDBC template redistemplate
    @Bean
    @LoadBalanced//load balancing
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}


2.4) modification of main startup class

@EnableEurekaClient  //Notice it's the Eureka client, not the Ribbon


2.5) modify the client access class

Because the original access is the localhost:8001 path interface, now access multiple paths

Change the path to the instance name registered by the microservice in Eureka

//    private static final String REST_URL_PREFIX = "http://localhost:8001";
    private static final String REST_URL_PREFIX = "http://CLOUD-DEPT";


Test:

1. Start cloud-eureka-7001\cloud-eureka-7002\cloud-eureka-7003
2. Start cloud-provider-dept-8001
3. Start cloud-consumer-dept-80
4. Visit http://localhost:80/consumer/dept/list

Conclusion: after the integration of Ribbon and Eureka, the Consumer can directly call the service through the service instance name without caring about the address and interface of the server (Load Balance is not used here, and cloud provider Dept will be added to verify next step)


3. Ribbon load balancing

3.1). Ribbon architecture

Ribbon works in two steps
The first step is to select Eureka server, which preferentially selects servers with less load in the same region
The second step is to select an address from 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 weighting according to response time.


3.2) add two new service providers

cloud-provider-dept-8002,cloud-provider-dept-8003

Modify their corresponding POM, YML, and startup classes

The names of the three exposed service instances must be the same


3.3) add corresponding database

It's not necessary. It's just to test load balancing and see which services are accessed

DROP DATABASE IF EXISTS cloudDB02;
CREATE DATABASE cloudDB02 CHARACTER SET UTF8;
USE cloudDB02;
CREATE TABLE dept
(
  deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  dname VARCHAR(60),
  db_source   VARCHAR(60)
);
 
INSERT INTO dept(dname,db_source) VALUES('Development Department',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('Ministry of Personnel',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('Finance Department',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('Marketing Department',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('Operation and maintenance department',DATABASE());
 
SELECT * FROM dept;

-------------------------------------------------------------

DROP DATABASE IF EXISTS cloudDB03;
CREATE DATABASE cloudDB03 CHARACTER SET UTF8;
USE cloudDB03;
CREATE TABLE dept
(
  deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  dname VARCHAR(60),
  db_source   VARCHAR(60)
);
 
INSERT INTO dept(dname,db_source) VALUES('Development Department',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('Ministry of Personnel',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('Finance Department',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('Marketing Department',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('Operation and maintenance department',DATABASE());
 
SELECT * FROM dept;


Test:

1. Start three Eureka services
2. Start 3 service providers
3. Start service consumer
4. Visit http://localhost:80/consumer/dept/list

Conclusion:
    Ribbon is actually a client component of soft load balancing,
    It can be used in combination with other clients that require requests, and combining with eureka is just one of them.


4. Ribbon core component IRule

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

There are seven default algorithms:

1. Roundrobin rule: polling

2. RandomRule: random

3. Availabilityfiltering rule: it will filter out the services in the breaker tripping state due to multiple access failures,
There are also services with concurrent connections exceeding the threshold, and then access the remaining service list according to the polling policy

4. Weighted response time rule: calculate the weight of all services according to the average response time. The faster the response time, the higher the service weight, the higher the probability of being selected If the statistics information is insufficient at the start, the roundrobin rule policy will be used. If the statistics information is enough, the system will switch to the
WeightedResponseTimeRule

5. RetryRule: first obtain the service according to the policy of roundrobin rule. If the service fails to be obtained, retry within the specified time to obtain the available service

6. BestAvailableRule: it will filter out the services in breaker trip state due to multiple access failures, and then select a service with the minimum concurrent amount

7. Zoneaavoidancerule: the default rule, which is used to determine the performance of the server area and the availability of the server


Change the load balancing mode:

Modify configBean in cloud-consumer-dept-80

package com.lee.cloud.cfgbean;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
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;

//Configuration class
@Configuration
public class ConfigBean {

    //RestTemplate provides a variety of convenient ways to access remote HTTP services
    //It is a simple and convenient access to restful service template class, which is a set of client template tools provided by spring for accessing Rest service
    //Similar to JDBC template redistemplate
    @Bean
    @LoadBalanced//Load balancing -- default roundrobin rule polling algorithm
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    //Modify to random algorithm
    @Bean
    public IRule myRule(){
        return new RandomRule();
    }

}


5. User defined load balancing algorithm

Before customizing the algorithm, let's take a look at the structure of the default algorithm

/ / polling
public class RoundRobinRule extends AbstractLoadBalancerRule
/ / random
public class RandomRule extends AbstractLoadBalancerRule 

They all inherit the AbstractLoadBalancerRule class

AbstractLoadBalancerRule: implements the IRule interface.
public abstract class AbstractLoadBalancerRule implements IRule, IClientConfigAware {
    private ILoadBalancer lb;

    public AbstractLoadBalancerRule() {
    }

    public void setLoadBalancer(ILoadBalancer lb) {
        this.lb = lb;
    }

    public ILoadBalancer getLoadBalancer() {
        return this.lb;
    }
}


 
public interface IRule {
	/ / choice
    Server choose(Object var1);
	//Set polling algorithm
    void setLoadBalancer(ILoadBalancer var1);
	//Acquisition algorithm
    ILoadBalancer getLoadBalancer();
}

So when we customize the algorithm, we can directly inherit AbstractLoadBalancerRule to implement setLoadBalancer and getLoadBalancer methods


When the microservice is started, our custom Ribbon configuration class can be loaded to make the configuration effective, as follows:
@RibbonClient(name="CLOUD-DEPT",configuration=MySelfRule.class) configuration


Be careful:

Official documents clearly give warning:
This custom configuration class cannot be placed under the current package and sub package scanned by @ ComponentScan,
Otherwise, our customized configuration class will be shared by all Ribbon clients, that is to say
We can't achieve the goal of customization.


 

Steps:

Custom algorithm:

package com.lee.myrule;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
 
@Configuration
public class MySelfRule
{
  @Bean
  public IRule myRule()
  {
   return new RandomRule_ZY();
  }
}


package com.lee.myrule
    
import java.util.List;
import java.util.Random;
 
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
 
public class RandomRule_ZY extends AbstractLoadBalancerRule {
 
  private int total = 0;    //The total number of calls. Currently, each set is required to be called 5 times
  private int currentIndex = 0;//Machine number of the current service
  
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        Server server = null;
 
        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> upList = lb.getReachableServers();
            List<Server> allList = lb.getAllServers();
 
            int serverCount = allList.size();
            if (serverCount == 0) {
                /*
                 * No servers. End regardless of pass, because subsequent passes
                 * only get more restrictive.
                 */
                return null;
            }
 
            
//            int index = rand.nextInt(serverCount);
//            server = upList.get(index);
            if(total < 5)
            {
                server = upList.get(currentIndex);
                total++;
                }else {
                total = 0;
                currentIndex++;
            if(currentIndex >= upList.size())
            {
              currentIndex = 0;
            }
            
            }
            if (server == null) {
                /*
                 * The only time this should happen is if the server list were
                 * somehow trimmed. This is a transient condition. Retry after
                 * yielding.
                 */
                Thread.yield();
                continue;
            }
 
            if (server.isAlive()) {
                return (server);
            }
 
            // Shouldn't actually happen.. but must be transient or a bug.
            server = null;
            Thread.yield();
        }
 
        return server;
 
    }
 
  @Override
  public Server choose(Object key) {
   return choose(getLoadBalancer(), key);
  }
 
  @Override
  public void initWithNiwsConfig(IClientConfig clientConfig) {
   
  }
}



Add main startup class:

@RibbonClient(name="CLOUD-DEPT",configuration=MySelfRule.class)


Test:

http://localhost:80/consumer/dept/list


II. Feign Load Balance

1. About Feign

 Feign is a declarative Web Service client. Using feign can make it easier to write a Web Service client. Its use method is to define an interface, and then add comments on it. At the same time, it also supports JAX-RS standard comments. Feign also supports pluggable encoders and decoders. Spring Cloud encapsulates feign to support Spring MVC standard annotations and HttpMessageConverters. Feign can be combined with Eureka and Ribbon to support load balancing.

 Feign is a declarative Web service client, which makes it very easy to write a Web service client,
Just create an interface and add comments on it.
Refer to the official website: https://github.com/OpenFeign/feign 
 
 What can Feign do
Feign is designed to make it easier to write Java Http clients.
When using Ribbon+RestTemplate, we used RestTemplate to encapsulate http requests, forming a set of template calling methods. However, in the actual development, because there may be more than one call to the service dependency, and often one interface will be called by more than one call, usually some client classes will be encapsulated for each microservice to package the calls of these dependent services. Therefore, Feign makes further encapsulation on this basis to help us define and implement the definition of dependent service interface. Under the implementation of Feign, we only need to create an interface and use annotation to configure it (formerly Mapper annotation on Dao interface and now Feign annotation on a microservice interface), which can complete the interface binding to the service provider, and simplify the development volume of service calling client when using Spring cloud Ribbon.
 
 
Feign integrates Ribbon
The service list information of microservicecloud Dept is maintained by the Ribbon, and the load balance of the client is realized by polling. Different from the Ribbon, feign only needs to define the service binding interface and implement the service invocation in a declarative way


Above, we use the Ribbon for load balancing, which is very powerful and even allows us to customize the algorithm. So how did Feign come out?
1. The Ribbon directly calls our microservices for access, such as
private static final String REST_URL_PREFIX = "http://CLOUD-DEPT";

But at present, we are all interface oriented programming in Xiguan, such as web service jieku, such as our DAO interface, which is already our specification.

So spring cloud provides two ways:
1. Obtain the calling address for the microservice name ----- > ribbon
2. Get our calling service through interface + annotation ----- feign


2. Feign project construction

2.1) create cloud-consumer-dept-80-feign with reference to cloud-consumer-dept-80

Note: do not copy controller

POM added

<! -- feign is related. Feign is based on the Ribbon, so you need something else from the Ribbon -- >
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>


New service

package com.lee.cloud.feign.service;

import com.lee.cloud.entity.Dept;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.util.List;

/**
 * Remote call to server interface -- the bottom layer is actually called through restTemplate
 * CLOUD-DEPT Instance name of the calling service
 */
@FeignClient(value = "CLOUD-DEPT")
public interface DeptFeignService {

    @RequestMapping(value = "/dept/get/{id}",method = RequestMethod.GET)
    public Dept get(@PathVariable("id") long id);

    @RequestMapping(value = "/dept/list",method = RequestMethod.GET)
    public List<Dept> list();

    @RequestMapping(value = "/dept/add",method = RequestMethod.POST)
    public boolean add(Dept dept);

}


controller modification

package com.lee.cloud.controller;

import com.lee.cloud.entity.Dept;
import com.lee.cloud.feign.service.DeptFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class DeptController_Consumer {

    @Autowired
    private DeptFeignService deptFeignService;

    @RequestMapping(value = "/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id)
    {
        return deptFeignService.get(id);
    }

    @RequestMapping(value = "/consumer/dept/list")
    public List<Dept> list()
    {
        return deptFeignService.list();
    }

    @RequestMapping(value = "/consumer/dept/add")
    public Object add(Dept dept)
    {
        return deptFeignService.add(dept);
    }
}


Startup class:

package com.lee.cloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;

@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients
public class DeptConsumer80Feign_App {

    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer80Feign_App.class,args);
    }
}


Note: Feign's load balancing customization is the same as that of Ribbon

Test:

1. Start three Eureka cloud-eureka-7001|7002|7003
2. Start three microservices cloud provider dept-8001 and 8002
3. Start service consumer cloud consumer dept 80 feign
4. Visit: http://localhost:80/consumer/dept/list


Tags: Programming Database Spring Load Balance Java

Posted on Fri, 01 Nov 2019 21:43:56 -0700 by jwer78