What is Feign

Feign is a declarative, templated HTTP client (available only in Consumer).

Second, what is declarative, what role does it play, and what problems do you solve?

Declarative invocation invokes remote methods as if they were local methods, with no awareness of remote HTTP requests.

1. The declarative invocation of SpringCloud makes it possible to use HTTP to request a remote service as if invoking a local method, without the developer being aware that it is a remote method. Even less aware that this is an HTTP request.

2. Like Dubbo, consumr calls the provider directly by calling the interface method, without having to parse the returned data through the normal Http Client construction request.

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

Write an introductory Feign case

1. The demand

Realize the basic operation of e-commerce platform

2. Project design

3. Create the product-service project

3.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-service</artifactId>
    <version>0.0.1 - the 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>
Copy the code
3.2 Creating a Service Interface
/** * Description: Commodity interface */
@RequestMapping("/product")
public interface ProductService {

    // Query all items
    @RequestMapping(value = "/findAll", method = RequestMethod.GET)
    public List<Product> findAll(a);
}
Copy the code
3.3 Creating a POJO Class
/** * Description: Commodity entity */
public class Product {

    private Integer id;
    private String name;

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

    public Integer getId(a) {
        return id;
    }

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

    public String getName(a) {
        return name;
    }

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

4. To create the Product – the Provider

4.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-provider</artifactId>
    <version>0.0.1 - the 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 - the SNAPSHOT</version>
        </dependency>
    </dependencies>

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

</project>
Copy the code
4.2 Modifying the Configuration File
Name =ego-product-provider server.port=9001 Register with all registries eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/Copy the code
4.3 write a Controller
/** * product-provider service */
@RestController
public class ProductController implements ProductService {

    @Override
    public List<Product> findAll(a) {
        ArrayList<Product> list = new ArrayList<>();
        list.add(new Product(1."Television"));
        list.add(new Product(2."Computer"));
        list.add(new Product(3."Refrigerator"));
        list.add(new Product(4."Flashlight"));
        returnlist; }}Copy the code
4.4 Writing the SpringBoot Boot Class
@EnableEurekaClient
@SpringBootApplication
public class ProviderApplication {

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

5. Create a Product – the 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 - the 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 feign coordinates -->
        <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 - the SNAPSHOT</version>
        </dependency>
    </dependencies>

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

</project>
Copy the code
5.2 Modifying a Configuration File
Name =ego-product-consumer server.port=9002 Register with all registries eureka.client.serviceUrl.defaultZone=http://user:123456@eureka1:8761/eureka/,http://user:123456@eureka2:8761/eureka/Copy the code
5.3 write a controller
/** * Product-Consumer service */
@RestController
public class ProductController {

    @Autowired
    private ProductConsumerService consumerService;
    /** * Select * from Consumer@return* /
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public List<Product> list(a){
        returnconsumerService.findAll(); }}Copy the code
5.4 write the service
// Specify the service that implements this interface
@FeignClient(name = "ego-product-provider")
public interface ProductConsumerService extends ProductService {}Copy the code
5.5 Modifying a Startup Class
// Add the following two annotations to enable support for Feign
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class ConsumerApplication {

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

4. Feign request parameter processing

1. Process a single parameter

1.1 Modifying the Product-Service project: Added the method
// Query the item based on the item id
@RequestMapping(value = "/getProductById", method = RequestMethod.GET)
RequestParam must specify parameters
public Product getProductById(@RequestParam("id") Integer id);
Copy the code
1.2 to modify the Product – the Provider
@Override
public Product getProductById(Integer id) {
    return new Product(id, "SpringCloud");
}
Copy the code
1.3 to modify the Product – Consumer
/** * Select */ from Consumer
@RequestMapping(value = "/get", method = RequestMethod.GET)
public Product getProduct(@RequestParam("id") Integer id){
    return consumerService.getProductById(id);
}
Copy the code

2. Multiple parameter processing mode 1: Get submission mode

2.1 modify the Product – Service
// Add goods, pass multiple parameters, get
@RequestMapping(value = "/add", method = RequestMethod.GET)
public Product addProduct(@RequestParam("id") Integer id, @RequestParam("name") String name);
Copy the code
2.2 to modify the Product – the Provider
@Override
public Product addProduct(Integer id, String name) {
    return new Product(id, name);
}
Copy the code
2.3 to modify the 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());
}
Copy the code

3. Multiple parameter processing mode 2: Post submission mode

3.1 modify the Product – Service
// Add goods, pass multiple parameters, post mode
@RequestMapping(value = "/add2", method = RequestMethod.POST)
public Product addProduct2(@RequestBody Product product);
Copy the code
3.2 to modify the Product – the Provider
@Override
public Product addProduct2(@RequestBody Product product) {
    return product;
}
Copy the code
3.3 to modify the Product – Consumer
/** * Add goods, pass multiple parameters, post mode */
@RequestMapping(value = "/add2", method = RequestMethod.GET)
public Product addProduct2(Product product){
    return consumerService.addProduct2(product);
}
Copy the code

5. Feign performance optimization

1. Use the Gzip compression algorithm to improve network communication speed

1.1 introduce gzip

How gZIP works: Gzip is a data format that uses deflate to compress data. Gzip is a popular file compression algorithm that is widely used, especially in Linux.

Gzip capability: This is very noticeable when gzip is compressed to a plain text file, reducing the file size by more than 70%.

What gZIP does: Compressed network data reduces the number of bytes transmitted over the network, most notably by increasing the speed of web page loading. In addition to saving traffic and improving the user’s 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 than normal manual crawling by reading gzip files directly.

1.2 Provisions on compressed transmission in HTTP

The client sends a request to the server with the accept-encoding: gzip, deflate field, indicating to the server that the client supports the compression format (gzip or deflate), and the server will not compress it if it does not send this header.

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 compresses the response packet and returns it to the client. It also carries the Content-Encoding:gzip header, which indicates that the response message is compressed according to the format.

Third: After receiving the request, the client checks whether there is a content-encoding: message header. If there is a content-encoding, decompress the message according to the changed format. Otherwise, the client processes the message as normal.

2. Write compression cases that support Gzip

2.1 Creating a Project
2.2 Modifying the Configuration File
  • Configure only the consumer to compress requests and responses through feign to the provider
# configuration request GZIP compression feign.com pression. Request. Enabled = true # configuration response GZIP compression feign.com pression. Respinse. Enabled = true # configuration compression support MIME TYPE Feign.com pression. Request. Mime - types = text/XML, application/XML, application/json # configure the minimum threshold, the compressed data size 2048 by default feign.compression.request.min-request-size=512Copy the code
  • Compress requests from the client browser and requests and responses from the consumer to the provider
# -- -- -- -- -- -- -- -- -- -- -- -- -- spring boot # whether to enable gzip compression pression. Server.com enabled = true server.compression.mime-types=application/json,application/xml,text/html,text/xml,type/plainCopy the code

3. Use HTTP connection pooling to improve Feign’s concurrent throughput

Why does HTTP connection pooling improve performance

3.1 Background principles of HTTP

A. Establishing an HTTP connection between two servers is a complex process, involving the exchange of multiple data packets and consuming a lot of time

B. HTP connection requires three handshakes and four waves, which is expensive. This overhead is higher for requests with more information but less information.

3.2 Optimize the solution

A. If we use HTTP connection pooling directly, we can save a lot of time for three handshakes and four waves, which can greatly improve throughput.

B. Campaign HTTP clients support HttpURLConnection, HttpClient, and OKHTTP. The default HTTP client is HttpURLConnection.

C. Traditional HttpURLConnection is native to the JDK and does not support connection pooling. You also need to manage the connection objects yourself. For relatively complex operations such as network requests, there is no need to manage the connection objects yourself if alternative schemes are available.

D. htpClient compared with HttpURLConnection in JDK, it encapsulates HTTP request headers, parameters, content bodies, and responses. It not only makes it easy to send HTTP requests, but also makes it easy for developers to test interfaces (based on HTTP protocol), which improves the efficiency of development, but also facilitates the robustness of code, and also improves throughput with connection pooling when a large number of requests are concurrent.

4. Change Feign’s HTTP tool to HttpClient

4.1 Creating a 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>
Copy the code
4.3 Modifying the Configuration File to enable the HttpClient Function
# enable httpClient feign.httpClient. enabled=trueCopy the code

Note: If you are using HttpClient as Feign’s client tool, you need to be careful when defining annotations on the interface. If you are passing a custom object (the object will be passed using json type), you need to add the specified type

4.4 the Product – Service
/** * Description: Commodity interface */
@RequestMapping("/product")
public interface ProductService {

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

    // Query the item based on the item id
    @RequestMapping(value = "/getProductById", method = RequestMethod.GET)
    public Product getProductById(@RequestParam("id") Integer id);

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


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

    // Use the HttpClient tool to add items, pass multiple parameters, based on Get
    @RequestMapping(value = "/add3", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE)
    public Product addProduct3(Product product);
}
Copy the code

6. View the URL, status code, and time consuming information of each interface recorded in the microservice log

1. Create projects

2. Add the logback. XML file

Set the output log level to DEBUG

<! -- Log output level -->
<root level="DEBUG">
    <appender-ref ref="Stdout" />
    <appender-ref ref="RollingFile" />
</root>
Copy the code

3. Add a method to the startup class

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

    /** * NONE: indicates that no information is recorded. Default ** BASIC: indicates that the request URL, request method, status code and time are recorded. ** FULL: indicates that the request and response are recorded
    @Bean
    public Logger.Level getLog(a){
        return Logger.Level.FULL;
    }

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

7. Set the Feign load balancing request timeout

Feign uses the Ribbon as the underlying load balancer

1. Modify the configuration file and set the timeout period

1.1 Global Configuration
Ribbon timeout =5000 Ribbon timeout =5000 ribbon timeout =5000 ribbonCopy the code
1.2 Configuring a local timeout based on the service name
Local configuration # # for all operation request retry ego - product - provider. Ribbon. OkToRetryOnAllOperations = true # retry count for the current instance Ego - product - provider. Ribbon. MaxAutoRetries = 2 # switch case retries ego - product - provider. Ribbon. MaxAutoRetriesNextServer = 0 # request connection timeout ego - product - provider. Ribbon. The ConnectTimeout = 3000 # request processing timeout ego - product - provider. Ribbon. The ReadTimeout = 3000Copy the code