This is the first day of my participation in the More text Challenge. For details, see more text Challenge

This article is participating in the “Java Theme Month – Java Development in action”. See the link to the event for more details

Related articles

I can implement all four mainstream traffic limiting strategies through Redis

Zuul, a distributed series of gateways, takes care of everything — no more piecing together interfaces

features

Gateway was born because Zuul 2.0 had been a flop, so in this case gateway can be said to be a replacement for Zuul. Since it is a substitute function, it must include Zuul.

  • The above zuul section mainly describes the dynamic routing of gateways. Gateway certainly supports it.
  • Zuul has four filters in addition to routing and gateway also has filters
  • Zuul carries hystrix internally, while Gateway integrates with Hystrix to fuse, degrade, and limit traffic, and internally implements traffic limiting based on token buckets
  • Both Zuul and Gateway support service discovery for routing and forwarding

precondition

SpringCloud Gateway is a non-blocking Gateway based on WebFlux. Therefore, the gateway environment requires Springboot 2.x+, Spring WebFlux, and Project Reactor

Because it is non-blocking, it will be different from our previous blocking frameworks.

Professional term

noun explain
Route The basic components of a gateway. It is defined by an ID, a target URI, a set of predicates, and a set of filters. If the aggregation predicate is true, the route is matched
Predicate This is a Java 8 function predicate. The input type is Spring Framework ServerWebeExchange. This allows you to match anything in the HTTP request, such as headers or parameters
Filter These are GatewayFilter instances constructed using a particular factory. Modify the request and response before or after sending the downstream request

Quick start

Click on my website to get started quickly

  • Create module Geteway and specify port 9091

  • Introducing coordinates

	<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
Copy the code
  • Configure the routing
spring:
  cloud:
    gateway:
      routes:
        - id: route
          uri: http://localhost:8001
          predicates:
            - Path=/payment/get/**
Copy the code
  • Interface testing: we visit http://localhost:9091/payment/get/1 there will be 8001 payment information

  • Let’s look at the gateway routing rules in detail

Routing rules

Springcloud Gateway matches routes as part of the Spring WebFlux HandlerMapping infrastructure. The Spring Cloud Gateway includes a number of built-in routing front factories. All of these match the different attributes of the HTTP request. We can combine multiple route predicate factories with logic and statements

  • We can also see that the SpringCloud Gateway has many matching factories built in. We passed the bill abovePathRoutePredicateFactoryTo implement the

path

spring:
  cloud:
    gateway:
      routes:
        - id: route
          uri: http://localhost:8001
          predicates:
            - Path=/payment/get/**
Copy the code

query

spring:
  cloud:
    gateway:
      routes:
        - id: route
          uri: http://localhost:8001
          predicates:
            - Path=/payment/get/**
            - Query=green
Copy the code
  • http://localhost:9091/payment/get/1 interface to access data, must add green parameters of http://localhost:9091/payment/get/1?green

  • The parameter alone is still not good, add we require the green parameter must be an array. We can configure it as follows

spring:
  cloud:
    gateway:
      routes:
        - id: route
          uri: http://localhost:8001
          predicates:
            - Path=/payment/get/**
            - Query=green,\d+
Copy the code
  • http://localhost:9091/payment/get/1?green=123sCan’t access,http://localhost:9091/payment/get/1?green=123Then you can

datetime

  • When we were developing a flash sale interface, we could use the Datetime feature to do this
spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: http://localhost:8001
          predicates:
            - Path=/payment/get/**
            - Query=green,\d+
            - After = 2017-01-20 T17:42:47. 789-07:00 [America/Denver]
Copy the code

ip

- RemoteAddr = 10.0.20.132/140Copy the code
  • For the remote address we are actually going to fill in the IP, the trailing slash indicates a range IP, and the configuration above indicates that10.0.20.132-10.0.20.140All IP addresses in this segment are accessible

cookie

- Cookie=zxhtom, hello
Copy the code
  • The cookie with zxhTom =hello in the request can be accessed

Service discovery

  • Default routing rules are found after integration of services in Zuul. The same gateway can access the service and find that the column is eureka again.

  • I won’t go over the configuration of service discovery here. The eureka project has been introduced, and the hystrix, Zuul, and other projects have related configurations, so I will post the gateway opening configuration directly

spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
Copy the code
  • The default routing rule is based on the name registered in EUREKA. For example, to access payment is the following addresshttp://localhost:9091/cloud-payment-service/payment/get/2
  • The ribbon performs load balancing internally. One small problem is that the gateway does not seem to update the service list after getting the service list when it is started.

The filter

  • The gateway built-in filter is really a lot. We can’t give one example by one here. Let’s take a look at some of the filters that correspond to Zuul. Before we look at that, let’s take a look at what’s built into gateway

  • In addition to local filters, the Gateway has several built-in global filters

RewiritePathGatewayFilterFactory

  • Remember that we implemented the default routing forwarding rule in zuul topic, that is, cloud-payment-service and cloud-order-service are the default proxy of the SERVICE prefix of PAYMENT and ORDER. It gives us the same functionality in the Gateway as service forwarding. But it’s for specific microservices. We can implement the functionality in Zuul by customizing global filters. Let’s try through RewiritePathGatewayFilterFactory realize it

  • In order to highlight the core configuration, I’m only putting part of the configuration here, and need the full reference context. Or look directly at the source code. The source code is released at the end of this article

		- id: order
          uri: lb://CLOUD-ORDER-SERVICE
          predicates:
            - Query=green
          filters:
            - RewritePath=/order/(? 
      
       .*),
       /$\{segment}
Copy the code
  • Similar to zuul’s configuration, we match our request with a regular, then extract what we need and route it to the real service for invocation

PrefixPathGatewayFilterFactory

  • And the above ` ` RewiritePathGatewayFilterFactory In a similar but different way,PrefixPathGatewayFilterFactoryIs to prefix our matching interfaces uniformly. For example, the following configuration
		- id: pre_route
          uri: http://localhost:8001
          predicates:
            - Path=/getTimeOut/**
          filters:
            - PrefixPath=/payment
Copy the code
  • inhttp://localhost:9091/getTimeOut/123To access this interface, we first configure the match in routespre_routeThen add payment in front of the interface and route it to the real service. Now, the thing to notice here is that we’re matching from the top down so we need to be careful about the order when we’re configuring it

StripPrefixGatewayFilterFactory

  • The function of this filter is to split our interface and then route. In fact, the Gateway provides us with a lot of filters that are very convenient to use. We basically see the case provided by the official website we can know how to use. It’s important that we understand the design inside. Design is king
		- id: pre_route
          uri: http://localhost:8001
          predicates:
            - Path=/zxhtom/**
          filters:
            - StripPrefix=2
Copy the code
  • accesshttp://localhost:9091/zxhtom/lqj/payment/getTimeOut/123It’s actually forwarded tohttp://localhost:8001/payment/getTimeOut/123On. Note our protocol for URI, if lb means service discovery. Here we are configuring a single node

SetPathGatewayFilterFactory

		- id: pre_route3
          uri: http://localhost:8001
          predicates:
            - Path=/hello/{segment}
          filters:
            - SetPath=/payment/get/{segment}
Copy the code
  • To put it bluntly, re-forward the request. It’s just a little more stiff here. So that’s going to behttp://localhost:9091/hello/123Forwarded to thehttp://localhost:8081/payment/get/123on

Custom gateway filters

  • Above we briefly looked at the filters that the Gateway provides for us. But built-in will never meet all your needs. Whether it’s to meet a need or to satisfy one’s own vanity. We should all look at how to implement our own filters

GatewayFilterFactory

  • Gateway has a number of built-in gateway filters. We just need to refer to their built-in filter implementation.
  • I don’t know if you’ve noticed anything like thisRewritePathGatewayFilterFactoryThese filters are configured toRewritePathAt the back of theGatewayFilterFactoryThere isn’t.
  • And why is that? Let’s not talk about that and let’s do the same thing.
  • Now we have a requirement to implement a login verification filter. The implementation logic is simple to verify that the user name and password match. The user name and password are set in the address

Creating a Filter class

@Component
public class LoginPathGatewayFilterFactory extends AbstractGatewayFilterFactory<LoginPathGatewayFilterFactory.Config> {
    public LoginPathGatewayFilterFactory(a) {
        super(Config.class);
    }
    @Override
    public GatewayFilter apply(Config config) {
        return (exchage,chain)->{
            String userName = config.getUserName();
            String password = config.getPassword();
            String requestUserName = exchage.getRequest().getQueryParams().getFirst("userName");
            String requestPassword = exchage.getRequest().getQueryParams().getFirst("password");
            if (userName.equals(requestUserName) && password.equals(requestPassword)) {
                return chain.filter(exchage);
            } else {
                throw new RuntimeException("User name wrong password..."); }}; }public static class Config {

        private String userName;

        private String password;

        public String getUserName(a) {
            return userName;
        }

        public Config setUserName(String userName) {
            this.userName = userName;
            return this;
        }

        public String getPassword(a) {
            return password;
        }

        public Config setPassword(String password) {
            this.password = password;
            return this; }}}Copy the code

The new configuration

		- id: pre_route4
          uri: http://localhost:8001
          predicates:
            - Path=/login/{segment}
          filters:
            - name: LoginPath
              args:
                userName: zxhtom
                password: test
            - SetPath=/payment/get/{segment}
Copy the code

test

  • We throw an exception when our username and password are inconsistent. If the user name and password are the same as the account we specified then the second filter will be routed.

GatewayFilter

  • In the first way above we can observe that the end is generationGatewayFilterObject. In factGatewayFilterThat’s what’s really filtering.

build

@Component
public class CustomLoginGatewayFilter implements GatewayFilter.Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("I'm in the filter...");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder(a) {
        return 0; }}Copy the code

registered

@Configuration
public class RouteConfig {
    @Bean
    public RouteLocator customRouteLocator(CustomLoginGatewayFilter customLoginGatewayFilter,RouteLocatorBuilder builder) {
        return builder.routes()
                .route("path_route", r -> r.path("/baidu")
                        .uri("https://www.baidu.com") .filter(customLoginGatewayFilter)) .build(); }}Copy the code

test

Custom global filters

  • Again, customization depends on how the built-in is implemented. Novartis,WebsocketRoutingFilterIs built into the Gateway and is implemented in the same way as one of the gateway filters.
@Slf4j
@Component
public class GlobalCustomRoutingFilter implements GlobalFilter.Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("I am a global filter...");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder(a) {
        return 0; }}Copy the code
  • Write a Java class to register with Spring, and we’ll see logs printed when we access our gateway routing interface. Of course, this is just to demonstrate the global filter in effect. In the global filter we can do permission authentication and so on.
  • After global authentication, we can write the login user information into the cookie or pass it downstream by adding parameters

Filter name

  • Remember when we talked in the gateway filter about why custom names need to be defined that way. Look at the above source code to understand. Register the filter key-value pair in the filter container

Current limiting

  • As for current limiting, the author realized four mainstream current limiting algorithms respectively through Redis. If you are interested in reading it, don’t forget to return to this article to continue watching Gateway. Don’t go too far!!

  • Above, we mainly introduce the use of gateway, namely the synthesis direction. But as a portal interface of the website traffic is much larger than other services. Other services normally rely on Hystrix to degrade the service and so on. We can also add Hystrix at the gateway level to achieve high availability of services. But in addition to Hystrix, gateway has its own built-in traffic limiting algorithm. Let’s simply implement lower bound streams.

  • When it comes to gateways, we can’t get around to limiting traffic. Gateways act as proxies but are not arbitrary proxies without conditions. Why the protection module in the gateway limits downstream services. Some interfaces limit traffic due to security and stability concerns

  • Gateway traffic limiting is also simple. All we need to do is introduce redis related modules to use built-in filters

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>	
Copy the code
@Component
public class HostAddrKeyResolver implements KeyResolver {
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        // Limit traffic by service address
        return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }
    @Bean
    KeyResolver apiKeyResolver(a) {
            // Limit traffic by URL
            return exchange -> Mono.just(exchange.getRequest().getPath().toString());
            }

    @Bean
    KeyResolver userKeyResolver(a) {
        // Limit traffic by user
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userName")); }}Copy the code

  • Redis-rate-limiter. ReplenishRate: The rate at which tokens are generated from the token barrel
  • Redis-rate-limiter. BurstCapaciry: indicates the capacity of the token bucket
  • Let’s take a look at real-time storage in Redis. The following data appears when we perform the pressure test using the JMeter isobarometer tool

counter

  • Counter is divided into fixed time window and sliding time window two strategies. The difference between the two is that the time interval is different. Essentially, there is no difference.
  • But because the sliding time window is so short, it gives us the illusion that the two are completely different

Fixed time window

Sliding time window algorithm

Leakage of copper

  • The leaky bucket algorithm is to prepare a request pool in advance, can enter the request pool can wait for resource allocation. This solves the problem of the count not being able to face the large volume of traffic

The token bucket

  • The token bucket is an upgrade over the leaky bucket. When we think about it, normal requests still take time and some of the most important interfaces take between 1 and 10 seconds to execute. But when we generate a token, we just generate a string of characters. Interface execution is much slower than generating tokens
  • I wonder if the reader has ever thought of such a situation. What should the leaky bucket algorithm do when the request pool is full and suddenly encounters a large amount of traffic? At this time, the leaky bucket algorithm can only ruthlessly refuse redundant requests.
  • Token buckets are different. The token bucket has the same pool, but internally stores the generated tokens, and what happens in the extreme case if the token bucket is slow and encounters a lot of traffic.
  • In the leaky bucket algorithm encountered large traffic can only reject and request pool state and update slowly. But the token bucket is full and there’s a lot of traffic coming in and taking the token away. The execution time of the interface is not directly related to the token bucket. At this point the status of the token bucket is updated very quickly. So the token bucket is better able to face the sudden heavy traffic

Integrated hystrix

  • Remember our previous hystrix service meltdown and downgrades column. Think carefully as a gateway is not more traffic than other interfaces? So how to perform service circuit breaker and other operations on the interface of our gateway?
  • The author here referred to write data only to achieve the realization of global backup

Function to access

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

  • After accessing the coordinates, we can directly add the filter based on the above implementation. Finally, the exception is redirected to the interface we wrote beforehand. This interface returns our uniform data structure
@RestController
@RequestMapping("/fallback")
public class FallbackController {

    @RequestMapping("")
    public String fallback(a){
        return "error"; }}Copy the code

conclusion

  • All right! On the gateway part, the author intermittently through three articles from different frames, different perspectives to interpret.
  • The zuul framework implements the gateway proxy function which is often used in our project. This article is to implement the gateway proxy in the project through the gateway
  • In addition, four mainstream traffic limiting policies were implemented through Redis two days ago. The two articles mentioned above were fruitful



Big brother big sister, leave a like in go! Original true not easy