Hystix, Feign, Zuul gateway

1 Hystix

1.1 INTRODUCTION

Hystix, i.e. fuse.
Home page: https://github.com/Netflix/Hystrix/ As shown in the figure:
Hystix is an open-source delay and fault-tolerant Library of Netflix, which is used to isolate access to remote services and third-party libraries to prevent cascading failures. As shown in the figure:

1.2. Working mechanism of fuse

As shown in the figure:
In normal operation, the client requests to call the service API interface. As shown in the figure:
When a service has an exception, it will directly roll back the failure and degrade the service. As shown in the figure:

1.3 hands on practice

1.3.1. Import dependency

1. First, introduce the code of Hystix dependency in user consumer as follows:

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

As shown in the figure:
2. Add the annotation as shown in the figure in the main startup class;
3. The code of UserDao is as follows:

package com.txw.consumerdemo.dao;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.txw.consumerdemo.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
/**
 * User persistence layer
 * @author Adair
 * E-mail: 1578533828@qq.com
 */
@SuppressWarnings("all")   // Annotation warning message
@Component
public class UserDao {

    @Autowired
    private RestTemplate restTemplate;

    private static final Logger logger = LoggerFactory.getLogger(UserDao.class);

    @HystrixCommand(fallbackMethod = "queryUserByIdFallback")
    public User queryUserById(Long id){
        long begin = System.currentTimeMillis();
        String url = "http://user-service/user/" + id;
        User user = this.restTemplate.getForObject(url, User.class);
        long end = System.currentTimeMillis();
        // Record access time:
        logger.info("Access time:{}", end - begin);
        return user;
    }

    public User queryUserByIdFallback(Long id){
        User user = new User();
        user.setId(id);
        user.setName("Exception in user information query!");
        return user;
    }
}

As shown in the figure:

@Component
public class UserDao {

    @Autowired
    private RestTemplate restTemplate;

    private static final Logger logger = LoggerFactory.getLogger(UserDao.class);

    @HystrixCommand(fallbackMethod = "queryUserByIdFallback")
    public User queryUserById(Long id){
        long begin = System.currentTimeMillis();
        String url = "http://user-service/user/" + id;
        User user = this.restTemplate.getForObject(url, User.class);
        long end = System.currentTimeMillis();
        // Record access time:
        logger.info("Access time:{}", end - begin);
        return user;
    }

    public User queryUserByIdFallback(Long id){
        User user = new User();
        user.setId(id);
        user.setName("Exception in user information query!");
        return user;
    }
}
  • @HystrixCommand(fallbackMethod="queryUserByIdFallback"): declare a failed rollback processing function queryUserByIdFallback. When the execution of queryUserById times out (1000 ms by default), the fallback function will be executed and an error message will be returned.
  • In order to check the trigger time of the fuse, we record the request access time.
    The code used to call the DAO in the original business logic is as follows:
package com.txw.consumerdemo.service;

import com.txw.consumerdemo.dao.UserDao;
import com.txw.consumerdemo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
 * User business layer
 * @author Adair
 * E-mail: 1578533828@qq.com
 */
@SuppressWarnings("all")   // Annotation warning message
@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    public List<User> queryUserByIds(List<Long> ids) {
        List<User> users = new ArrayList<>();
        ids.forEach(id -> {
            // We test multiple queries,
            users.add(this.userDao.queryUserById(id));
        });
        return users;
    }
}

As shown in the figure:

1.3.2 transformation service provider

Transform the service provider and sleep randomly for a period of time to trigger the fuse. The code is as follows:

package com.txw.userservice.service;

import com.txw.userservice.entity.User;
import com.txw.userservice.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Random;
/**
 * User business layer
 * @author Adair
 * E-mail: 1578533828@qq.com
 */
@Service
@SuppressWarnings("all")   // Annotation warning message
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public User queryById(Long id) throws InterruptedException {
        // In order to demonstrate the timeout phenomenon, we use thread hibernation here, with a random time of 0 ~ 2000 milliseconds
        Thread.sleep(new Random().nextInt(2000));
        return this.userMapper.selectByPrimaryKey(id);
    }
}

As shown in the figure:
Start the project and test, as shown in the figure:

1.3.3 optimization

Although it has been implemented, our retry mechanism does not seem to be effective, is that right?
In fact, this is because our Ribbon timeout is set to 1000ms, as shown in the figure:
The timeout of Hystix is also 1000ms by default, so the retry mechanism is not triggered, but the fuse is triggered first.
Therefore, the timeout of Ribbon must be less than that of Hystix.
We can set the Hystrix timeout through hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds.

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMillisecond: 3000 # Set the timeout of hystrix to 3000ms

As shown in the figure:

2 Feign

In the previous study, we used Ribbon's load balancing function to greatly simplify the code when calling remotely:

String baseUrl = "http://user-service/user/";
User user = this.restTemplate.getForObject(baseUrl + id, User.class)

If you learn this, you may need to write a lot of similar repetitive code in the future. The format is basically the same, but the parameters are different. Is there a more elegant way to optimize this code again?
This is the function of Feign we will learn next.

2.1 introduction

English interpretation of Youdao Dictionary:
Why camouflage?
Feign can hide the Rest request and disguise it as a Controller similar to spring MVC. You don't have to splice URLs, splice parameters and so on. Let feign do everything.

Project home page: https://github.com/OpenFeign/feign , as shown in the figure:

2.2 quick start

1. Introduce the following dependent Codes:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

As shown in the figure:
2. Write the code of UserFeignClient as follows:

package com.txw.consumerdemo.feign;

import com.txw.consumerdemo.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
 * Feign Client for
 * @author Adair
 * E-mail: 1578533828@qq.com
 */
@FeignClient("user-service")
public interface UserFeignClient {

    @GetMapping("/user/{id}")
    User queryUserById(@PathVariable("id") Long id);
}

As shown in the figure:

  • First, this is an interface. Feign will help us generate implementation classes through dynamic proxies. This is very similar to mybatis's mapper.
  • @FeignClient, declare that this is a Feign client, similar to the @ Mapper annotation. At the same time, specify the service name through the value attribute.
  • The definition methods in the interface completely adopt the annotations of spring MVC. Feign will help us generate URL s according to the annotations and access the results.
    3. Write the UserService code as follows:
package com.txw.consumerdemo.service;

import com.txw.consumerdemo.entity.User;
import com.txw.consumerdemo.feign.UserFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
 * User business layer
 * @author Adair
 * E-mail: 1578533828@qq.com
 */
@SuppressWarnings("all")   // Annotation warning message
@Service
public class UserService {

    @Autowired
    private UserFeignClient userFeignClient;

    public List<User> queryUserByIds(List<Long> ids) {
        List<User> users = new ArrayList<>();
        ids.forEach(id -> {
            // We test multiple queries,
            users.add(this.userFeignClient.queryUserById(id));
        });
        return users;
    }
}

As shown in the figure:
We add comments on the startup class, and the code for starting Feign function is as follows:

package com.txw.consumerdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableHystrix
@EnableFeignClients // Enable Feign function
@EnableDiscoveryClient // Enable EurekaClient function
@SpringBootApplication
public class ConsumerDemoApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ConsumerDemoApplication.class, args);
    }
}

As shown in the figure:
You will find that I deleted the registration of RestTemplate. Ribbon load balancing has been automatically integrated in Feign, so we don't need to define our own RestTemplate.
Start the project, and the test results are shown in the figure:

The results were obtained normally.

2.3 load balancing

Ribbon dependency and automatic configuration have been integrated in Feign itself, as shown in the figure:
Therefore, we do not need to introduce additional dependencies or register the RestTemplate object.
In addition, we can perform global configuration through ribbon.xx. You can also configure the specified service through the service name. Ribbon.xx. The code is as follows:

user-service:
  ribbon:
    ConnectTimeout: 250 # Connection timeout (ms)
    ReadTimeout: 1000 # Communication timeout (ms)
    OkToRetryOnAllOperations: true # Retry all operations
    MaxAutoRetriesNextServer: 1 # Number of retries for different instances of the same service
    MaxAutoRetries: 1 # Number of retries for the same instance

2.4 Hystix support

However, it is off by default. We need to enable it through the following parameters:

feign:
  hystrix:
    enabled: true # Turn on the fusing function of Feign

However, the Fallback configuration in Feign is not as simple as that in Ribbon.
1) First, we need to define a class to implement the UserFeignClient just written. The code as the fallback processing class is as follows:

package com.txw.consumerdemo.feign;

import com.txw.consumerdemo.entity.User;
import org.springframework.stereotype.Component;
/**
 * @author Adair
 * E-mail: 1578533828@qq.com
 */
@Component
public class UserFeignClientFallback implements UserFeignClient {
    @Override
    public User queryUserById(Long id) {
        User user = new User();
        user.setId(id);
        user.setName("Exception in user query!");
        return user;
    }
}

As shown in the figure:
2) Then, in UserFeignClient, specify the code of the implementation class just written as follows:

package com.txw.consumerdemo.feign;

import com.txw.consumerdemo.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
 * Feign Client for
 * @author Adair
 * E-mail: 1578533828@qq.com
 */
@FeignClient(value = "user-service",fallback = UserFeignClientFallback.class)
public interface UserFeignClient {

    @GetMapping("/user/{id}")
    User queryUserById(@PathVariable("id") Long id);
}

As shown in the figure:
3) Restart test:
We close the user service service, and then visit the following page:

2.5 request compression (understand)

Spring Cloud Feign supports GZIP compression of requests and responses to reduce performance loss during communication. The compression function of request and response can be enabled through the following parameters:

feign:
  compression:
    request:
      enabled: true # Turn on request compression
    response:
      enabled: true # Turn on response compression

At the same time, we can also set the requested data type and the lower limit of the size to trigger compression:

feign:
  compression:
    request:
      enabled: true # Turn on request compression
      mime-types: text/html,application/xml,application/json # Set compressed data type
      min-request-size: 2048 # Sets the lower limit of the size that triggers compression

Note: the above data type and the lower limit of compression size are the default values.

2.6 log level (understand)

As mentioned earlier, set the log level through logging.level.xx=debug. However, this will not work for the Fegin client. Because a new instance of Fegin.Logger will be created when the client modified by the @ FeignClient annotation is proxy. We need to specify the level of this log.
1) The code for setting the log level under the com.leyou package to debug is as follows:

logging:
  level:
    com.txw: debug   

2) Write the configuration class and define the log level as follows:

package com.txw.consumerdemo.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @author Adair
 * E-mail: 1578533828@qq.com
 */
@Configuration
public class FeignConfig {
    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

As shown in the figure:

  • NONE: no log information is recorded, which is the default value.
  • BASIC: only record the requested method, URL, response status code and execution time.
  • HEADERS: on the basis of BASIC, additional header information of request and response is recorded.
  • FULL: records the details of all requests and responses, including header information, request body and metadata.
    3) The code of the configuration class specified in FeignClient is as follows:
package com.txw.consumerdemo.feign;

import com.txw.consumerdemo.config.FeignConfig;
import com.txw.consumerdemo.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
 * Feign Client for
 * @author Adair
 * E-mail: 1578533828@qq.com
 */
@FeignClient(value = "user-service",fallback = UserFeignClientFallback.class,configuration = FeignConfig.class)
public interface UserFeignClient {

    @GetMapping("/user/{id}")
    User queryUserById(@PathVariable("id") Long id);
}

As shown in the figure:
4) Restart the project to see the log of each access. As shown in the figure:

3 Zuul gateway

Through the previous study, the architecture of microservices using Spring Cloud is basically formed, which is roughly as follows:
We use Eureka in Spring Cloud Netflix to implement the service registry and service registration and discovery; The service room realizes service consumption and load balancing through Ribbon or Feign; The externalized configuration and version management of multiple application environments are realized through Spring Cloud Config. In order to make the service cluster more robust, Hystrix's breaking mechanism is used to avoid the fault spread caused by the exception of individual services in the microservice architecture.
In this architecture, our service cluster includes internal services Service A and Service B, both of which register and subscribe to Eureka Server, while Open Service is an external service, which is exposed to the service caller through load balancing. We focus on external services and directly expose our service address. Is this implementation reasonable, or is there a better implementation?
Let's talk about some things that need to be done and shortcomings of this architecture:

  • First, it destroys the stateless characteristics of services.
    • In order to ensure the security of external services, we need to realize the permission control of service access, and the permission control mechanism of open services will run through and pollute the business logic of the whole open services. The most direct problem will be that it destroys the stateless characteristics of rest APIs in service clusters.
    • From the perspective of specific development and testing, in addition to the actual business logic, we also need to consider the control and processing of interface access.
  • Secondly, existing interfaces cannot be reused directly.
    • When we need to access external services to an existing access interface in the cluster, we have to add verification logic to the original interface or add a proxy call to realize permission control, so we can't reuse the original interface directly.
      Facing the above problems, how should we solve them? The answer is: service gateway!

In order to solve the above problems, we need to separate things such as permission control from our service unit, and the most suitable place for these logic is at the front end of external access. We need a more powerful service gateway of load balancer.

Service gateway is an indispensable part of microservice architecture. In the process of uniformly providing rest APIs to the external system through the service gateway, it not only has the functions of service routing and load balancing, but also has the functions of permission control. Zuul in Spring Cloud Netflix plays such a role, providing front door protection for the micro service architecture. At the same time, it migrates the heavy non business logic content of permission control to the service routing level, so that the service cluster subjects can have higher reusability and testability.

3.1 introduction

Official website: https://github.com/Netflix/zuul , as shown in the figure:
Zuul: Wikipedia:
Zuul, the monster in the movie ghost hunters, caused a huge riot in New York.
In fact, Zuul is the big Boss guarding the door in the microservice architecture! When one man is in charge, ten thousand men cannot open!

3.2 architecture after zuul's accession

Whether it is a request from the client (PC or mobile terminal) or an internal call of the service. All requests for services will pass through zuul gateway, and then the gateway will realize authentication, dynamic routing and other operations. Zuul is the unified entrance to our services.

3.3 quick start

1... Select an item and right-click New – Module, as shown in the figure:
2. Select Spring Initializr and click Next. As shown in the figure:
3. Fill in the items and click Next. As shown in the figure:
4. Click Next, as shown in the figure:
5. Click Finish, as shown in the figure:
6. Modify pom.xml as follows:

<?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.txw</groupId>
        <artifactId>eureka-demo</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <artifactId>zuui-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>zuui-demo</name>
    <description>zuul gateway</description>
    <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</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

As shown in the figure:
7. Write the startup class. The code to enable Zuul's function through the @ EnableZuulProxy annotation is as follows:

package com.txw.zuuidemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy // Enable Zuul's gateway function
@SpringBootApplication
@SuppressWarnings("all")   // Annotation warning message
public class ZuuiDemoApplication {

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

As shown in the figure:
8. Write the code to configure application.yml as follows:

server:
  port: 10010 #Service port
spring:
  application:
    name: api-gateway #Specify the service name

As shown in the figure:
We need to use Zuul to proxy the user service. First look at the service status in the control panel:
The mapping rules are as follows:

zuul:
  routes:
    user-service: # Here is the routing id. write it at will
      path: /user-service/** # Here is the mapping path
      url: http://127.0.0.1: actual url address corresponding to 8082 # mapping path

We proxy all requests that comply with the path rule to the address specified by the url parameter
In this example, we proxy the request starting with / user service / * * to http://127.0.0.1:8082
9. Start up test:
The mapping path of the configuration rule needs to be added to the access path. We access: http://127.0.0.1:10010/user-service/user/1, as shown in the figure:

3.4 service oriented routing

In the routing rule just now, we wrote the service address corresponding to the path to death! This is obviously unreasonable if there are multiple instances of the same service.
According to the service name, we should go to Eureka registry to find the list of all instances corresponding to the service, and then conduct dynamic routing!
1. Add Eureka client dependent code as follows:

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

As shown in the figure:
2. The code to enable Eureka client discovery function is as follows:

package com.txw.zuuidemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy // Enable Zuul's gateway function
@SpringBootApplication
@EnableDiscoveryClient
@SuppressWarnings("all")   // Annotation warning message
public class ZuuiDemoApplication {

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

As shown in the figure:
3. Modify the mapping configuration and obtain it through the service name
Because we already have Eureka client, we can obtain the address information of the service from Eureka. Therefore, during mapping, we do not need to specify the IP address, but access it through the service name. Moreover, Zuul has integrated the load balancing function of Ribbon.

eureka:
  client:
    service-url: # The address of EurekaServer is now your own address. If it is a cluster, you need to add the addresses of other servers.
      defaultZone: http://127.0.0.1:1086/eureka

As shown in the figure:
4. Modify the mapping configuration and obtain it through the service name
Because we already have Eureka client, we can obtain the address information of the service from Eureka. Therefore, during mapping, we do not need to specify the IP address, but access it through the service name. Moreover, Zuul has integrated the load balancing function of Ribbon.

zuul:
  routes:
    user-service: # Here is the routing id. write it at will
      path: /user-service/** # Here is the mapping path
      serviceId: user-service # Specify the service name

As shown in the figure:

5. Start test
Start again. This time, Zuul will use the Ribbon for load balancing access when acting as an agent:
In the log, you can see that the load balancer is used, as shown in the figure:

3.5 simplified routing configuration

In the configuration just now, our rule is as follows:

  • Zuul. Routes. < route >. Path = / xxx / * *: to specify the mapping path< Route > is a custom route name
  • Zuul. Routes. < route >. Serviceid = / user service: to specify the service name.

In most cases, our < route > route name is often written the same as the service name. Therefore, Zuul provides a simplified configuration syntax: Zuul. Routes. < serviceid > = < Path >
For example, our user service configuration can be simplified to one:

zuul:
  routes:
    user-service: /user-service/** # Here is the mapping path

As shown in the figure:

Take the project and visit: http://localhost:10010/user-service/user/1, as shown in the figure:
The configuration of service name is omitted.

3.6 default routing rules

In the process of using Zuul, the above rules have greatly simplified the configuration items. However, when there are many services, the configuration is cumbersome. Therefore, Zuul specifies the default routing rule:

  • By default, the mapping path of all services is the service name itself.
    • For example, if the service name is user service, the default mapping path is: / user service/**

In other words, it's OK if we don't configure the mapping rules just now. If you don't believe it, try it.

3.7 routing prefix

Configuration example:

zuul:
  prefix: /api # Add routing prefix
  routes:
      user-service: # Here is the routing id. write it at will
        path: /user-service/** # Here is the mapping path
        service-id: user-service # Specify the service name

As shown in the figure:
We specify the prefix of the route through zuul.prefix=/api, so that when a request is initiated, the path must start with / api.
The path / API / user service / user / 1 will be proxied to ` / user service / user / 1, as shown in the figure:

3.8 filter

As one of the important functions of Zuul gateway, it is to realize the authentication of requests. We often implement this action through the filter provided by Zuul.

3.8.1 ZuulFilter

ZuulFilter is the top-level parent of the filter. Here we take a look at the codes of the four most important methods defined as follows:

public abstract ZuulFilter implements IZuulFilter{

	// Filter type
    abstract public String filterType();

	// Filter sequence
    abstract public int filterOrder();
    
    // From IZuulFilter do you want to filter
    boolean shouldFilter();

	// IZuulFilter filter logic
    Object run() throws ZuulException;
}

As shown in the figure:

  • shouldFilter: returns a Boolean value to judge whether the filter needs to be executed. Return true to execute and false to not execute.
  • run: the specific business logic of the filter.
  • filterType: returns a string representing the type of filter. It includes the following four types:
    • pre: the request is executed before being routed
    • Routing: called when routing a request
    • post: call after routing and errror filter.
    • Error: an error occurred while processing the request
  • filterOrder: defines the execution order of the filter by the returned int value. The smaller the number, the higher the priority.

3.8.2 filter execution life cycle:

This is the request life cycle diagram provided by Zuul's official website, which clearly shows the execution sequence of a request in each filter.

  • Normal process:
    • The request will first pass through the pre type filter and then reach the routing type for routing. The request will reach the real service provider. After executing the request and returning the results, it will reach the post filter. Then return the response.
  • Abnormal process:
    • In the whole process, if the pre or routing filter is abnormal, it will directly enter the error filter. After the error is processed, the request will be handed over to the POST filter and finally returned to the user.
    • If the error filter has its own exception, it will eventually enter the POST filter and return.
    • If the POST filter is abnormal, it will jump to the error filter, but different from pre and routing, the request will not reach the POST filter again.

List of all built-in filters:

3.8.3 usage scenarios

There are many scenarios:

  • Request authentication: it is generally placed in the pre type. If it is found that there is no access permission, it will be intercepted directly.
  • Exception handling: it is generally handled in combination with error type and post type filters.
  • Statistics of service call duration: pre and post are used together.

3.9 custom filters

Next, we customize a filter to simulate the verification of a login. Basic logic: if there is an access token parameter in the request, the request is considered valid and released.
1. The code for defining the filter class is as follows:

package com.txw.zuuidemo.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
/**
 * Custom filter class
 * @author Adair
 * E-mail: 1578533828@qq.com
 */
@SuppressWarnings("all")   // Annotation warning message
@Component
public class LoginFilter extends ZuulFilter {
    @Override
    public String filterType() {
        // Login verification must be pre intercepted
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        // Sequence setting
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
    }

    @Override
    public boolean shouldFilter() {
        // Return true to indicate that the filter is effective.
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        // Login verification logic.
        // 1. Get the request context object provided by Zuul
        RequestContext ctx = RequestContext.getCurrentContext();
        // 2. Get the request object from the context
        HttpServletRequest req = ctx.getRequest();
        // 3. Get the token from the request
        String token = req.getParameter("access-token");
        // 4. Judgment
        if(token == null || "".equals(token.trim())){
            // No token, login verification failed, blocking
            ctx.setSendZuulResponse(false);
            // Return 401 status code. You can also consider redirecting to the login page.
            ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
        }
        // If the verification passes, you can consider putting the user information into the context and continue to execute backward
        return null;
    }
}

As shown in the figure:
Start project, access http://localhost:10010/api/user -service/user/1? Access token = 12345, as shown in the figure:

3.10 load balancing and fusing

By default, Zuul has integrated Ribbon load balancing and Hystix fuse mechanism. However, all timeout policies are the default values. For example, the fuse timeout is only 1S, which is easy to trigger. Therefore, it is recommended that we manually configure the code as follows:

zuul:
  retryable: true
ribbon:
  ConnectTimeout: 250 # Connection timeout (ms)
  ReadTimeout: 2000 # Communication timeout (ms)
  OkToRetryOnAllOperations: true # Retry all operations
  MaxAutoRetriesNextServer: 2 # Number of retries for different instances of the same service
  MaxAutoRetries: 1 # Number of retries for the same instance
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMillisecond: 6000 # Fuse timeout: 6000ms

As shown in the figure:

Keywords: Java Spring Boot

Added by djetaine on Wed, 10 Nov 2021 12:53:04 +0200