This is the 29th day of my participation in the August Wenwen Challenge.More challenges in August

🌈 Column Introduction

Thank you for reading, I hope to help you, if there is a flaw in the blog, please leave a message in the comment area or add my private chat in the home page, thank you for every little partner generous advice. I am XiaoLin, a boy who can both write bugs and sing rap. This column mainly introduces the most mainstream micro services solution, SpringCloudAlibaba, which will be introduced in groups. Column address: SpringCloudAlibaba

  • 6️ Sentinel (suggested collection)
  • 5️ retail Feign
  • 4️ Ribbon (suggested collection)
  • 3️ Nacos (suggested collection)
  • 2️ retail (suggested collection)
  • 1️ retail (suggested collection)

Ix. Service Gateway: Gateway

9.1 Introduction to gateway

Everyone knows that in microservices architecture, a system is broken up into many microservices. So how do you call so many microservices as a client? If there is no gateway, we can only record the address of each microservice on the client side and call it separately.

There are many problems with such an architecture:

  1. The client requests different microservices multiple times, adding complexity to client code or configuration authoring.
  2. Authentication is complex and each service requires independent authentication.
  3. Cross-domain requests exist and are complicated to process in certain scenarios.

Gateways are designed to solve these problems. The so-called API gateway is the unified entrance of the system, which encapsulates the internal structure of the application program and provides unified services for the client. Some common logic unrelated to the business function can be realized here, such as authentication, authentication, monitoring, routing and forwarding, etc.

9.2 Common gateways

9.2.1, Ngnix + lua

Nginx reverse proxy and load balancing can realize load balancing and high availability of API server.

Lua is a scripting language for writing simple logic, and Nginx supports Lua scripting

9.2.2, Kong

Based on Nginx+Lua development, high performance, stable, there are multiple available plug-ins (current limiting, authentication, etc.) can be used out of the box.

His faults:

  1. Only Http is supported.
  2. Secondary development, free expansion is difficult.
  3. Provide management API, lack of easy to use control, configuration.

9.2.3, Zuul

Netflix open source gateway, rich functions, the use of JAVA development, easy secondary development.

His faults:

  1. Lack of control, cannot dynamically configure.
  2. There are many dependent components.
  3. Processing Http requests relies on the Web container, which is not as powerful as Nginx.

9.2.4、Spring Cloud Gateway

Spring company developed the Gateway service to replace Zuul, but SpringCloud Alibaba technology stack does not provide its own Gateway, so we can use SpringCloud Gateway as the Gateway

9.3 Introduction to Gateway

Spring Cloud Gateway is a Gateway developed by Spring company based on Spring 5.0, Spring Boot 2.0 and Project Reactor technology. It aims to provide a simple and effective unified API route management method for microservice architecture. It aims to replace Netflix Zuul, which not only provides a unified routing mode, but also provides basic gateway functions based on Filter chain, such as security, monitoring and traffic limiting.

Its main functions are:

  1. The forwarding redirection is performed.
  2. At the beginning, all classes need to do initialization.
  3. Perform network isolation.

9.4. Quick Start

Requirements: Access the API gateway through a browser and forward the request to the commodity microservice through the gateway.

9.4.1 basic Edition

Create an API-Gateway module and import the following dependencies.


      
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <parent>
    <artifactId>Shop-parent</artifactId>
    <groupId>cn.linstudy</groupId>
    <version>1.0.0</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <artifactId>api-gateway</artifactId>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>
    <! - gateway gateway - >
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
  </dependencies>

</project>
Copy the code

Writing configuration files

server:
  port: 9000 Specify the port for the gateway service
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes: Routing specifies which microservice to refer to when a request meets any criteria.
        - id: product_route The current route must be unique
          uri: http://localhost:8081 Request the address to forward to
          order: 1 # Priority of the route. The smaller the number, the higher the priority
          predicates: # assertion (which is the condition to be satisfied by routing and forwarding)
            - Path=/product-serv/** Route forwarding is performed only when the requested Path satisfies the rule specified by Path
          filters: The request can be modified as it passes through the filter
            - StripPrefix=1 Delete layer 1 path before forwarding
Copy the code

test

9.4.2. Updated version

We found a big problem with the upgrade, that is, the address of the forwarding path is written in the configuration file, we need to get the address in the registry.

Add nacOS dependencies


      
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <parent>
    <artifactId>Shop-parent</artifactId>
    <groupId>cn.linstudy</groupId>
    <version>1.0.0</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <artifactId>api-gateway</artifactId>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>
    <! - gateway gateway - >
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <! -- NacOS client -->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
    </dependency>
  </dependencies>

</project>
Copy the code

Add annotations to the main class

@SpringBootApplication
@EnableDiscoveryClient
public class GateWayServerApp {
  public static void main(String[] args) { SpringApplication.run(GateWayServerApp.class,args); }}Copy the code

Modifying a Configuration File

server:
  port: 9000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0. 01.: 8848
    gateway:
      discovery:
        locator:
          enabled: true Enable the Gateway to discover microservices in NACOS
      routes:
        - id: product_route # The name of the route
          uri: lb://product-service # LB refers to getting microservices by name from NACOS and following a load balancing policy
          predicates:
            - Path=/product-serv/** # 1 forward only if this rule is met
          filters:
            - StripPrefix=1 Remove the first layer
Copy the code

We can also customize multiple routing rules.

spring:
  application:
    gateway:
      routes:
        - id: product_route
          uri: lb://product-service 
          predicates:
            - Path=/product-serv/**
          filters:
            - StripPrefix=1
        - id: order_route
          uri: lb://order-service 
          predicates:
            - Path=/order-serv/**
          filters:
            - StripPrefix=1
Copy the code

9.4.3 abbreviated version

Our configuration file does not need to be as complex as 1 to implement functions, there is a shortened version.

server:
  port: 9000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true Enable the Gateway to discover microservices in NACOS
Copy the code

We found, as long as the gateway address/microservice name/interface format to access, we can get a successful response.

9.5 Gateway core architecture

9.5.1 Basic Concepts

A Route is one of the most basic components of a Gateway. It represents a specific routing information carrier. It mainly defines the following information:

  1. Id: indicates the Route identifier, which is different from other routes.
  2. Uri: The destination URI to which the route points, that is, the microservice to which the client request is ultimately forwarded.
  3. Order: Used to sort multiple routes. The smaller the value is, the higher the order is and the higher the matching priority is.
  4. Predicate: Predicate is used to determine conditions. A route is executed only when both assertions return true.
  5. Filter: Modifies the request and response information.
  6. Predicate: Predicate, used to determine conditions. A route is executed only when both assertions return true.

9.5.2 Implementation Principle

  1. The request is received by the user, the request handler is handed to the processor mapper, and the chain of execution is returned.
  2. The request handler invokes the Web handler and processes our path 1 in the Web handler. Hypothesis 1 our path is: http://localhost:9000/product-serv/get? If the id is 1, search for the service information locally based on the configured routing rule. The host IP address of product-service is 192.168.10.130.
  3. Select a node based on the load balancing policy of 1Ribbon, and then join the node. Replace the path with 192.168.10.130:8081. If filter is configured, the node will also use filter.
  4. By default Gateway removes the first layer for you if you do not have a custom route. Gateway port from this one/Let’s go to number two/Let’s do the first level.

9.6. Filters

The Gateway’s filter is used to manipulate requests and responses as they pass through.

The lifecycle of the Gateway’s filter:

  1. PRE: This filter is invoked before the request is routed. We can use this filter to authenticate, select requested microservices in the cluster, log debugging information, and so on.
  2. POST: This filter is executed after routing to the microservice. Such filters can be used to add standard HTTP headers to responses, collect statistics and metrics, send responses from microservices to clients, and so on.

The Filter of Gateway can be divided into two types: GatewayFilter and GlobalFilter:

  1. GatewayFilter: Applies to a single route or a group of routes.
  2. GlobalFilter: Applies to all routes.

9.6.1 Local filters

A local filter is a filter for a single route. There are built-in filters and custom filters.

9.6.1.1 Built-in filter

There are many different types of Gateway routing filters built into the SpringCloud Gateway.

9.6.1.1.1 Local filter content
Filter plant role parameter
AddRequestHeader Add the Header for the original request Header name and value
AddRequestParameter Add request parameters to the original request Parameter name and value
AddResponseHeader Add the Header for the original response Header name and value
DedupeResponseHeader Removes duplicate values from the response header The name of the Header to be deleted and the deduplication policy
Hystrix Introduce Hystrix circuit breaker protection for routes The name of the HystrixCommand
FallbackHeaders Add specific exception information to the request header of the fallbackUri The name of the Header
PrefixPath Prefixes the original request path The prefix path
PreserveHostHeader Add a preserveHostHeader=true attribute to the request, which the routing filter checks to determine whether to send the original Host There is no
RequestRateLimiter It is used to limit the traffic of requests. The traffic limiting algorithm is token bucket KeyResolver,

RateLimiter,

StatusCode,

DenyEmptyKey,

emptyKeyStatus
RedirectTo Redirects the original request to the specified URL HTTP status code and redirected URL
RemoveHopByHopHeadersFilter Removes a set of headers specified by the IETF organization for the original request This is enabled by default, and you can specify which headers to delete only
RemoveRequestHeader Delete a Header for the original request The Header name
RemoveResponseHeader Remove a Header for the original response The Header name
RewritePath Override the original request path The original path regular expression and the rewritten path regular expression
RewriteResponseHeader Overrides a Header in the original response The Header name, the regular expression for the value, and the overridden value
SaveSession Enforce the request before forwarding itWebSession::saveoperation There is no
secureHeaders Add a series of response headers for security purposes to the original response None. You can modify the values of the security response headers
SetPath Modifies the original request path Modified path
SetResponseHeader Modify the value of a Header in the original response Header name, modified value
SetStatus Modifies the status code of the original response The HTTP status code can be a number or a string
StripPrefix The path used to truncate the original request Use numbers to indicate the number of paths to truncate
Retry Retry for different responses Retries, Statuses, Methods, series
RequestSize Sets the maximum size of a request packet that can be received. If the size of the request packet exceeds the specified value, 413 Payload Too Large is returned Request packet size, in bytes. The default value is 5M
ModifyRequestBody Modify the original request body content before forwarding the request Modified request body content
ModifyResponseBody Modify the contents of the original response body Modified response body content
9.6.1.1.2 Use of local filters
server:
  port: 9000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0. 01.: 8848
    gateway:
      discovery:
        locator:
          enabled: true Enable the Gateway to discover microservices in NACOS
      routes:
        - id: product_route # The name of the route
          uri: lb://product-service # LB refers to getting microservices by name from NACOS and following a load balancing policy
          predicates:
            - Path=/product-serv/** # 1 forward only if this rule is met
          filters:
            - StripPrefix=1 Remove the first layer
            - SetStatus=2000 # Use the built-in filter here to modify the return state
Copy the code

9.6.1.2. Customize local filters

Most of the time, the built-in filter does not meet our needs, this time must customize the local filter. Let’s assume that one requirement is to count the order service invocation time.

Write a class that implements the logic

The name is in a fixed format xxxGatewayFilterFactory

@Component
public class TimeGatewayFilterFactory extends AbstractGatewayFilterFactory<TimeGatewayFilterFactory.Config> {

  private static final String BEGIN_TIME = "beginTime";

  // constructor
  public TimeGatewayFilterFactory(a) {
    super(TimeGatewayFilterFactory.Config.class);
  }

  // Assign parameters from the configuration file to the configuration class
  @Override
  public List<String> shortcutFieldOrder(a) {
    return Arrays.asList("show");
  }

  @Override
  public GatewayFilter apply(Config config) {
    return new GatewayFilter() {
      @Override
      public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if(! config.show){// If the value of show in the configuration class is false, the system permits the configuration
          return chain.filter(exchange);
        }
        exchange.getAttributes().put(BEGIN_TIME, System.currentTimeMillis());
        / * * *, the logic of the pre * chain in the filter (). Then (Mono) fromRunable (() - > {* post logic *})) * /
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
          Long startTime = exchange.getAttribute(BEGIN_TIME);
          if(startTime ! =null) {
            System.out.println(exchange.getRequest().getURI() + Request Time: + (System.currentTimeMillis() - startTime) + "ms"); }})); }}; }@Setter
  @Getter
  static class Config{
    private booleanshow; }}Copy the code

Write application. XML

server:
  port: 9000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0. 01.: 8848
    gateway:
      discovery:
        locator:
          enabled: true Enable the Gateway to discover microservices in NACOS
      routes:
        - id: product_route # The name of the route
          uri: lb://product-service # LB refers to getting microservices by name from NACOS and following a load balancing policy
          predicates:
            - Path=/product-serv/** # 1 forward only if this rule is met
          filters:
            - StripPrefix=1 Remove the first layer
        - id: order_route
          uri: lb://order-service
          predicates:
            - Path=/order-serv/**
          filters:
            - StripPrefix=1
            - Time=true
Copy the code

Access path: http://localhost:9000/order-serv/getById? o=1&pid=1

9.6.2 global Filters

The global filter applies to all routes and does not need to be configured. Global filters can be used to verify permissions and security. The SpringCloud Gateway also handles the entire route forwarding internally through a set of built-in global filters.

Authentication logic under development:

  • When the client first requests the service, the server authenticates the user’s information (login).
  • If the authentication succeeds, the user information is encrypted to form a token and returned to the client as a login certificate.
  • After each request, the client carries the authenticated token.
  • The server decrypts the token to check whether it is valid.

Let’s simulate a requirement: to implement the unified authentication function, we need to determine whether the request contains token in the gateway and, if not, do not forward the route. If yes, the normal logic will be executed.

Writing global filters

@Component
public class AuthGlobalFilter implements GlobalFilter {

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    String token = exchange.getRequest().getQueryParams().getFirst("token");
    if (StringUtils.isBlank(token)) {
      System.out.println("Authentication failed");
      exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
      return exchange.getResponse().setComplete();
    }
    returnchain.filter(exchange); }}Copy the code

9.6.3. Gateway Traffic limiting

The gateway is the common entrance of all requests, so traffic limiting can be carried out at the gateway, and there are many ways to limit traffic. In this case, the Sentinel component learned in the previous section is used to achieve traffic limiting of the gateway. Sentinel supports traffic limiting for mainstream gateways such as SpringCloud Gateway and Zuul.

Starting from version 1.6.0, Sentinel provides an adaptation module of the SpringCloud Gateway, which can provide flow limiting of two resource dimensions:

  • Route dimension: the route entry configured in the Spring configuration file with the corresponding routeId resource name
  • Custom API dimensions: Users can customize some API groups using the API provided by Sentinel

9.6.3.1 Gateway integration Sentinel

Add the dependent

<dependency>
	<groupId>com.alibaba.csp</groupId>
	<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
Copy the code

Write configuration classes for traffic limiting

The essence of configuration classes is to substitute code for NACOS graphical interface limiting.

@Configuration
public class GatewayConfiguration {
    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ObjectProvider
       
        > viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer)
        {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }
    // Configure the exception handler for limiting traffic
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler(a) {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }
    // Initialize a stream limiting filter
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter(a) {
        return new SentinelGatewayFilter();
    }
    // Add traffic limiting for commodity microservices
     @PostConstruct
    private void initGatewayRules(a) {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("product_route")
                .setCount(3) / / three times
                .setIntervalSec(1) // 1 second, which means 1 per second will limit the current if it exceeds 3 times); GatewayRuleManager.loadRules(rules); }}Copy the code

Modify the default return format of stream limiting

If we do not want to return the default error when limiting the flow, we need to customize the error and specify a custom return format. We just need to add a configuration section to the class.

@PostConstruct
public void initBlockHandlers(a) {
	BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
	public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
		Map map = new HashMap<>();
		map.put("code".0);
		map.put("message"."The interface is restricted.");
			returnServerResponse.status(HttpStatus.OK). contentType(MediaType.APPLICATION_JSON). body(BodyInserters.fromValue(map)); }}; GatewayCallbackManager.setBlockHandler(blockRequestHandler); }Copy the code

test

9.6.3.2. Customize API grouping

We can see that the above definition, limiting the flow of the entire service, is not fine-grained. Custom API grouping is a more fine-grained definition of traffic limiting rules that can implement fine-grained traffic limiting for a method.

Add ApiController in the shop-order-server project

@RestController
@RequestMapping("/api")
public class ApiController {
    @RequestMapping("/hello")
    public String api1(a){
        return "api"; }}Copy the code

Add the configuration in GatewayConfiguration

@PostConstruct
private void initCustomizedApis(a) {
	Set<ApiDefinition> definitions = new HashSet<>();
	ApiDefinition api1 = new ApiDefinition("order_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/order-serv/api/**").                 setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
	definitions.add(api1);
	GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
@PostConstruct
private void initGatewayRules(a) {
	Set<GatewayFlowRule> rules = new HashSet<>();
	rules.add(new GatewayFlowRule("product_route")
                .setCount(3)
                .setIntervalSec(1)); rules.add(new GatewayFlowRule("order_api").
                setCount(1).
                setIntervalSec(1));
    GatewayRuleManager.loadRules(rules);
}
Copy the code

test

Current limiting, direct access to the http://localhost:8082/api/hello would not have happened to http://localhost:9000/order-serv/api/hello will appear the current limit.