今天我们来学习一下SpringCloud中的GateWay,其实看到概念我想大家已经非常了解他的功能了,网关,举个例子,Nginx,我想大家都知道,也知道他是做什么的,使用场景是啥。没错就是网络,主要起到代理,过滤,权限控制,跨域等所有需要访问真正服务之前需要处理的都在网关中进行。我们今天先不拿Nginx来对比,我们来对比一下之前的Zuul然后说说为什么不使用Zuul而需要转向GateWay,因为Zuul是基于servlet,servlet的线程模型大家都清楚,就BIO,就是每个请求一个线程,这种模型的最大弊端就是不能很好的支持大并发,而在Spring5之后,Spring推出了WebFlux基于Reactive线程模型的异步非阻塞框架,服务通过底层的Netty来进行发布及运行,异步非阻塞可以有效降低系统的线程数量,只需要很少的线程就可以完成较高的并发支持,但对于我们习惯了阻塞式编程方式后,我们要逐步改变这种思想,阻塞式编程最直接的就是我要做什么比如IO操作,我就要阻塞当前线程,让当前线程等待我想干的事,等我干完了你再干,虽然说是比较直接,但是并不是很理想,因为没有充分利用少量的资源去做充分的事。异步非阻塞,类似于Nodejs的事件回调,可有效提高系统的并发能力,好处不言而喻。我们说的同步异步都是针对服务器说的,客户端或者用户是感觉不出来的,他们能感觉的就是系统的相应效率。而且异步不一定执行时间就比通不时间短,因为异步是需要很多后续操作的,他只是平衡了一下时间和空间,找到了一个更优的,当下最好的解决方案,所以大家也应该明白了我们为什么选择GateWay了,因为GataWay依赖WebFlux而不依赖servlet所以性能比Zuul要好很多且对高并发有较好的实现。
接下来我们说说GateWay的具体原理,讲完原理性知识,我们在通过代码来总结一些实例,达到快速理解的目的,我们SpringCloud微服务都是将服务注册到服务注册中心,Spring中就是SpringEureka,为了高可用我们会大奖Eureka的集群,之后我们服务都已经注册上了,现在我们需要将服务发布出去,但是直接发布,服务的安全性又没有了保证,所以我们需要一层屏障来对服务进行代理,你可能会说我们做单点登录就可以了啊,可以是可以,当微服务数量比较少的时候还可以,当服务数量较多的时候,单点服务器的认证压力,每个服务的横向扩展又成了瓶颈,所以我们要尽可能的减少可能出现的瓶颈,尽可能保证系统的稳定可靠性,所以我们还是使用网关效果最好。网关我们主要使用的功能主要有两个方面,分别是路由和请求过滤。默认情况下,GateWay的路由策略是
http://gateway_ip:gateway_port/serviceName[全大写]/**
是不是感觉全大写很不爽,没问题他也有转换小写的配置项,下面我们把开启路由和配置小写的配置拿出来。
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true
现在你运行程序应该已经可以代理到你想访问到的应用了,现在我们想要做一些配置,比如校验必须有token才能访问,没有压根都不处理,还有我们现在要做跨域访问,需要做跨域。
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.EnableWebFlux;
@Configuration
@EnableWebFlux
@ComponentScan(basePackages = "com.easy.cloud")
public class GateWayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes().route(t -> t.path("/appA/**").and().uri("http://localhost:8011")).build();
}
}
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange swe, WebFilterChain filterChain) {
ServerHttpRequest request = swe.getRequest();
if (CorsUtils.isCorsRequest(request)) {
ServerHttpResponse response = swe.getResponse();
HttpHeaders headers = response.getHeaders();
headers.add("Access-Control-Allow-Origin", "*");
headers.add("Access-Control-Allow-Methods", "*");
headers.add("Access-Control-Max-Age", "3600");
headers.add("Access-Control-Allow-Headers", "*");
headers.add("Access-Control-Allow-Credentials", "true");
}
return filterChain.filter(swe);
}
}
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class TokenFilter implements GatewayFilter, Ordered {
private static final String AUTHORIZE_TOKEN = "token";
private static final String AUTHORIZE_UID = "uid";
@Override
public int getOrder() {
return 1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
String token = headers.getFirst(AUTHORIZE_TOKEN);
String uid = headers.getFirst(AUTHORIZE_UID);
if (token == null) {
token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
}
if (uid == null) {
uid = request.getQueryParams().getFirst(AUTHORIZE_UID);
}
ServerHttpResponse response = exchange.getResponse();
if (StringUtils.isEmpty(token) || StringUtils.isEmpty(uid)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// String authToken = redisTemplate.opsForValue().get(uid);
String authToken = "uid1001";
if (authToken == null || !authToken.equals(token)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
return chain.filter(exchange);
}
}