前言
Nacos: 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。–>Nacos
Spring Cloud Gateway: 该项目提供了一个用于在Spring WebFlux之上构建API网关的库。Spring Cloud Gateway旨在提供一种简单而有效的方法来路由到API,并为它们提供跨领域的关注,例如:安全性,监视/度量和弹性–>Srping Cloud Gateway
概念介绍
这一模块留待后续补充,目前先介绍项目搭建过程。
Nacos
Nacos配置见–>Nacos-server,可以参考阿里官方文档–>Nacos手册。这里我在本地使用单机模式启动并连接本地数据库。
在nacos中,与gateway相关的文件有三个:
- Application-dev.properties
- Gateway-dev.properties
- gateway-router
第一个文件是所有服务通用的配置,例如:redis,knife4j等等可以通用的配置。第二个文件作为有Gateway服务特有的配置文件,例如:端口号,数据库地址等等。第三个文件是Gateway路由文件,通过这个文件可以实现动态路由的功能。为了方便,这三个文件我都创建在了默认分组下。
前两个文件的内容就不贴出来了,gateway-router内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| [{ "id": "api", "order": 0, "predicates": [{ "args": { "pattern": "/api/**" }, "name": "Path" }], "filters": [{ "name": "StripPrefix", "args": { "_genkey_0": "1" } } ], "uri": "lb://shiming-gateway" },{ "id": "auth-router", "order": 1, "predicates": [{ "args": { "_genkey_0": "/shiming-auth/**" }, "name": "Path" }], "filters": [{ "name": "StripPrefix", "args": { "_genkey_0": "1" } } ], "uri": "lb://shiming-auth" },{ "id": "system", "order": 2, "predicates": [{ "args": { "pattern": "/community-system/**" }, "name": "Path" }], "filters": [{ "name": "StripPrefix", "args": { "_genkey_0": "1" } } ], "uri": "lb://community-system" }]
|
其中,api部分是Gateway服务的路由,auth部分是权限服务,以后再介绍,这里忽略。system部分是系统服务即服务提供者。
具体解释可以看这篇文章–>SpringCloud gateway (史上最全)
Gateway yml格式配置转为json格式见–>gateway yml配置文件转成json
Gateway
接下来步入正题,开始Gateway模块的搭建:
导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
|
创建配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| spring.application.name=@project.artifactId@ spring.profiles.active=@profiles.active@ spring.main.allow-bean-definition-overriding=true
spring.cloud.nacos.discovery.server-addr=@profiles.nacos@ spring.cloud.nacos.config.server-addr=@profiles.nacos@ spring.cloud.nacos.config.file-extension=properties spring.cloud.nacos.config.shared-dataids=application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} spring.cloud.nacos.config.refreshable-dataids=application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
nacos.gateway.route.config.data-id=gateway-router
spring.cloud.gateway.globalcors.corsConfigurations.'[/**]'.allowedOrigins=* spring.cloud.gateway.globalcors.corsConfigurations.'[/**]'.allowedMethods=[GET,POST,PUT,DELETE]
|
编写代码
路由配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| ** * 路由类配置 */ @Configuration public class GatewayConfig { public static final long DEFAULT_TIMEOUT = 30000;
public static String NACOS_SERVER_ADDR;
public static String NACOS_NAMESPACE;
public static String NACOS_ROUTE_DATA_ID;
public static String NACOS_ROUTE_GROUP;
@Value("${spring.cloud.nacos.discovery.server-addr}") public void setNacosServerAddr(String nacosServerAddr){ NACOS_SERVER_ADDR = nacosServerAddr; }
@Value("${spring.cloud.nacos.discovery.namespace:}") public void setNacosNamespace(String nacosNamespace){ NACOS_NAMESPACE = nacosNamespace; }
@Value("${nacos.gateway.route.config.data-id:}") public void setNacosRouteDataId(String nacosRouteDataId){ NACOS_ROUTE_DATA_ID = nacosRouteDataId; }
@Value("${nacos.gateway.route.config.group:DEFAULT_GROUP}") public void setNacosRouteGroup(String nacosRouteGroup){ NACOS_ROUTE_GROUP = nacosRouteGroup; }
}
|
动态路由实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
|
@Slf4j @Service public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware { @Autowired private RouteDefinitionWriter routeDefinitionWriter; @Autowired private RouteDefinitionLocator routeDefinitionLocator;
@Autowired private ApplicationEventPublisher publisher;
@Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher; }
public String delete(String id) { try { log.info("gateway delete route id {}",id); this.routeDefinitionWriter.delete(Mono.just(id)).subscribe(); this.publisher.publishEvent(new RefreshRoutesEvent(this)); return "delete success"; } catch (Exception e) { return "delete fail"; } }
public String updateList(List<RouteDefinition> definitions) { log.info("gateway update route {}",definitions); List<RouteDefinition> routeDefinitionsExits = routeDefinitionLocator.getRouteDefinitions().buffer().blockFirst(); if (!CollectionUtils.isEmpty(routeDefinitionsExits)) { routeDefinitionsExits.forEach(routeDefinition -> { log.info("delete routeDefinition:{}", routeDefinition); delete(routeDefinition.getId()); }); } definitions.forEach(definition -> { updateById(definition); }); return "success"; }
public String updateById(RouteDefinition definition) { try { log.info("gateway update route {}",definition); 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"; } }
public String add(RouteDefinition definition) { log.info("gateway add route {}",definition); routeDefinitionWriter.save(Mono.just(definition)).subscribe(); this.publisher.publishEvent(new RefreshRoutesEvent(this)); return "success"; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| ** * * 通过nacos下发动态路由配置,监听Nacos中gateway-route配置 * */ @Component @Slf4j @DependsOn({"gatewayConfig"}) public class DynamicRouteServiceImplByNacos {
@Autowired private DynamicRouteServiceImpl dynamicRouteService;
private ConfigService configService;
@PostConstruct public void init() { log.info("gateway route init..."); try{ configService = initConfigService(); if(configService == null){ log.warn("initConfigService fail"); return; } String configInfo = configService.getConfig(GatewayConfig.NACOS_ROUTE_DATA_ID, GatewayConfig.NACOS_ROUTE_GROUP, GatewayConfig.DEFAULT_TIMEOUT); log.info("获取网关当前配置:\r\n{}",configInfo); List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class); for(RouteDefinition definition : definitionList){ log.info("update route : {}",definition.toString()); dynamicRouteService.add(definition); } } catch (Exception e) { log.error("初始化网关路由时发生错误",e); } dynamicRouteByNacosListener(GatewayConfig.NACOS_ROUTE_DATA_ID,GatewayConfig.NACOS_ROUTE_GROUP); }
public void dynamicRouteByNacosListener (String dataId, String group){ try { configService.addListener(dataId, group, new Listener() { @Override public void receiveConfigInfo(String configInfo) { log.info("进行网关更新:\n\r{}",configInfo); List<RouteDefinition> definitionList = JSON.parseArray(configInfo, RouteDefinition.class); log.info("update route : {}",definitionList.toString()); dynamicRouteService.updateList(definitionList); } @Override public Executor getExecutor() { log.info("getExecutor\n\r"); return null; } }); } catch (NacosException e) { log.error("从nacos接收动态路由配置出错!!!",e); } }
private ConfigService initConfigService(){ try{ Properties properties = new Properties(); properties.setProperty("serverAddr",GatewayConfig.NACOS_SERVER_ADDR); properties.setProperty("namespace",GatewayConfig.NACOS_NAMESPACE); return configService= NacosFactory.createConfigService(properties); } catch (Exception e) { log.error("初始化网关路由时发生错误",e); return null; } } }
|
最后在启动类上添加两个注解
1 2
| @SpringBootApplication @SpringCloudApplication
|
就可以通过网关访问其他模块微服务了
在上图中,我的网关端口为18080,网关路由为api,服务提供者路由为commmunity-sytem。网关访问服务后会自动减去路由访问下一层。
所以通过http://localhost:18080/api/community-system
,这个请求被转发到18082端口的系统管理服务,并最终调用getUserInfo接口返回结果。
参考
Nacos+Spring Cloud Gateway动态路由配置