Learning spring cloud feign takes you from 0 to 1

Time:2020-9-19

1、 What is feign

Feign is a declarative, templated HTTP < font color =’Red ‘>client(only used in consumer) < / font >.

2、 What is declarative, what is its function, and what problem is solved?

Declarative calls are like callsLocal approachThe remote method is also called, and the remote HTTP request is not sensed.

1. The declarative call of spring cloud can achieve the same experience as calling local methods when using HTTP to request remote services. Developers are totally unaware of thisRemote method。 moreThis is not perceived as an HTTP request

2. Like Dubbo, consumer directly calls the interface method to call provider instead of parsing the returned data through the normal HTTP client construction request.

3. It solves the problem that developers call remote interfaces just like calling local methods, without paying attention to the details of remote interaction, let alone the development of distributed environment.

3、 How to write feign

1. Demand

Realize the basic operation of e-commerce platform

2. Project design

Learning spring cloud feign takes you from 0 to 1

3. Create the project product service

3.1 add coordinates
<?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>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.13.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.luyi</groupId>
    <artifactId>springcloud-ego-product-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-ego-product-service</name>
    <description>Demo project for Spring Boot</description>

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <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.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
    </dependencies>

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

</project>
3.2 create service interface
/**
 *Description: Product Interface
 */
@RequestMapping("/product")
public interface ProductService {

    //Query all products
    @RequestMapping(value = "/findAll", method = RequestMethod.GET)
    public List<Product> findAll();
}
3.3 create POJO class
/**
 *Description: Commodity entity
 */
public class Product {

    private Integer id;
    private String name;

    public Product() {
    }

    public Product(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

4. Create product provider

4.1 add coordinates
<?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>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.13.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.luyi</groupId>
    <artifactId>springcloud-ego-product-provider</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-ego-product-provider</name>
    <description>Demo project for Spring Boot</description>

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <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.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

        <!--product service-->
        <dependency>
            <groupId>com.luyi</groupId>
            <artifactId>springcloud-ego-product-service</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

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

</project>
4.2 modify configuration file
spring.application.name=ego-product-provider
server.port=9001

#Set the address of service registry and register with all registries
eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/
4.3 write controller
/**
 *Product provider service
 */
@RestController
public class ProductController implements ProductService {

    @Override
    public List<Product> findAll() {
        ArrayList<Product> list = new ArrayList<>();
        list.add (new product (1, "TV");
        list.add (new product (2, "computer");
        list.add (new product (3, "refrigerator");
        list.add (new product (4, "flashlight");
        return list;
    }
}
4.4 writing the startup class of springboot
@EnableEurekaClient
@SpringBootApplication
public class ProviderApplication {

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

}

5. Create product consumer

5.1 adding coordinates
<?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>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.13.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.luyi</groupId>
    <artifactId>springcloud-ego-product-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloud-ego-product-consumer</name>
    <description>Demo project for Spring Boot</description>

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Dalston.SR5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <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.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <! -- add the coordinates of feign -- >
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>

        <!--product service-->
        <dependency>
            <groupId>com.luyi</groupId>
            <artifactId>springcloud-ego-product-service</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

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

</project>
5.2 modify configuration file
spring.application.name=ego-product-consumer
server.port=9002

#Set the address of service registry and register with all registries
eureka.client.serviceUrl.defaultZone=http://user:[email protected]:8761/eureka/,http://user:[email protected]:8761/eureka/
5.3 writing controller
/**
 *Product consumer service
 */
@RestController
public class ProductController {

    @Autowired
    private ProductConsumerService consumerService;
    /**
     *How to query all products in consumer
     * @return
     */
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public List<Product> list(){
        return consumerService.findAll();
    }
}
5.4 writing service
//Specifies the service that implements the interface
@FeignClient(name = "ego-product-provider")
public interface ProductConsumerService extends ProductService {
}
5.5 modify startup class
//Add the following two annotations to enable feign support
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {

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

}

4、 Feign’s request parameter processing

1. Single parameter processing

1.1 modify the product service project and add methods
//Query products according to the commodity ID
@RequestMapping(value = "/getProductById", method = RequestMethod.GET)
//Requestparam must specify parameters
public Product getProductById(@RequestParam("id") Integer id);
1.2 modify product provider
@Override
public Product getProductById(Integer id) {
    return new Product(id, "SpringCloud");
}
1.3 modify product consumer
/**
 *In the consumer, the product is queried according to the commodity ID
 */
@RequestMapping(value = "/get", method = RequestMethod.GET)
public Product getProduct(@RequestParam("id") Integer id){
    return consumerService.getProductById(id);
}

2. Multiple parameter processing method 1: get submit mode

2.1 modify product service
//Add goods, pass multiple parameters, get method
@RequestMapping(value = "/add", method = RequestMethod.GET)
public Product addProduct(@RequestParam("id") Integer id, @RequestParam("name") String name);
2.2 modify product provider
@Override
public Product addProduct(Integer id, String name) {
    return new Product(id, name);
}
2.3 modify product consumer
/**
 *Add goods, pass multiple parameters, get method
 */
@RequestMapping(value = "/add", method = RequestMethod.GET)
public Product addProduct(Product product){
    return consumerService.addProduct(product.getId(), product.getName());
}

3. Multiple parameter processing method 2: Post submission method

3.1 modify product service
//Add goods, pass multiple parameters, post method
@RequestMapping(value = "/add2", method = RequestMethod.POST)
public Product addProduct2(@RequestBody Product product);
3.2 modify product provider
@Override
public Product addProduct2(@RequestBody Product product) {
    return product;
}
3.3 modify product consumer
/**
 *Add goods, pass multiple parameters, post mode
 */
@RequestMapping(value = "/add2", method = RequestMethod.GET)
public Product addProduct2(Product product){
    return consumerService.addProduct2(product);
}

5、 Performance optimization of feign

1. Through gzip compression algorithm, improve the network communication speed

1.1 gzip introduction

Gzip principle: gzip is adata formatGzip is a popular file compression algorithm, which is widely used, especially in Linux platform.

Gzip capability: when gzip is compressed to aPlain text fileThe effect is very obvious, about 70% of the file size can be reduced.

The role of gzip: after the network data is compressedIt also reduces the number of bytes transmitted over the networkThe most obvious is thatIt can improve the speed of web page loading。 The advantages of faster web loading speed are self-evident. In addition to saving traffic and improving users’ browsing experience, another potential benefit is that gzip has a better relationship with search engine extraction tools. Google, for example, can retrieve web pages faster by reading gzip files than by hand.

1.2 provisions on compressed transmission in HTTP protocol

First, the client requests the server with:Accept-Encoding:gzip, deflate field, indicating to the server that the compression format (gzip or deflate) supported by the client will not be compressed if the header is not sent.

Second: after receiving the request, if the server finds that the request header contains the accept encoding field and supports this type of compression, it will compress the response message and return it to the client.And carry content- Encoding:gzip Message headerIndicates that the response message is compressed according to the format.

Third: after receiving the request, the client first judges whether there is oneContent encoding: message headerIf any, decompress the message according to the changed format, otherwise it will be treated as normal message.

2. Write compression cases supporting gzip

2.1 create project
2.2 modify configuration file
  • Only the consumer is configured to compress the request and response from feign to provider
#Configure request gzip compression
feign.compression.request.enabled=true
#Configure response gzip compression
feign.compression.respinse.enabled=true
#Configure compression to support MIME type
feign.compression.request.mime-types=text/xml,application/xml,application/json
#Configure the minimum threshold of compressed data size. The default value is 2048
feign.compression.request.min-request-size=512
  • Compress the request of client browser and the request and response of consumer to provider
#-------------spring boot gzip
#Is compression enabled
server.compression.enabled=true
server.compression.mime-types=application/json,application/xml,text/html,text/xml,type/plain

3. Using HTTP connection pool to improve the concurrent throughput of feign

Why can HTTP connection pool improve performance

3.1 background principle of HTTP

A. the process of establishing HTTP connection between two servers is a very complex process, which involves the exchange of multiple data packets, and it also consumes time

B. HTTP connection requires three handshakes and four waves, which is costly. The cost is higher for requests with more requests but less information.

3.2 optimization solution

A. if we directly adopt HTTP connection pool, we can save a lot of time for three handshakes and four wave hands, which can greatly improve the throughput.

B. feign’s HTTP client supports three frameworks: httpurlconnection, httpclient and okhttp. The default is httpurlconnection.

C. The traditional httpurlconnection comes with JDK and does not support connection pooling. If you want to implement the connection pool mechanism. You also need to manage the connection objects yourself. For the network request, which is a relatively complex operation at the bottom, if there are other schemes available, there is no need to manage the connection objects by yourself.

Compared with httpurlconnection, d.httpclient encapsulates the request header, parameter, content body, response and so on to access http. It not only makes it easy to send HTTP requests, but also makes it convenient for developers to test the interface (based on HTTP protocol). That is to say, it improves the efficiency of development and the robustness of the code. In addition, when there are a large number of concurrent requests, the connection pool is still used to improve the throughput.

4. Modify feign’s HTTP tool to httpclient

4.1 create project
4.2 adding coordinates
<! -- Apache httpclient replaces feign native httpurlconnection -- >
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>
<dependency>
    <groupId>com.netflix.feign</groupId>
    <artifactId>feign-httpclient</artifactId>
    <version>8.17.0</version>
</dependency>
4.3 modify the configuration file to enable the use of httpclient
#Enable httpclient
feign.httpclient.enabled=true

Note: if you use httpclient as feign’s client tool, you need to pay attention when defining the annotation on the interface. If you pass a custom object (the object will be passed by using JSON type), you need to add the specified type

4.4 Product-Service
/**
 *Description: Product Interface
 */
@RequestMapping("/product")
public interface ProductService {

    //Query all products
    @RequestMapping(value = "/findAll", method = RequestMethod.GET)
    public List<Product> findAll();

    //Query products according to the commodity ID
    @RequestMapping(value = "/getProductById", method = RequestMethod.GET)
    public Product getProductById(@RequestParam("id") Integer id);

    //Add goods, pass multiple parameters, get method
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public Product addProduct(@RequestParam("id") Integer id, @RequestParam("name") String name);


    //----------------------------------HttpClient------------------------------------
    //Add goods, pass multiple parameters, post method
    @RequestMapping(value = "/add2", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
    public Product addProduct2(@RequestBody Product product);

    //Use httpclient tool to add products and pass multiple parameters based on get method
    @RequestMapping(value = "/add3", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE)
    public Product addProduct3(Product product);
}

6、 Check the microservice log to record the URL, status code and time-consuming information of each interface

1. Create project

2. Add logback.xml file

Set the output log level toDEBUG

<! -- log output level -- >
<root level="DEBUG">
    <appender-ref ref="Stdout" />
    <appender-ref ref="RollingFile" />
</root>

3. Add a method to the startup class

//Add the following two annotations to enable feign support
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {

    /**
     *None: do not record any information, default value
     *Basic: record the request URL, request method, status code and usage time
     *Heads: record some common information based on basic
     *Full: record all information of the request and response
     */
    @Bean
    public Logger.Level getLog(){
        return Logger.Level.FULL;
    }

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

}

7、 Configure feign load balancing request timeout

The bottom layer of feign’s load balancing is ribbon

1. Modify the configuration file and set the timeout

1.1 global configuration
#Global configuration
#The timeout time of the request connection. The default value is 1000ms
ribbon.ConnectTimeout=5000
#Timeout for processing requests
ribbon.ReadTimeout=5000
1.2 configure the local timeout according to the service name
#Local configuration
#All operation requests are retried
ego-product-provider.ribbon.OkToRetryOnAllOperations=true
#The number of retries to the current instance
ego-product-provider.ribbon.MaxAutoRetries=2
#The number of retries to switch the instance
ego-product-provider.ribbon.MaxAutoRetriesNextServer=0
#Timeout for requesting a connection
ego-product-provider.ribbon.ConnectTimeout=3000
#Timeout for request processing
ego-product-provider.ribbon.ReadTimeout=3000

Recommended Today

Solutions to leetcode problems and knowledge points 1. Sum of two numbers

Title Link 1. Two Sum  difficulty: $/ color {00965e} {easy}$ Knowledge points 1. Hash (hash) function The classic hash function times33 is widely used, and the core algorithm is as follows: hash(i) = hash(i-1) * 33 + str[i] Laruence has an article about:Hash algorithm in PHP 2. Hash collision processing method 2.1 chain address method […]