Springcloud Gateway
Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
一、实现服务的路由
引入相关pom依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
做下简单配置
management:
endpoints:
web:
exposure:
include: '*' #打开endpoint
server:
port: 18085
spring:
application:
name: api-gateway
cloud:
gateway:
discovery:
locator:
lower-case-service-id: true #gateway可以通过开启以下配置来打开根据服务的serviceId来匹配路由,默认是false大写
enabled: true # 是否可以通过其他服务的serviceId来转发到具体的服务实例。默认为false
routes:
- id: service-hi
uri: lb://service-hi # lb://serviceId
predicates:
- Path=/admin/service-hi/** # 如果请求地址满足/service-hi/**,则转发到service-hi服务
filters:
- StripPrefix=2 # 去除请求url中的/admin/service-hi/
nacos:
discovery:
server-addr: 127.0.0.1:8848 # 使用nacos作为注册中心
启动service-hi、api-gateway服务,访问 http://localhost:18085/admin/service-hi/hello?name=123
不排除,有的喜欢用java config的方式去做路由,这里也给出案例,将上面路由配置注视掉,添加如下config,重启api-gateway服务,访问 http://localhost:18085/admin/service-hi/hello?name=123
@Configuration
public class RouterConfig {
static final String prefix = "/admin";
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("SERVICE-FEIGN", r ->
r.path(prefix + "/service-hi/**").
filters(f -> f.rewritePath(prefix + "/service-hi/(?<remaining>.*)", "/$\\{remaining}"))
.uri("lb://service-hi"))
.build();
}
}
二、nacos动态配置是拉还是推呢?
上代码我们可以在启动类中写入一下的代码,在nacos配置groupId为test-gateway,group为test-group
public class ServiceHiApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(ServiceHiApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
ConfigService configService = NacosFactory.createConfigService("localhost:8848");//获取ConfigService
configService.addListener("test-gateway", "test-group", new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
System.out.println(configInfo);
}
@Override
public Executor getExecutor() {
return null;
}
});
}
}
客户端是通过一个定时任务来检查自己监听的配置项的数据的,一旦服务端的数据发生变化时,客户端将会获取到最新的数据,并将最新的数据保存在一个 CacheData 对象中,然后会重新计算 CacheData 的 md5 属性的值,此时就会对该 CacheData 所绑定的 Listener 触发 receiveConfigInfo 回调。
博客参考: http://blog.didispace.com/nacos-yuanli-1/ 讲的比较详细,在此基础上又做了扩展,能不能动态刷新路由?
三、如何动态刷新路由
基于时间监听机制,我们可以动态刷新Springcloud gateway的RouteDefinition,提供一个服务类,主要用于通过代码的方式对路由进行更新。至于springboot事件监听,以后会有博客专门讲解,请持续关注。
@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
private ApplicationEventPublisher publisher;
/**
* 增加路由
*
* @param definition
* @return
*/
public String add(RouteDefinition definition) {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
}
/**
* 更新路由
*
* @param definition
* @return
*/
public String update(RouteDefinition definition) {
try {
this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
} catch (Exception e) {
return "update fail,not find route routeId: " + definition.getId();
}
try {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
} catch (Exception e) {
return "update route fail";
}
}
/**
* 删除路由
*
* @param id
* @return
*/
public String delete(String id) {
try {
this.routeDefinitionWriter.delete(Mono.just(id));
return "delete success";
} catch (Exception e) {
e.printStackTrace();
return "delete fail";
}
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
}
还需要你个动态刷新监听,也就是我们上面讲到的代码,这里只需要做下简单的封装
@Component
public class DynamicRouteServiceImplByNacos {
@Autowired
private DynamicRouteServiceImpl dynamicRouteService;
public DynamicRouteServiceImplByNacos() {
dynamicRouteByNacosListener("test-gateway", "test-group");
}
/**
* 监听Nacos Server下发的动态路由配置
*
* @param dataId
* @param group
*/
public void dynamicRouteByNacosListener(String dataId, String group) {
try {
ConfigService configService = NacosFactory.createConfigService("localhost:8848");
// String content = configService.getConfig(dataId, group, 5000);
configService.addListener(dataId, group, new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
MyRoute myRoute = JSON.parseObject(configInfo, MyRoute.class);
if (!CollectionUtils.isEmpty(myRoute.getRouteDefinitionList())) {
myRoute.getRouteDefinitionList().forEach(i -> dynamicRouteService.update(i));
}
}
@Override
public Executor getExecutor() {
return null;
}
});
} catch (NacosException e) {
//todo 提醒:异常自行处理此处省略
}
}
}
把之前所有配置路由信息都要注释掉,这里为了我们通过nacos,动态得去配置路由服务,启动api-gateway、service-feign、service-hi,在nacos配置groupId为test-gateway,group为test-group,里面配置了service-hi的路由配置,发布一下。即可看到可以路由到service-hi服务,这样就是实现了基于nacos的动态路由
{
"routeDefinitionList": [
{
"filters": [
{
"args": {
"_genkey_0": "1"
},
"name": "StripPrefix"
}
],
"id": "service-hi",
"order": 0,
"predicates": [
{
"args": {
"pattern": "/service-hi/**"
},
"name": "Path"
}
],
"uri": "lb://service-hi"
},
{
"filters": [ ],
"id": "jd_route",
"order": 0,
"predicates": [
{
"args": {
"pattern": "/jd"
},
"name": "Path"
}
],
"uri": "http://www.baidu.com"
}
]
}