上一篇文章《微服务实战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格式最外面是一个数组,配置的一个路由为一个对象
最后按照惯例,附上本案例的源码下载,登录后即可下载。



















还没有评论,来说两句吧...