在以前的 zuul 咱們講了。怎麼去動態的獲取路由。做爲Spring Cloud 親兒子的存在 gateway 不可能不支持動態路由。今天咱們初探一下gateway 的動態路由。java
思考:react
配置中心刷新routes配置信息。路由信息刷新改變。利用事件發佈,利用配置中心完成動態刷新路由。本次改造咱們使用自定義存儲方式達到手動觸發動態路由 InMemoryRouteDefinitionRepository 默認使用。這個類就是把握們當前的全部的路由routes 存儲在內存當中。當服務重啓或者刷新,內存就不復存在。ps: 由於本項目是用nacos 註冊中心也是配置中心。能夠存儲在nacos 配置中內心面。 RouteDefinitionRepository 接口是InMemoryRouteDefinitionRepository 是它的接口類 繼承了 RouteDefinitionLocator 、RouteDefinitionWriter倆個接口。
RouteDefinitionWriter 用來實現路由的添加與刪除。git
基於這個原則咱們在動態添加或者刪除路由的時候,就能夠根據這個接口實現去知足咱們動態的控制路由規則。
RouteDefinitionRepository 這個接口成爲了關鍵點咱們來從新動態路由其實也是基於這個接口來實現github
實體類web
package com.xian.cloud.model; import lombok.Data; /** * @Author: xlr * @Date: Created in 5:10 PM 2019/9/29 */ @Data public class GatewayRoutesEntity { private Long id; private String serviceId; private String uri; private String predicates; private String filters; }
GatewayRoutesService 接口redis
package com.xian.cloud.service; import com.xian.cloud.model.GatewayRoutesEntity; import org.springframework.cloud.gateway.filter.FilterDefinition; import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; import java.util.List; /** * <Description> * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/11/08 16:08 */ public interface GatewayRoutesService { List<GatewayRoutesEntity> findAll() throws Exception; String loadRouteDefinition() throws Exception; GatewayRoutesEntity save(GatewayRoutesEntity gatewayDefine) throws Exception; void deleteById(String id) throws Exception; boolean existsById(String id)throws Exception; List<PredicateDefinition> getPredicateDefinition(String predicates) ; List<FilterDefinition> getFilterDefinition(String filters) ; }
GatewayRoutesService 實現類spring
package com.xian.cloud.service.impl; import com.alibaba.fastjson.JSON; import com.xian.cloud.model.GatewayRoutesEntity; import com.xian.cloud.service.GatewayRoutesService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; import org.springframework.cloud.gateway.filter.FilterDefinition; import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionWriter; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import reactor.core.publisher.Mono; import java.net.URI; import java.util.List; /** * <Description> * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/11/08 16:09 */ @Service @Slf4j public class GatewayRoutesServiceImpl implements GatewayRoutesService { public static final String GATEWAY_DEFINE_LIST_KEY = "gateway_routes_list_key"; @Autowired private RedisTemplate redisTemplate; @Autowired private RouteDefinitionWriter routeDefinitionWriter; private ApplicationEventPublisher publisher; @Override public List <GatewayRoutesEntity> findAll() throws Exception { Long size = redisTemplate.opsForList().size( GATEWAY_DEFINE_LIST_KEY ); List<GatewayRoutesEntity> list = redisTemplate.opsForList().range( GATEWAY_DEFINE_LIST_KEY, 0, size ); return list; } @Override public String loadRouteDefinition() { try { List <GatewayRoutesEntity> gatewayDefineServiceAll = findAll(); if (gatewayDefineServiceAll == null) { return "none route defined"; } for (GatewayRoutesEntity gatewayDefine : gatewayDefineServiceAll) { RouteDefinition definition = new RouteDefinition(); definition.setId( gatewayDefine.getServiceId() ); definition.setUri( new URI( gatewayDefine.getUri() ) ); List <PredicateDefinition> predicateDefinitions = getPredicateDefinition(gatewayDefine.getPredicates()); if (predicateDefinitions != null) { definition.setPredicates( predicateDefinitions ); } List <FilterDefinition> filterDefinitions = getFilterDefinition(gatewayDefine.getFilters()); if (filterDefinitions != null) { definition.setFilters( filterDefinitions ); } routeDefinitionWriter.save( Mono.just( definition ) ).subscribe(); publisher.publishEvent( new RefreshRoutesEvent( this ) ); } return "success"; } catch (Exception e) { e.printStackTrace(); return "failure"; } } /** * 獲取全部的 自定義路由規則 * @param gatewayDefine * @return * @throws Exception */ @Override public GatewayRoutesEntity save(GatewayRoutesEntity gatewayDefine) throws Exception { log.info( "save RouteDefinition : {}",gatewayDefine ); redisTemplate.opsForList().rightPush( GATEWAY_DEFINE_LIST_KEY, gatewayDefine ); return gatewayDefine; } @Override public void deleteById(String id) throws Exception { List <GatewayRoutesEntity> all = findAll(); for (GatewayRoutesEntity gatewayDefine : all) { if(gatewayDefine.getServiceId().equals( id )){ redisTemplate.opsForList().remove( GATEWAY_DEFINE_LIST_KEY,0, gatewayDefine); } } } @Override public boolean existsById(String id) throws Exception { List <GatewayRoutesEntity> all = findAll(); for (GatewayRoutesEntity gatewayDefine : all) { if(gatewayDefine.getServiceId().equals( id )){ return true; } } return false; } @Override public List<PredicateDefinition> getPredicateDefinition(String predicates) { if ( StringUtils.isNotBlank( predicates )) { List<PredicateDefinition> predicateDefinitionList = JSON.parseArray(predicates, PredicateDefinition.class); return predicateDefinitionList; } else { return null; } } @Override public List<FilterDefinition> getFilterDefinition(String filters) { if (StringUtils.isNotBlank( filters )) { List<FilterDefinition> filterDefinitionList = JSON.parseArray(filters, FilterDefinition.class); return filterDefinitionList; } else { return null; } } }
而後咱們在進行 RouteDefinitionLocator 、RouteDefinitionWriter 倆個接口的聲明 在配置文件中數據庫
package com.xian.cloud.config; import com.xian.cloud.repository.GatewayRoutesRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.route.RouteDefinitionLocator; import org.springframework.cloud.gateway.route.RouteDefinitionWriter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; /** * <Description> 動態更新路由 * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/11/08 17:12 */ @Configuration @Slf4j public class GatewayRoutesDefinitionConfig { @Bean RouteDefinitionLocator routeDefinitionLocator(){ return new GatewayRoutesRepository(); } @Bean @Primary RouteDefinitionWriter routeDefinitionWriter(){ return new GatewayRoutesRepository(); } }
RefreshRoutesEvent 事件發佈刷新routes事件,通知網關。 這個事件是gateway 的事件。apache
package com.xian.cloud.event; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.stereotype.Component; /** * <Description> * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/11/08 17:20 */ @Component @Slf4j public class RefreshRouteService implements ApplicationEventPublisherAware { private ApplicationEventPublisher publisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { this.publisher = publisher; } /** * 刷新路由表 */ public void refreshRoutes() { publisher.publishEvent(new RefreshRoutesEvent(this)); } }
而後咱們還差一個手動觸發的接口,建立GatewayRoutesControllerjson
package com.xian.cloud.controller; import com.xian.cloud.event.RefreshRouteService; import com.xian.cloud.model.RestResult; import com.xian.cloud.model.RestResultBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * <Description> * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/11/08 17:18 */ @RestController @RequestMapping("/gateway") public class GatewayRoutesController { @Autowired private RefreshRouteService refreshRouteService; @GetMapping("/refreshRoutes") public RestResult refreshRoutes(){ refreshRouteService.refreshRoutes(); return RestResultBuilder.builder().success().build(); } }
到此爲止,就完成了全部的代碼。
啓動服務
其實還有一種很簡單直接的寫法。參考從新定義 Spring Cloud 實戰中的寫法。
建立事件增長類DynamicRouteServiceImpl
package com.xian.cloud.event; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; 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 reactor.core.publisher.Mono; /** * <Description> * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/11/09 10:40 */ @Slf4j @Service public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware { @Qualifier("routeDefinitionRepositor") @Autowired private RouteDefinitionWriter routeDefinitionWriter; private ApplicationEventPublisher publisher; /** * 添加路由實體類 * @param definition * @return */ public boolean add(RouteDefinition definition){ routeDefinitionWriter.save((Mono<RouteDefinition>) Mono.just(definition).subscribe()); this.publisher.publishEvent(new RefreshRoutesEvent(this)); return true; } /** * * @param definition 路由實體類 * @return */ public boolean update(RouteDefinition definition){ try { routeDefinitionWriter.delete(Mono.just(definition.getId())); }catch (Exception e){ log.error("update 失敗。沒有找到對應的路由ID :{}",definition.getId()); } routeDefinitionWriter.save((Mono<RouteDefinition>) (Mono.just(definition)).subscribe()); this.publisher.publishEvent(new RefreshRoutesEvent(this)); return true; } /** * serviceId * @param id * @return */ public boolean del(String id){ routeDefinitionWriter.delete(Mono.just(id)); this.publisher.publishEvent(new RefreshRoutesEvent(this)); return true; } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher; } }
修改controller 添加三個方法
package com.xian.cloud.controller; import com.xian.cloud.event.DynamicRouteServiceImpl; import com.xian.cloud.event.RefreshRouteService; import com.xian.cloud.model.RestResult; import com.xian.cloud.model.RestResultBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.web.bind.annotation.*; /** * <Description> * * @author xianliru@100tal.com * @version 1.0 * @createDate 2019/11/08 17:18 */ @RestController @RequestMapping("/gateway") public class GatewayRoutesController { @Autowired private RefreshRouteService refreshRouteService; @Autowired private DynamicRouteServiceImpl dynamicRouteService; @GetMapping("/refreshRoutes") public RestResult refreshRoutes(){ refreshRouteService.refreshRoutes(); return RestResultBuilder.builder().success().build(); } /** * * @param definition * @return */ @RequestMapping(value = "routes/add",method = RequestMethod.POST) public RestResult add(@RequestBody RouteDefinition definition){ boolean flag = dynamicRouteService.add(definition); if(flag){ return RestResultBuilder.builder().success().build(); } return RestResultBuilder.builder().failure().build(); } /** * * @param definition * @return */ @RequestMapping(value = "routes/update",method = RequestMethod.POST) public RestResult update(@RequestBody RouteDefinition definition){ boolean flag = dynamicRouteService.add(definition); if(flag){ return RestResultBuilder.builder().success().build(); } return RestResultBuilder.builder().failure().build(); } /** * * @param serviceId * @return */ @RequestMapping(value = "routes/del",method = RequestMethod.POST) public RestResult update(@RequestParam("serviceId") String serviceId){ boolean flag = dynamicRouteService.del(serviceId); if(flag){ return RestResultBuilder.builder().success().build(); } return RestResultBuilder.builder().failure().build(); } }
增刪改。三個接口對外暴露。
以上就是gateway 的動態刷新路由。
動態刷新能知足咱們在服務運維上的管理方便。可是由於這個總體使用的nacos 配置中心作的。這樣作的成本考慮。動態刷新是否是真的有必要這麼作。徹底能夠在配置中心配置而後刷新。就能觸發路由的總體刷新。若是須要本身搭建運維管理中心。有本身的管理體系能夠實現動態路由。
摘自參考 spring cloud 官方文檔
服務器nacos 地址 http://47.99.209.72:8848/nacos
往期地址 spring cloud 文章地址
Spring Cloud Alibaba (nacos 註冊中心搭建)
Spring Cloud Alibaba 使用nacos 註冊中心
Spring Cloud Alibaba nacos 配置中心使用
Spring Cloud alibaba網關 sentinel zuul 四 限流熔斷
Spring Cloud gateway 網關服務二 斷言、過濾器
Spring Cloud gateway 三 自定義過濾器GatewayFilter
如何喜歡能夠關注分享本公衆號。