Your “like” is my biggest support.

Original: Taste of Little Sister (wechat official ID: XjjDog), welcome to share, please reserve the source.

This article will start with knowledge topology, talk about the capabilities of the API Gateway, and how to use the Spring Cloud Gateway. The article is very long, you can go through the table of contents first.

Knowledge topology (uses and Principles) 2. Functions of gateways 3. Predicate, route matching 4Copy the code

Why do many people find Spring Cloud Gateway difficult to use? Because it uses WebFlux behind it, it involves reactive programming rather than traditional procedural programming.

Taking a look at the technology behind it, it’s not hard to see that the source of this obscurity is project Reactor’s “future-oriented” responsive programming framework, which runs parallel to the Spring project.

You end up with code that looks like a lambda. The idea behind it, a product of the observer model and non-blocking hybridization, is that the learning curve is relatively steep.

1. Knowledge topology

Spring Cloud Gateway involves a lot of relatively new knowledge and ideas, but the slope is not very steep just for use.

1.1 Usage Related

We can imagine the essential element of a route: the Web request, with some matching criteria, to locate the actual service node. And before and after the forwarding process, some fine control.

Predicate is our matching condition; Filter, on the other hand, can be interpreted as an omnipotent interceptor. With these two elements, plus the target URI, you can implement a concrete route.

Since the Spring Cloud Gateway is based on SpringBoot, yML is used for routing configuration. Yml is usually very hierarchical, which makes the configuration file look very messy. It is also possible to write routes using Java code (or Kotlin) in a style biased towards functional programming, so you need to know how to write lambda expressions first.

Most of the time, the Spring Cloud Gateway serves as the gateway of HTTP services. It can carry out some fine-grained control over HTTP packets. Therefore, it is necessary to have a good understanding of THE HTTP protocol to use it with ease.

1.2 Principle Correlation

But in terms of principle, it is much more complicated. Due to a lag in practice, most existing components have yet to catch up with the “futuristic” idea of responsiveness, resulting in a plethora of arcane components (mostly dedicated functions). Fortunately, direct contact with these apis is not required to use the Spring Cloud Gateway.

The most important is the encapsulation of the WebFlux framework. Webflux is an alternative to Spring MVC and can be used to write responsive applications. The relationship between the two can be seen below. Its underlying uses NetTY, so operations are asynchronous and non-blocking.

Further down the line, WebFlux is a wrapper that runs on top of Project Reactor, which provides the underlying features. Like vert.x, this one can feel weird on first touch.

Reactor is the development of the observer model, so there is the concept of Publisher in it, among which the most important implementations are Flux and Mono. This is what webFlux takes its name from.

Reactor Reference: url.cn/5B7f5iY

There is a certain cost to transition from the traditional development model to reactor’s development model. If you have the time to understand the rationale behind it, it will be useful to use the Spring Cloud Gateway.

Second, the role of gateway

As the name suggests, it is a networked level, and no matter how complex the back end, the external level behaves the same.

More importantly, some generic transactions hidden behind levels can be abstracted and processed. Think of the gateway as a kind of customs facility, where your visa preparation, security, scheduling, and so on can all be handled in one place.

API gateway is an architectural pattern that has emerged with the concept of microservices, but is not limited to microservices. We can see the location of the gateway in the picture.

Consider the specific role of the gateway below.

2.1 Reverse Proxy

This is the basic functionality of all gateways, including Nginx. In addition to being able to shape services, a very important additional benefit of the gateway is that it shields service details from the back end.

The reverse proxy also provides load balancing functions, including weighted traffic distribution.

2.2 authentication

It is authority authentication, which is often referred to as authority system. Because the authentication services have very high similarity, they can be abstracted and placed in the gateway layer.

For example, unified access through HTTPS, distributed session processing, and access to new login authentication channels.

2.3 Flow Control

Traffic control is a disaster if it is spread across every service, and gateways are the best place to do it.

Traffic control usually has multiple policies to mask back-end services. Abnormal requests and requests that exceed the load capacity are quickly blocked, providing essential support for system stability.

There are two methods of flow control: single-machine flow limiting and distributed flow limiting. The latter is more precise, and spring Cloud Gateway provides both.

2.4 a fuse

The main difference between fusing and flow control is that the former service is “unavailable” for a period of time, while the latter only fails probabilistically.

In addition to the calls between services that involve fuses, fuses at the gateway layer have a wider scope and require accurate classification of services.

2.5 Gray control

One of the ultimate functions of gateways is grayscale publishing of services. For example, AB test is a grayscale distribution method.

Gray level will be refined control, such as some gray level control for a class of users, a physical area, a specific request path, a specific module, random percentage and so on.

Gray scale is the result of an overall architecture coordination, but the entrance to coordination is the gateway. By adding some specific marks to the request header or parameters, each request can be divided to decide whether to fall into gray scale.

2.6 Log Monitoring

The gateway is the best place to monitor logs. Through the detailed analysis of the access log, a lot of valuable data can be obtained, and then provide decision basis for the optimization of the back-end service.

For example, visit trends for a “business”, operational data, QPS peaks, year over year, quarter over quarter, etc.

Predicate, route matching

Spring Cloud Gateway is configured in both Fluent API and YML, both of which suck.

Predicate means Predicate in English. We can think of this as conditional matching, which can be matched based on HTTP headers or HTTP parameters.

3.1 Time Matching

Matches before or after a certain point in time. For example, giving way takes effect during a certain period of time.

A configuration file looks like this:

spring: cloud: gateway: routes: - id: after_route uri: https://example.org predicates: T17 - After = 2020-10-20:42:47. 789 - from America/DenverCopy the code

One of them. The id is the unique non-repeatable name of this route, the URI specifies the matched route address, and the predicates After is our time matter. predicates

After 1.

Or translated into code.

builder.routes().route(
r -> r.after(LocalDateTime.of(2020, 10, 17, 42, 47).atZone(ZoneId.of("America/Denver")))
    .uri("https://example.org")
);
Copy the code

Since most of the code is similar, we will only capture the most important snippets in the following sections.

Before 2.

Above is written after a certain point in time, before, as follows:

Before=2017-01-20T17:42:47.789-07:00[America/Denver] r.before(LocalDateTime. Of (2020, 10, 17, 42,) 47).atZone(ZoneId.of("America/Denver")))Copy the code

3. Between and within a certain period of time

Between = 2017-01-20 T17:42:47. 789 - from America/Denver, 2017-01-21T17:42:41.79-07:00 [America/Denver] r.tweween (LocalDateTime. Of (2020, 10, 17, 42, Denver) 47).atZone(ZoneId.of("America/Denver")), LocalDateTime.of(2027, 10, 17, 42, 47).atZone(ZoneId.of("America/Denver")) )Copy the code

3.2 Http information

Let’s take a quick look at an HTTP Request, where both the General and Request Headers information can be controlled for matching. For cookies, Host and other commonly used information, special optimization is also carried out. The most common ones are path, cookie, host, query, and so on.

Path

Path is the most important matching method. Multiple paths can be used and separated.

Path=/foo/{segment},/bar/{segment}
r.path("/foo/{segment}","/bar/{segment}")
Copy the code

Notice that {segment} is enclosed in braces. This value can be retrieved in code.

Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);

String segment = uriVariables.get("segment");
Copy the code

The Header Header information

Header=X-Request-Id, \d+
r.header("Header=X-Request-Id", "\\d+")
Copy the code

Similar to cookies, this is HTTP header matching, where a lot of grayscale information, or trace information, likes to be put.

Cookie [header]

Cookie=chocolate, ch.p
r.cookie("chocolate","ch.p")
Copy the code

Whether there is a Cookie named chocolate in the HTTP message and whether it matches the regular ch.p.

Host information [header] Although the Host information is also in the header information, it is so commonly used that there is a dedicated matcher.

Host=**.somehost.org,**.anotherhost.org
r.host("Host=**.somehost.org","**.anotherhost.org")
Copy the code

Note that the match string here, which is more concise in Ant style, is not a regular expression in Java. Multiple hosts are used to separate them.

Request Method

Method=GET
r.method("GET")
Copy the code

Note that I found no case conversion code in the source code, so keep it uppercase in the route. All except CONNECT are supported.

Query

This refers to the string of parameters following the url question mark.

Query=baz
r.query("baz")

Query=foo, ba.
r.query("foo","ba.")
Copy the code

It’s so simple, I don’t need to say much about it.

RemoteAddr

RemoteAddr = 192.168.1.1/24 r - > r.r emoteAddr (" 192.168.1.1/24 ")Copy the code

3.3 the weight

Weight information configuration, a little bit 2B. For example, we have two servers behind us, and spring Cloud Gateway does two routes to them, where the hub of the link is a group called Weight.

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2
Copy the code

The same code is shown below.

builder.routes()
.route("weight_high",r -> r.weight("group1", 8).uri("https://weighthigh.org"))
.route("weight_low",r -> r.weight("group1", 2).uri("https://weightlow.org"));
Copy the code

Suppose the service has 100 nodes and a bunch of filters that need to be configured 100 times? I have to say very fucking.

Four, Filter, Filter writing

Match to locate the route to be proxied. Now, it’s inside our route. Most of the routing functions mentioned above are configured here.

As anyone who has used zuul gateway knows, there are pre and Post annotations that control the routing behavior before and after the proxy when customizing routes. Spring Cloud Gatewa has the same effect.

4.1 Information Modification

Crud exists not only in THE SSM, but also in the configuration of routes. You might modify HTTP headers or other information before routing to the real back-end service; Or make some changes after the proxy goes to the corresponding link.

According to our understanding, the so-called request corresponds to the pre, and the response corresponds to the POST.

AddRequestHeader=X-Request-Foo, Bar
AddRequestParameter=foo, bar
AddResponseHeader=X-Response-Foo, Bar

RemoveRequestHeader=X-Request-Foo
RemoveResponseHeader=X-Response-Foo
RemoveRequestParameter=foo

SetRequestHeader=X-Request-Foo, Bar
SetResponseHeader=X-Response-Foo, Bar

SetStatus=401
Copy the code

4.2 Request Body Modification

This is a bit of a pain, but the reason is still caused by Webflux.

.filters(f -> f.modifyRequestBody(String.class, String.class, MediaType.APPLICATION_JSON_VALUE,
    (exchange, s) -> {
            return Mono.just(s.toUpperCase());
})
Copy the code

The above code capitalizes everything in the requestBody.

Similarly, response corresponds to modifyResponseBody, so it’s written similarly. Specific can see ModifyRequestBodyGatewayFilterFactory code. If you have not been exposed to the theoretical part mentioned above, it is still a difficult read.

4.3 the redirection

RedirectTo=302, https://acme.org

.filters(f -> f.redirect(302,"https://acme.org"))
Copy the code

Direct redirect. This is a simple one, I won’t go into too much detail.

4.4 Removing prefixes

The point.

StripPrefix=2

.filters(f->f.stripPrefix(2))
Copy the code

StripPrefix can accept a non-negative integer to remove the corresponding prefix. For example, if the path of external access is /a/b/c/d, then the path to the back-end service is /c/d without the /a/b prefix.

This is a special form of path rewriting, commonly used for microservice path rewriting with the LB :// protocol URI.

4.5 Path Rewriting

RewritePath is very similar to nginx path rewriting.

RewritePath=/foo(? <segment>/? .*), $\{segment} f.rewritePath("/foo(? <segment>/? .*)", "${segment}")Copy the code

The official explanation is due to the YML configuration file. Write $as $\, but this is not required in Java code. Because of the internal use of Java re, and the use of the group concept, the code can be really dirty.

4.6 Fuse Breaker Configuration

The default integrated circuit breaker is still Hystrix.

Hystrix=myCommandName

.filters(f -> f.hystrix(c->c.setName("myCommandName")))
Copy the code

In addition, there is a parameter called fallbackUri, but unfortunately, only forward mode is supported. Such as:

fallbackUri: forward:/myfallback
Copy the code

4.7 Retry Configuration

For some services that require very high stability, an unavoidable problem is retry. There are many retry parameters. A typical configuration is as follows:

- name: Retry
    args:
        retries: 3
        statuses: BAD_GATEWAY
        backoff:
            firstBackoff: 10ms
            maxBackoff: 50ms
            factor: 2
            basedOnPreviousValue: false
Copy the code

Backoff specifies the retry policy and interval, which is incremented by the formula firstBackoff * (factor ^ n).

Fuses ensure service security and retries ensure service robustness. Use scenarios must be identified.

4.8 current limiting

The built-in flow limiter, if triggered, will return HTTP 429 – Too Many Requests errors.

The parameter to the limiter is an implementation called KeyResolver, which has the concept Mono mentioned above. So if you want to extend this limiter, you need to know about WebFlux.

public interface KeyResolver {
    Mono<String> resolve(ServerWebExchange exchange);
}
Copy the code

At the same time, distributed traffic limiting based on the token bucket principle of Redis. Because the underlying layer uses “spring-boot-starter-data-redis-reactive”, it has the characteristics of “reactive” and supports the WebFlux (Reactor) Backpressure. For the configuration, there are some twists, such as this section of the official configuration.

- name: RequestRateLimiter
    args:
        key-resolver: '#{@ipKeyResolver}'
        redis-rate-limiter.replenishRate: 10
        redis-rate-limiter.burstCapacity: 20
Copy the code

We need to specify a bean named ipKeyResolver.

There are many dimensions of traffic limiting, so you need to develop your own management background. For reasons of space, we won’t discuss it.

5. Customize filters

Spring Cloud Gateway filters are classified into global filters and local filters. The corresponding interfaces are GatewayFilter and GlobalFilter.

If built-in filters cannot meet requirements, you can customize filters. More flexible control can be achieved by implementing the GatewayFilter and Ordered interfaces.

You can refer to the implementation of built-in filters. In a later article, we will describe the specific code implementation in detail.

6. Frequently asked Questions

Lb :// what does that mean?

Lb ://serviceName is a load balancing URI that the Spring Cloud Gateway automatically creates for us in microservices. In some special cases, it can be written directly. For example, if the registration name in eureka is pay-rpc, then this is written as:

lb://pay-rpc
Copy the code

How do I modify HTTP content? Such as the method?

Notice the ServerWebExchange thing. Using its exchange.mutate() function, you can enter modify mode. For example, convert GET to POST:

ServerHttpRequest request = exchange.getRequest();
if (request.getMethod() == HttpMethod.GET) {
    exchange = exchange.mutate().request(request.mutate().method(HttpMethod.POST).build()).build();
}
Copy the code

How do I dynamically update routes? The management interface of the actuator ensures that the contents are stored on the Intranet.

GET/physical/gateway/routes routing list GET/physical/gateway/routes / {id} for a routing information GET/physical/gateway/globalfilters global filter GET/physical/gateway/routefilters filter list POST/physical/gateway/refresh to refresh routing POST /gateway/routes/{id_route_to_create} Create a route DELETE /gateway/routes/{id_route_to_delete} DELETE a routeCopy the code

How to do some statistics

This feature is very simple, we just need to implement a global filter, can add any statistics function. There are two common methods: Log analysis; Analysis was performed by applying internal polymerization.

Neither is difficult, and it’s the planning of the functionality rather than the code.

I have more advanced functions, such as the need to decrypt data, what should I do?

This is going to implement the filter itself.

 Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
Copy the code

With ServerWebExchange, you can control the addition, modification, deletion, and rewriting of any parameter in the entire request process. Before and after the proxy method, you can pass

exchange.getAttributes().put();
exchange.getAttribute()
Copy the code

These two functions pass arguments

So, even if the authorities don’t write any of the above mentioned filters, we can still play with this basic interface.

End

Wechat public number is not really suitable for writing some tutorial articles, so this article is still a summary of experience.

With the withdrawal of Zuul1 and the dystocia of Zuul2, the biological SCG became the preferred choice. The Spring team is interesting in adopting WebFlux directly as a back-end technology. This will make many of us feel the pain of learning new skills.

This article does not test the performance of SCGS, which have been validated by many teams with good results.

But with Spring Cloud Gateway, there are still many problems. Fortunately, the problem is one of usage, not functionality. There’s a lot of Predicate and Filter built in, but a lot of times it doesn’t work and you have to create your own filters. Well, MOST of my filters are self-created.

In addition, the configuration of Fluent API and YML is really ugly. A management background needs to be developed. And all that complicated Java regex stuff, it’s maddening — look at the deep claw marks on the wall, my work.

Xjjdog is a public account that doesn’t allow programmers to get sidetracked. Focus on infrastructure and Linux. Ten years architecture, ten billion daily flow, and you discuss the world of high concurrency, give you a different taste. My personal wechat xjjdog0, welcome to add friends, further communication.