Spring Cloud-Gateway Spring-Cloud-Gateway

brief introduction

Spring Cloud Gateway is a new project of Spring Cloud, which is a gateway based on technologies such as Spring 5.0, Spring Boot 2.0 and Project Reactor. It is designed to provide a simple and effective unified API routing management method for the micro service architecture.

As a gateway in the Spring Cloud ecosystem, Spring Cloud Gateway aims to replace Netflix Zuul, which not only provides a unified routing method, but also provides basic gateway functions such as security, monitoring/metrics, and flow restriction based on the Filter chain.

concept

  • Route: Route is the basic element of a gateway and consists of ID, target URI, assertion, filter.When a request arrives at a gateway, a route match is made by the Gateway Handler Mapping through an assertion, and a route is matched when the assertion is true.
  • Predicate: Predicate is a function provided in Java 8.Allows developers to match requests from HTTP, such as request headers or request parameters.Simply put, it is the matching criteria.
  • Filter: Filter is a filter in Gateway that allows for some business processing before and after a request is sent.

Working principle

When a client request reaches Spring Cloud Gateway, Gateway Handler Mapping intercepts it and determines which route the request matches based on predicates.If the match is successful, the request is sent to the Gateway web handler.The Gateway web handler processes the request through a series of pre-type filters and then executes the proxy request.After execution, a series of "post" type filters are passed back to the client.

Quick Start

Create a new subproject named spring-cloud-gateway

Introducing dependencies

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

application.properties Configuration Routing

spring.application.name=spring-cloud-gateway
server.port=8074

############ Define a router(Notice is the form of an array) ############
# Route ID, keep unique
spring.cloud.gateway.routes[0].id=my-gateway
# Target Service Address
spring.cloud.gateway.routes[0].uri=http://httpbin.org
# Routing Conditions
spring.cloud.gateway.routes[0].predicates[0]=Path=/get

The above configuration means that a routing rule with id of my-gateway is configured and automatically forwarded to when the access address is /get. http://httpbin.org/get

Routing can also be configured as code

/**
 * Configuring routes as code
 * @param builder
 * @return
 */
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
	return builder.routes()
			.route(
					p -> p.path("/get").uri("http://httpbin.org")
					)
			.build();
}

Application.propertise Configuration Routing and Code Configuration Routing Choose one of them, and personally recommend the application.propertise form of configuration.

Start the service, access http://localhost:8074/get

The following should be output:

{
  "args": {}, 
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 
    "Accept-Encoding": "gzip, deflate, br", 
    "Accept-Language": "zh-CN,zh;q=0.9", 
    "Cache-Control": "max-age=0", 
    "Cookie": "Webstorm-2f8f75da=e0c5ee46-9276-490c-b32b-d5dc1483ca18; acw_tc=2760828015735472938194099e940a3c3ebc07316bcb1096abc6fefde61bf8; BD_UPN=12314353; H_PS_645EC=8749qnWwXCzugp%2FwPJDVeB7bqBisqx6VKFthj5OZOsWBAz1JPX2YkatsizA; BD_HOME=0", 
    "Forwarded": "proto=http;host=\"localhost:8074\";for=\"0:0:0:0:0:0:0:1:51881\"", 
    "Host": "httpbin.org", 
    "Sec-Fetch-Mode": "navigate", 
    "Sec-Fetch-Site": "none", 
    "Sec-Fetch-User": "?1", 
    "Upgrade-Insecure-Requests": "1", 
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36", 
    "X-Forwarded-Host": "localhost:8074"
  }, 
  "origin": "0:0:0:0:0:0:0:1, 183.14.135.71, ::1", 
  "url": "https://localhost:8074/get"
}

Integrate Eureka

Add dependencies for Eureka Client

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

Configuring Eureka-based routing

spring.application.name=spring-cloud-gateway
server.port=8074

########### Configure Registry ###########
# Get a list of registered instances
eureka.client.fetch-registry=true
# Register with the registration center of Eureka
eureka.client.register-with-eureka=true
# Configure Registry Address
eureka.client.service-url.defaultZone=http://localhost:8070/eureka

############ Defines a base Eureka Of router(Notice is the form of an array) ############
# Route ID, keep unique
spring.cloud.gateway.routes[0].id=my-gateway
# Target Service Address
spring.cloud.gateway.routes[0].uri=lb://spring-cloud-provider
# Routing Conditions
spring.cloud.gateway.routes[0].predicates[0]=Path=/user-service/**

uri begins with lb:// (lb stands for getting services from the registry), followed by the name of the service you need to forward to, which must correspond to Eureka, otherwise the service will not be found.

The interfaces provided by the spring-cloud-provider service are as follows:

@RestController
@RequestMapping("/user-service")
public class UserController {

	@Value("${spring.application.name}")
	private String applicationName;
	
	@Value("${server.port}")
	private String post;
	
	@GetMapping("/users/{name}")
	public String users(@PathVariable("name") String name) {
		return String.format("hello %s,from server %s,post: %s", name, applicationName, post);
	}
}

Start spring-cloud-eureka-server (registry), spring-cloud-provider, and spring-cloud-gateway

Access http://localhost:8074/user-service/users/zhangsan The output is as follows:

Configure default routes

Spring Cloud Gateway provides the same forwarding capabilities as Zuul for all services

The configuration is as follows:

spring.application.name=spring-cloud-gateway
server.port=8074

########### Configure Registry ###########
# Get a list of registered instances
eureka.client.fetch-registry=true
# Register with the registration center of Eureka
eureka.client.register-with-eureka=true
# Configure Registry Address
eureka.client.service-url.defaultZone=http://localhost:8070/eureka

# Configure default routes
spring.cloud.gateway.discovery.locator.enabled=true

After opening, we need to access the service by address in the following format:

http://Gateway Address/Service Name (Uppercase)/**

For example: http://localhost:8074/SPRING-CLOUD-PROVIDER/user-service/users/zhangsan

The result is as follows:

Service names can also be configured in lowercase format by adding only one configuration:

# Configuration service name lowercase
spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true

Routing Assertion Factory

There are many routing assertion factories that are commonly used, as shown in the figure:

1. Path Routing Assertion Factory

Path Routing Assertion Factory receives a parameter that determines whether the URI s accessed match based on Path's defined rules

Fixed Path

# spring.cloud.gateway.routes[0].predicates[0]=Path=/users/zhangsan

Path with prefix

# spring.cloud.gateway.routes[0].predicates[0]=Path=/users/{segment}

Path using wildcards

# spring.cloud.gateway.routes[0].predicates[0]=Path=/users/**

2. Query Routing Assertion Factory

Query Routing Assertion Factory receives two parameters, a required parameter and an optional regular expression

# spring.cloud.gateway.routes[0].predicates[0]=Query=foo, ba.

If the request contains foo query parameters, the route will match.bar and baz will also match, because the second argument is a regular expression (note that there is a.)

Test link:

http://localhost:8074/users/zhangsan?foo=ba

http://localhost:8074/users/zhangsan?foo=bar

http://localhost:8074/users/zhangsan?foo=baz

3. Method Routing Assertion Factory

The Method Routing Assertion Factory receives a parameter, the HTTP method to match.

# spring.cloud.gateway.routes[0].predicates[0]=Method=GET

4. Header Routing Assertion Factory

The Header Routing Assertion Factory receives two parameters, the request header name and the regular expression.

# spring.cloud.gateway.routes[0].predicates[0]=Header=X-Request-Id, \d+

If the request has a request header name x-request-id with a value that matches the \d+ regular expression (with a value of one or more numbers), the route matches.

Specifically, you can see the official documents, which are clearly written.

https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.0.0.RELEASE/single/spring-cloud-gateway.html

Custom Routing Assertion Factory

Custom routing assertion factories need to inherit the AbstractRoutePredicateFactory class to override the logic of the apply method.

In the apply method, you can get the ServerHttpRequest object through exchange.getRequest(), so you can get the parameters of the request, the request method, the request header, and so on.

The parameters of the apply method are custom configuration classes, i.e. static internal classes Config, which can be directly obtained from the apply method by configuring the parameters when they are used.

Let's write a Query Routing Assertion Factory by ourselves, named MyQueryRoutePredicateFactory (the name needs to end with RoutePredicateFactory)

The code is as follows:

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

	public MyQueryRoutePredicateFactory() {
		super(Config.class);
	}
	
	/**
	 * Returns tips on the number of args and the order in which shortcuts are analyzed.
	 * <p>This method must be overridden or Config cannot set parameters.
	 */
	@Override
	public List<String> shortcutFieldOrder() {
		return Arrays.asList("param", "regexp");
	}



	/**
	 * Self-Implementing Query Routing Assertion Factory
	 * <p>Write logic in this method
	 */
	@Override
	public Predicate<ServerWebExchange> apply(Config config) {
		return exchange -> {
			System.out.println(config.toString());
			if (config.getRegexp() == null || "".equals(config.getRegexp())) {
				return exchange.getRequest().getQueryParams().containsKey(config.getParam());
			}
			List<String> values = exchange.getRequest().getQueryParams().get(config.getParam());
			if (values == null) {
				return false;
			}
			for (String value : values) {
				if (value != null && value.matches(config.getRegexp())) {
					return true;
				}
			}
			return false;
		};
	}

	/**
	 * Config Static internal classes are used to hold configuration information
	 * @author miansen.wang
	 * @date 2019-11-14
	 */
	public static class Config {

		// Request parameter name
		private String param;

		// Regular Request Parameter Value
		private String regexp;

		public String getParam() {
			return param;
		}

		public void setParam(String param) {
			this.param = param;
		}

		public String getRegexp() {
			return regexp;
		}

		public void setRegexp(String regexp) {
			this.regexp = regexp;
		}

		@Override
		public String toString() {
			return "Config {param=" + param + ", regexp=" + regexp + "}";
		}
		
	}
}

Use in configuration file

spring.cloud.gateway.routes[0].predicates[0]=MyQuery=foo, ba.

Restart service, breakpoint at apply method, access http://localhost:8074/user-service/users/zhangsan?foo=bar Enter the apply method to indicate that our custom routing assertion factory is working.

Filter Factory

Spring Cloud Gateway is divided into GatewayFilter and GlobalFilter according to their scope of action, with the following differences:

  • GatewayFilter: needs to be configured with spring.cloud.routes.filters for specific routes, only for current routes
  • GlobalFilter: A global filter that does not need to be configured in a configuration file to work on all routes

Many GatewayFilter s and GlobalFilter s are officially available and can be viewed here: https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.0.0.RELEASE/single/spring-cloud-gateway.html

GatewayFilter

Take a look at the official GatewayFilter

Let's write an example that requires adding a request header before the request arrives at the service and a response header after the service responds.

As you can see from the diagram, the GatewayFilter that adds the request header is the AddRequestHeader GatewayFilterFactory (convention written as AddRequestHeader)

GatewayFilter with response header added is AddResponseHeaderGatewayFilterFactory (convention written as AddResponseHeader)

So the profile can be configured like this

spring.application.name=spring-cloud-gateway
server.port=8074

########### Configuring routes as configurations ###########
# Custom route ID, keep unique
spring.cloud.gateway.routes[0].id=my-gateway
# Target Service Address
spring.cloud.gateway.routes[0].uri=http://httpbin.org
# Routing Conditions
spring.cloud.gateway.routes[0].predicates[0]=Path=/get
# GatewayFilter (add request header)
spring.cloud.gateway.routes[0].filters[0]=AddRequestHeader=X-Request-Foo, Bar
# GatewayFilter (add response header)
spring.cloud.gateway.routes[0].filters[1]=AddResponseHeader=X-Response-Foo, Bar

You can also configure it through code

/**
 * Configuring routes as code
 * @param builder
 * @return
 */
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
	return builder.routes()
			.route(
					p -> p.path("/get")
					     .filters(f -> f.addRequestHeader("X-Request-Foo", "Bar").addResponseHeader("X-Response-Foo", "Bar"))
					     .uri("http://httpbin.org")
					)
			.build();
}

Start the service, access http://localhost:8074/get

The following response information was obtained:

{
  "args": {}, 
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 
    "Accept-Encoding": "gzip, deflate, br", 
    "Accept-Language": "zh-CN,zh;q=0.9", 
    "Cache-Control": "max-age=0", 
    "Cookie": "Webstorm-2f8f75da=e0c5ee46-9276-490c-b32b-d5dc1483ca18; acw_tc=2760828015735472938194099e940a3c3ebc07316bcb1096abc6fefde61bf8; BD_UPN=12314353; BD_HOME=0", 
    "Forwarded": "proto=http;host=\"localhost:8074\";for=\"0:0:0:0:0:0:0:1:55395\"", 
    "Host": "httpbin.org", 
    "Sec-Fetch-Mode": "navigate", 
    "Sec-Fetch-Site": "none", 
    "Sec-Fetch-User": "?1", 
    "Upgrade-Insecure-Requests": "1", 
    "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36", 
    "X-Forwarded-Host": "localhost:8074", 
    "X-Request-Foo": "Bar"
  }, 
  "origin": "0:0:0:0:0:0:0:1, 183.14.135.71, ::1", 
  "url": "https://localhost:8074/get"
}

Notice that a request header has been added:'X-Request-Foo':'Bar'

Response header added

This example shows that when Gateway Handler Mapping determines which route the request matches, it sends the request to the Gateway web handler for GatewayFilter interception.GatewayFilter is divided into request filter, which can filter request information, and response filter, which can filter response information.

Customize GatewayFilter

A custom Spring Cloud Gateway filter factory needs to inherit the AbstractGateway FilterFactory class, overriding the logic of the apply method.Naming needs to end with GatewayFilterFactory.

Let's implement an AddRequestHeaderGatewayFilterFactory by ourselves, named MyAddRequestHeaderGatewayFilterFactory.MyAddRequestHeader is the name of this filter factory when used.

The code is as follows:

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

	// You must show the constructor that calls the parent class, specify configClass, or report a type conversion exception
	public MyAddRequestHeaderGatewayFilterFactory() {
		super(Config.class);
	}

	// Specify the number and order of args, otherwise Config cannot be initialized
	@Override
	public List<String> shortcutFieldOrder() {
		return Arrays.asList("headerName", "headerValue");
	}

	// Write logic here
	@Override
	public GatewayFilter apply(Config config) {
		return (exchange, chain) -> {
			ServerHttpRequest request = exchange.getRequest().mutate()
					.header(config.getHeaderName(), config.getHeaderValue()).build();
			return chain.filter(exchange.mutate().request(request).build());
		};
	}

	// Configuration Class
	public static class Config {

		// Name of Request Header
		private String headerName;

		// Value of Request Header
		private String headerValue;

		public String getHeaderName() {
			return headerName;
		}

		public void setHeaderName(String headerName) {
			this.headerName = headerName;
		}

		public String getHeaderValue() {
			return headerValue;
		}

		public void setHeaderValue(String headerValue) {
			this.headerValue = headerValue;
		}
	}
}

Profile:

spring.cloud.gateway.routes[0].filters[0]=MyAddRequestHeader=X-Request-Foo, Bar

If you don't want to use the filter factory as a configuration file, you can implement the GatewayFilter interface directly in the myRoutes method and write your filtering logic.

The code is as follows:

@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
	return builder.routes().route(p -> p.path("/get").filters(f -> f.filter((exchange, chain) -> {
		ServerHttpRequest request = exchange.getRequest().mutate().header("X-Request-Foo", "Bar").build();
		return chain.filter(exchange.mutate().request(request).build());
	}).addResponseHeader("X-Response-Foo", "Bar")).uri("http://httpbin.org")).build();
}

This chain of code calls is complex, but the logic can be understood by simply tapping it yourself.

Start the service, access http://localhost:8074/get If you see a request header named X-Request-Foo added with a value of Bar, our custom filter factory is in effect.

GlobalFilter

Customize a GlobalFilter to limit IP addresses.

The code is as follows:

@Component
public class IPCheckFilter implements GlobalFilter, Ordered {

	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		HttpHeaders headers = exchange.getRequest().getHeaders();
		InetSocketAddress host = headers.getHost();
		String hostName = host.getHostName();
		// The IP address here is dead and needs to be configured in practice
		if ("localhost".equals(hostName)) {
			ServerHttpResponse response = exchange.getResponse();
			byte[] datas = "{\"code\": 401,\"message\": \"Illegal Request\"}".getBytes(StandardCharsets.UTF_8);
            DataBuffer buffer = response.bufferFactory().wrap(datas);
			response.setStatusCode(HttpStatus.UNAUTHORIZED);
			response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
			return response.writeWith(Mono.just(buffer));
		}
		return chain.filter(exchange);
	}

}

Source download: https://github.com/miansen/SpringCloud-Learn

Original Link: https://miansen.wang/2019/10/21/spring-cloud-gateway/

Tags: Programming Spring xml encoding Windows

Posted on Wed, 06 May 2020 22:00:41 -0700 by joeami