Gateway service is very important. Learn gateway

Keywords: Java gateway

The article has been included in my Github collection. Welcome Star: https://github.com/yehongzhi/learningSummary

Introduce service gateway

The best way to know something is to start with why you need it.

According to the characteristics of current mainstream microservice architecture, suppose there are three services A, B and C. if these three services need to do some request filtering and permission verification, how can they be implemented?

  • Each service is implemented by itself.
  • Write it in A public service, and then let services A, B and C introduce Maven dependencies of public services.
  • Using the service gateway, all clients request the service gateway for request filtering and permission verification, and then route and forward to services A, B and C.

The first way is obviously against the sky, which is not discussed here. The second method is a little smarter, but if the logic of public services changes, all services that rely on public services need to be repackaged and deployed to take effect.

Therefore, it is obvious that using the service gateway solves the above problems. Other services do not need to add any dependencies. They only need to configure some parameters in the gateway, and then they can be routed and forwarded to the corresponding back-end services. If request filtering and permission verification are required, they can be implemented in the gateway layer. If the logic of permission verification needs to be updated, You only need to modify the gateway layer, and other back-end services do not need to be modified.

Next, let's introduce the functions of the service gateway, mainly including:

  • Routing forwarding
  • API monitoring
  • Permission control
  • Current limiting

So the service GateWay is very important! Then let's learn from the current mainstream GateWay.

Getting started with GateWay

The first step is to create a project as a gateway. The SpringBoot version used here is 2.0.1, introducing dependencies:

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

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

We need to use the gateway to forward requests. First, we need to have a back-end service. Here I simply create a user project. Then start the user project and write an interface to obtain all user information:

Now we configure the application.yml of the gateway to implement request forwarding.

server:
  port: 9201
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: user_getList #ID of the route
          uri: http://localhost:8080/user/getList # final destination request address
          predicates: #Assert
            - Path=/user/getList #Route with path matching

That means I ask http://localhost:9201/user/getList After that, port 9201 is a gateway service. It will match the route of / user / GetList and finally forward it to the target address http://localhost:8080/user/getList .

This is even a simple use of the gateway gateway.

Keep going

In the example of getting started above, we noticed that there is a configuration of predictions, which is a bit vague. It is called assertion in Chinese translation, which is a bit similar to the Predicate function in the Stream stream of Java 8. If the assertion is true, the route is matched.

In addition, another core of gateway is Filter, which has two types: global and local. What is the process of the whole gateway? See the figure below:

As can be seen from the figure, the two cores of gateway are predicate and filter. Next, we will focus on the use of these two.

Use of Route Predicate

Spring Cloud Gateway includes many built-in route predict factories, so you can directly use various built-in predict through configuration.

After Route Predicate

Requests to match the route after the specified time.

spring:
  cloud:
    gateway:
      routes:
        - id: user_getList
          uri: http://localhost:8080/user/getList
          predicates:
            - After=2021-10-30T01:00:00+08:00[Asia/Shanghai]

Before Route Predicate

Requests before the specified time will match the route.

spring:
  cloud:
    gateway:
      routes:
        - id: user_getList
          uri: http://localhost:8080/user/getList
          predicates:
            - Before=2021-10-30T02:00:00+08:00[Asia/Shanghai]

Between Route Predicate

Requests within the specified time interval will match the route.

spring:
  cloud:
    gateway:
      routes:
        - id: user_getList
          uri: http://localhost:8080/user/getList
          predicates:
          	- Between=2021-10-30T01:00:00+08:00[Asia/Shanghai],2021-10-30T02:00:00+08:00[Asia/Shanghai]

Cookie Route Predicate

Requests with the specified Cookie match the route.

spring:
  cloud:
    gateway:
      routes:
        - id: user_getList
          uri: http://localhost:8080/user/getList
          predicates:
          	- Cookie=username,yehongzhi

Use POSTMAN to send a request with username=yehongzhi in the Cookie.

Header Route Predicate

Requests with the specified request header match the route.

spring:
  cloud:
    gateway:
      routes:
        - id: user_getList
          uri: http://localhost:8080/user/getList
          predicates:
        	- Header=X-Id, \d+

Use POSTMAN to send a request with an X-Id in the request header.

Host Route Predicate

Requests with the specified Host match the route.

spring:
  cloud:
    gateway:
      routes:
        - id: user_getList
          uri: http://localhost:8080/user/getList
          predicates:
            - Host=**.yehongzhi.com

Use POSTMAN to send a request with Host=www.yehongzhi.com in the request header.

Path Route Predicate

The request to send the specified path will match the route.

spring:
  cloud:
    gateway:
      routes:
        - id: user_getList
          uri: http://localhost:8080/user/getList
          predicates:
            - Path=/user/getList

Enter the address directly in the browser http://localhost:9201/user/getList , you can access.

Method Route Predicate

The request to send the specified method matches the route.

spring:
  cloud:
    gateway:
      routes:
        - id: user_getList
          uri: http://localhost:8080/user/getList
          predicates:
            - Method=POST

Send the request in POST mode with POSTMAN.

Query Route Predicate

Requests with specified query parameters can match the route.

spring:
  cloud:
    gateway:
      routes:
        - id: user_query_byName
          uri: http://localhost:8080/user/query/byName
          predicates:
            - Query=name

Enter in the browser http://localhost:9201/user/query/byName?name=tom Address, send request.

Weight Route Predicate

Use the weight to route the corresponding requests. The following configuration indicates that 80% of the requests will be routed to localhost:8080 and 20% will be routed to localhost:8081.

spring:
  cloud:
    gateway:
      routes:
        - id: user_1
          uri: http://localhost:8080
          predicates:
            - Weight=group1, 8
        - id: user_2
          uri: http://localhost:8081
          predicates:
            - Weight=group1, 2

RemoteAddr Route Predicate

Requests originating from the specified remote address can match the route.

spring:
  cloud:
    gateway:
      routes:
        - id: user_1
          uri: http://localhost:8080/user/getList
          predicates:
            - RemoteAddr=192.168.1.4

Use the browser to request.

Combined use

spring:
  cloud:
    gateway:
      routes:
        - id: user_1
          uri: http://localhost:8080/user/getList
          predicates:
            - RemoteAddr=192.168.1.4
            - Method=POST
            - Cookie=username,yehongzhi
            - Path=/user/getList

POST man is used to initiate the request, and POST mode is used. The uri is / user/getList with Cookie and RemoteAddr.

Customize predict

If we need to customize predict, how can we play it? In fact, it's very simple. Look at the source code and learn from it. You need to inherit the AbstractRoutePredicateFactory class.

The following is an example. If the token value is abc, the route will be matched. How to write it? See the code:

@Component
public class TokenRoutePredicateFactory extends AbstractRoutePredicateFactory<TokenRoutePredicateFactory.Config> {

    public static final String TOKEN_KEY = "tokenValue";

    public TokenRoutePredicateFactory() {
        //The Config class of the current class will use reflection to create config and assign values, which will be passed back in apply
        super(TokenRoutePredicateFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        //"tokenValue" is consistent with the receiving field of Config
        return Arrays.asList(TOKEN_KEY);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        //The config object obtained here is the following custom config object
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange exchange) {
                MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
                //Get request parameters
                String value = params.getFirst("token");
                //Compare the request parameters with the token defined in the configuration file, and return true if they are equal
                return config.getTokenValue() != null && config.getTokenValue().equals(value);
            }
        };
    }
	//Used to receive the values defined in the configuration file
    public static class Config {

        private String tokenValue;

        public String getTokenValue() {
            return tokenValue;
        }

        public void setTokenValue(String tokenValue) {
            this.tokenValue = tokenValue;
        }
    }
}

One thing to note here is that the class name must end with RoutePredicateFactory, and the previous one is used as the configuration name. For example, the configuration name of TokenRoutePredicateFactory is Token, which is an agreed configuration.

Then add the configuration to the configuration file:

spring:
  cloud:
    gateway:
      routes:
        - id: user_1
          uri: http://localhost:8080/user/getList
          predicates:
            - Token=abc ##Assertion using TokenRoutePredicateFactory

Then use POSTMAN to send the request with the token parameter, and the parameter value is abc.

If the value of token is incorrect, 404 will be reported.

Integrated registry

Why integrate the registry? Because there is usually more than one machine behind each service, and the service name is generally used for configuration instead of configuring the IP address of the service, and load balancing calls should be realized.

Here I use Nacos as the registry.

Introduce Maven dependency:

<dependency><!-- SpringCloud nacos Dependency of service discovery -->
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>0.2.2.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

The startup class is annotated to open the registry.

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

Add configuration to application.yml:

spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        service: ${spring.application.name}
    gateway:
      routes:
        - id: consumer
          uri: lb://consumer # uses the lb protocol. consumer is the service name. IP address configuration is no longer used
          order: 1
          predicates:
            - Path=/consumer/** #Request path matching / consumer / * *
server:
  port: 9201

Create a consumer, register with nacos, and provide an interface:

@RestController
public class ConsumerController {

    @Value("${server.port}")
    private String port;
    
    @RequestMapping("consumer/getDetail/{id}")
    public String getDetail(@PathVariable("id") String id) {
        return "Port number:" + port + ",obtain ID by:" + id + "Product details";
    }
}

Start the consumer and gateway projects, and then open the nacos console to see the two services.

Continuous request address http://localhost:9201/consumer/getDetail/1 , you can see that load balancing is implemented and the service is invoked.

Some people may think that each service must be equipped with a route, which is very troublesome. There is a very simple configuration to solve this problem:

spring:
    gateway:
      discovery:
        locator:
          enabled: true

Then start the service and try again. The service name needs to be added to the requested address. There is still no problem!

Write at the end

This article mainly introduces the routing and forwarding function of GateWay, and integrates the registry. Permission control can be realized by filter. Because the length is a little long, the filter will be put in the next article. Thank you for reading.

If you think it's useful, just praise it. Your praise is the biggest driving force for my creation~

I am a programmer trying to make everyone remember. See you next time!!!

Limited ability. If there are any mistakes or improper places, please criticize and correct them and learn and communicate together!

Posted by rosy on Tue, 02 Nov 2021 17:48:14 -0700