Understanding of SringCloud-Netflix-OpenFeign/Hystrix/Zuul components

1. Client load balancing—OpenFeign

1.What is Feign?

Feign is a declarative http client. Feign can be used to implement declarative REST calls. Its purpose is to make Web Service calls simpler. Feign integrates Ribbon and SpringMvc annotations, which makes Feign’s client interface look like a Controller. Feign provides a template for HTTP requests. By writing a simple interface and inserting annotations, you can define the parameters, format, address and other information of the HTTP request. Feign will completely proxy the HTTP request. We only need to call it like a method to complete the service request and related processing. At the same time, Feign integrates Hystrix, which can easily implement service interruption and downgrade.

2.Feign coding practice

Create subproject pay module

Import dependencies
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.xl</groupId>
        <artifactId>springcloud-netflix-parent</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>service-pay</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!--1. Import the EurekaClient package-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <!--2. Import Feign’s package-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <!--web package-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--pojo-common-->
        <dependency>
            <groupId>com.xl</groupId>
            <artifactId>common-pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>
Configuration class with annotations
package com.xl;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients // Scan all classes under the current package and sub-packages that contain FeginClient annotations
public class PayApp {
    public static void main(String[] args) {
        SpringApplication.run(PayApp.class);
    }
}
yml configuration
server:
  port: 1400

eureka:
  instance:
    hostname: localhost
    prefer-ip-address: true
    instance-id: service-pay:1400
  client:
    serviceUrl:
      defaultZone: http://localhost:1000/eureka/
    registry-fetch-interval-seconds: 5 #The fetch service time is five seconds
spring:
  application:
    name: service-pay #Add a name to the application instance
Writing Feign client interface

Feign’s client interface is used to call microservices. I have written a client interface here to call user services, as follows:

package com.xl.feignclients;

import com.xl.domain.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(value = "service-user")
public interface UserServiceFeignClient {

    /**
     * The request address must be consistent with the transferred party
     * The parameters must be consistent with the called party
     * The return value must be consistent with the callee
     * The request method must be consistent with the transferred party
     * Method names do not need to be consistent
     * @param id
     * @return
     */
    @GetMapping("/getUserById/{id}")
    User getUser(@PathVariable("id")Long id);
}

Explanation @FeignClient(“service-user”): service-user is the service name of the user service. Feign can find the communication address of the target service in the registration center based on the service name.

You will find that the methods in the interface are the same as those in the user service (springcloud-user-server-1200). In fact, Feign uses the methods in the client interface to determine the resource path url, parameters and return value of the target service. Here We can directly copy the controller method of the target service to be called, and then remove the method body.

Feign can find the user service according to @FeignClient(“service-user”), and find the controller method of the target service according to @GetMapping(“/getUserById/{id}”) on the method. When we use the Feign interface The parameters passed in will be used as parameters of the target service controller method, and the return value is the return value of the target service controller method.

Write controller to use Feign interface

Initiate the call directly by injecting UserFeignClient

package com.xl.controller;

import com.xl.domain.User;
import com.xl.feignclients.UserServiceFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PayController {
    @Autowired
    private UserServiceFeignClient userFeignClient; // Inject the interface to be called

    @GetMapping("/getUserById/{id}")
    public User getUserById(@PathVariable("id")Long id){
        return userFeignClient.getUser(id);
    }

}

The UserServiceFeignClient.getUserById method is called here, which looks like calling a local method. In fact, the interface has been proxied by Feign. When we initiate the call, we are actually initiating a call to the target service configured by the Feign interface.

Test

Start the registration center springcloud-eureka-server-1000 in sequence, the two user services springcloud-user-server-1200 and 1300, start the payment service springcloud-pay-server-1400, and access the pay-server controller through the browser: http:/ /localhost:1400/getUserById/1, multiple requests found that polling is still used by default.

Two: Hystrix fuse

1.Hystrix introduction

Hystrix is a very popular high-availability architecture framework open sourced by Netflix, a well-known foreign video website. Hystrix can perfectly solve a series of technical problems faced in building high-availability services in distributed system architecture, such as avalanche.

Hystrix is a framework for handling dependency isolation. It isolates failed services through circuit breakers, downgrades and other means. This will not affect the main business of the entire system (for example, if you have an infectious disease, will you be shut down? up and isolate), and it can also help us with governance and monitoring of services.

The English name of Hystrix is porcupine, and the Chinese translation is fuse. Its idea comes from the safety switch in our home. When there is a short circuit in the home, the safety switch cuts off the circuit in time to ensure the safety of the people in the home. Its purpose is to play a protective role.

The design principles of Hystrix are as follows:

  1. Prevent a single service exception from causing the entire microservice to fail.

  2. Fail fast. If the service fails, requests to the service fail quickly and the thread will not wait.

  3. If the service is degraded and the request fails, the set second-hand solution data (cover data) can be returned.

  4. The circuit breaker mechanism prevents faults from spreading and paralyzing the entire service.

  5. Service monitoring provides Hystrix Bashboard dashboard to monitor fuse status in real time

2. Functions of Hystrix

Resource isolation

Resource isolation includes thread pool isolation and semaphore isolation. Its function is to limit the resource usage of calling distributed services. Problems with a certain called service will not affect other service calls. This can be simply understood as resource isolation is to limit the number of requests.

Just like during the outbreak of pneumonia, should we restrict the movement of the population? The greater the movement, the more pneumonia patients may appear.

Thread pool isolation: Use a thread pool to store the current request, the thread pool processes the request, sets the task return processing timeout, and the accumulated requests are first entered into the thread pool queue. This method requires applying for a thread pool for each dependent service, which consumes a certain amount of resources. The advantage is that it can cope with sudden traffic (when the traffic peak comes, if the processing cannot be completed, the data can be stored in the thread pool team and processed slowly)

Semaphore isolation: Use an atomic counter (or semaphore) to record how many threads are currently running. When making a request, first judge the value of the counter. If the set maximum number of threads is exceeded, new requests of this type will be discarded. If not, then Perform a counting operation request to counter + 1, and request to return counter – 1. This method strictly controls threads and returns immediately, and cannot cope with sudden traffic (when the traffic peak comes, the number of processing threads exceeds the number, and other requests will be returned directly without continuing to request dependent services)

Service circuit breaker

The circuit breaker mechanism is a protection mechanism for service links. If a service on the link is inaccessible, the call times out, an exception occurs, etc., the service will trigger a downgrade to return the underlying data, and then circuit breaker the call of the service (the failure rate reaches a certain threshold value service is marked as short-circuit status), the service will be quickly restored when it is checked that the node can be used normally.

A simple understanding is that when the service is inaccessible or the server reports an error or the service call fails to return a result after a certain period of time, the circuit breaker mechanism is immediately triggered to cooperate with the downgrade to return the pre-prepared back-up data, so as not to cause a large number of requests due to a long wait for the service response. Blocking does not return some error information to the client, but returns some cryptic data.

Downgrade mechanism

Degraded over timeout or when resources are insufficient (threads or semaphores). After downgrading, you can cooperate with the downgrade interface to return the underpinning data.

A simple understanding is that service degradation means returning some pre-prepared data to the client when the service is unreachable due to network failure, server failure, read timeout, etc.

Service degradation can be seen everywhere in life. For example, if there is a system failure, we will return a friendly prompt “Service is temporarily unavailable.” Or, for example, Taobao’s refund service and message service are temporarily unavailable during Double 11. This is to ensure the normal shopping process. There is no problem with the service, and then some unimportant services are artificially closed, and some supporting data is returned to the user in conjunction with the downgrade (for example, a friendly prompt message “No refunds are temporarily available”).

Caching

It provides request caching and request merging implementation. In high concurrency scenarios, Hystrix request caching can easily enable and use request caching to optimize the system, thereby reducing the consumption of request threads and reducing request response time during high concurrency.

3.Hystrix working mechanism

Under normal circumstances, the circuit breaker is in the closed state (Closed). If the call continues to error or the timeout reaches the set threshold, the circuit is opened and enters the fuse state (Open). At this time, requesting this service will trigger a fast failure (return to the bottom of the data immediately, do not let it go) thread dies, etc.), all subsequent calls will be rejected (Fail Fast). After a period of time (withCircuitBreakerSleepWindowInMilliseconds=5s), the protector will try to enter the half-open state (Half-Open), allowing a small number of requests to come in and try. If If the call still fails, it will return to the fuse state. If the call is successful, it will return to the circuit closed state;

4.Hystrix coding practice (with Ribbon version)

Import dependencies
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
The main configuration class enables Hystrix
package com.xl;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableCircuitBreaker // Turn on Hystrix circuit breaker
public class OrderApp {
    public static void main(String[] args) {
        SpringApplication.run(OrderApp.class);
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
Method circuit breaker

The method is marked with the @HystrixCommand tag, and the fallbackMethod attribute of the tag specifies the mopping method. Then when an exception occurs when the method initiates a call to a remote service, or an exception occurs in the method itself, the execution of the underlay method will be triggered, and the final result is the return result of the underlay method.

package com.xl.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.xl.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class OrderController {
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/getUserById/{id}")
    @HystrixCommand(fallbackMethod = "getUserFallBack")
    public User getUser(@PathVariable("id")Long id){
        String url = "http://SERVICE-USER/getUserById/1";
        return restTemplate.getForObject(url, User.class);
    }

    //Downgrade method, parameters and return value must be consistent with the blown method, method name must be consistent with the value of fallbackMethod
    public User getUserFallBack(@PathVariable("id")Long id){
        return new User(-1L,"The service is unreachable, please try again later",-1);
    }

}
Test fuse

Start in sequence: springcloud-service-eureka-1000, springcloud-service-user-1200 and 1300, springcloud-service-order-1100

The browser accesses http://localhost:1100/order/1getUserByIdhttp://localhost:1100/order/1. When the user service springcloud-service-user-1200 starts normally, the order service can be accessed and the browser can also access it. After receiving the result, when the user service is closed, the order service will trigger the circuit breaker and return the underpinning data.

5.OpenFeign uses Hystrix

Official documentation: Spring Cloud

There is a payment module above that integrates Feign. Modify the project and use Hystrix directly.

Start Hystrix

Different from Ribbon, the Ribbon project needs to import dependencies to use Hystrix. When OpenFeign integrates Feign, the jar package contains Hystrix, so there is no need to import another jar package to introduce Hystrix. It can be opened directly in yml.

feign:
  hystrix:
    enabled: true #Enable circuit breaker support
Fiegn interface fuse-fallbackFactory method

If the service calls an exception or times out through the Feign interface, it needs to trigger downgrade and return the underpinning data. There are two ways here, namely through @FeignClient(fallback=..) and @FeignClient(fallbackFactory=..) to specify the backing class. The difference is that the backing written through fallback cannot print out the exception log. , and the fallbackFactory method can print out the exception log, we use the fallbackFactory method directly:

package com.xl.feignclients;

import com.xl.domain.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(value = "service-user",fallbackFactory = UserFeignFallBackFactory.class)
public interface UserServiceFeignClient {

    /**
     * The request address must be consistent with the transferred party
     * The parameters must be consistent with the called party
     * The return value must be consistent with the callee
     * The request method must be consistent with the transferred party
     * Method names do not need to be consistent
     * @param id
     * @return
     */
    @GetMapping("/getUserById/{id}")
    User getUser(@PathVariable("id")Long id);
}

Use the fallbackFactory attribute to specify the backing using the factory method

Write a backing class

The engineering-style backing class needs to implement the FallbackFactory interface and specify the generic as “”Feign Client Interface (UserFeignClient). The create method of FallbackFactory returns an instance of the Feign client interface. The throwable parameter of this method is the exception information of Feign call failure, as follows:

package com.xl.feignclients;

import com.xl.domain.User;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

@Component
public class UserFeignFallBackFactory implements FallbackFactory<UserServiceFeignClient> {
    @Override
    public UserServiceFeignClient create(Throwable throwable) {
        throwable.printStackTrace();
        return new UserServiceFeignClient() {
            @Override
            public User getUser(Long id) {
                return new User(-1L,"The service is unreachable, we are beating the programmers, please wait",-1);
            }
        };
    }
}
Start test

The test method is the same as above, except that when triggering the support in this way, the exception information can be seen on the console, which is convenient for us to debug.

Three: Service gateway-spring cloud zuul

1.What is zuul

Zuul is an open source API Gateway server from Netflix. It is essentially a web servlet (filter) application. Zuul provides a framework for dynamic routing (request distribution), monitoring, elasticity, security and other edge services on the cloud platform. Zuul is equivalent to the front door for all requests to the device and the web website backend of the Netflix streaming application. It must also be registered in Eureka. Use a diagram to understand the role of zuul in the architecture:

It should be noted that zuul itself is an independent service, which is integrated with Ribbon by default. zuul distributes client requests to downstream microservices through Ribbon, so zuul needs to use Eureka for service distribution, and zuul also integrates Hystrix.

According to the understanding above, we need to create an independent project to build the Zuul service, and at the same time, we need to register Zuul to EurekaServer, because when a request comes, zuul needs to obtain the downstream microservice communication address through EurekaServer and use Ribbon to initiate the call.

Construction of 2.zuul

Build module springcloud-service-zuul-1500

Modify springcloud-service-zuul-1500 to integrate EurekaClient and zuul

3. Import dependencies

Because Zuul needs to do service discovery through Eureak, we imported the basic dependencies of eureka-client and Zuul’s own basic dependencies: netflix-zuul

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.xl</groupId>
        <artifactId>springcloud-netflix-parent</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>service-zuul</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
    </dependencies>

</project>

4. Configure and enable Zuul

The configuration class enables the zuul service function through the @EnableZuulProxy annotation.

package com.xl;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

/**
 * User startup class
 * @EnableEurekaClient: Mark the application as an Eureka client
 * @EnableZuulProxy: Enabling zuul can be regarded as an enhanced version of @EnableZuulServer. This is generally used
 * @EnableZuulServer: This tag can also enable zuul, but this tag has fewer Filters enabled.
 */
@SpringBootApplication
@EnableZuulProxy
public class ZuulApp {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApp.class);
    }
}

5.application.yml configuration file configuration zuul

Two things are configured here. One is the configuration of EurekaClien, which allows zuul to register to EurekaServer. The second one is the configuration of zuul.

server:
  port: 1500
#Register to EurekaServer
eureka:
  instance:
    hostname: localhost
    prefer-ip-address: true
    instance-id: service-zuul:1500
  client:
    serviceUrl:
      defaultZone: http://localhost:1000/eureka/
spring:
  application:
    name: service-zuul #Add a name to the application instance

zuul:
  prefix: "/services" #Unified access prefix
  ignoredServices: "*" #Disable using the browser to access the service through the service name
  routes:
    service-pay: "/pay/**" #Specify the pay-server service to use the /pay path to access - alias
    service-order: "/order/**" #Specify the order-server service to use the /order path to access it
    service-user: "/user/**" #Specify the order-server service to use the /order path to access

6. Test zuul

The test is the same as above