|
Spring Cloud Gateway是Spring生态系统中的一个API网关,它可以处理HTTP请求和响应,并充当微服务架构中的入口点。它是一个基于Spring Framework 5和Spring Boot 2.x的开源项目,采用响应式编程模型,旨在提供高性能、高可靠性和易于使用的API网关解决方案。
工作原理
- 请求路由:当一个客户端发送请求到Spring Cloud Gateway时,Gateway会将请求路由到适当的微服务实例。Gateway会根据请求的URI和HTTP方法来匹配预定义的路由规则,并将请求转发给目标微服务。
- 过滤器:Gateway还支持过滤器,开发人员可以编写过滤器来实现请求和响应的定制逻辑,例如鉴权、限流、日志记录等。
- 负载均衡:Gateway可以通过Spring Cloud的负载均衡器来负载均衡请求,这可以确保请求被均匀分配到各个微服务实例中,从而提高系统的可用性和性能。
- 响应转换:Gateway还支持响应转换,可以将微服务返回的响应转换成另一种格式,例如JSON或XML。
- 故障转移:Gateway还支持故障转移,当微服务实例出现故障时,Gateway会自动将请求转发到备用实例,以确保系统的可用性。
GateWay快速开始
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>路由的组成
Route(路由):路由是构建网关的基本模块,它由路由(ID,目标URI,一系列的断言)和过滤器组成,如果断言为true则匹配该路由;
什么是断言
路由中的断言可以看做是一种规则或条件,用来匹配用户的请求是否符合我们所设定的路由规则。
在Spring Cloud Gateway中,路由配置可以包含一个或多个断言(Predicate),用于匹配请求是否符合当前路由规则。如果请求匹配成功,Gateway就会将请求路由到目标微服务,否则会返回404 Not Found错误。
断言通常用于检查HTTP请求的特定属性,例如请求的URI、HTTP方法、请求头、查询参数等。以下是一些常用的断言:
- Path断言:根据请求的URI匹配路由规则,可以使用Ant风格的通配符。
- Method断言:根据HTTP方法匹配路由规则,例如GET、POST、PUT、DELETE等。
- Header断言:根据请求头匹配路由规则,例如Content-Type、User-Agent等。
- Query断言:根据查询参数匹配路由规则,例如id、name等。
添加路由配置
配置路由:gateway配置路由主要有两种方式,1.用yml配置文件,2.写在代码里
yml配置文件中配置路由
spring:
cloud:
gateway:
routes:
- predicates:
- Path =/gateway/** #匹配规则
uri: http://localhost:8099/getUser #服务1的访问地址
filters:
- StripRrefix: 1 #去掉前缀
server:
port: 8077针对上面配置含义说明:
- uri:目标服务地址,可配置uri和lb://应用服务名称
- predicates:匹配条件,根据规则匹配是否请求该路由
- filters: 过滤规则,这个过滤包含前置过滤和后置过滤,
- StripPrefix=1,表示去掉前缀,即在转发目标url的时候去掉’gateway&#39; 这个时候我们启动服务之后发现服务启动日志:Netty started on port(s): 8077.说明我们服务成功了,并且网关依赖的是nettyserver启动几个服务监听。 我们访问:
curl http://localhost:8077/gateway/getUser在配置正确的情况下将会返回服务返回的结果。使用Java Bean配置
- 添加相关配置类,并配置一个RouteLocator对象:然后使用注入Bean的方式,注入spring
/**
* Created by macro on 2019/9/24.
*/
@Configuration
publicclass GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
//配置路由详情
return builder.routes()
.route(&#34;path_route2&#34;, r -> r.path(&#34;/user/getByUsername&#34;)
.uri(&#34;http://localhost:8201/user/getByUsername&#34;))
.build();
}
}动态路由
了解即可
gateway在启动网关后将无法修改路由配置,如有新服务要上线,则需要先把网关下线,修改 yml 配置后,再重启网关。
这种方式如果在网关上没有优雅停机就会出现服务间断,这无疑是不能被接受的。gateway网关启动时,路由信息默认会加载内存中,路由信息被封装到 RouteDefinition 对象中,我们可以通过RouteDefinitionWriter这类,封装的方法,对路由配置进行增删改查。从而实现动态路由配置。
/**
* 动态路由服务
*/
@Service
public class GoRouteServiceImpl implements ApplicationEventPublisherAware {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
//增加路由
public String add(RouteDefinition definition) {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return &#34;success&#34;;
}
//更新路由
public String update(RouteDefinition definition) {
try {
delete(definition.getId());
} catch (Exception e) {
return &#34;update fail,not find route routeId: &#34;+definition.getId();
}
try {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return &#34;success&#34;;
} catch (Exception e) {
return &#34;update route fail&#34;;
}
}
//删除路由
public Mono<ResponseEntity<Object>> delete(String id) {
return this.routeDefinitionWriter.delete(Mono.just(id)).then(Mono.defer(() -> {
return Mono.just(ResponseEntity.ok().build());
})).onErrorResume((t) -> {
return t instanceof NotFoundException;
}, (t) -> {
return Mono.just(ResponseEntity.notFound().build());
});
}
}编写 Rest接口,通过这些接口实现动态路由功能.
@RestController
@RequestMapping(&#34;/changeRoute&#34;)
public class ChangeRouteController {
@Autowired
private GoRouteServiceImpl goRouteServiceImpl;
//增加路由
@PostMapping(&#34;/add&#34;)
public String add(@RequestBody GatewayRouteDefinition gwdefinition) {
String flag = &#34;fail&#34;;
try {
RouteDefinition definition = assembleRouteDefinition(gwdefinition);
flag = this.goRouteService.add(definition);
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
//删除路由
@DeleteMapping(&#34;/routes/{id}&#34;)
public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
try {
return this.goRouteService.delete(id);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
//更新路由
@PostMapping(&#34;/update&#34;)
public String update(@RequestBody GatewayRouteDefinition gwdefinition) {
RouteDefinition definition = assembleRouteDefinition(gwdefinition);
return this.goRouteService.update(definition);
}
//把传递进来的参数转换成路由对象
private RouteDefinition assembleRouteDefinition(GatewayRouteDefinition gwdefinition) {
RouteDefinition definition = new RouteDefinition();
definition.setId(gwdefinition.getId());
definition.setOrder(gwdefinition.getOrder());
//设置断言
List<PredicateDefinition> pdList=new ArrayList<>();
List<GatewayPredicateDefinition> gatewayPredicateDefinitionList=gwdefinition.getPredicates();
for (GatewayPredicateDefinition gpDefinition: gatewayPredicateDefinitionList) {
PredicateDefinition predicate = new PredicateDefinition();
predicate.setArgs(gpDefinition.getArgs());
predicate.setName(gpDefinition.getName());
pdList.add(predicate);
}
definition.setPredicates(pdList);
//设置过滤器
List<FilterDefinition> filters = new ArrayList();
List<GatewayFilterDefinition> gatewayFilters = gwdefinition.getFilters();
for(GatewayFilterDefinition filterDefinition : gatewayFilters){
FilterDefinition filter = new FilterDefinition();
filter.setName(filterDefinition.getName());
filter.setArgs(filterDefinition.getArgs());
filters.add(filter);
}
definition.setFilters(filters);
URI uri = null;
if(gwdefinition.getUri().startsWith(&#34;http&#34;)){
uri = UriComponentsBuilder.fromHttpUrl(gwdefinition.getUri()).build().toUri();
}else{
// uri为 lb://consumer-service 时使用下面的方法
uri = URI.create(gwdefinition.getUri());
}
definition.setUri(uri);
return definition;
}
}其实一般我们很少通过API去调用rest服务去增删路由信息,一般我们主流都是通过集成nacos的config功能动态增添路由。
过滤器
过滤器种类
Spring Cloud Gateway的过滤器是由GatewayFilterFactory来创建的,它们可以拦截、修改、转换、验证和处理请求和响应。Spring Cloud Gateway内置了许多常用的过滤器,包括:
- AddRequestHeader:在请求头中添加指定的键值对;
- AddResponseHeader:在响应头中添加指定的键值对;
- ForwardTo:将请求转发到指定的URI;
- Hystrix:在请求处理失败时执行熔断逻辑;
- RedirectTo:将请求重定向到指定的URI;
- RemoveRequestHeader:从请求头中删除指定的键;
- RemoveResponseHeader:从响应头中删除指定的键;
- RewritePath:将请求路径重写为指定的路径;
- SetPath:设置请求路径为指定的路径;
- SetRequestHeader:设置请求头中指定键的值为指定的值。
开发人员还可以根据自己的需求实现自定义的过滤器,只需要实现GatewayFilterFactory接口并注册到Spring容器中即可。
Spring Cloud Gateway的过滤器可以在配置文件中进行配置,如下所示:
spring:
cloud:
gateway:
routes:
- id: myroute
uri: http://example.org
filters:
- RewritePath=/foo,/bar这个配置表示,当请求访问/myroute/foo时,网关将请求重写为/http://example.org/bar,然后将请求转发到http://example.org网站。RewritePath过滤器是在请求到达网关之前进行处理的,它会将请求路径中的/foo替换为/bar。
需要注意的是,Spring Cloud Gateway的过滤器是有顺序的,即过滤器的执行顺序与它们在配置文件中的顺序一致。开发人员可以通过调整过滤器的顺序来实现不同的请求处理逻辑。同时,Spring Cloud Gateway还提供了灵活的断言(Predicate)机制,用于匹配请求的路径、参数、请求头等,可以实现更加复杂的请求处理逻辑。
过滤器的顺序
在Spring Cloud Gateway中,过滤器的执行顺序是非常重要的,因为过滤器的执行顺序会影响到请求处理的结果。Spring Cloud Gateway中的过滤器执行顺序如下:
- 前置过滤器(Pre filters):在路由之前执行,可以做一些请求参数的校验、安全认证等工作。
- 路由过滤器(Route filters):用于请求转发,实现动态路由、负载均衡、限流等功能。
- 后置过滤器(Post filters):在路由之后执行,可以对响应结果进行处理,如响应头信息的处理等。
- 错误过滤器(Error filters):当上述过滤器出现异常时会执行该过滤器,用于统一处理异常信息并返回友好的错误信息给客户端。
- 最后才是自定义过滤器(自定义过滤器的执行顺序,再遵循使用@Order定义的优先级)
默认情况下,Spring Cloud Gateway中的过滤器按照上述顺序执行,定义的前置过滤器和后置过滤器具有预定义的顺序,并且在任何使用@Order注解指定的优先级值之前执行即先执行前置过滤器,再执行路由过滤器,然后是后置过滤器,最后是错误过滤器,在最后最后才是我们通过@Order设置的自定义过滤器。当然,也可以通过@Order注解或者OrderedGatewayFilter来控制过滤器的执行顺序。但是需要注意的是,过滤器的执行顺序会对请求处理结果产生影响,所以要谨慎地选择和控制过滤器的执行顺序。
使用@Order定义过滤器执行顺序
标准级越高,越优先执行
使用@Order注解定义执行顺序,其是有参考标准级的,标准优先级为0,如果没有指定优先级,则默认为标准优先级。
其中负数标准高于标准级,高于正数标准级:其中@Order(-1)比较高,可以优先执行,其次是@Order(0)标准标准级,再者才是@Order(+1)正数的标准级
自定义过滤器
实现自定义过滤器只需要两步
- 定义一个配置类,实现过滤器工厂类GatewayFilterFactory接口
- 在yaml配置里面配置对应的过滤器路由即可
Gateway 是一个基于 Spring Boot 的 API 网关服务,它可以处理所有进入应用程序的请求,并对这些请求进行路由、监控、安全性等处理。
Gateway 允许我们通过自定义过滤器来实现一些自定义的业务逻辑,下面是一个示例:
javaCopy code@Component
public class CustomFilter implements GatewayFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 在这里可以编写自己的逻辑,例如获取请求头、参数等信息进行校验、处理等操作
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
String token = headers.getFirst(&#34;Authorization&#34;);
if (token == null || token.isEmpty()) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 继续执行过滤器链
return chain.filter(exchange);
}
@Override
public int getOrder() {
// 设置过滤器的执行顺序,数值越小越先执行
return -1;
}
}上述示例中,我们创建了一个名为 CustomFilter 的过滤器类,实现了 GatewayFilter 接口和 Ordered 接口。其中,filter 方法中编写了自定义逻辑,如果请求头中没有包含 Authorization 参数,则返回未授权的错误响应。如果有该参数,就会继续执行过滤器链。
在 getOrder 方法中,我们设置了该过滤器的执行顺序为 -1,这意味着该过滤器会在所有内置过滤器之前执行。
接下来,在配置文件中配置该过滤器:
spring:
cloud:
gateway:
routes: #配置自定义的过滤器路由
- id: myroute # 定义了一个名为myroute的路由规则。id属性用于指定该路由规则的唯一标识
uri: http://example.org # 指定了该路由规则要转发的目标URL
filters: #定义的过滤器
- name: AddHeader #指定了要使用的过滤器的名称
args: #定义了过滤器的参数
name: X-MyHeader #指定了要添加的请求头的名称
value: Added by Gateway #指定了要添加的请求头的值这个配置表示,在访问myroute路由时,网关会将请求转发到http://example.org,并在请求头中添加一个名为X-MyHeader,值为Added by Gateway的键值对。
通过这个示例,我们可以看到如何实现一个自定义的过滤器,并将它应用到Spring Cloud Gateway中。需要注意的是,在使用GatewayFilterFactory时,我们需要在配置文件中为过滤器指定name,并通过args属性传递配置参数。
Zuul和Gateway的异同
Zuul和Gateway都是用于构建微服务架构中的API网关,它们都有类似的功能,例如路由、负载均衡、安全等。然而,它们有一些不同之处。
- 语言和技术栈:Zuul是Netflix开源的项目,使用Java编写,而Spring Cloud Gateway则是Spring开源的项目,使用Spring Framework 5和Spring Boot 2.x编写。
- 响应式编程:Spring Cloud Gateway采用响应式编程模型,这使得它更加适合处理高流量和高并发的情况。相比之下,Zuul则采用传统的Servlet模型。
- 性能:由于Spring Cloud Gateway采用了响应式编程模型,它的性能比Zuul更好。Spring Cloud Gateway支持异步IO,可以处理更多的请求,而Zuul则是同步处理请求的。
- 易用性:Spring Cloud Gateway比Zuul更易于使用。Spring Cloud Gateway的API设计更加简洁明了,而且有更好的文档和示例。
- 可扩展性:Zuul具有更好的可扩展性,因为它支持插件和过滤器。这使得开发人员可以自定义和扩展Zuul的功能。Spring Cloud Gateway也可以扩展,但是它的扩展点比Zuul少。
总体来说,如果你需要高性能Spring Cloud Gateway是更好的选择。但是,如果你需要更高的可扩展性,那么Zuul可能更适合你的需求。 |
|