上一篇文章《微服务实战spring cloud alibaba(十五)全局链路日志追踪》我们介绍了微服务中的全局链路日志追踪,这篇文章我们介绍下常用的spring cloud gateway,在之前我们写过一篇文章《微服务实战spring cloud alibaba(九)为服务增加spring cloud gateway网关》,这篇文章介绍的是直接在配置文件里面配制好固定的路由,此时项目启动的时候就会去加载这里的路由。但是在真实环境中,我们的微服务会越来越多,那么如果增加了服务我们就去启动一次gateway这样是不是很不方便?所以这里我们介绍下使用nacos来配置动态路由,即把路由规则配置到nacos上,springcloud-gateway直接去nacos上读取对应的配置即可,同时在nacos上做了更新,springcloud-gateway会自动更新。下面直接介绍下实战。
一、创建一个maven项目,引入下面的依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> </dependency>
备注:
1、在spring cloud gateway里面如果没有特殊的使用的话,一般引入这两个依赖即可。
二、创建yml的配置读取自定义properties
这里的话,由于我们需要指定去读取nacos配置中心的某个配置文件,因此我们需要在代码里面添加一块自定义读取的实体类,实现自定义读取yml配置的内容
2.1、在application.yml中配置如下:
gateway: routes: config: serveraddr: 192.168.31.20:38848 data-id: gateway-routes group: DEFAULT_GROUP timeout: 5000 namespace: 25b0d27d-03f6-4032-8688-72f4d2863e1d
这个配置的意思是去192.168.31.20上的nacos上读取名称空间为25b0d27d-03f6-4032-8688-72f4d2863e1d下面的gateway-routes配置文件。
同时我们还需要配置一个基础的配置
spring: cloud: nacos: discovery: server-addr: 192.168.31.20:38848 namespace: 25b0d27d-03f6-4032-8688-72f4d2863e1d gateway: discovery: locator: enabled: true #开启通过服务中心的自动根据 serviceId 创建路由的功能
这里的话需要配置这个根据serviceid创建路由功能。
2.2、创建读取properties
这里的话就是java的实体类,完整代码如下:
package org.gateway.service.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import lombok.Data; @ConfigurationProperties(prefix = "gateway.routes.config") @Component @Data public class GatewayRouteConfigProperties { private String serveraddr; private String dataId; private String group; private String namespace; private Long timeout; }
可以看到我们在yml文件中配置了哪些属性就读取哪些属性即可。
2.3、自定义实现nacos的读取配置
在上面我们配置了读取的信息,那么这里需要对nacos的读取进行配置,指定配置读取上面配置的nacos地址和名称空间,完整代码如下:
package org.gateway.service.config; import java.util.Properties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.exception.NacosException; @Configuration public class GatewayConfigServiceConfig { @Autowired private GatewayRouteConfigProperties configProperties; @Bean public ConfigService configService() throws NacosException { Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.SERVER_ADDR, configProperties.getServeraddr()); properties.setProperty(PropertyKeyConst.NAMESPACE, configProperties.getNamespace()); return NacosFactory.createConfigService(properties); } }
三、实现自定义路由规则
对于spring cloud gateway 读取路由配置只有两种规则,一种是新增,也就是有新增规则的时候会触发此方法,另外一种是更新,也就是在发生更新的时候我们会出发此方法,所以这里我们首先创建一个路由的service,定义新增和更新方法,完整代码如下:
package org.gateway.service.config; import org.springframework.cloud.gateway.route.RouteDefinition; /** * 创建一个路由发布的service * * @author Administrator * */ public interface RouteService { /** * 更新路由配置 * * @param routeDefinition */ void update(RouteDefinition routeDefinition); /** * 添加路由配置 * * @param routeDefinition */ void add(RouteDefinition routeDefinition); }
上面的代码是一个interface,我们需要来实现一下,完整代码如下:
package org.gateway.service.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionWriter; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.stereotype.Service; import lombok.extern.slf4j.Slf4j; import reactor.core.publisher.Mono; /** * 实现自定义路由发布的service * * @author Administrator * */ @Slf4j @Service public class RouteServiceImpl implements RouteService, ApplicationEventPublisherAware { @Autowired private RouteDefinitionWriter routeDefinitionWriter; private ApplicationEventPublisher publisher; /** * 更新路由 */ @Override public void update(RouteDefinition routeDefinition) { log.info("更新路由配置项:{}", routeDefinition); this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId())); routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe(); this.publisher.publishEvent(new RefreshRoutesEvent(this)); } /** * 添加路由 */ @Override public void add(RouteDefinition routeDefinition) { log.info("新增路由配置项:{}", routeDefinition); routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe(); this.publisher.publishEvent(new RefreshRoutesEvent(this)); } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher; } }
以上对于spring cloud gateway的路由规则定义也实现完毕了。
四、配置路由初始化读取
在spring cloud gateway里面默认读取的路由是从本地读取的,这里在启动的时候,我们需要修改为从nacos读取,完整代码如下:
package org.gateway.service.config; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; import javax.annotation.PostConstruct; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.stereotype.Component; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.Listener; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; /** * 初始化配置 * @author Administrator * */ @Component @Slf4j @RefreshScope public class GatewayRouteInitConfig { @Autowired private GatewayRouteConfigProperties configProperties; @Autowired private RouteService routeService; /** * nacos 配置服务 */ @Autowired private ConfigService configService; /** * JSON 转换对象 */ private final ObjectMapper objectMapper = new ObjectMapper(); @PostConstruct public void init() { log.info("开始网关动态路由初始化..."); try { // getConfigAndSignListener()方法 发起长轮询和对dataId数据变更注册监听的操作 // getConfig 只是发送普通的HTTP请求 String initConfigInfo = configService.getConfigAndSignListener(configProperties.getDataId(), configProperties.getGroup(), configProperties.getTimeout(), new Listener() { @Override public Executor getExecutor() { return null; } @Override public void receiveConfigInfo(String configInfo) { if (StringUtils.isNotEmpty(configInfo)) { log.info("接收到网关路由更新配置:\r\n{}", configInfo); List<RouteDefinition> routeDefinitions = null; try { routeDefinitions = objectMapper.readValue(configInfo, new TypeReference<List<RouteDefinition>>() { }); } catch (JsonProcessingException e) { log.error("解析路由配置出错," + e.getMessage(), e); } for (RouteDefinition definition : Objects.requireNonNull(routeDefinitions)) { routeService.update(definition); } } else { log.warn("当前网关无动态路由相关配置"); } } }); log.info("获取网关当前动态路由配置:\r\n{}", initConfigInfo); if (StringUtils.isNotEmpty(initConfigInfo)) { List<RouteDefinition> routeDefinitions = objectMapper.readValue(initConfigInfo, new TypeReference<List<RouteDefinition>>() { }); for (RouteDefinition definition : routeDefinitions) { routeService.add(definition); } } else { log.warn("当前网关无动态路由相关配置"); } log.info("结束网关动态路由初始化..."); } catch (Exception e) { log.error("初始化网关路由时发生错误", e); } } }
以上基于我们的代码部分就完成了。
五、nacos配置路由
这里最后一步我们就是需要在nacos上配置动态的路由
5.1、登录nacos,找到对应的名称空间
5.2、创建一个新的配置
点击列表右上角的+号
备注:
创建的dataid为:gateway-routes,这个id是我们在前面的配置文件里面配置的dataid,需要对应起来。 创建的group默认即可,也是和上面的配置文件对应起来。 创建的配置格式为json,所有的路由我们统一使用json的格式进行配置
配置内容的话,这里主要是为了演示项目,所以我们暂时只配置user-service即可,所以填写的配置内容如下:
[{ "predicates": [{ "args": { "pattern": "/users/**" }, "name": "Path" }], "id": "user-service", "filters": [{ "args": { "parts": 1 }, "name": "StripPrefix" }], "uri": "lb://user-service", "order": 1 }]
完整的示例图如下:
然后我们点击发布即可
就可以看到我们刚才创建的配置了。
六、启动项目
这里我们挨个启动user-service,good-service,gateway-service。
启动起来之后,我们看到gateway项目里面的启动日志里面自动的读取了nacos上的配置
七、测试项目
现在开发完成,配置完成,项目启动完成,接下来我们就测试一下,请求地址如下:http://127.0.0.1:9010/users/user/good/1 这里我们请求这个地址看看效果
可以发现我们请求成功的,通过gateway进行请求。后端服务调用到了user-service。
八、新增路由服务
在前面我们仅仅配置了user-service的路由,这里我们需要增加一个good-service的路由需求,那么怎么办呢?其实我们只需要在nacos上新增一个json对象即可,项目也不需要重启就自动实现了路由的更新,下面我们区nacos里面修改gateway-routes的配置内容,修改如下:
[{ "predicates": [{ "args": { "pattern": "/users/**" }, "name": "Path" }], "id": "user-service", "filters": [{ "args": { "parts": 1 }, "name": "StripPrefix" }], "uri": "lb://user-service", "order": 1 }, { "predicates": [{ "args": { "pattern": "/goods/**" }, "name": "Path" }], "id": "goods-service", "filters": [{ "args": { "parts": 1 }, "name": "StripPrefix" }], "uri": "lb://goods-service", "order": 2 } ]
修改完之后,记得点一下发布。
此时我们新增了goods的路由,然后我们看下gateway的日志信息
可以看到gateway自动实现了路由更新,下面我们测试通过gateway访问下goods服务,请求地址如下:http://127.0.0.1:9010/goods/good/info/1
可以看到调用成功了,没有任何问题。
备注:
1、在nacos中配置的路由是json格式。
2、json格式最外面是一个数组,配置的一个路由为一个对象
最后按照惯例,附上本案例的源码下载,登录后即可下载。
还没有评论,来说两句吧...