前期回顾:
【Zero to One系列】springcloud微服务集成nacos,形成分布式系统
1、Gateway依赖配置
主要Maven依赖如下:
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId><version>2.2.5.RELEASE</version></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>2.2.5.RELEASE</version></dependency>
</dependencies>
在Gateway的启动类上,需要加上注解:
@EnableDiscoveryClient
2、Gateway路由配置
网关在nacos上注册配置:
spring:cloud:nacos:discovery:server-addr: ip:8848namespace: a663dc6b-98ff-4518-9a06-ffb3e3c4b697group: ***-dev
然后是路由配置,如下:
spring:application:name: ***-gatewaycloud:gateway:routes: - id : ***-useruri : lb://***-user #在服务注册中心找服务名为 ***-user 的服务predicates : - Path= /user/* #设置路由断言,当请求 /user 时进行转发- id : ***-senduri : lb://***-send #在服务注册中心找服务名为 ***-user 的服务predicates : - Path= /send/* #设置路由断言,当请求 /send 时进行转发
路由规则的配置说明,主要有上面写的这部分。如果需要写的更特殊化一些,可以参考gateway的路由配置说明参考链接:spring cloud gateway核心概念之断言和过滤器
3、使用Nacos配置路由
在一般系统开发中,微服务子模块,可能会比较多。当配置路由时,可能有较高评率的更新变动,如果像上述方式,放在系统的配置文件中,那每次修改,都得重新发布网关服务,所以不太合适。因此,我们可以将服务的路由配置放在Nacos的配置文件中,如此可以随时修改和更新啦。
首先,我们在nacos上建一个网关的配置文件,如gateway.yml文件,文件内容如下:
spring:application:name: ***-gatewaycloud:gateway:routes: - id : ***-useruri : lb://***-user #在服务注册中心找服务名为 ***-user 的服务predicates : - Path= /user/* #设置路由断言,当请求 /user 时进行转发- id : ***-senduri : lb://***-send #在服务注册中心找服务名为 ***-send 的服务predicates : - Path= /send/* #设置路由断言,当请求 /send 时进行转发
然后,我们需要在服务的pom依赖中有config的依赖,
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><version>2.2.5.RELEASE</version>
</dependency>
最后,配置对应的nacos配置文件即可,如下:
spring:cloud:nacos:discovery:server-addr: ip:8848namespace: a663dc6b-98ff-4518-9a06-ffb3e3c4b697group: ***-devconfig: #config配置必须放在bootstrap配置内,才能读取到nacos配置,否则报错server-addr: ip:8848namespace: a663dc6b-98ff-4518-9a06-ffb3e3c4b697group: ***-devprefix: gatewayfile-extension: ymlrefresh-enabled: true
此处需要着重注意:config相关的配置,必须放在bootstrap的yml或properties文件中,因为bootstrap的优先级最高,否则启动会因读取不到nacos上的配置数据。。(我实践时就是在这里,测试了好一阵。。。)
4、Gateway全局过滤处理
Gateway有一个全局过滤接口GlobalFilter,实现其filter方法,就可以加入一些我们代码逻辑的自定义处理了,例如:
package com.***.gateway.filter;import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;/*** @Description 与 GatewayFilter 的作用一样。* 区别在于 GlobalFilter 的逻辑可以写代码来自定义规则;* 而 GatewayFilter 通过配置定义,处理逻辑是固定的。*/
public class GatewayFilter implements GlobalFilter,Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//todo 例如请求加解密处理、白名单校验等等return chain.filter(exchange);}//设置过滤器优先级,值越低优先级越高@Overridepublic int getOrder() {return 0;}
}
此处过滤处理的细节实现,此处暂不展开,后续博文中,我会陆续输出,感兴趣请关注一下我相关系列的博文吧!!
5、异常情况说明
gateway启动异常,异常信息如下:
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2023-03-23 10:29:03.849 ERROR 36416 --- [ restartedMain] o.s.boot.SpringApplication : Application run failedreactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.IllegalArgumentException: Unable to find RoutePredicateFactory with name Path
Caused by: java.lang.IllegalArgumentException: Unable to find RoutePredicateFactory with name Path at org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator.lookup(RouteDefinitionRouteLocator.java:264) ~[spring-cloud-gateway-core-2.2.2.RELEASE.jar:2.2.2.RELEASE]at org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator.combinePredicates(RouteDefinitionRouteLocator.java:244) ~[spring-cloud-gateway-core-2.2.2.RELEASE.jar:2.2.2.RELEASE]at org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator.convertToRoute(RouteDefinitionRouteLocator.java:169) ~[spring-cloud-gateway-core-2.2.2.RELEASE.jar:2.2.2.RELEASE]at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:100) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxFlatMap$FlatMapMain.tryEmitScalar(FluxFlatMap.java:480) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:413) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:704) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:580) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxFlatMap$FlatMapInner.onSubscribe(FluxFlatMap.java:970) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:161) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:86) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.Flux.subscribe(Flux.java:8357) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:418) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:267) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:225) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:363) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:161) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:86) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.Flux.subscribe(Flux.java:8357) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxFlatMap$FlatMapMain.onNext(FluxFlatMap.java:418) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:267) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:225) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxFlatMap$FlatMapMain.onSubscribe(FluxFlatMap.java:363) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:161) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:86) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.Mono.subscribe(Mono.java:4252) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.Mono.subscribeWith(Mono.java:4363) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at reactor.core.publisher.Mono.subscribe(Mono.java:4083) ~[reactor-core-3.3.13.RELEASE.jar:3.3.13.RELEASE]at org.springframework.cloud.gateway.route.CachingRouteLocator.onApplicationEvent(CachingRouteLocator.java:73) ~[spring-cloud-gateway-core-2.2.2.RELEASE.jar:2.2.2.RELEASE]at org.springframework.cloud.gateway.route.CachingRouteLocator.onApplicationEvent(CachingRouteLocator.java:35) ~[spring-cloud-gateway-core-2.2.2.RELEASE.jar:2.2.2.RELEASE]at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:404) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:361) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]at org.springframework.cloud.gateway.route.RouteRefreshListener.reset(RouteRefreshListener.java:68) ~[spring-cloud-gateway-core-2.2.2.RELEASE.jar:2.2.2.RELEASE]at org.springframework.cloud.gateway.route.RouteRefreshListener.onApplicationEvent(RouteRefreshListener.java:49) ~[spring-cloud-gateway-core-2.2.2.RELEASE.jar:2.2.2.RELEASE]at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:404) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:361) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:898) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.finishRefresh(ReactiveWebServerApplicationContext.java:129) ~[spring-boot-2.2.13.RELEASE.jar:2.2.13.RELEASE]at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:554) ~[spring-context-5.2.12.RELEASE.jar:5.2.12.RELEASE]at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:66) ~[spring-boot-2.2.13.RELEASE.jar:2.2.13.RELEASE]at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) [spring-boot-2.2.13.RELEASE.jar:2.2.13.RELEASE]at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:405) [spring-boot-2.2.13.RELEASE.jar:2.2.13.RELEASE]at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) [spring-boot-2.2.13.RELEASE.jar:2.2.13.RELEASE]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) [spring-boot-2.2.13.RELEASE.jar:2.2.13.RELEASE]at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) [spring-boot-2.2.13.RELEASE.jar:2.2.13.RELEASE]at com.***.gateway.***GatewayApplication.main(***GatewayApplication.java:12) [classes/:na]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_241]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_241]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_241]at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_241]at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-2.2.13.RELEASE.jar:2.2.13.RELEASE]Disconnected from the target VM, address: '127.0.0.1:2687', transport: 'socket'
2023-03-23 10:29:03.856 WARN 36416 --- [ Thread-10] c.a.n.common.http.HttpClientBeanHolder : [HttpClientBeanHolder] Start destroying common HttpClient
2023-03-23 10:29:03.857 WARN 36416 --- [ Thread-10] c.a.n.common.http.HttpClientBeanHolder : [HttpClientBeanHolder] Destruction of the endProcess finished with exit code 0
这个问题,报错信息是:Unable to find RoutePredicateFactory with name Path
其实是一个路由配置的格式问题导致,解决方式就是必须严格注意以下2点:
1、Path的首字母必须大写
2、- Path= 这个等于号前面,不能有空格
另一个异常,可能会报:
Cannot determine local hostname
需要在配置中加上如下内容:
spring:cloud:inetutils:ignored-interfaces: 'VMware Virtual Ethernet Adapter for VMnet1,VMware Virtual Ethernet Adapter for VMnet8'
至此,Gateway网关对于微服务的路由配置实现,就基本完成啦!!!