Using Spring Cloud Gateway to design open platform

What is open platform

Enterprises need to provide their own services through the form of interface, which is called open platform. such as Alipay open platformTaobao open platform.

The call interface party is generally called ISV, independent software vendor, and the open platform party is called ISP, Internet Service Provider. In general, it is necessary to assign an appKey and appSecret to the ISV, which can be simply understood as the user name and password. With this, the open platform interface can be called normally.

In order to ensure the validity of the request parameters, the client needs to generate a signature string, and then the open platform needs to verify the signature string. ISV can generate signature strings through appKey and AppSecret, so as to ensure that the client's request is legal. The server needs to verify whether the signature strings are legal and whether the appKey is legal. Here, the open platform will provide a set of signature algorithms, common ones are: Alipay open platform signature algorithm

How to design an open platform

An important part of the open platform is authentication. The authentication function has nothing to do with the specific business. It can be done separately. If it is a single application, this part of the operation can be written in a Controller. If it is a microservice system, it is a good choice to put the authentication part in the gateway, because the gateway is a unified entrance, and the authentication should be done at the entrance, and the subsequent microservices do not need to do the authentication processing, just need to realize their own business logic.

In the Spring Cloud microservice system, there are two common roles as gateways: zuul and Spring Cloud Gateway. Zuul is a Servlet in nature, IO model is BIO, blocking, while Spring Cloud Gateway is developed based on Netty, IO model is AIO, that is, asynchronous io. Spring Cloud Gateway has obvious advantages in processing high concurrent requests. Both of them have advantages and disadvantages. Zuul has the advantages of simple architecture and convenient expansion. It is a little weak in handling high concurrent requests. Spring Cloud Gateway has the advantages of high performance and can handle high concurrent requests. The disadvantages are complex architecture. It is difficult to debug because it needs to understand the basic principles of asynchronous programming, Netty, React and other frameworks.

This article uses Spring Cloud Gateway to demonstrate how to design a simple open platform.

First, it briefly introduces the basic functions of Spring Cloud Gateway. As A gateway, the primary function is the routing function. The simple understanding is to turn A request A into A request B, similar to the reverse proxy of Nginx. Another function is request filtering. Spring Cloud Gateway allows developers to implement custom filters to process current requests.

There are two types of routing configurations for Spring Cloud Gateway: one is written in the configuration file, and the other is implemented using code (Java DSL).

  • Use profile
server:  
  port: 8090  

spring:  
  cloud:  
    gateway:  
      routes:  
        - id: host_route  
          uri: https://www.baidu.com/  
          pedicates:  
            - Path=/

For the above configuration, visit http://localhost:8090 in the browser, and Baidu homepage will appear on the page.

Another way to use Java code:

@Bean  
public RouteLocator customRouteLocator(RouteLocatorBuilder routeBuilder) {  
    return routeBuilder.routes()  
            .route("host_route", r ->  
                    r.path("/")  
                     .uri("https://www.baidu.com/")  
            )  
            .build();  
}

The effect is the same as the configuration file. If the routing configuration is fixed and unchangeable, it can be written in the configuration file. If it involves changing the routing configuration dynamically, it must be written in Java code, because the code is more flexible.

Suppose our open platform interface address is: http://open.xx.com, which provides a parameter method to represent the interface name. The interface name determines which microservice to request. For example, visit http://open.xx.com/?method=goods.get and forward it to the product microservice http://192.168.1.1:8080/getGoods, as shown in the following figure:

It can be seen that a set of routes need to be configured in the gateway:

spring:
  cloud:
    gateway:
      routes:
        - id: getGoods
          uri: http://192.168.1.1:8080
          predicates:
            - Path=/getGoods
        - id: getOrder
          uri: http://192.168.1.2:8080
          predicates:
            - Path=/getOrder

Next, you need to do several things in the gateway:

  1. Authentication. If authentication fails, an error code will be returned
  2. Pass authentication, route and forward

These things can be executed in the global filter. The filter code is as follows:

package com.example.gateway.filter;

import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

/**
 Gateway inlet filter
 * @author tanghc
 */
@Component
public class IndexFilter implements WebFilter, Ordered {

    private static Map<String, String> methodPathMap = new HashMap<>(16);
    // Store the path corresponding to the interface name
    static {
        methodPathMap.put("goods.get", "/getGoods");
        methodPathMap.put("order.get", "/getOrder");
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        // Get request parameters
        Map<String, String> query = exchange.getRequest().getQueryParams().toSingleValueMap();
        // authentication
        this.check(query);

        String method = query.get("method");
        String path = methodPathMap.get(method);
        if (path != null) {
            // Copy a new request
            ServerHttpRequest newRequest = exchange.getRequest()
                    .mutate()
                    // ==The key is to redefine the path of forwarding
                    .path(path)
                    .build();
            // Copy a new exchange, and request uses the new
            ServerWebExchange newExchange = exchange
                    .mutate()
                    .request(newRequest)
                    .build();
            // Forward new exchange back
            return chain.filter(newExchange);
        }
        return chain.filter(exchange);
    }

    /**
     * authentication
     * @param query
     */
    private void check(Map<String, String> query) {

    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

}

Here, a map is used to store the corresponding relationship between method and path, and then the forwarding function is realized by rewriting the path in request. Request http://localhost:8090/?method=order.get in the browser and forward it to http://192.168.1.1:8080/getGoods

So far, one of the basic functions of the open platform has been realized, but there are still two problems:

  1. The routing configuration is write dead and cannot be loaded dynamically
  2. The Path corresponding to the interface name is write dead and cannot be changed dynamically

For the first problem, the solution is to use java code (Java DSL) to configure the route. The specific idea is to let microservice maintain the route relationship, and then the gateway will pull the route configuration from each microservice end after startup and save it locally.

Second, you can use dynamic configuration, such as Spring Cloud Config or Apollo Configure to change dynamically.

All the functions mentioned above are SOP Has been fully implemented in.

Tags: Programming Spring Java Netty

Posted on Tue, 28 Jan 2020 20:06:10 -0800 by Bennettman