Case building
Parent Pom
<com.alibaba.cloud.version>2.2.8.RELEASE</com.alibaba.cloud.version>
<com.cloud.version>Hoxton.SR12</com.cloud.version>
<com.dubbo.version>2.2.7.RELEASE</com.dubbo.version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${com.alibaba.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${com.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-dubbo -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
<version>${com.dubbo.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
Engineering POM
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
server:
port: 9005
spring :
application:
name: gateway-server
cloud:
nacos:
discovery:
server-addr: ip:8848
gateway:
routes:
- id: gateway-provider
uri: http://127.0.0.1:8088
predicates:
- Path=/provider/**
filters:
- StripPrefix=1
- id: gateway-consumer
uri: http://127.0.0.1:8089
predicates:
- Path=/consumer/**
filters:
- StripPrefix=1 #转发之前将路径前面的一个字符串去除 比如/consumer/echo 最终转发到服务是 http://127.0.0.1:8089/echo
- id: gateway-lb
uri: lb://openfeign-provider #负载均衡,填写nacos里面的服务名称
predicates:
- Path=/lb/**
filters:
- StripPrefix=1
Start an ordinary server and consumer, and set up a cluster with three services. It is relatively easy to set up these nodes. You can see the notes in the same directory.
How to achieve load balancing? How to explore the source code to find the load balancer?
org.springframework.cloud.gateway.filter.LoadBalancerClientFilter
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
if (url == null
|| (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
return chain.filter(exchange);
}
// preserve the original url
addOriginalRequestUrl(exchange, url);
if (log.isTraceEnabled()) {
log.trace("LoadBalancerClientFilter url before: " + url);
}
// 选择实例
final ServiceInstance instance = choose(exchange);
if (instance == null) {
throw NotFoundException.create(properties.isUse404(),
"Unable to find instance for " + url.getHost());
}
URI uri = exchange.getRequest().getURI();
// if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,
// if the loadbalancer doesn't provide one.
String overrideScheme = instance.isSecure() ? "https" : "http";
if (schemePrefix != null) {
overrideScheme = url.getScheme();
}
// 选择好节点以后重新组织请求URI
URI requestUrl = loadBalancer.reconstructURI(
new DelegatingServiceInstance(instance, overrideScheme), uri);
if (log.isTraceEnabled()) {
log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
}
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
return chain.filter(exchange);
}
Custom interception
First, let’s look at the several assertions provided by the official website. They are all relatively simple and will not be demonstrated here:
official website address
source code. These classes provide these capabilities:
custom local
custom class. This class can be defined with reference to DedupeResponseHeaderGatewayFilterFactory.
@Component
public class CostGatewayFilterFactory extends
AbstractGatewayFilterFactory<CostGatewayFilterFactory.CustomConfig> {
public CostGatewayFilterFactory() {
super(CustomConfig.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("switchStatus");
}
@Override
public GatewayFilter apply(CostGatewayFilterFactory.CustomConfig config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
if (!config.switchStatus.equalsIgnoreCase("on")) {
// 如果这个开关是关闭状态
return chain.filter(exchange);
}
long sTime = System.currentTimeMillis();
exchange.getAttributes().put("STIME", sTime);
return chain.filter(exchange).then(Mono.fromRunnable(
() -> {
long stime = (long) exchange.getAttributes().get("STIME");
long cost = System.currentTimeMillis() - stime;
System.out.println("cost is : " + cost + " ms");
}));
}
@Override
public String toString() {
return filterToStringCreator(
CostGatewayFilterFactory.this)
.append(config.getSwitchStatus(), config.getSwitchStatus())
.toString();
}
};
}
public static class CustomConfig {
private String switchStatus;
public String getSwitchStatus() {
return switchStatus;
}
public void setSwitchStatus(String switchStatus) {
this.switchStatus = switchStatus;
}
}
}
Configuration
routes:
- id: gateway-provider
uri: http://127.0.0.1:8088
predicates:
- Path=/provider/**
filters:
- StripPrefix=1
- Cost=ON # 这个名字取前面GatewayFilterFactory的
Custom global
@Component
public class GlobalCustomFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 过滤前
System.out.println("Before : " + exchange.getRequest().getURI());
return chain.filter(exchange).then(Mono.fromRunnable(
() -> {
long stime = (long) exchange.getAttributes().get("STIME");
long cost = System.currentTimeMillis() - stime;
System.out.println("End cost time is : " + cost);
}));
}
}
Spring Cloud Gateway is implemented based on Netty, not Tomcat. I originally wanted to try the priorities of mvc filters and gateway filters, but it didn't take effect no matter how I configured the filter. It turns out that a dishonest person is a dishonest person after all.
Can be replaced by Tomcat
Forward redirection principle
- Gateway framework system processing entry org.springframework.http.server.reactive.ReactorHttpHandlerAdapter
- Build the gateway context: org.springframework.web.server.adapter.HttpWebHandlerAdapter
- Traverse the Mapping to obtain the Handler that actually handles the request org.springframework.web.reactive.DispatcherHandler
- Build the filter chain: org.springframework.cloud.gateway.handler.FilteringWebHandler
- The only path for filters: org.springframework.cloud.gateway.handler.FilteringWebHandler.DefaultGatewayFilterChain
- Load balancing filter: org.springframework.cloud.gateway.filter.LoadBalancerClientFilter
- Use Netty to send network request filter: org.springframework.cloud.gateway.filter.NettyRoutingFilter
RouteToRequestUrlFilter This filter processes the Route configuration and stores the real address of the request in
URI mergedUrl = UriComponentsBuilder.fromUri(uri)
// .uri(routeUri)
.scheme(routeUri.getScheme()).host(routeUri.getHost())
.port(routeUri.getPort()).build(encoded).toUri();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, mergedUrl);
NettyRoutingFilter makes the real forwarding call.
This classic diagram must be pasted: