1. What is a service gateway?

In the traditional monolithic architecture, only one service needs to be opened to the client to call, but the microservice architecture is to split a system into multiple microservices, if there is no gateway, the client can only record the call address of each microservice locally, when the number of microservices that need to be called is large, it needs to understand the interface of each service, which is a lot of work. So what kind of improvement can be achieved after having a gateway?

As the only traffic entry point of the system, the gateway encapsulates the architecture of the internal system, and all requests pass through the gateway first, and the gateway routes the requests to the appropriate microservices, so the advantages of using the gateway are:

But API Gateway also has shortcomings, in the decentralized architecture of microservices, the gateway has become a central point or bottleneck point, which adds a highly available component that we must develop, deploy, and maintain. It is for this reason that the gateway design must consider that even if the API Gateway goes down, it does not affect the call and operation of the service, so it is necessary to have data caching capability for the response result of the gateway, by returning cached data or default data to mask the failure of the backend service.

In the service call mode, the gateway also has certain requirements, API gateway is best to support I/O asynchronous, synchronous non-blocking, if the service is synchronous blocking call, it can be understood that there is no complete decoupling between microservice modules, that is, if A depends on the API provided by B, if the service provided by B is not available, it will directly affect A is not available, unless the synchronous service call is cached at the API gateway layer or the client.

Therefore, in order to completely decouple, it is more recommended to choose asynchronous mode for microservice calls. For the scenario where API Gateway needs to combine multiple fine-grained APIs through the underlying layer, it is recommended to use a reactive programming model instead of the traditional asynchronous callback method to combine code, the reason is not only the code confusion caused by the callback method, but also the parallel or successive calls to the API combination itself, which is often difficult to control the use of callback methods.

The location of the traffic gateway and the service gateway in the overall system architecture is shown in the preceding figure, and the traffic gateway (such as Nignx) refers to the provision of global, unrelated policies related to back-end service applications, such as HTTPS certificate offload, Web firewall, and global traffic monitoring.

Second, the deployment of the service gateway

Kong Gateway: The performance of Kong is very good, very suitable for traffic gateway, but for complex systems, it is not recommended to use Kong for business gateways, mainly for engineering considerations

Zuul1.x Gateway: Zuul 1.0 has rich experience in landing, but the performance is poor, based on synchronous blocking IO, suitable for small and medium-sized architectures, not suitable for scenarios with high concurrent traffic, because it is easy to produce thread exhaustion, resulting in requests being rejected

gateway gateway: powerful and rich, good performance, the official benchmark RPS (number of requests per second) is 1.6 times that of Zuul, can be very compatible with the SpringCloud ecosystem, and streaming programming + support asynchronous is enough for developers to choose it.

Zuul 2.x: Similar in performance to gateways, non-blocking-based, supporting long connections, but SpringCloud has no plans to integrate zuul2, and Netflix-related components have announced a maintenance period, the prospect is unknown.

In summary, the gateway gateway is more suitable for the SpringCloud project, and from the development trend, the gateway instead of zuul is also inevitable.

(1) Declare the dependency version number:

(2) Add dependencies:

Note: Be sure to exclude the spring-boot-starter-web dependency, otherwise the startup error will be reported

(3) Configure the project name and port:

Well, the gateway project is built, in fact, add such a dependency, the detailed configuration and role of the following introduction.

Before introducing the configuration items for Spring Cloud Gateway, let’s look at a few of the core terms of Spring Cloud Gateway:

Route is mainly composed of route ids, target uries, assertion sets, and filter sets, so let’s take a brief look at what these properties do.

Predicate comes from the interface of Java8. Predicate accepts an input parameter and returns a Boolean result. The interface contains several default methods to combine Predicate into other complex logic (such as: with, or, not).

Predicate can be used to verify the parameters of interface requests and determine whether there are changes in new and old data that need to be updated. Spring Cloud Gateway has a number of built-in Predicts, which are sourced in the org.springframework.cloud.gateway.handler.predicate package, so you can read them for interest. Some of the built-in assertions are as follows:

The above 11 assertions are no longer described here on how to configure, and the official documentation is very clear:

https://docs.spring.io/spring-cloud-gateway/docs/2.2.9.RELEASE/reference/html/

Let’s take the last weight assertion as an example to introduce how to configure it. The configuration is as follows:

The assertion naming in Spring Cloud Gateway is standardized, the format: “xxx + RoutePredicateFactory”, such as the weight assertion WeightRoutePredicateFactory, then the configuration directly takes the preceding “Weight”.

If the route forwarding matches two or more, it is forwarded in the order of configuration, and the path is configured above: “Path=/gateway/provider/**”, if no weight is configured, it must be forwarded to “http://localhost:9024” first, but since the configuration is configured with weights and the same grouping, the traffic is distributed according to the weight ratio.

Life cycle of a Gateway filter:

Gateway filters can be divided into two types of scopes:

(1) Local filter GatewayFilter:

Spring Cloud Gateway has a number of built-in local filters, as shown in the following figure:

Local filters need to be configured in the specified route to take effect, but by default they are not valid. Take the “AddResponseHeaderGatewayFilterFactory” filter as an example, add a header to the original response, and configure it as follows:

The browser requests, and finds that the X-Response-Foo=Bar key-value pair already has in the response header, as shown in the following figure:

In the previous example, we also used another local filter, StripPrefixGatewayFilterFactory, which is mainly used to truncate the path of the original request, and when we request localhost:9023/gateway/provider/test, the actual request is forwarded to the http://localhost:9024 service and truncated to ” http://localhost:9024/provider/test”

Note: The name of the filter only needs to be prefixed and the filter name must be “xxx + GatewayFilterFactory” (including customization).

For more filter configurations, refer to the official documentation:

https://docs.spring.io/spring-cloud-gateway/docs/2.2.9.RELEASE/reference/html/#gatewayfilter-factories

(2) Custom local filter:

Although the built-in filter can solve many scenarios, it is inevitable that there are still some special needs that need to customize a filter, so let’s introduce how to customize the local filter.

Local filters need to be configured in the route to take effect, as follows:

At this point, direct access: http://localhost:9023/gateway/provider/port, do not carry tokens, return to the following figure:

The request parameter is taken with token:http://localhost:9023/gateway/provider/port?token=abcdcdecd-ddcdeicd12, and returns successfully, as shown in the following figure:

The above AuthorizeGatewayFilterFactory only involves the pre-processing of the filter, the post-processing is done in the then() method in chain.filter().then(), see the TimeGatewayFilterFactory in the project source code, the code is no longer pasted, as shown in the following figure:

(3) GlobalFilter global filter:

The global filter is applied to all routes, no developer configuration, Spring Cloud Gateway also has some built-in global filters, as shown in the following figure:

The function of GlobalFilter is actually the same as that of GatewayFilter, except that the scope of GlobalFilter is all route configurations, not bound to the specified route configuration. Multiple GlobalFilters can specify the order of execution through the @Order or getOrder() method, and the smaller the order value, the higher the priority of execution.

Note that since the filter has two types, the pre type filter should be at the top of the pre filter chain if the order value is smaller, and the post type filter should be at the bottom of the post filter chain if the order value is smaller. The schematic diagram is as follows:

(4) Customize the global filter:

Of course, in addition to the built-in global filter, in actual work also need to customize the filter, the following to introduce how to customize. We simulate Nginx’s Access Log feature to record information about each request. The code is as follows:

Well, the global filter does not have to be configured on the route, and it can take effect globally by injecting it into the IOC container.

A request is made and the console prints the following message:

Request path: /gateway/provider/port, remote IP address: /0:0:0:0:0:0:1:64114, response code: 200 OK

There is no integrated registry in the above demo, and each routing configuration specifies a fixed service uri, as shown in the following figure:

What’s the harm in doing so?

Then at this time we can integrate the registry, so that the gateway can automatically obtain the uri from the registry, and achieve load balancing, here we take the nacos registry as an example to introduce it

(1) Added dependencies in pom files:

(2) Start the class to add @EnableDiscoveryClient annotation to open the registration center function, as shown in the following figure:

(3) Configure the address of the nacos registry:

(4) Service routing configuration:

The only difference in the routing configuration is the route’s uri, format: lb://service-name, which is a fixed expression:

Why specify lb to enable load balancing, as mentioned earlier, the global filter LoadBalancerClientFilter is responsible for route addressing and load balancing, you can see the following source code:

(5) Enable gateway automatic routing configuration:

With the continuous development of our system architecture, the number of microservices in the system will definitely increase, and it is impossible for us to configure a new routing rule on the gateway every time we add a service, which is very expensive to maintain; Especially in many cases, we will carry a route ID in the request path to facilitate forwarding, and this route ID is generally the service name of the service in the registry, so this is the automatic routing function of the spring cloud gateway that we can turn on, the gateway automatically creates a router according to the service name of the registration center for each service, and forwards the request path starting with the service name to the corresponding service, configured as follows:

It should be noted here that since our gateway project is configured with the server.servlet.context-path property, which causes automatic routing to fail, we need to make the following two modifications:

At this point, we have turned on the automatic routing function of spring cloud gateway, which automatically creates a router for each service according to the service name of the registration center, and forwards the request path beginning with the service name to the corresponding service.

Assuming that our service provider’s service in the nacos registry is named “gateway-provider”, we only need to visit the “http://localhost:9023/gateway/gateway-provider/test” to successfully forward the request

The above examples are a series of configurations of the gateway written to the configuration file of the project, once the routing policy changes must restart the project, so that the maintenance cost is very high, especially the service gateway as the central point of the system, once the restart of the problem, the impact surface will be very huge, therefore, we will store the configuration of the gateway in the configuration center, so that the configuration center unified management, once the route changes, only need to modify in the configuration center, reduce the risk and real-time failure. This section uses the Apollo configuration center as an example to introduce the following dynamic routing configuration:

(1) Add the apollo configuration center dependency:

(2) Add the Apollo Route Change Listener refresh class:

(3) Expose endpoint endpoints:

At this point, we have completed the Gateway Gateway Integration Apollo Configuration Center to implement dynamic route configuration, once the route changes, only need to be modified in the configuration center can be monitored and disabled in real time

If you have integrated Nacos or MySQL for dynamic routing configuration, you can refer to the following two articles:

(1) Integrate Nacos for dynamic routing configuration:

https://www.cnblogs.com/jian0110/p/12862569.html

(2) Integrate MySQL for dynamic routing configuration:

https://blog.csdn.net/qq_42714869/article/details/92794911

Through the previous test, we can see a phenomenon: once the routed microservice goes offline or is missing, Spring Cloud Gateway directly returns an error page, as shown in the following figure:

Obviously, this exception information is not friendly, and the returned exception information must be customized in the front and back end separation architecture. Traditional Spring Boot services use @ControllerAdvice to wrap global exception handling, but the request did not arrive because the service went offline. Therefore, a layer of global exception handling must also be customized in the gateway to make it more user-friendly to the client.

Spring Cloud Gateway offers a variety of global processing methods, and today we will only introduce one of them, and the implementation is relatively elegant:

Directly create a class GlobalErrorExceptionHandler, implement ErrorWebExceptionHandler, and override the handle method in it, the code is as follows:

Well, the global exception handling has been customized, in the test, at this time the normal return of JSON data (JSON style according to the architecture needs to customize itself), as shown in the following figure:

About me: Brother Tom, a former Ali P7 technical expert, offered harvesters, participated in many Taobao Double 11 promotion activities. Welcome to pay attention, I will continue to output more classic original articles to help you advance to the big factory

WeChat 8.0 will let go of friends to ten thousand, small partners can add my big number, first-come, first-served, the circle of friends can also learn knowledge!