Hello everyone, I am a side dish.
A man who hopes to be about architecture ! If you also want to be the person I want to be, otherwise click on your attention and be a companion, so that Xiaocai is no longer alone!
This article mainly introduces
SpringCloud's service gateway Gateway
If necessary, you can refer to
If it helps, don’t forget 160eb8611d2cb5 ❥
The WeChat public account has been opened, , students who are not following please remember to pay attention!
When chatting with my friends some time ago, I talked about the status quo of their company. In the near future and in the future, the company will fully transition from a single service to a microservice architecture. Here we heard the key - . At first glance, it seems reasonable. The Internet is constantly developing. This is not only the development of the industry, but also the development of the system architecture. The current architecture on the market has roughly gone through these stages:
Is this a good thing? It's a good thing, no doubt. Because they do not keep up with the tide of the times, they will always be shot to death on the beach. But is it a good thing? That's not necessarily true.
We must first understand what problems microservices solve? Generally speaking, it should be the application level and the human level.
- application level: single service is very simple, and the low cost of project development and maintenance is its undisputed advantage. But bloated coupling will put an excessive burden on the infrastructure. If an application processing resource occupies a large amount of CPU, it will cause other processing resources to starve, and the system delay will increase, which directly affects the availability of the system.
- person level:
independent,
multilingual ecology is also the label of microservices. In a single service, the more human resources invested, the more efficient it is not, but the easier it is to make mistakes. But microservices are different. Each service is independent. Teams can develop collaboratively more easily. There is even a system, multiple services, and multiple languages without conflict.
But we cannot be blinded by benefits in everything. The shortcomings of microservices have always existed:
- Need to consider the fault tolerance between various services
- Need to consider data consistency issues
- Need to consider distributed transaction issues
- ...
Many people think that the core of is 160eb8611d2e90 micro. The finer the service division, the better, just like a single principle that is not considered when writing code, but it is used incisively and vividly in service division. Here you need to understand a core concept:
not in micro, but in the appropriate size
This sentence seems to be very simple, but how big is the right size? This may depend on different teams and different projects. The right size may depend on fewer code repositories, fewer deployment queues, fewer languages... there is no final decision here!
If you can't achieve the right size and split the service brainlessly, then microservices may become a burden to your project. Therefore, sometimes the full transformation of microservices is not a good thing. What do you think?
The topic is a bit far off, let's try to get it back. Now that microservices have become mainstream, how to design microservices is what we should do. When talking about microservices, what we think of is just how to argue with people about how to reject microservices. So this article we are going to talk about SpringCloud service gateway Gateway
SpringCloud Service Gateway Gateway
One, know the gateway
What is a service gateway? Don't give yourself a head. Let's ask another question, why do we need a service gateway?
The service gateway provides a single unified access entry across one or more service nodes
Its function is not dispensable, but vital. We can implement routing and forwarding and
filter in the service gateway. The advantages are briefly described as follows:
- Prevent internal service concerns from being exposed to external clients
- Added an extra layer of security to our multiple internal services
- Reduce the complexity of microservice access
According to the content in the figure, we can get the following information:
- User access entrance, unified access to other microservice nodes through the gateway
- The service gateway functions include
routing and forwarding,
API monitoring,
permission control,
current limiting
And these are the service gateway !
1) Zuul comparison
SpringCloud Gateway is a brand new project of SpringCloud, the goal is to replace Netflix Zuul. It is Spring5.0 + SpringBoot2.0 + WebFlux
and has higher performance than Zuul. According to official information, the performance is 1.6 times that of Zuul. It is intended to provide a simple and effective unified API routing management method for the microservice architecture.
SpringCloud Gateway not only provides a unified routing method (reverse proxy), but also provides basic functions of the gateway based on the Filter chain (defining filters to filter requests), such as authentication, flow control, fusing, path rewriting, Log monitoring, etc.
In fact, when it comes to Netflix Zuul, small partners who are using or preparing to use the microservice architecture should not be unfamiliar. After all, Netflix is an established microservice open source. The competition between rookies and veterans, if the rookies do not have any hard power, how to make people feel at ease to transform!
Here we can take a look at Weflux , Webflux appeared to fill the gap in Spring's reactive programming.
There may be many friends who don’t know Webflux. Xiaocai will also publish an explanation about Webflux, which is really delicious!
Webflux's responsive programming is not just a change in programming style, but also a development kit that provides responsive access to a series of well-known frameworks, such as Netty , Redis (If you don’t know the strength of Netty, you can think Think why Nginx can carry so much concurrency, the bottom layer is based on Netty)
So what does it have to do Zuul We can look at the IO model of Zuul
The Zuul version integrated in SpringCloud uses the Tomcat container and uses the traditional Servlet IO processing model. Servlet is managed by the Servlet Container life cycle.
But the problem is that Servlet is a simple network IO model. When the request enters ServletContainer , a thread will be bound to it. This model is not a problem in a scenario where the concurrency is not high, but once the concurrency comes up , The number of threads will increase. The problem that causes is that context switching is frequently performed, memory consumption is serious, and the time to process the request will become longer. As the so-called touches the whole body!
And SpriingCloud Zuul is a blocking processing model based on servlet, that is, Spring implements a servlet (DispatcherServlet) that processes all request requests, and the servlet performs blocking processing. Although SpringCloud Zuul 2.0 started using Netty as a concurrent IO framework, SpringCloud officially has no plans to integrate this version!
Note: The meaning of Gateway is not respected here, the specific use depends on the specific project
Three, master the gateway
1. Gateway dependency
The most critical step is to introduce the dependency of the gateway
<!--gateway网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2. Project structure
store-gateway
project here. There is a 060eb8611d334f service gateway and a store-order
order service in the project. Because we only explain the role of the service gateway in this article, there are not too many service providers and consumers!
There is only one controller OrderController
in the store-order
order service, and there is also only one simple API
@RestController
@RequestMapping("order")
public class OrderController {
@GetMapping("/{id:.+}")
public String detail(@PathVariable String id) {
return StringUtils.join("获取到了ID为", id, "的商品");
}
}
We start two services separately, and then access the API of the order service:
The result is definitely in line with expectations and will not overturn. 8001
is the interface of the order service. At this time, we can understand that each service of the original microservice architecture is started independently and can be accessed independently, which is equivalent to a traditional single service.
Let's think about it, if we use ports to distinguish each service, can we also achieve the effect of microservices? It seems to be possible in theory, but what if there are hundreds of services? Port explosion, maintenance explosion, governance explosion... If nothing else, the mentality exploded directly! At this time, we thought that it would be great if we only used a unified port to access each service! Ports are consistent, routing prefixes are inconsistent, and services are differentiated by routing prefixes. This approach brings us into the service gateway scenario. Yes, here is one of the functions of the service gateway --- routing and forwarding.
3. The gateway appears
Since the gateway is going to be used, one of the services we created above, store-gateway
will come in handy! how to use? We make a simple modification in the configuration file:
spring:
application:
name: store-gateway
cloud:
gateway:
routes:
- id: store-order
uri: http://localhost:8001
order: 1
predicates:
- Path=/store-order/**
filters:
- StripPrefix=1
No nonsense, let's start the gateway directly and visit http://localhost:8000/store-order/order/123
see if we can get the order?
Very smoothly, we successfully got the order item with ID 123!
Let's look at the composition of the URL:
Being able to access our service indicates that the gateway configuration has taken effect. Let's take a look at what this configuration item is all about!
spring.cloud.gateway
is the configuration prefix of the service gateway Gateway, there is nothing to say, you just need to remember it.
routes
following 060eb8611d353f is worthy of our attention. routes
is a plural form, so you can be sure that this configuration item is in the form of an array, so it means that we can configure multiple routing and forwarding, and when the request meets what conditions, it will go to which microservice .
- the above mentioned id: current route
unique identification
- uri: The address to which the
- order : routing priority, the smaller the number, the higher the level
- predicates: routing needs to meet the conditions, also an array (here is the relationship
or)
- filters: filters, the request can be modified to some extent through the filters during the delivery process
After understanding the necessary parameters, we happily deployed and used them, but the good times did not last long, and we ushered in new problems. My order service originally used the 8001
port, which was used by other services for some reason. At this time, my little head became bigger again. In this case, the wrong result of
Let's think about how to solve this kind of problem more appropriately? Now that we have adopted microservices, can we use the service name to jump access, so that no matter how the port changes, it will not affect our normal business! If you use the service method, you need a registration center, so that the service we started can be synchronized to the registry
registration center, so that the sub-gateway can find the service in the registry
service name for routing Jumped! Then we need a registration center, here we use Nacos as the registration center.
Regarding the Nacos , you can airborne the rookie Nacos, you will see it, I will say!
We have made the following configurations in the configuration files of the service gateway and order service respectively:
After starting the two services, we can see the two services in the console service list of Nacos
At this time, you can see that the service name of order service store-order
, then we can make the following changes in the gateway configuration file section:
The configuration here is one of the differences between http
lb
( lb refers to obtaining microservices from nacos by name and following the load balancing strategy), and the second port is replaced by
service name
Then we continue to visit the above URL to see if we can successfully access the order service:
The result is still not overturned! In this way, no matter how the port of the order service changes, as long as our service name remains the same, we can always access our corresponding service!
Days go by~ The service is also increasing bit by bit! routes
is finally annoying, because every time you increase the service, you have to add a configuration column of 060eb8611d3a00 to the configuration file. Although it is only CV his hands, then ~~~ count. Forget it, look for something to be lazy, and finally live up to Xiao Caixin and find a simplified version! The configuration is as follows:
That's it? Yes, that's it! No matter how many services you provide, my configuration file doesn't need to be modified. The purpose of this sub-configuration is to request a unified way to gateway address: gateway port/service name/interface name. Visit the page again and it still works!
But the convenience is convenient, and it also limits a lot of extended functions at the same time, so you need to think twice when using it! Do not greedy!
Four, master the core
The simple use of the gateway has already been mentioned above, and the little friends who have read it must already be able to get started! Next, we continue to strike while the iron is hot and learn about the core Gateway Not to mention anything else, routing and forwarding must be the core of the gateway! We have learned a specific routing information carrier from the above, which mainly defines the following information ( recalled ):
- id: route, which is different from other Routes
- uri: The route points to the destination uri, which is the microservice to which the client request is finally forwarded
- order: used for sorting between multiple Routes, the smaller the value, the higher the sorting, the higher the matching priority
- predicate: used to make conditional judgments. Only when all the assertions return true, the routing will be executed.
- filter: filter is used to modify request and response information
Here to sort out the access process:
This picture clearly describes the calling process of the service gateway ( blindly confident )
- GatewayClient to GatewayServer request
- The request will first be HttpWebHandlerAdapter into the gateway context
- Then the context of the gateway will be passed to DispatcherHandler , which is responsible for distributing the request to RoutePredicateHandlerMapping
- RoutePredicateHandlerMapping is responsible for route lookup, and judges whether the route is available based on route assertion
- If the assertion is successful, FilteringWebHandler creates a filter chain and calls
- The request will go through PreFilter ---> ---> PostFilter method, and finally return a response
After understanding the process, let's extract the key! assertion and
filter
1. Assertion
Predicate also an assertion, mainly suitable for conditional judgment, will only execute routing if all the assertions return true.
1) Assert the factory
There are many assertion factories built in SpringCloud Gateway, and all of these assertions match the different attributes of the HTTP request, as follows;
- Based on Datetime type assertion factory
This type of assertion factory makes judgments based on time
1. AfterRoutePredicateFactory: receives a , and judges whether the request date is later than the specified date
2. BeforeRoutePredicateFactory: receives a , and judges whether the request date is earlier than the specified date
3. BetweenRoutePredicateFactory: receives two , and judges whether the requested date is within the specified time period
- based on the remote address assertion factory
This type of plant is asserted receiving a parameter, the IP address terminal, determines whether the requesting host address in the address segment. (Eq: -RemoteAddr=192.168.1.1/24)
- Cookie-based assertion factory CookieRoutePredicateFactory
This type of assertion factory receives two parameters , Cookie name and a regular expression, and judges whether the requested cookie has a given name and the value matches the regular expression. (Eq: -Cookie=cbuc)
- Header-based assertion factory HeaderRoutePredicateFactory
This type of assertion factory receives two parameters , title name and regular expression. Determine whether the request Header has the given name and the value matches the regular expression. (Eq:-Header=X-Request)
- Host-based assertion factory HostRoutePredicateFactory
This type of assertion factory receives a parameter, the host name pattern. Determine whether the requested host meets the matching rules. (Eq: -Host=**.cbuc.cn)
- Based on the method request method assertion factory MethodRoutePredicateFactory
This type of assertion factory receives a parameter of determine whether the request type matches the specified type. (Eq: -Method=GET)
- PathRoutePredicateFactory
This type of assertion factory receives a parameter of , and judges whether the URI part of the request satisfies the path rule. (-Eq:-Path=/order/)
- based on the Query request parameter assertion factory
This type of plant assertion received two parameters, the request the Param and regular expressions. Determine whether the request parameter has the given name and the value matches the regular expression. (Eq: -Query=cbuc)
- WeightRoutePredicateFactory
This type of assertion factory receives a [group name, weight], and then forwards the routes in the same group according to the weight
2) Use
With so many assertion factories, I will not use them one by one here. Let's demonstrate the use of several assertion factories.
We don’t talk nonsense as usual, just go to the code:
CustomPredicateRouteFactory
configuration file
test result
success
fail
While exclaiming Amazing , don't worry about looking down. Let's go back to the code and see why one can access successfully but one fails. Two aspects: 1. What are the differences between the URLs accessed by the two? 2. Which part of the code handles the URL
Develop independent thinking first, then look at the solution
After you think about it, some students may already have a result, then let us continue to look down! The first is a CustomRoutePredicateFactory
class. This class acts a bit like an interceptor. It is intercepted when the request is forwarded. We can make a breakpoint when we request:
As you can see, it is indeed the function of the interceptor, which intercepts each request when it is initiated. The result of question 2 came out. The original URL processing was processed in RoutePredicateFactory . In the apply method, you can get the ServerHttpRequest exchange.getRequest()
, so that you can get the request parameters, request method, request header and other information. . shortcutFieldOrder()
method is also one of the keys to rewriting, we need to return here, the attributes defined in our entity class, and then we can receive the attribute parameters we assigned apply()
Note: If there are multiple attributes in the custom entity that need to be judged, shortcutFieldOrder()
method should be consistent with the order of the parameters in the configuration file
So when we write the assertion factory, how to make it take effect? @Component
must be indispensable, the purpose is to let the Spring container management. So how does the registered assertion factory declare its use? Then you have to go back to the configuration file!
Here we focus on predicates
. There are three configurations. One is Path
that we are already familiar with. The other two are a bit unfamiliar. But let’s take a look at Custom
Isn’t it familiar? Yes, we seem to have defined an CustomRoutePredicate
above. The two are a bit similar, but they seem to be almost something. Then I will give you another hint:
Let's take a look at the self-implemented classes of the abstract assertion factory! Is there PathRoutePredicateFactory
Yes, it is what you think! Is there a sees the blue sky through the rain and fog! It turns out that the key of our configuration file is declared with the prefix of the class name, that is to say, the format of the assertion factory class must be: custom name + RoutePredicateFactory as the suffix, and then declared in the configuration file. By analogy, we naturally understand the
- Before
, which is: limit the request time to before xxx.
And -Custom= cbuc
, this cbuc is our restriction rule, only the user whose name is cbuc can request success. If there are multiple parameters, they can be ,
, and the order needs to be consistent with the order shortcutFieldOrder()
If you encounter any obstacles on the way to the custom assertion factory, take a look at how the built-in assertion factory is implemented. look at the source code is always right!
2. Filter
Next enter the second core, which is the filter. The core role is also quite simple, that is, in the process of request transmission, a series of manipulation of the request and response. In order to fear that you can check the request process too troublesome when you check it back, Xiaocai intimately posted the flow chart again:
The Gateway filter can be divided into local filter and global filter . Listen to the name to know its role, local is used for a certain route, global is used for all routes. But whether it is local or global , the life cycle is divided into pre
and post
.
- pre: is called before routing to the microservice. We can use this filter to achieve authentication, select the requested microservices in the cluster, record debugging records, etc.
- post: is executed after routing to microservices. We can use this filter to add a standard HTTP Header in response, collect statistics and metrics, and send the response from the microservice to the client.
1) Partial filter
A local filter is a filter for a single route. Similarly, Gateway has built-in many filters
We choose several commonly used filters for description: (The following filters omit the suffix GaewayFilterFactory
, and the full name is prefix + suffix)
Filter prefix | effect | parameter |
---|---|---|
StripPrefix | Used to truncate the path of the original request | Use numbers to indicate the number of paths to be truncated |
AddRequestHeader | Add a header to the original request | Header name and value |
AddRequestParameter | Add request parameters to the original request | Parameter name and value |
Retry | Retry for a different response | reties、statuses、methods、series |
RequestSize | Set the size of the largest request packet allowed to be received | Request packet size, unit byte, default 5M |
SetPath | Modify the path of the original request | Modified path |
RewritePath | Rewrite the original request path | The regular expression of the original path and the regular expression of the rewritten path |
PrefixPath | Add a prefix to the original request path | Prefix path |
RequestRateLimiter | Limit the request flow, the flow-limiting algorithm is the token bucket | KeyResolver、reteLimiter、statusCode、denyEmptyKey |
Friends of the built-in filter can try it out on their own. If you have any questions, please ask questions!
We next talk about how to customize the filter factory. 060eb8611d4677, we don't say so much
CustomGatewayFilterFactory
configuration file
When we enable request counting, we can see that the console has made statistics on the number of requests:
So we can easily implement local filters in this way
2) Global filter
The global filter acts on all routes, no configuration is required. Through the global filter, unified verification of permissions, security verification and other functions can be realized
, let’s take a look at what global filters exist in 160eb8611d473e Gateway
Compared with the local filter, the naming of the global filter is not too restrictive, after all, there is no need to configure it in the configuration file.
Let’s get familiar with the classic global filter
Filter name | effect |
---|---|
ForwardPathFilter / ForwardRoutingFilter | Path forwarding related filters |
LoadBalanceerClientFilter | Load balancing client related filters |
NettyRoutingFilter / NettyWriteResponseFilter | Http client related filters |
RouteToRequestUrlFilter | Route URL related filters |
WebClientHttpRoutingFilter / WebClientWriteResponseFilter | Request the WebClient client to forward the real URL of the request and write the response to the current request response |
WebsocketRoutingFilter | websocket related filters |
After understanding the built-in filters, let's take a look at how to define global filters!
CustomerGlobalFilter
For the global filter, we do not need to configure it in the configuration file, because it applies to all routes
test results
success
fail
As you can see, we used the global filter to perform the authentication token is not carried, it will not be accessible!
So far, we have learned about the routing and forwarding of the service gateway, and the permission verification can even make rough and simple API monitoring and current limiting assertions and filters.
But in fact, for API monitoring and current limit , SpringCloud already has better components to complete these two tasks. After all, there is a single principle, the more you do, the more mistakes you will make!
I will continue to organize articles about SpringCloud components later, so stay tuned!
For the framework of microservices, it is up to us to decide which one is good or bad. But no matter which is good or bad, facing the emergence of a new technology, what we need to do most is to accept it, tolerate it, and then use it well. It's a mule or a horse, and you know it by yourself.
Don't talk empty talk, don't be lazy, and be X as an architecture with Xiaocai~ Follow me to be a companion, so Xiaocai is no longer alone. See you below!
If you work harder today, you will be able to say less begging words tomorrow!
I am Xiaocai, a man who becomes stronger with you.
💋
The WeChat public account has been opened, , students who have not followed please remember to pay attention!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。