catalogue
RestTemplate: Official Website Link
Ribbon load balancing algorithm
Introduction to Ribbon
Spring Cloud Ribbon It is a set of client load balancing tools based on Netflix Ribbon. In short, Ribbon It is an open source project released by Netflix. Its main function is to provide software load balancing algorithm and service invocation on the client. 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 certain rules (such as simple polling, random connection, etc.). It is easy for us to use the ribbon to implement a custom load balancing algorithm.
Load Balance:
Simply put, the user's requests are evenly distributed to multiple services, so as to achieve the HA (high availability) of the system. Common load balancing include software Ngnix, LVS, hardware F5, etc.
Ribbon local load balancing client VS Nginx server load balancing difference:
Nginx is server load balancing. All requests from the client will be handed over to nginx, and then nginx will forward the requests. That is, load balancing is realized by the server.
Ribbon local load balancing, when calling the micro service interface, will obtain the registration information service list in the registry and cache it to the JVM local, so as to realize the RPC remote service call technology locally.
Centralized load balancing: an independent LB facility (which can be hardware, such as F5, or software, such as Nginx) is used between the service consumer and the service provider, and the facility is responsible for forwarding the access request to the service provider through some policy;
Program load balancing: integrate LB logic into the consumer. The consumer knows which addresses are available from the service registry, and then selects an appropriate server from these addresses. Ribbon belongs to in-process lb, which is just a class library integrated into the consumer process, through which the consumer obtains the address of the service provider.
Ribbon is actually a client component of soft load balancing. It can be used in combination with other clients requiring requests. The combination with eureka is just one example.
Ribbon # works in two steps
Step 1: first select Eureka server, which gives priority to 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. The Ribbon provides a variety of strategies, such as polling, random and weighting according to the response time.
The main function of Ribbon is load balancing + RestTemplate call
Eureka contains Ribbon
Ribbon's {Maven coordinates
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-netflix-ribbon</artifactId> </dependency>
RestTemplate: Official website link
The Module adopted by Eureka above is adopted here:
cloud-provider-payment8001 cloud-provider-payment8002 cloud-consumer-order80 cloud-eureka-server7001 cloud-eureka-server7002
In the controller code of cloud-consumer-order80, modify it to the following code
@RestController @Slf4j public class OrderController { // The stand-alone version is as follows // public static final String PAYMENT_URL = "http://localhost:8001"; // Using microservices public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE"; @Resource private RestTemplate restTemplate; @GetMapping("/consumer/payment/create") public CommonResult<Payment> create(Payment payment) { return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class); // return restTemplate.postForEntity(PAYMENT_URL+"/payment/create", payment, CommonResult.class).getBody(); } @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().toString()); return entity.getBody(); }else{ return new CommonResult<>(444, "operation failed"); } } }
getForObject: the returned object is the object converted from the data in the response body, which can be basically understood as JSON
getForEntity: the returned object is ResponseEntity object, which contains some important information, including response header, response status code, response body, etc.
Ribbon core component IRule
IRule: select a service to access from the service list according to a specific algorithm
com.netflix.loadbalancer.RoundRobinRule | polling |
com.netflix.loadbalancer.RandomRule | random |
com.netflix.loadbalancer.RetryRule | First obtain the service according to the roundrobin rule policy. If the service acquisition fails, it will be retried within the specified time |
WeightedResponseTimeRule | For the extension of roundrobin rule, the faster the response speed, the greater the instance selection weight, and the easier it is to be selected |
BestAvailableRule | The service in the circuit breaker tripping state due to multiple access faults will be filtered out first, and then a service with the least concurrency will be selected |
AvailabilityFilteringRule | First filter out the fault instances, and then select the instances with smaller concurrency |
ZoneAvoidanceRule | The default rule is to judge the performance of the region where the server is located and the availability of the server, and select the server |
How to replace the default load balancing algorithm:
1. Modify cloud-consumer-order80
2. Pay attention to the configuration details:
The official document clearly gives a warning: this custom configuration class cannot be placed under the current package and sub package scanned by @ Componentcan, otherwise our custom configuration class will be shared by all Ribbon clients and the purpose of special customization will not be achieved.
3. Create a new package and its contained classes: com atyixuan. rule. MySelfRule
The MySelfRule class is defined as follows:
@Configuration public class MySelfRule { @Bean public IRule rule(){ return new RandomRule(); } }
4. Add @ RibbonClient annotation on the main startup class
@EnableEurekaClient @SpringBootApplication // Use your own defined IRule configuration class // CLOUD-PAYMENT-SERVICE indicates the service to be accessed @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class) public class OrderMain80 { public static void main(String[] args) { SpringApplication.run(OrderMain80.class, args); } }
5. Test: http://localhost/consumer/payment/get/31
Ribbon load balancing algorithm
Load balancing algorithm: the number of requests of the rest interface% the total number of server clusters = the subscript of the actual calling server location. The rest interface counts from 1 after each service restart
List<ServiceInstance> iinstances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
For example: List [0] instances = 127.0.0.1:8002
List [1] instances = 127.0.0.1:8001
8001 + 8002 are combined into clusters. There are 2 machines in total, and the total number of clusters is 2. According to the principle of polling algorithm:
When the total number of requests is 1: 1% 2 = 1 and the corresponding subscript position is 1, the service address obtained is 127.0.0.1:8001
When the total request digit is 2: 2% 2 = 0 and the corresponding subscript position is 0, the service address is 127.0.0.1:8002
When the total request digit is 3: 3% 2 = 1 and the corresponding subscript position is 1, the service address is 127.0.0.1:8001
When the total request digit is 4: 4% 2 = 0 and the corresponding subscript position is 0, the service address is 127.0.0.1:8002
And so on
A load balancing algorithm
1. Start the eureka-server7001 and cloud eureka-server7002} service registries first
2. Add in the controller of cloud provider payment8001 and cloud provider payment8002
@GetMapping(value = "/payment/lb") public String getPaymentLB(){ return serverPort; }
3. Transformation of cloud-consumer-order80
3.1. Comment out the default load balancing comment
@Configuration public class ApplicationContextConfig { @Bean // @LoadBalanced / / start load balancing public RestTemplate getRestTemplate() { return new RestTemplate(); } }
3.2. Create self-defined load balancing interface and its implementation class, com atyixuan. springcloud. lb. loadbalancer, we want the implementation class of the interface to be scanned automatically, so we put the lb package under the package of the main startup class.
public interface LoadBalancer { ServiceInstance instance(List<ServiceInstance> serviceInstances); }
@Component public class MyLB implements LoadBalancer { private AtomicInteger atomicInteger = new AtomicInteger(0); public final int getAndIncrement() { int current; int next; // Spin lock do { current = this.atomicInteger.get(); next = current >= Integer.MAX_VALUE ? 0 : current + 1; } while (!this.atomicInteger.compareAndSet(current, next)); System.out.println("*********** The first " + next + " Several visits"); return next; } @Override public ServiceInstance instance(List<ServiceInstance> serviceInstances) { int index = getAndIncrement() % serviceInstances.size(); return serviceInstances.get(index); } }
4. Modify OrderController
@RestController @Slf4j public class OrderController { // The stand-alone version is as follows // public static final String PAYMENT_URL = "http://localhost:8001"; // Using microservices 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); // return restTemplate.postForEntity(PAYMENT_URL+"/payment/create", payment, CommonResult.class).getBody(); } @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().toString()); return entity.getBody(); }else{ return new CommonResult<>(444, "operation failed"); } } @GetMapping(value = "/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.instance(instances); URI uri = serviceInstance.getUri(); return restTemplate.getForObject(uri+"/payment/lb", String.class); } }
5. Test: http://localhost/consumer/payment/lb