Ribbon load balancing of spring cloud and Feign consumer call service

Posted by NoDoze on Sat, 11 Jan 2020 11:25:19 +0100

Article directory

1. Microservice calls Ribbon

brief introduction

We have learned about eureka service registration and discovery before, but we haven't learned about service invocation combined with eureka cluster.
In this case, we need to use the Ribbon, combined with eureka, to realize the service call;

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.

Preliminary application

Ribbon is the client side load balancing, so it must integrate the consumer side, that is, the consumer side
Modify microservice-student-consumer-80
pom.xml adds ribbon related 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>

Add in application.yml

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

The ribbon calls the service provider in combination with eureka;
Spring cloudconfig needs to add a load balancing configuration @ LoadBalanced


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

Add configuration to application.yml of service provider microservice-student-provider-1001, and specify the following application name:

application:
    name: microservice-student


To modify the PRE_HOST of StudentConsumerController, change it to the specified microservice application name;
Our 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 is completed, you can test it
Start three eureka first, then the service provider, then the service consumer;

If the following result appears, the configuration is successful

2. Ribbon load balancing

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

copy all packages in microservice-student-provider-1001 to microservice-student-provider
Then related pom

<?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.wr</groupId>
        <artifactId>T237microservice</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>microservice-student-provider</artifactId>


    <properties>
        <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>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.wr</groupId>
            <artifactId>microservice-common</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </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/xm?useUnicode=true&characterEncoding=utf8
    username: root
    password: 1234
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  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.wr.com:2001/eureka/,http://eureka2002.wr.com:2002/eureka/,http://eureka2003.wr.com:2003/eureka/

info:
  groupId: com.wr.testSpringcloud
  artifactId: microservice-student-provider-1001
  version: 1.0-SNAPSHOT
  userName: http://wr.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/xm?useUnicode=true&characterEncoding=utf8
    username: root
    password: 1234
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  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.wr.com:2001/eureka/,http://eureka2002.wr.com:2002/eureka/,http://eureka2003.wr.com:2003/eureka/

info:
  groupId: com.wr.testSpringcloud
  artifactId: microservice-student-provider-1002
  version: 1.0-SNAPSHOT
  userName: http://wr.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/xm?useUnicode=true&characterEncoding=utf8
    username: root
    password: 1234
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  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.wr.com:2001/eureka/,http://eureka2002.wr.com:2002/eureka/,http://eureka2003.wr.com:2003/eureka/

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

Startup class

package com.wr.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.wr.*.*")
@EnableEurekaClient
@SpringBootApplication
public class MicroserviceStudentProviderApplication {

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

}

StudentProviderController.java

@Value("${server.port}")
private String port;

@RequestMapping("/ribbon")
public String ribbon(){
    return "Job number ["+port+"]Serving you";
}

StudentConsumerController.java

@RequestMapping("/ribbon")
public String ribbon(){
    return restTemplate.getForObject(SERVER_IP_PORT + "/student/ribbon", String.class);
}

Last configuration, start (start the registry, then the service producer, and finally the service consumer)
The above is just a preliminary example. It has not really used ribbon load balancing yet. Now we need to build three service provider clusters

Final test results
Eureka Registration Center: http://eureka2001.wr.com:2001/

Service producer:

Service consumer: http://localhost/student/list
http://localhost/student/ribbon

If a service producer, provider1001aapplication, is disconnected at this time, and the 1001 producer may be connected when executing http://localhost/student/ribbon, it will be down

This is also true when executing http://localhost/student/list

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. So let's take a look at the strategies that ribbon provides by default??
We use custom polling algorithm here
Service consumer SpringCloudConfig configuration class
Specify the IRule implementation;

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 passage seems official. 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 inconvenient for unified maintenance. When Feign comes, it's necessary to unify the request to make a service as FeignClient, and then other calls to the Controller need to be used 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;
Create a new StudentClientService interface;

package com.wr.microservicecommon.service;

import com.wr.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();
}

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 dependence

<?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.wr</groupId>
        <artifactId>T237microservice</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>microservice-student-consumer-feign-80</artifactId>

    <properties>
        <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>
        <!--  Effective immediately after modification, hot deployment  -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</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.wr</groupId>
            <artifactId>microservice-common</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

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

</project>

SpringCloudConfig.java

package com.wr.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 call rules (the service provider will not call after dropping the line to solve the polling problem)
     * @return
     */
    @Bean
    public IRule myRule(){
        return new RetryRule();
//        return new RandomRule();
    }
}

yml file

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

Modify startup class

package com.wr.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.wr.*.*")
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class MicroserviceStudentConsumerFeign80Application {

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

}

StudentConsumerController.java

package com.wr.microservicestudentconsumerfeign80.controller;

import com.wr.microservicecommon.entity.Student;
import com.wr.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 with Fiegn, the restTemplate is removed, and the service is injected instead, and the service method is called to realize the service call;
Finally, test the load balance;
The result is consistent with the figure above, except that when one of the services goes down, the other two can be called directly and continue to use.

Published 67 original articles, won praise 2, visited 887
Private letter follow

Topics: Spring Java Maven MySQL