This article has been included in my Github selection, welcome to Star: github.com/yehongzhi/l…

Writing in the front

The previous article wrote about Gateway Predicate(used for routing and forwarding), so this article covers the other main core, which is the Filter.

What does a filter do? How does it work? See the picture below:

It is obvious from the figure that the Filter is required before and after the request to the back-end service, so the function of Filter is clear. Parameter verification, traffic monitoring, log recording, request content modification and so on can be done in PreFilter(request pre-processing). The response content can be modified in PostFilter(request post-processing).

The filter

Filters are classified into local and global types:

  • A local Filter(subclass of GatewayFilter) applies to a single route. If you want to use global routing, you need to configure Default Filters.
  • GlobalFilter (a subclass of GlobalFilter), does not need to configure routes, system initialization is used on all routes.

Local filter

The SpringCloud Gateway has a number of routing filters built in, which are generated by the Factory class of GatewayFilter.

AddRequestParameter GatewayFilter

This filter can add parameters to a request.

For example, I have an interface with userName parameter in the Consumer service, and I want to request the gateway to add a parameter of userName= Yehongzhi when routing and forwarding.

@RequestMapping(value = "/getOrder",method = RequestMethod.GET) public String getOrder(@RequestParam(name = "userName") String userName) {return "return userName; }Copy the code

The configuration is as follows:

spring:
  cloud:
    gateway:
      routes:
        - id: add_request_parameter
          uri: http://localhost:8081/getOrder
          predicates:
            - Method=GET
            - Path=/getOrder
          filters:
            - AddRequestParameter=userName,yehongzhi
Copy the code

So when I asked the gateway, enter http://localhost:9201/getOrder, we can see the default add userName.

StripPrefix GatewayFilter

This filter removes a specified number of path prefixes.

For example, if I want to remove the first level of the path prefix of the request gateway, I can configure it as follows:

spring:
  cloud:
    gateway:
      routes:
        - id: strip_prefix_gateway
          uri: http://localhost:8081
          predicates:
            - Path=/consumer/**
          filters:
            - StripPrefix=1
Copy the code

When the request path http://localhost:9201/consumer/getDetail/1, can get the result.

Equivalent to a request to http://localhost:8081/getDetail/1, the result is the same.

PrefixPath GatewayFilter

This filter, in contrast to the previous filter, prefixes the existing path with the specified prefix.

spring:
  cloud:
    gateway:
      routes:
        - id: prefix_path_gateway
          uri: http://localhost:8081
          predicates:
            - Path=/getUserInfo/**
          filters:
            - PrefixPath=/consumer
Copy the code

When the request to http://localhost:9201/getUserInfo/1, http://localhost:8081/consumer/getUserInfo/1 is the same with request.

Hystrix GatewayFilter

The gateway of course has a circuit breaker mechanism, so the filter integrates Hystrix to achieve the circuit breaker function. How do you use it? The first step is to introduce Hystrix’s Maven dependencies.

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

Add the fallback() method to the Gateway service

@restController public class FallBackController {@requestMapping ("/fallback") public String fallback(){return "The system is busy, Please try again later!" ; }}Copy the code

If the configuration of the gateway service is as follows, service degradation is triggered when the route forwarding of the GET request fails:

spring:
  cloud:
    gateway:
      routes:
        - id: hystrix_filter
          uri: http://localhost:8081
          predicates:
            - Method=GET
          filters:
            - name: Hystrix
              args:
                name: fallbackcmd
                fallbackUri: forward:/fallback
Copy the code

At this time, we stop the service of 8081, so that the gateway can not request the corresponding service, thus triggering the service degradation.

RequestRateLimiter GatewayFilter

This filter can provide the ability to limit the flow, using the RateLimiter implementation to determine whether the current request is allowed to continue. If the request is too large, the default return HTTP 429- too many requests status is returned. How do you use it? First, Maven dependencies need to be introduced.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
Copy the code

Then add a configuration class.

@configuration public class LimiterConfig {/** * IP limiter */ @bean public KeyResolver ipKeyResolver() {return exchange ->  Mono.just(exchange.getRequest().getRemoteAddress().getHostName()); }}Copy the code

Configure the traffic limiting policy for GET requests as follows:

spring: cloud: gateway: routes: - id: hystrix_filter uri: http://localhost:8081 predicates: - Method=GET filters: Name: RequestRateLimiter args: Redis-rate-limiter. ReplenishRate: 1 # Number of requests allowed to be processed per second redis-rate-limiter. BurstCapacity: replenishRate: replenishRate: 1 # replenishRate: number of requests allowed to be processed per second 2 # Maximum number of requests per second key-resolver: "#{@ipkeyresolver}Copy the code

Then start the service, continuous request address, http://localhost:9201/getDetail/1, will trigger the current limit, to 429 error.

Because there are so many built-in filters, I will not list them here. If you are interested, you can go to the official website to learn by yourself.

Custom local filters

If the built-in local filter is not enough, then we have to use a custom filter. How? As an example, we customize a whitelist filter. Users who are not whitelisted can access the whitelist with an Unauthorized 401 error code.

Local filters need to implement the GatewayFilter and Ordered interface, the code is as follows:

Public class WhiteListGatewayFilter implements GatewayFilter, Ordered {// Private List<String> whiteList; // Initialize the WhiteListGatewayFilter(List<String> whiteList) {this.whiteList = whiteList; } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String userName = exchange.getRequest().getQueryParams().getFirst("userName"); // The whitelist is not empty, and userName is included in the whitelist to access if (! CollectionUtils.isEmpty(whiteList) && whiteList.contains(userName)) { return chain.filter(exchange); } // If the whitelist is empty or the userName is not whitelisted, return 401 exchange.getresponse ().setStatusCode(httpstatus.unauthorized); return exchange.getResponse().setComplete(); } @override public int getOrder() {return 0; }}Copy the code

Then define a filter factory and inject it into the Spring container as follows:

@Component public class WhiteListGatewayFilterFactory extends AbstractConfigurable<WhiteListGatewayFilterFactory.Config>  implements GatewayFilterFactory<WhiteListGatewayFilterFactory.Config> { private static final String VALUE = "value"; protected WhiteListGatewayFilterFactory() { super(WhiteListGatewayFilterFactory.Config.class); } @Override public List<String> shortcutFieldOrder() { return Collections.singletonList(VALUE); } @override public GatewayFilter apply(Config Config) {String whiteString = config.getValue(); List<String> whiteList = new ArrayList<>(Arrays.asList(whiteString.split(","))); // Create a WhiteListGatewayFilter instance, return new WhiteListGatewayFilter(whiteList); } public static class Config {private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; }}}Copy the code

Finally, we can add config to the application.yaml configuration file using:

spring: cloud: gateway: routes: - id: white_list_filter uri: http://localhost:8081 predicates: - Method=GET filters: - WhiteList= Yehongzhi # Specifies a WhiteList followed by an equal sign. Use commas (,) to separate the namesCopy the code

Then start the project, request first localhost: 9201 / getDetail / 1, do not bring the userName, according to the expected return 401, can not access.

Request a userName = yehongzhi address http://localhost:9201/getDetail/1? UserName = Yehongzhi, it is in the whitelist, so it can be accessed normally.

Global filter

The global filter applies to all routes during system initialization and does not need to be configured separately. The interface definition class for global filters is GlobalFilter. Gateway also has many built-in filters.

Let’s take a few representative ones to introduce, such as LoadBalancerClientFilter.

LoadBalancerClientFilter

The filter resolves to urIs starting with lb://, such as this configuration:

Spring: Application: name: api-gateway Cloud: nacos: Discovery: server-addr: 127.0.0.1:8848 service: ${spring.application. Name} gateway: routes: -id: consumer uri: lb://consumer 1 predicates: - Path=/consumer/**Copy the code

The global filter picks up the service name consumer and then gets the ServiceInstance ServiceInstance via LoadBalancerClient. Reassemble the requested URL from the obtained service instance.

This is an example of a global filter application that applies globally and does not require configuration. Let’s explore custom global filters. Suppose we need to count the total number of times a user’s IP address accesses the gateway.

Custom global filters

To customize global filters, implement the GlobalFilter interface and Ordered interface.

@Component public class IPAddressStatisticsFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { InetSocketAddress host = exchange.getRequest().getHeaders().getHost(); if (host == null || host.getHostName() == null) { exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST); return exchange.getResponse().setComplete(); } String hostName = host.getHostName(); AtomicInteger count = IpCache.CACHE.getOrDefault(hostName, new AtomicInteger(0)); count.incrementAndGet(); IpCache.CACHE.put(hostName, count); System.out.println("IP address: "+ hostName + ", count. IntValue ()); return chain.filter(exchange); } @Override public int getOrder() { return 10101; Public class IpCache {public static final Map<String, AtomicInteger> CACHE = new ConcurrentHashMap<>();  }Copy the code

Start the project, then request the service, and you can see the console print the result.

IP address: 192.168.1.4, count of accesses: 1 IP address: 192.168.1.4, count of accesses: 2 IP address: 192.168.1.4, count of accesses: 3 IP address: localhost, count of accesses: 1 IP address: localhost, count of accesses: 2 IP address: localhost, count of access: 3 IP address: 192.168.1.4, count of access: 4Copy the code

conclusion

Predicates and Filters in this paper have basically realized all the functions of the service gateway, including routing and forwarding, permission interception, traffic statistics, traffic control, service fusing, logging, and so on. Therefore, gateway service is a very important part of micro-service architecture, and many front-line Internet companies will develop their own service gateway. Therefore, mastering the service gateway is a necessary skill for back-end development. Thank you for reading.

Please give me a thumbs-up if you think it is useful. Your thumbs-up is the biggest motivation for my creation

I’m a programmer who tries to be remembered. See you next time!!

Ability is limited, if there is any mistake or improper place, please criticize and correct, study together!