This document is provided by B Station dark horse and Le byte video tutorial documents
1.Nacos configuration management
In addition to being a registry, Nacos can also be used for configuration management.
1.1. Unified configuration management
When there are more and more instances of microservice deployment, reaching tens or hundreds, modifying the microservice configuration one by one will make people crazy and easy to make mistakes. We need a unified configuration management scheme, which can centrally manage the configuration of all instances.
On the one hand, Nacos can centrally manage the configuration. On the other hand, when the configuration changes, Nacos can timely notify the micro service to realize the hot update of the configuration.
1.1.1. Add configuration file in nacos
How do I manage configuration in nacos?
Then fill in the configuration information in the pop-up form:
Note: the core configuration of the project requires hot update before it is necessary to put it into nacos management. It is better to save some configurations that will not be changed locally.
1.1.2. Pull configuration from microservice
The microservice needs to pull the configuration managed in nacos and merge it with the local application.yml configuration to complete the project startup.
But how do you know the nacos address if you haven't read application.yml yet?
Therefore, spring introduces a new configuration file: bootstrap.yaml, which will be read before application.yml. The process is as follows:
1) Introducing Nacos config dependency
First, in the user service service, the client dependency of Nacos config is introduced:
<!--nacos Configuration management dependency--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
2) Add bootstrap.yaml
Then, add a bootstrap.yaml file in user service, as follows:
spring: application: name: userservice # Service name profiles: active: dev #Development environment, this is dev cloud: nacos: server-addr: localhost:8848 # Nacos address config: file-extension: yaml # File suffix
Here, the Nacos address will be obtained according to spring.cloud.nacos.server-addr, and then
${spring. Application. Name} - ${spring. Profiles. Active}. ${spring. Cloud. Nacos. Config. File extension} is used as the file id to read the configuration.
In this example, read userservice-dev.yaml:
3) Read nacos configuration
Add business logic to UserController in user service and read the configuration of pattern.dateformat:
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-f3ATg26J-1635923652299)(assets/image-20210714170337448.png)]
Full code:
package cn.itcast.user.web; import cn.itcast.user.pojo.User; import cn.itcast.user.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @Slf4j @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @Value("${pattern.dateformat}") private String dateformat; @GetMapping("now") public String now(){ return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat)); } // ... slightly }
On the page, you can see the effect:
1.2. Configure hot update
Our ultimate goal is to modify the configuration in nacos so that the configuration can take effect without restart in the micro service, that is, configure hot update.
There are two ways to configure hot updates:
1.2.1. Mode I
Add the annotation @ RefreshScope on the class of the variable injected by @ Value:
1.2.2. Mode II
Use the @ ConfigurationProperties annotation instead of the @ Value annotation.
In the user service service, add a class and read the pattern.dateformat attribute:
package cn.itcast.user.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @Data @ConfigurationProperties(prefix = "pattern") public class PatternProperties { private String dateformat; }
Use this class in UserController instead of @ Value:
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-f3uzeo04-163592652302) (assets / image-20210714171316124. PNG)]
Full code:
package cn.itcast.user.web; import cn.itcast.user.config.PatternProperties; import cn.itcast.user.pojo.User; import cn.itcast.user.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @Slf4j @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @Autowired private PatternProperties patternProperties; @GetMapping("now") public String now(){ return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat())); } // slightly }
1.3. Configuration sharing
In fact, when the micro service is started, it will go to nacos to read multiple configuration files, for example:
-
[spring.application.name]-[spring.profiles.active].yaml, for example: userservice-dev.yaml
-
[spring.application.name].yaml, for example: userservice.yaml
[spring.application.name].yaml does not contain an environment, so it can be shared by multiple environments.
Let's test configuration sharing through a case
1) Add an environment sharing configuration
We add a userservice.yaml file in nacos:
2) Read the shared configuration in the user service
In the user service, modify the PatternProperties class and read the newly added attributes:
In the user service, modify the UserController and add a method:
3) Run two userapplications with different profile s
Modify the startup item UserApplication2 and change its profile value:
Thus, the profile used by UserApplication(8081) is dev, and the profile used by userapplication 2 (8082) is test.
Start UserApplication and UserApplication2
visit http://localhost:8081/user/prop , result:
visit http://localhost:8082/user/prop , result:
It can be seen that both dev and test environments have read the value of the envSharedValue property.
4) Configure shared priorities
When the same attribute appears in nacos and local service at the same time, the priority can be divided into high and low:
2.Feign remote call
Let's first look at the code we used to initiate remote calls using RestTemplate:
The following problems exist:
• poor code readability and inconsistent programming experience
• complex parameters and difficult to maintain URL s
Feign is a declarative http client. Its official address is: https://github.com/OpenFeign/feign
Its function is to help us send http requests gracefully and solve the problems mentioned above.
2.1.Feign replaces RestTemplate
The steps of using Fegin are as follows:
1) Introduce dependency
We introduce feign dependency in pom file of order service service:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
2) Add annotation
Add comments to the startup class of order service to enable Feign:
3) Write Feign's client
Create a new interface in order service, as follows:
package cn.itcast.order.client; import cn.itcast.order.pojo.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient("userservice") public interface UserClient { @GetMapping("/user/{id}") User findById(@PathVariable("id") Long id); }
This client mainly declares the remote call information based on the annotation of spring MVC, such as:
- Service Name: userservice
- Request method: GET
- Request path: / user/{id}
- Request parameter: Long id
- Return value type: User
In this way, Feign can help us send http requests without using RestTemplate.
4) Testing
Modify the queryOrderById method in the OrderService class in order service, and use Feign client instead of RestTemplate:
Does it look much more elegant.
5) Summary
To use Feign:
① Introduce dependency
② Add @ EnableFeignClients annotation
③ Write FeignClient interface
④ Use the method defined in FeignClient instead of RestTemplate
2.2. User defined configuration
Feign can support many custom configurations, as shown in the following table:
type | effect | explain |
---|---|---|
feign.Logger.Level | Modify log level | There are four different levels: NONE, BASIC, HEADERS and FULL |
feign.codec.Decoder | Parser for response results | Parse the result of the http remote call, for example, parse the json string as a java object |
feign.codec.Encoder | Request parameter code | Encode the request parameters for sending through http request |
feign. Contract | Supported annotation formats | The default is the annotation of spring MVC |
feign. Retryer | Failure retry mechanism | The retry mechanism for request failure is not available by default, but the Ribbon retry mechanism will be used |
Generally, the default value can meet our needs. If you want to customize, you only need to create a custom @ Bean to overwrite the default Bean.
The following takes the log as an example to demonstrate how to customize the configuration.
2.2.1. Configuration file mode
Modifying the log level of feign based on the configuration file can be for a single service:
feign: client: config: userservice: # Configuration for a micro service loggerLevel: FULL # log level
For all services:
feign: client: config: default: # default here is the global configuration. If the service name is written, it is the configuration for a micro service loggerLevel: FULL # log level
There are four levels of logs:
- NONE: no log information is recorded, which is the default value.
- BASIC: only record the requested method, URL, response status code and execution time
- HEADERS: on the basis of BASIC, additional header information of request and response is recorded
- FULL: records the details of all requests and responses, including header information, request body and metadata.
2.2.2.Java code mode
You can also modify the log level based on Java code, first declare a class, and then declare an object of Logger.Level:
public class DefaultFeignConfiguration { @Bean public Logger.Level feignLogLevel(){ return Logger.Level.BASIC; // The log level is BASIC } }
If you want the global effect, put it in the @ EnableFeignClients annotation of the startup class:
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class)
If it is partially effective, put it in the corresponding @ FeignClient annotation:
@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class)
2.3.Feign usage optimization
Feign initiates http requests at the bottom, which depends on other frameworks. Its underlying client implementation includes:
• URLConnection: it is implemented by default and does not support connection pool
• Apache HttpClient: supports connection pooling
• OKHttp: support connection pool
Therefore, the main way to improve Feign's performance is to use the connection pool instead of the default URLConnection.
Here we use Apache's HttpClient to demonstrate.
1) Introduce dependency
Introduce Apache's HttpClient dependency into the pom file of order service:
<!--httpClient Dependence of --> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
2) Configure connection pool
Add configuration in application.yml of order service:
feign: client: config: default: # default global configuration loggerLevel: BASIC # At the log level, BASIC is the BASIC request and response information httpclient: enabled: true # Enable feign's support for HttpClient max-connections: 200 # Maximum connections max-connections-per-route: 50 # Maximum connections per path
Next, break the point in the loadBalance method in FeignClientFactoryBean:
Start the order service in Debug mode. You can see the client here. The bottom layer is Apache HttpClient:
In conclusion, Feign's Optimization:
1. Try to use basic for log level
2. Use HttpClient or OKHttp instead of URLConnection
① Introduce feign httpclient dependency
② The configuration file enables the httpClient function and sets the connection pool parameters
2.4. Best practices
The so-called recent practice is the experience summarized in the use process, and the best way to use it.
Through self-study observation, it can be found that Feign's client is very similar to the controller code of the service provider:
feign client:
UserController:
Is there a way to simplify this repetitive coding?
2.4.1. Inheritance method
The same code can be shared through inheritance:
1) Define an API interface, define methods, and make declarations based on spring MVC annotations.
2) Feign client and Controller have integrated interface
advantage:
- simple
- Code sharing is realized
Disadvantages:
-
Tight coupling between service providers and service consumers
-
The annotation mapping in the parameter list does not inherit, so the method, parameter list and annotation must be declared again in the Controller
2.4.2. Extraction method
Extract Feign's Client as an independent module, and put the POJO related to the interface and the default Feign configuration into this module for all consumers to use.
For example, the default configurations of UserClient, User and Feign are extracted into a Feign API package, and all micro services can reference the dependent package for direct use.
2.4.3. Implement extraction based best practices
1) Extract
First, create a module named feign API:
Project structure:
Then introduce feign's starter dependency in feign API
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
Then, the UserClient, User and defaultfeign configuration written in order service are copied to the feign API project
2) Using feign API in order service
First, delete the UserClient, User, DefaultFeignConfiguration and other classes or interfaces in the order service.
Introduce the dependency of feign API in the pom file of order service:
<dependency> <groupId>cn.itcast.demo</groupId> <artifactId>feign-api</artifactId> <version>1.0</version> </dependency>
Modify all the packages related to the above three components in the order service to import the packages in feign API
3) Restart test
After restarting, it is found that the service reports an error:
This is because the UserClient is now in the cn.itcast.feign.clients package,
The @ EnableFeignClients annotation of order service is under the cn.itcast.order package, which is not in the same package and cannot scan the UserClient.
4) Resolve scan package issues
Mode 1:
Specify the packages Feign should scan:
@EnableFeignClients(basePackages = "cn.itcast.feign.clients")
Mode 2:
Specify the Client interface to load:
@EnableFeignClients(clients = {UserClient.class})
3.Gateway service gateway
Spring Cloud Gateway is a new project of Spring Cloud. The project is a gateway developed based on responsive programming and event flow technologies such as Spring 5.0, Spring Boot 2.0 and Project Reactor. It aims to provide a simple, effective and unified API routing management method for microservice architecture.
3.1. Why do I need a gateway
Gateway gateway is the gatekeeper of our services and the unified entrance of all micro services.
Core functions and features of gateway:
- Request routing
- Permission control
- Current limiting
Architecture diagram:
Permission control: as a micro service portal, the gateway needs to verify whether the user is qualified to request. If not, it will be intercepted.
Routing and load balancing: all requests must pass through the gateway first, but the gateway does not process services, but forwards requests to a micro service according to certain rules. This process is called routing. Of course, when there are multiple target services for routing, load balancing needs to be done.
Flow restriction: when the request flow is too high, release the request in the gateway according to the speed acceptable to the downstream microservice to avoid excessive service pressure.
In spring cloud, there are two implementations of gateway:
- gateway
- zuul
Zuul is a Servlet based implementation and belongs to blocking programming. Spring cloud gateway is based on WebFlux provided in spring 5. It belongs to the implementation of responsive programming and has better performance.
3.2.gateway quick start
Next, we will demonstrate the basic routing function of the gateway. The basic steps are as follows:
- Create a SpringBoot project gateway and introduce gateway dependencies
- Write startup class
- Write basic configuration and routing rules
- Start the gateway service for testing
1) Create a gateway service and introduce dependencies
Create service:
Import dependency:
<!--gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--nacos Service discovery dependency--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
2) Write startup class
package cn.itcast.gateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }
3) Write basic configuration and routing rules
Create the application.yml file as follows:
server: port: 10010 # Gateway port spring: application: name: gateway # Service name cloud: nacos: server-addr: localhost:8848 # nacos address gateway: routes: # Gateway routing configuration - id: user-service # Routing id, user-defined, as long as it is unique # uri: http://127.0.0.1: the destination address of 8081 # route http is the fixed address uri: lb://The target address lb of userservice # route is load balancing, followed by the service name predicates: # Routing assertion is to judge whether the request meets the conditions of routing rules - Path=/user/** # This is matched according to the path. It meets the requirements as long as it starts with / user /
We proxy all requests that meet the Path rule to the address specified by the uri parameter.
In this example, we proxy the request starting with / user / * * to lb://userservice. lb is load balancing. Pull the service list according to the service name to achieve load balancing.
4) Restart test
Restart the gateway to access http://localhost:10010/user/1 If the / user / * * rule is met, the request is forwarded to the uri: http://userservice/user/1 , the results are as follows:
5) Flow chart of Gateway Routing
The whole access process is as follows:
Summary:
Gateway building steps:
-
Create a project to introduce nacos service discovery and gateway dependency
-
Configure application.yml, including basic service information, nacos address and routing
Routing configuration includes:
-
Route id: the unique identifier of the route
-
Routing destination (uri): the destination address of the route. http represents the fixed address and lb represents load balancing according to the service name
-
Route assertions: Rules for judging routes,
-
Routing filters: process requests or responses
Next, we will focus on the detailed knowledge of route assertion and route filter
3.3. Factory
The assertion rules we write in the configuration file are just strings. These strings will be read and processed by the predict factory and transformed into conditions for routing judgment
For example, Path=/user / * * matches according to the path. This rule is determined by
org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory class
There are more than a dozen assertion factories like this in the spring cloud gateway:
name | explain | Example |
---|---|---|
After | Is a request after a certain point in time | - After=2037-01-20T17:42:47.789-07:00[America/Denver] |
Before | Is a request before a certain point in time | - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] |
Between | Is a request before two points in time | - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver] |
Cookie | The request must contain some cookie s | - Cookie=chocolate, ch.p |
Header | The request must contain some header s | - Header=X-Request-Id, \d+ |
Host | The request must be to access a host (domain name) | - Host=.somehost.org,.anotherhost.org |
Method | The request mode must be the specified mode | - Method=GET,POST |
Path | The request path must conform to the specified rules | - Path=/red/{segment},/blue/** |
Query | The request parameter must contain the specified parameter | -Query=name, Jack or - Query=name |
RemoteAddr | The ip address of the requester must be in the specified range | - RemoteAddr=192.168.1.1/24 |
Weight | Weight processing |
We only need to master the routing project of Path.
3.4. Filter factory
Gateway filter is a filter provided in the gateway, which can process the requests entering the gateway and the responses returned by the microservice:
3.4.1. Types of routing filters
Spring provides 31 different routing filter factories. For example:
name | explain |
---|---|
AddRequestHeader | Add a request header to the current request |
RemoveRequestHeader | Remove a request header from the request |
AddResponseHeader | Add a response header to the response result |
RemoveResponseHeader | There is a response header removed from the response result |
RequestRateLimiter | Limit requested traffic |
3.4.2. Request header filter
Let's take AddRequestHeader as an example.
Requirement: add a request header to all requests entering userservice: truth = itcast is freeing awesome!
You only need to modify the application.yml file of the gateway service and add routing filtering:
spring: cloud: gateway: routes: - id: user-service uri: lb://userservice predicates: - Path=/user/** filters: # filter - AddRequestHeader=Truth, Itcast is freaking awesome! # Add request header
The current filter is written under the userservice route, so it is only valid for requests to access the userservice.
3.4.3. Default filter
If you want to be effective for all routes, you can write the filter factory to default. The format is as follows:
spring: cloud: gateway: routes: - id: user-service uri: lb://userservice predicates: - Path=/user/** default-filters: # Default filter - AddRequestHeader=Truth, Itcast is freaking awesome!
server: port: 9999 # Gateway port spring: application: name: gateway # Service name redis: timeout: 10000 #Connection timeout host: 123.57.155.19 #Connection address password: qyb123 #Connection password database: 0 lettuce: pool: max-active: 1024 max-wait: 10000 max-idle: 200 min-idle: 5 cloud: nacos: server-addr: localhost:8848 # nacos address gateway: routes: # Gateway routing configuration - id: user-service # Routing id, user-defined, as long as it is unique uri: lb://The target address lb of userservice # route is load balancing, followed by the service name predicates: # Routing assertion is to judge whether the request meets the conditions of routing rules - Path=/user/** # This is matched according to the path. It meets the requirements as long as it starts with / user / - id: order-service # Routing id, user-defined, as long as it is unique uri: lb://The destination address lb of the orderservice # route is load balancing, followed by the service name filters: # filter - AddRequestHeader=Word, QYB is freaking awesome! # Add request header # - PrefixPath=/order # Prefix all requests - StripPrefix=1 # Intercept for all requests # - RewritePath=/order/order/(?<segment>.*), /order/$\{segment} predicates: - Path=/order/order/** # - Query=token # default-filters: # Default filter # - AddRequestHeader=Word, WANGBADUZI is freaking awesome!
3.4.4. Summary
What is the function of the filter?
① Process the routed request or response, such as adding a request header
② The filter configured under the route is only effective for the requests of the current route
What is the role of defaultFilters?
① Filters in effect for all routes
3.5. Global filter
The gateway provides 31 kinds of filters learned in the previous section, but the function of each filter is fixed. If we want to intercept requests and do our own business logic, we can't do it.
3.5.1. Global filter function
The global filter is also used to handle all requests and microservice responses entering the gateway, just like the gateway filter. The difference is that the gateway filter is defined by configuration, and the processing logic is fixed; The logic of GlobalFilter needs to be implemented by writing code.
This is defined by implementing the GlobalFilter interface.
public interface GlobalFilter { /** * Process the current request. If necessary, pass the request to the next filter through {@ link GatewayFilterChain} * * @param exchange Request context, which can obtain request, Response and other information * @param chain Used to delegate requests to the next filter * @return {@code Mono<Void>} Return to indicate the end of the current filter business */ Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain); }
Writing custom logic in filter can realize the following functions:
- Login status judgment
- Permission verification
- Request current limit, etc
3.5.2. User defined global filter
Requirement: define a global filter, intercept the request, and judge whether the requested parameters meet the following conditions:
-
Whether there is authorization in the parameter,
-
Whether the authorization parameter value is admin
If both are met, release, otherwise intercept
realization:
Define a filter in the gateway:
package cn.itcast.gateway.filters; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.annotation.Order; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Order(-1) @Component public class AuthorizeFilter implements GlobalFilter { @Override public Mono<Void> filter(Ser verWebExchange exchange, GatewayFilterChain chain) { // 1. Get request parameters MultiValueMap<String, String> params = exchange.getRequest().getQueryParams(); // 2. Get the authorization parameter String auth = params.getFirst("authorization"); // 3. Calibration if ("admin".equals(auth)) { // Release return chain.filter(exchange); } // 4. Interception // 4.1. Prohibit access and set the status code exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN); // 4.2. End processing return exchange.getResponse().setComplete(); } }
3.5.3. Filter execution sequence
The request to enter the gateway will encounter three types of filters: the filter of the current route, DefaultFilter and GlobalFilter
After requesting a route, the current route filter, DefaultFilter and GlobalFilter will be merged into a filter chain (Collection), and each filter will be executed in turn after sorting:
What are the sorting rules?
- Each filter must specify an order value of type int. the smaller the order value, the higher the priority and the higher the execution order.
- GlobalFilter specifies the order value by implementing the Ordered interface or adding the @ order annotation, which is specified by ourselves
- The order of routing filter and defaultFilter is specified by Spring. By default, it is incremented from 1 in the order of declaration.
- When the order value of the filter is the same, it will be executed in the order of defaultfilter > routing filter > globalfilter.
For details, you can view the source code:
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters() method is to load defaultFilters first, then load filters of a route, and then merge.
The org.springframework.cloud.gateway.handler.FilteringWebHandler#handle() method will load the global filter, merge it with the previous filter, sort it according to the order, and organize the filter chain
3.6. Cross domain issues
3.6.1. What are cross domain issues
Cross domain: domain name inconsistency is cross domain, mainly including:
-
Domain names are different: www.taobao.com and www.taobao.org and www.jd.com and miaosha.jd.com
-
The domain name is the same, but the port is different: localhost:8080 and localhost8081
Cross domain problem: the browser prohibits cross domain ajax requests between the initiator of the request and the server, and the request is intercepted by the browser
Solution: CORS, which should have been studied before, will not be repeated here. If you don't know, you can check it https://www.ruanyifeng.com/blog/2016/04/cors.html
3.6.2. Simulating cross domain problems
Put it into a web server such as tomcat or nginx, start and access it.
You can see the following error in the browser console:
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-uos2otog-163592652322) (assets / image-20210714215832675. PNG)]
Accessing localhost:10010 from localhost:8090 is obviously a cross domain request with different ports.
3.6.3. Solving cross domain problems
In the application.yml file of gateway service, add the following configuration:
spring: cloud: gateway: # . . . globalcors: # Global cross domain processing add-to-simple-url-handler-mapping: true # Solve the problem that the options request is intercepted corsConfigurations: '[/**]': allowedOrigins: # Which websites are allowed to make cross domain requests - "http://localhost:8090" allowedMethods: # Allowed cross domain ajax request mode - "GET" - "POST" - "DELETE" - "PUT" - "OPTIONS" allowedHeaders: "*" # Header information allowed to be carried in the request allowCredentials: true # Allow cookie s maxAge: 360000 # Validity of this cross domain detection
3.7. Gateway current limiting
3.7.1 why limit current
For example, Web services and external APl. There are several types of services that may cause the machine to collapse
- Users grow too fast
- Because of a hot event (microblog hot search)
- Competing object crawler
- Malicious request
These situations are unpredictable. I don't know when there will be 10 times or even 20 times of traffic. If this happens, it's too late to expand the capacity.
It can be seen from the above figure that internally, the upstream A and B services directly depend on the downstream basic service C. in this scenario, both A and B services rely on the basic service C. in fact, service A and B are in A competitive relationship. If the concurrency threshold of service A is set too large, when the traffic peak comes, it may directly drag down basic service C and affect service B, that is, the avalanche effect.
3.7.2 current limiting algorithm
There are three common current limiting algorithms
- Counter algorithm
- Leaky Bucket algorithm
- Token bucket algorithm
Counter algorithm is the simplest and easiest algorithm in current limiting algorithm. For example, we stipulate that the number of visits in one minute can not exceed 100 for A connection. Then we can do this: at the beginning, we can set A counter. Whenever A request comes, the counter will increase by 1. If the counter value is greater than 100 and the interval between the request and the first request is still within 1 minute, the current limit will be triggered; If the interval between the request and the first request is greater than 1 minute, reset the counter and count again. The specific algorithm is shown as follows:
This algorithm is the simplest, but it has a fatal defect, that is, the critical problem, as shown in the figure:
We can see from the above figure that if a malicious user sends 100 requests instantaneously at 0:59 and 100 requests instantaneously at 1:00, the user actually sends 200 requests instantaneously in one second. What we have just specified is that there are up to 100 requests per minute, that is, up to 1.7 requests per second. Users can instantly exceed our rate limit by burst requests at the reset node in the time window. Users may crush our application in an instant through this loophole in the algorithm. There is also the problem of resource waste. Our expectation is that 100 requests can be evenly distributed in this minute. Assuming that we have the upper request limit within 30s, the server will be idle for the remaining half a minute
3.7.3 leaky bucket algorithm
In fact, the leaky bucket grate method is also very simple. It can be roughly regarded as the process of water injection and leakage. The water flows into the bucket at a desired rate and out at a certain rate. When the water exceeds the bucket flow, it will be discarded, because the bucket capacity is unchanged to ensure the overall rate
3.7.4 token bucket algorithm
The token bucket algorithm is an improvement of the leaky bucket algorithm. The leaky bucket algorithm can limit the chirp rate, and the token bucket algorithm can not only limit the average chirp rate, but also allow a certain degree of burst calls. In the token bucket algorithm, there is a bucket used to store the fixed number of tokens. There is a mechanism in the algorithm, which puts tokens into the bucket at a certain rate. Each request call needs to obtain the token first. Only when you get the token can you have the opportunity to continue execution. Otherwise, you can choose to wait for the available token or refuse directly. The action of putting tokens is continuous. If the number of tokens in the bucket reaches the upper limit, the tokens will be discarded.
The scenario is like this: there are always a large number of available tokens in the bucket. At this time, incoming requests can be directly executed with tokens. For example, if the QPS is set to 100/s, there will be 100 tokens in the bucket one second after the initialization of the current limiter. When the service is started and the external service is provided, the current limiter can resist 100 instantaneous requests. When there is no token in the bucket, the request will wait, and finally it is equivalent to executing at a certain rate.
- The Spring Cloud Gateway uses this algorithm internally, which is roughly described as follows:
- All requests need an available token before being processed;
- Add tokens to the bucket at a certain rate according to the current limit;
- The bucket sets the maximum token placement limit. When the bucket is full, the newly added token is discarded or rejected;
- After the request arrives, first obtain the token in the token bucket, and then carry out other business logic with the token. After processing the business logic, delete the token directly
- The token bucket has a minimum limit. When the token in the bucket reaches the minimum limit, the token will not be deleted after the request is processed, so as to ensure sufficient current limit.
The main purpose of leaky bucket algorithm is to protect it, while the main purpose of token bucket algorithm is to protect itself and hand over the request pressure to the target service. Suppose that many requests come in suddenly. As long as you get the token, these requests will be processed instantaneously to call the target service
server: port: 9999 # Gateway port spring: application: name: gateway # Service name redis: timeout: 10000 #Connection timeout host: 127.0.0.1 #Connection address password: #Connection password database: 1 lettuce: pool: max-active: 1024 max-wait: 10000 max-idle: 200 min-idle: 5 cloud: nacos: server-addr: localhost:8848 # nacos address gateway: routes: # Gateway routing configuration - id: user-service # Routing id, user-defined, as long as it is unique # uri: http://127.0.0.1: the destination address of 8081 # route http is the fixed address uri: lb://The target address lb of userservice # route is load balancing, followed by the service name predicates: # Routing assertion is to judge whether the request meets the conditions of routing rules - Path=/user/** # This is matched according to the path. It meets the requirements as long as it starts with / user / - id: order-service # Routing id, user-defined, as long as it is unique # uri: http://127.0.0.1: the destination address of 8081 # route http is the fixed address uri: lb://The destination address lb of the orderservice # route is load balancing, followed by the service name predicates: - Path=/order/** filters: # filter - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 1 #Number of token bucket fills per second redis-rate-limiter.burstCapacity: 2 #Total token bucket capacity key-resolver: "#{@ipKeyResolver}" #Use a SpEL expression to reference bean s by name
3.8 Gateway current limiting
Spring Cloud Gateway officially provides a RequestRateLimiterGatewayFilterFactory filter factory, which uses Redis and Lua scripts to implement the token bucket
3.8.1 injection dependency
<!--spring data redis reactive rely on--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency> <!--commons-pool2 Object pool dependency--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
3.8.2 configuration yml file
spring: application: name: gateway # Service name redis: timeout: 10000 #Connection timeout host: 123.57.155.19 #Connection address password: qyb123 #Connection password database: 0 lettuce: pool: max-active: 1024 max-wait: 10000 max-idle: 200 min-idle: 5 cloud: nacos: server-addr: localhost:8848 # nacos address gateway: routes: # Gateway routing configuration - id: user-service # Routing id, user-defined, as long as it is unique uri: lb://The target address lb of userservice # route is load balancing, followed by the service name predicates: # Routing assertion is to judge whether the request meets the conditions of routing rules - Path=/user/** # This is matched according to the path. It meets the requirements as long as it starts with / user / - id: order-service # Routing id, user-defined, as long as it is unique uri: lb://The destination address lb of the orderservice # route is load balancing, followed by the service name filters: # filter - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 1 #Number of token bucket fills per second redis-rate-limiter.burstCapacity: 2 #Total token bucket capacity key-resolver: "#{@pathKeyResolver}" #Use a SpEL expression to reference bean s by name
3.8.3 current limiting rules
3.8.3.1 URI current limiting
@Configuration public class KeyResolverConfiguration { /** * @title URI Current limiting * @description Verify the URI current limit of the Gateway * @author qyb * @updateTime 2021/10/30 15:51 * @throws */ @Bean public KeyResolver pathKeyResolver(){ // jdk 1.8 return exchange -> Mono.just(exchange.getRequest().getURI().getPath()); } }
3.8.3.2 parameter current limiting
/** * @title Parameter current limiting * @description Verify the current limiting parameters of the Gateway * @author qyb * @updateTime 2021/10/30 15:51 * @throws */ @Bean public KeyResolver paramemterKeyResolver(){ // jdk 1.8 return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("id")); }
3.8.3.3 IP current limiting
/** * @title IP Current limiting * @description Verify the IP current limit of the Gateway * @author qyb * @updateTime 2021/10/30 15:51 * @throws */ @Bean public KeyResolver ipKeyResolver(){ // jdk 1.8 return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName()); }
3.9 Hystrix fuse
3.9.1 maven dependency import
<!-- Hystrix rely on--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
3.9.2 setting yml file
- id: product_consumer uri: lb://service-consumer predicates: - Path=/Cus_echo/** #Intercept the path and forward it to the URI filters: - name: Hystrix args: name: fallbackcmd fallbackUri: forward:/fallback #Return path # Set the global timeout information of Hystrix in yml #fallbackcmd here is the name of the second step hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 3000
3.9.3 setting / fallback path
@RestController public class DefaultHystrixController{ @RequestMapping(value = "/fallback",method = RequestMethod.GET) public String fallback(){ System.out.println("fallback****************Gateway"); return "welcome to fallback"; } }
3.10 high availability gateway
How many nines are usually used in the industry to measure the availability of websites. For example, the availability of QQ is four nines, that is, QQ can ensure that the service is available 99.99% of the time in a year, only 0.01% of the time is unavailable, about 53 minutes at most. For most websites, two nines are basically available; Three nines are called high availability; Four 9s are highly available with automatic recovery capability
The main means to achieve high availability are redundant data backup and service failure transfer. How can these two means be done and reflected in the gateway?
There are mainly the following directions
- Cluster deployment
- load balancing
- health examination
- Node auto restart
- Fuse
- service degradation
- Interface retry
3.9.1 Nginx + gateway cluster to achieve high availability
Enter the conf directory of Nginx, open the nginx.conf file, and configure the gateway cluster:
# The gateway cluster is at the same level as the server upstream gateway { server 127.0.0.1:9999 server:127.0.0.1:9998 } server { listen 80; server_name location localhost; location / { proxy_pass http://gateway; } }
When a request comes, it first passes through the first layer load of Nginx and reaches the gateway, and then the gateway loads it to the real back end. If there is a problem with the back end, the gateway will retry the access. If it still returns failure after multiple accesses, it can return the result immediately through fuse or service degradation. Moreover, due to load balancing, the gateway does not necessarily access the wrong back end when retrying