最近做项目使用到了springcloude gateway作为网关,因此在此记录下springcloud gateway的入门操作,后续再将源码解读写出来,先立个flag。
回归正题,Springcloud gateway是spring 最新推出的网关中间件,用于代替 Netflix Zuul,因为 Netflix Zuul是基于mvc实现的,并发性能较低,springcloud gateway底层是通过基于netty的webflux实现的,并发性能比较好,因为比较适合高并发的网关。
一、依赖配置(pom文件配置)
配置property和dependencyManagement(用于声明版本,给pom的子类直接引用)
<properties>
<!-- <spring-cloud.version>Greenwich.SR2</spring-cloud.version>-->
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.9.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
二、yml文件以及property文件的配置
1、bootstrap.yml文件(启动优先级最高)的配置
spring:
application:
#声明服务名称
name: test-gateway-service
cloud:
gateway:
discovery:
locator:
#这里配置声明是否使用springcloud gateway的服务注册,false 代表不使用
enabled: false
default-filters:
#声明默认的filter为Hystrix
- name: Hystrix
args:
name : default
#声明默认的回调接口
fallbackUri: 'forward:/defaultFallback'
#这里配置路由规则,可以通过配置文件配置,也可以通过写代码配置,
#这里只展示通过配置文件配置的方式,往后展示通过代码配置的方式
routes:
- id: test1-inside-router
#这里指定命中path中的路径的请求会跳转到test1-service的微服务中
uri: lb://test1-service
predicates:
- Path=/justTest/**
filters:
- StripPrefix=1
- id: test2-inside-router
- #这里指定命中path中的路径的请求会跳转到test2-service的微服务中
uri: lb://test2-service
predicates:
- Path=/test/shop/**
filters:
#这里单独声明hystrix熔断服务配置
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE
thread:
timeoutInMilliseconds: 4000
至此,springboot集成springcloud gateway即完成
对于路由规则的配置,除了可以通过yml或者property文件配置之外,还可以通过代码的形式,代码的形式样例代码如下:
@Configuration
@Log4j2
public class FilterConfigration {
@Bean
public RouteLocator testOutSideRoute(RouteLocatorBuilder builder) {
return builder.routes()
.route(r ->
r.path("/justTest/**")
.filters(
f -> f.filters(new CommonAuthFilter())
)
.uri("lb://test1-service")
)
.build();
}
@Bean
public RouteLocator shopOutSideRoute(RouteLocatorBuilder builder) {
return builder.routes()
.route(r ->
r.path("test/shop/**")
.filters(
f -> f.filters(new CommonAuthFilter())
)
.uri("lb://test2-service")
)
.build();
}
}
至此,网关已经可以跑起来,并路由到相应的接口,但是,网关的作用并不是仅限于此,还可以做鉴权、给修改、增加请求的参数等等(暂时先不说限流、熔断的功能,后续会详聊),如果想拦截请求,并做相应的修改或者做日志记录等,springcloud gateway给我们开了口子,可以通过定义全局的filter或者自定义的filter来实现。
这里仅仅展示做简单的认证并记录请求的全局filter。
import com.alibaba.fastjson.JSON;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
@Log4j2
public class AuthAndLogFilter implements GlobalFilter, Ordered {
@Autowired
private RedisTemplate redisTemplate;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest serverHttpRequest = exchange.getRequest();
ServerHttpResponse serverHttpResponse = exchange.getResponse();
log.info("==========网关收到请求:{}==========", serverHttpRequest.getPath());
StringBuilder logBuilder = new StringBuilder();
List<String> tokenList = serverHttpRequest.getHeaders().get("token");
Map<String, Object> mapResult = new HashMap<>();
String requestUrl = exchange.getRequest().getPath().toString();
if (requestUrl.startsWith("/test/a/b/")
) {
log.info("白名单路径,无需校验token");
} else {
if (!CollectionUtils.isEmpty(tokenList)) {
String token = tokenList.get(0);
log.info("上送的token为:{}" , token);
if (!redisTemplate.hasKey(token)) {
log.info("登录超时,需要重新登录");
mapResult.put("code", 500);
mapResult.put("message", "登录超时,请重新登录");
DataBuffer bodyDataBuffer = serverHttpResponse.bufferFactory().wrap(JSON.toJSONString(mapResult).getBytes());
serverHttpResponse.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
return serverHttpResponse.writeWith(Mono.just(bodyDataBuffer));
}
} else {
log.info("没有上送token");
mapResult.put("code", 500);
mapResult.put("message", "登录超时,请重新登录");
DataBuffer bodyDataBuffer = serverHttpResponse.bufferFactory().wrap(JSON.toJSONString(mapResult).getBytes());
serverHttpResponse.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
return serverHttpResponse.writeWith(Mono.just(bodyDataBuffer));
}
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
/**
* Get the order value of this object.
* Higher values are interpreted as lower priority. As a consequence,
* the object with the lowest value has the highest priority (somewhat
* analogous to Servlet {@code load-on-startup} values).
* Same order values will result in arbitrary sort positions for the
* affected objects.
* 这个值越大,优先级越低,也就是加载的顺序越往后
*/
return -20;
}
}
到这里,gateway网关可以跑起来了,但是如果放到生产环境还元不行,主要是有两个地方需要改进:
1)、路由规则都是写死的,万一又接口改动或者新增或者删除,则需要停服,重新发布才能生效。
2)、这里没有使用服务发现中间件,不适合微服务环境
针对问题1,可以使用基于分布式配置平台,将路由规则配置到分布式配置中间件,要改路由规则,只需要修改分布式配置文件即可,无需重启服务,或者通过开发api接口,实现代码层级自动化动态修改路由规则。
针对问题2,可以使用springcloud gateway + nacos的组合来实现服务发现和分布式配置。
以上两点,下一篇文章再做详细在展开。