Ribbon load balancing of spring cloud and Feign consumer call service

Ribbon load balancing of spring cloud and Feign consumer call service

1. Get to know the Ribbon

Ribbon is a load balancer published by Netflix, which helps to control the behavior of HTTP and TCP clients. After the service provider address is configured for the ribbon, the ribbon can automatically help service consumers to request based on some load balancing algorithm. Ribbon provides us with many load balancing algorithms by default, such as polling, random, etc. Of course, we can also implement a custom load balancing algorithm for the ribbon.
In Spring Cloud, when the Ribbon is used with Eureka, the Ribbon can automatically obtain the service provider address list from Eureka Server, and request one of the service provider instances based on the load balancing algorithm. It shows the architecture of Ribbon when used with Eureka.

Microservice calls Ribbon

Using Ribbon and eureka to call the service;

Preliminary application
Ribbon is the client side load balancing, so it must integrate the re consumer side, that is, the consumer side
We modify microservice-student-consumer-80
First, we introduce dependency, pom.xml adds ribbon dependency

<!--ribbon Dependent dependence-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

application.yml plus

server:
  port: 80
  context-path: /
eureka:
  client:
    service-url:
      defaultZone: http://eureka2001.javachz.com:2001/eureka/,http://eureka2002.javachz.com:2002/eureka/,http://eureka2003.javachz.com:2003/eureka/
    register-with-eureka: false

2. Ribbon load balancing

The ribbon calls the service provider in combination with eureka;

Spring cloudconfig also needs to add a load balancing configuration @ LoadBalanced

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

@Configuration
public class SpringCloudConfig {
    @LoadBalanced  // Introducing ribbon load balancing
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

Because it is integrated with eureka, start the class studentconsumerapplication and annotate @ EnableEurekaClient

package com.javachz.microservicestudentconsumer80;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableEurekaClient
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class MicroserviceStudentConsumer80Application {

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

}

There is another one here. To change the PRE_HOST of the StudentConsumerController to the specified microservice application name;
Of course, you need to add the configuration in application.yml of microservice-student-provider-1001, and specify the following application name:
application:
name: microservice-student

Microservice application name is microservice student
Therefore, in the controller on the service caller's side, the pre ﹣ host can be changed to http://microservice-study;
Microservice-study is the application name of Eureka registry

After the above configuration, we can test it;
Start three eureka first, then the service provider, then the service consumer;

Execute http://localhost/student/list

The result shows that the configuration is OK;

Ribbon load balancing

According to its microservice-student-provider-1001, build a microservice-student-provider subproject, and then kill the subproject microservice-student-provider-1001;

We have set up a preliminary example, but we haven't realized the real load balancing yet. We need to build three service provider clusters before we can demonstrate the load balancing and load balancing strategies;

New project microservice-student-provider-1002, microservice-student-provider-1003
Copy pom.xml, application.yml, and java classes, and change the name of the startup class;
There are two corresponding changes in the yml configuration file: port and service instance name;
The relevant codes are as follows
Related pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.javachz</groupId>
        <artifactId>microservice</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>microservice-student-provider</artifactId>

    <properties>
        <java.version>1.8</java.version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>com.javaxl</groupId>
            <artifactId>microservice-common</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>


        <!--Add registry Eureka Related configuration-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

        <!-- actuator Monitoring introduction -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

---
---
server:
  port: 1001
  context-path: /
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mysql?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 123456
  application:
    name: microservice-student
  profiles: provider-1001
eureka:
  instance:
    hostname: localhost
    appname: microservice-student
    instance-id: microservice-student:1001
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://eureka2001.javachz.com:2001/eureka/,http://eureka2002.javachz.com:2002/eureka/,http://eureka2003.javachz.com:2003/eureka/

info:
  groupId: com.javachz.testSpringcloud
  artifactId: microservice-student-provider-1001
  version: 1.0-SNAPSHOT
  userName: http://javachz.com
  phone: 123456


---
server:
  port: 1002
  context-path: /
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mysql?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 123456
  application:
    name: microservice-student
  profiles: provider-1002
eureka:
  instance:
    hostname: localhost
    appname: microservice-student
    instance-id: microservice-student:1002
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://eureka2001.javachz.com:2001/eureka/,http://eureka2002.javachz.com:2002/eureka/,http://eureka2003.javachz.com:2003/eureka/

info:
  groupId: com.javachz.testSpringcloud
  artifactId: microservice-student-provider-1002
  version: 1.0-SNAPSHOT
  userName: http://javachz.com
  phone: 123456


---
server:
  port: 1003
  context-path: /
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mysql?useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 123456
  application:
    name: microservice-student
  profiles: provider-1003
eureka:
  instance:
    hostname: localhost
    appname: microservice-student
    instance-id: microservice-student:1003
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://eureka2001.javachz.com:2001/eureka/,http://eureka2002.javachz.com:2002/eureka/,http://eureka2003.javachz.com:2003/eureka/

info:
  groupId: com.javachz.testSpringcloud
  artifactId: microservice-student-provider-1003
  version: 1.0-SNAPSHOT
  userName: http://javachz.com
  phone: 123456

Startup class

package com.javchz.microservicestudentprovider;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EntityScan("com.javachz.*.*")
@EnableEurekaClient
@SpringBootApplication
public class MicroserviceStudentProviderApplication {

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

}

StudentProviderController.java

StudentConsumerController.java

Test service provider first:
http://localhost:1001/student/list

http://localhost:1002/student/list

http://localhost:1003/student/list

See if there is any result;

Under retest, eureka:
http://eureka2001.javachz.com:2001/

http://eureka2002.javachz.com:2002/

http://eureka2003.javachz.com:2003/



Then start the service consumer:
http://localhost/student/list refresh the console several times. We see that there is a default polling policy to access the corresponding service provider;
http://localhost/student/ribbon




But this default polling strategy can't meet the actual needs. For example, there are three service providers who suddenly hang up one. In this case, the default polling always has a 1 / 3 probability of access failure;
Give an example:
Suppose a service provider is down, as shown in the figure:


There are several implementation strategies for load balancing, including:

Random
Round robin
Consistent hash
Hash (Hash)
Weighted
So let's take a look at the strategies that ribbon provides by default;

Policy name Policy statement
BestAvailableRule public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule
AvailabilityFilteringRule public class AvailabilityFilteringRule extends PredicateBasedRule
WeightedResponseTimeRule public class WeightedResponseTimeRule extends RoundRobinRule
RetryRule public class RetryRule extends AbstractLoadBalancerRule
RoundRobinRule public class RoundRobinRule extends AbstractLoadBalancerRule
RandomRule public class RandomRule extends AbstractLoadBalancerRule
ZoneAvoidanceRule public class ZoneAvoidanceRule extends PredicateBasedRule

Default 7 strategies, according to specific product needs, we choose;
Service consumer SpringCloudConfig configuration class
Specify the IRule implementation;
RetryRule is used for demonstration here. You can test it by yourself;

package com.javachz.microservicestudentconsumer80.config;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RetryRule;
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
public class SpringCloudConfig {
    @LoadBalanced  // Introducing ribbon load balancing
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
    /**
     * Custom polling algorithm
     * @return
     */
    @Bean
    public IRule myRule(){
        return new RetryRule();
    }
}

The usage is very simple;

3. Feign introduction and Application

brief introduction
A brief introduction to the declarative service call Feign;

Feign is a declarative Web Service client, which makes it easier to write a Web Service client. We just need to use feign to create an interface and configure it with annotations. It has pluggable annotation support, including feign annotation and JAX-RS annotation. Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotation to feign, and integrates Ribbon and Eureka to provide load balanced HTTP client implementation.

This seems rather muddled. Let's talk about the actual use. The previous Ribbon calls the service provider through restTemplate. The disadvantage is that the same request needs to be written multiple times, which is not convenient for unified maintenance. When Feign comes, it's directly to make a service as FeignClient, and then other calls to the Controller need to be used directly Connect the injection service and call the service method directly; meanwhile, Feign integrates the Ribbon and Eureka, so if you want to configure the load balance, you can configure the Ribbon directly, without any other special place; of course, Fiegn also integrates the service fault-tolerant protection, breaker Hystrix, later on.
application
1. Build a service in the common project (the actual project must be multiple services) as Feign client, use Feign client to call the server provider, of course, you can configure load balancing; the purpose of Feign client definition is to facilitate calls to other projects;
Modify microservice common
To introduce Feign dependency into pom.xml:

<!--Introduce Feign rely on-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

We defined FeignClient and specified the service name microservice-study to be called
After the common project is modified, under maven clean and then install;

Build the StudentClientService interface;

package com.javachz.microservicecommon.service;

import com.javachz.microservicecommon.entity.Student;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;


/**
 * Student Feign Interface client
 * @author Administrator
 *
 */
@FeignClient(value="MICROSERVICE-STUDENT")
public interface StudentClientService {
 
    /**
     * Query student information according to id
     * @param id
     * @return
     */
    @GetMapping(value="/student/get/{id}")
    public Student get(@PathVariable("id") Integer id);
     
    /**
     * Query student information
     * @return
     */
    @GetMapping(value="/student/list")
    public List<Student> list();
     
    /**
     * Add or modify student information
     * @param student
     * @return
     */
    @PostMapping(value="/student/save")
    public boolean save(Student student);
     
    /**
     * Delete student information according to id
     * @return
     */
    @GetMapping(value="/student/delete/{id}")
    public boolean delete(@PathVariable("id") Integer id);

    @RequestMapping("/student/ribbon")
    public String ribbon();
}

2. Create a Feign consumer project;
Refer to microservice-student-consumer-80 to build a microservice-student-consumer-feign-80
Copy the code, including pom.xml


pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.javachz</groupId>
        <artifactId>microservice</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>microservice-student-consumer-feign-80</artifactId>

    <properties>
        <druid.version>1.1.10</druid.version>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>


        <!--ribbon Dependent dependence-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>

        <!--Introduce Feign rely on-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.javachz</groupId>
            <artifactId>microservice-common</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        <!--  Connection pool  -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

server:
  port: 80
  context-path: /
eureka:
  client:
    service-url:
      defaultZone: http://eureka2001.javachz.com:2001/eureka/,http://eureka2002.javachz.com:2002/eureka/,http://eureka2003.javachz.com:2003/eureka/
    register-with-eureka: false

MicroserviceStudentConsumerFeign80Application startup class
Note: modify the name of the startup class, change it to studentconsumerfeignapplication? 80 with annotation, and add a annotation @ EnableFeignClients to support Feign;

package com.javachz.microservicestudentconsumerfeign80;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;

@EnableEurekaClient
@EnableFeignClients(value = "com.javachz.*.*")
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class MicroserviceStudentConsumerFeign80Application {

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

}

config level
SpringCloudConfig

package com.javachz.microservicestudentconsumerfeign80.config;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RetryRule;
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
public class SpringCloudConfig {
    @LoadBalanced  // Introducing ribbon load balancing
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
    /**
     * Custom polling algorithm
     * @return
     */
    @Bean
    public IRule myRule(){
        return new RetryRule();
    }
}

controller
StudentConsumerController

package com.javachz.microservicestudentconsumerfeign80.controller;

import com.javachz.microservicecommon.entity.Student;
import com.javachz.microservicecommon.service.StudentClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@RequestMapping("/student")
public class StudentConsumerController {

    @Autowired
    private StudentClientService studentClientService;

    @Autowired
    private RestTemplate restTemplate;

    @PostMapping(value = "/save")
    private boolean save(Student student) {
        return studentClientService.save(student);
    }

    @GetMapping(value = "/list")
    public List<Student> list() {
        return studentClientService.list();
    }

    @GetMapping(value = "/get/{id}")
    public Student get(@PathVariable("id") Integer id) {
        return studentClientService.get(id);
    }

    @GetMapping(value = "/delete/{id}")
    public boolean delete(@PathVariable("id") Integer id) {
        try {
            studentClientService.delete(id);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @RequestMapping("/ribbon")
    public String ribbon(){
        return studentClientService.ribbon();
    }
}

Because Fiegn is used now, the restTemplate is removed, and the service is injected instead, and the service method is called to implement the service call;

5. Test load balancing;
myRule of the SpringCloudConfig class. You can modify the test by yourself;

Published 64 original articles, won praise 4, visited 876
Private letter follow

Tags: Spring MySQL Java Maven

Posted on Sat, 11 Jan 2020 05:46:35 -0800 by cbcampbell