本篇實戰所使用Spring有關版本:html
SpringBoot:2.1.7.RELEASEjava
Spring Cloud:Greenwich.SR2react
Spring CLoud Alibaba:2.1.0.RELEASEgit
前面幾篇文章咱們介紹了《Nacos服務註冊與發現》和《Nacos配置管理》,還沒看過的小夥伴們快去看一下,本篇文章是創建在這兩篇文章基礎上的一次實戰。github
在Spring Cloud微服務體系下,經常使用的服務網關有Netflix公司開源的Zuul,還有Spring Cloud團隊本身開源的Spring Cloud Gateway,其中NetFlix公司開源的Zuul版本已經迭代至2.x,可是Spring Cloud並未集成,目前Spring Cloud集成的Spring Cloud Zuul仍是Zuul1.x,這一版的Zuul是基於Servlet
構建的,採用的方案是阻塞式的多線程方案,即一個線程處理一次鏈接請求,這種方式在內部延遲嚴重、設備故障較多狀況下會引發存活的鏈接增多和線程增長的狀況發生。Spring Cloud本身開源的Spring Cloud Gateway則是基於Spring Webflux
來構建的,Spring Webflux
有一個全新的非堵塞的函數式 Reactive Web
框架,能夠用來構建異步的、非堵塞的、事件驅動的服務,在伸縮性方面表現很是好。使用非阻塞API, Websockets獲得支持,而且因爲它與Spring緊密集成,將會獲得更好的開發體驗。web
本文將基於Gateway服務網關來介紹如何使用Nacos的配置功能來實現服務網關動態路由。spring
在開始以前咱們先介紹一下具體實現方式:apache
首先,須要準備一個Nacos服務,我這裏的版本是使用的Nacos v1.1.3,若是不會配置Nacos服務的同窗,請參考以前的文章《Nacos服務中心初探》json
建立工程gateway-nacos-config,工程依賴pom.xml以下:瀏覽器
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.springcloud.alibaba</groupId> <artifactId>gateway-nacos-config</artifactId> <version>0.0.1-SNAPSHOT</version> <name>gateway-nacos-config</name> <description>gateway-nacos-config</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> <spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <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> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
<dependencyManagement>
中需配置spring-cloud-alibaba-dependencies
,它管理了Spring Cloud Alibaba組件的版本依賴。配置文件application.yml以下:
server: port: 8080 spring: application: name: spring-cloud-gateway-server cloud: nacos: discovery: server-addr: 192.168.44.129:8848 management: endpoints: web: exposure: include: '*'
spring.cloud.nacos.discovery.server-addr
:配置爲Nacos服務地址,格式爲ip:port接下來進入核心部分,配置Spring Cloud Gateway動態路由,這裏須要實現一個Spring提供的事件推送接口ApplicationEventPublisherAware
,代碼以下:
@Component public class DynamicRoutingConfig implements ApplicationEventPublisherAware { private final Logger logger = LoggerFactory.getLogger(DynamicRoutingConfig.class); private static final String DATA_ID = "zuul-refresh-dev.json"; private static final String Group = "DEFAULT_GROUP"; @Autowired private RouteDefinitionWriter routeDefinitionWriter; private ApplicationEventPublisher applicationEventPublisher; @Bean public void refreshRouting() throws NacosException { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "192.168.44.129:8848"); properties.put(PropertyKeyConst.NAMESPACE, "8282c713-da90-486a-8438-2a5a212ef44f"); ConfigService configService = NacosFactory.createConfigService(properties); configService.addListener(DATA_ID, Group, new Listener() { @Override public Executor getExecutor() { return null; } @Override public void receiveConfigInfo(String configInfo) { logger.info(configInfo); boolean refreshGatewayRoute = JSONObject.parseObject(configInfo).getBoolean("refreshGatewayRoute"); if (refreshGatewayRoute) { List<RouteEntity> list = JSON.parseArray(JSONObject.parseObject(configInfo).getString("routeList")).toJavaList(RouteEntity.class); for (RouteEntity route : list) { update(assembleRouteDefinition(route)); } } else { logger.info("路由未發生變動"); } } }); } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } /** * 路由更新 * @param routeDefinition * @return */ public void update(RouteDefinition routeDefinition){ try { this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId())); logger.info("路由更新成功"); }catch (Exception e){ logger.error(e.getMessage(), e); } try { routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe(); this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this)); logger.info("路由更新成功"); }catch (Exception e){ logger.error(e.getMessage(), e); } } public RouteDefinition assembleRouteDefinition(RouteEntity routeEntity) { RouteDefinition definition = new RouteDefinition(); // ID definition.setId(routeEntity.getId()); // Predicates List<PredicateDefinition> pdList = new ArrayList<>(); for (PredicateEntity predicateEntity: routeEntity.getPredicates()) { PredicateDefinition predicateDefinition = new PredicateDefinition(); predicateDefinition.setArgs(predicateEntity.getArgs()); predicateDefinition.setName(predicateEntity.getName()); pdList.add(predicateDefinition); } definition.setPredicates(pdList); // Filters List<FilterDefinition> fdList = new ArrayList<>(); for (FilterEntity filterEntity: routeEntity.getFilters()) { FilterDefinition filterDefinition = new FilterDefinition(); filterDefinition.setArgs(filterEntity.getArgs()); filterDefinition.setName(filterEntity.getName()); fdList.add(filterDefinition); } definition.setFilters(fdList); // URI URI uri = UriComponentsBuilder.fromUriString(routeEntity.getUri()).build().toUri(); definition.setUri(uri); return definition; } }
這裏主要介紹一下refreshRouting()
這個方法,這個方法主要負責監聽Nacos的配置變化,這裏先使用參數構建一個ConfigService
,再使用ConfigService
開啓一個監聽,而且在監聽的方法中刷新路由信息。
Nacos配置如圖:
{ "refreshGatewayRoute":false, "routeList":[ { "id":"github_route", "predicates":[ { "name":"Path", "args":{ "_genkey_0":"/meteor1993" } } ], "filters":[ ], "uri":"https://github.com", "order":0 } ] }
配置格式選擇JSON,Data ID和Group與程序中的配置保持一致,注意,我這裏的程序配置了namespace,若是使用默認namespace,能夠不用配置。
這裏配置了一個路由/meteor1993
,直接訪問這個路由會訪問到做者的Github倉庫。
剩餘部分的代碼這裏就不一一展現了,已經上傳至代碼倉庫,有須要的同窗能夠自行取用。
啓動工程,這時是沒有任何路由信息的,打開瀏覽器訪問:http://localhost:8080/meteor1993 ,頁面返回404報錯信息,如圖:
同時,也能夠訪問連接:http://localhost:8080/actuator/gateway/routes ,能夠看到以下打印:
[]
打開在Nacos Server端的UI界面,選擇監聽查詢,選擇namespace爲springclouddev
的欄目,輸入DATA_ID爲zuul-refresh-dev.json
和Group爲DEFAULT_GROUP
,點擊查詢,能夠看到咱們啓動的工程gateway-nacos-config正在監聽Nacos Server端,如圖:
筆者這裏的本地ip爲:192.168.44.1。監聽正常,這時,咱們修改剛纔建立的配置,將裏面的refreshGatewayRoute
修改成true
,以下:
{"refreshGatewayRoute": true, "routeList":[{"id":"github_route","predicates":[{"name":"Path","args":{"_genkey_0":"/meteor1993"}}],"filters":[],"uri":"https://github.com","order":0}]}
點擊發布,能夠看到工程gateway-nacos-config的控制檯打印日誌以下:
2019-09-02 22:09:49.254 INFO 8056 --- [38-2a5a212ef44f] c.s.a.g.config.DynamicRoutingConfig : { "refreshGatewayRoute":true, "routeList":[ { "id":"github_route", "predicates":[ { "name":"Path", "args":{ "_genkey_0":"/meteor1993" } } ], "filters":[ ], "uri":"https://github.com", "order":0 } ] } 2019-09-02 22:09:49.268 INFO 8056 --- [38-2a5a212ef44f] c.s.a.g.config.DynamicRoutingConfig : 路由更新成功
這時,咱們的工程gateway-nacos-config的路由已經更新成功,訪問路徑:http://localhost:8080/actuator/gateway/routes ,能夠看到以下打印:
[{"route_id":"github_route","route_definition":{"id":"github_route","predicates":[{"name":"Path","args":{"_genkey_0":"/meteor1993"}}],"filters":[],"uri":"https://github.com","order":0},"order":0}]
咱們再次在瀏覽器中訪問連接:http://localhost:8080/meteor1993 ,能夠看到頁面正常路由到Github倉庫,如圖:
至此,Nacos動態網關路由就介紹完了,主要運用了服務網關端監聽Nacos配置改變的功能,實現服務網關路由配置動態刷新,同理,咱們也可使用服務網關Zuul來實現基於Nacos的動態路由功能。
基於這個思路,咱們可使用配置中心來實現網關的動態路由,而不是使用服務網關自己自帶的配置文件,這樣每次路由信息變動,無需修改配置文件然後重啓服務。
目前市面上使用比較多的配置中心有攜程開源的Apollo,服務網關還有Spring Cloud Zuul,下一篇文章咱們介紹如何使用Apollo來實現Spring Cloud Zuul的動態路由。
原文出處:https://www.cnblogs.com/babycomeon/p/11450899.html