springboot+zuul(一)------實現自定義過濾器、動態路由、動態負載。

Aruze發表於2018-09-14

參考:https://blog.csdn.net/u014091123/article/details/75433656 
https://blog.csdn.net/u013815546/article/details/68944039

Zuul是Netflix開源的微服務閘道器,他的核心是一系列的過濾器,通過這些過濾器我們可以輕鬆的實現服務的訪問認證、限流、路由、負載、熔斷等功能。

基於對已有專案程式碼零侵入的需求,本文沒有將zuul閘道器專案註冊到eureka中心,而是將zuul與springboot結合作為一個獨立的專案進行請求轉發,因此本專案是非spring cloud架構。

開始編寫zuul閘道器專案 
首先,新建一個spring boot專案。加入zuul依賴,開啟@EnableZuulProxy註解。 
pom.xml

1 <dependency>
2     <groupId>org.springframework.cloud</groupId>
3     <artifactId>spring-cloud-starter-zuul</artifactId>
4     <version>1.4.4.RELEASE</version>
5 </dependency>

application.properties

1 server.port=8090
2 eureka.client.enable=false
3 zuul.ribbon.eager-load.enabled=true
4 
5 zuul.SendErrorFilter.post.disable=true

由於後續會使用到動態路由,所以這裡我們並不需要在application.properties中做閘道器地址轉發對映。

SpringBootZuulApplication.java

 1 package com.syher.zuul;
 2 
 3 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 4 import com.syher.zuul.core.zuul.router.PropertiesRouter;
 5 import org.springframework.beans.factory.annotation.Autowired;
 6 import org.springframework.boot.CommandLineRunner;
 7 import org.springframework.boot.SpringApplication;
 8 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 9 import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
10 import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent;
11 import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
12 import org.springframework.context.ApplicationEventPublisher;
13 import org.springframework.context.annotation.ComponentScan;
14 
15 import java.io.File;
16 import java.util.concurrent.Executors;
17 import java.util.concurrent.ScheduledExecutorService;
18 import java.util.concurrent.TimeUnit;
19 
20 /**
21  * @author braska
22  * @date 2018/06/25.
23  **/
24 @EnableAutoConfiguration
25 @EnableZuulProxy
26 @ComponentScan(basePackages = {
27         "com.syher.zuul.core",
28         "com.syher.zuul.service"
29 })
30 public class SpringBootZuulApplication implements CommandLineRunner {
31     @Autowired
32     ApplicationEventPublisher publisher;
33     @Autowired
34     RouteLocator routeLocator;
35 
36     private ScheduledExecutorService executor;
37     private Long lastModified = 0L;
38     private boolean instance = true;
39 
40     public static void main(String[] args) {
41         SpringApplication.run(SpringBootZuulApplication.class, args);
42     }
43 
44     @Override
45     public void run(String... args) throws Exception {
46         executor = Executors.newSingleThreadScheduledExecutor(
47                 new ThreadFactoryBuilder().setNameFormat("properties read.").build()
48         );
49         executor.scheduleWithFixedDelay(() -> publish(), 0, 1, TimeUnit.SECONDS);
50     }
51 
52     private void publish() {
53         if (isPropertiesModified()) {
54             publisher.publishEvent(new RoutesRefreshedEvent(routeLocator));
55         }
56     }
57 
58     private boolean isPropertiesModified() {
59         File file = new File(this.getClass().getClassLoader().getResource(PropertiesRouter.PROPERTIES_FILE).getPath());
60         if (instance) {
61             instance = false;
62             return false;
63         }
64         if (file.lastModified() > lastModified) {
65             lastModified = file.lastModified();
66             return true;
67         }
68         return false;
69     }
70 }

一、自定義過濾器

自定義zuul過濾器比較簡單。我們先講過濾器。 
zuul過濾器分為pre、route、post、error四種型別。作用我就不詳細講了,網上資料一大把。本文主要寫路由前的過濾,即pre型別。 
要自定義一個過濾器,只需要要繼承ZuulFilter,然後指定過濾型別、過濾順序、是否執行這個過濾器、過濾內容就OK了。

為了便於擴充套件,這裡用到了模板模式。 
AbstractZuulFilter.java

 1 package com.syher.zuul.core.zuul.filter;
 2 
 3 import com.netflix.zuul.ZuulFilter;
 4 import com.netflix.zuul.context.RequestContext;
 5 import com.syher.zuul.core.zuul.ContantValue;
 6 
 7 /**
 8  * @author braska
 9  * @date 2018/06/29.
10  **/
11 public abstract class AbstractZuulFilter extends ZuulFilter {
12 
13     protected RequestContext context;
14 
15     @Override
16     public boolean shouldFilter() {
17         RequestContext ctx = RequestContext.getCurrentContext();
18         return (boolean) (ctx.getOrDefault(ContantValue.NEXT_FILTER, true));
19     }
20 
21     @Override
22     public Object run() {
23         context = RequestContext.getCurrentContext();
24         return doRun();
25     }
26 
27     public abstract Object doRun();
28 
29     public Object fail(Integer code, String message) {
30         context.set(ContantValue.NEXT_FILTER, false);
31         context.setSendZuulResponse(false);
32         context.getResponse().setContentType("text/html;charset=UTF-8");
33         context.setResponseStatusCode(code);
34         context.setResponseBody(String.format("{\"result\":\"%s!\"}", message));
35         return null;
36     }
37 
38     public Object success() {
39         context.set(ContantValue.NEXT_FILTER, true);
40         return null;
41     }
42 }

定義preFilter的抽象類,繼承AbstractZuulFilter。指定pre型別,之後所有的pre過濾器都可以繼承這個抽象類。 
AbstractPreZuulFilter.java

 1 package com.syher.zuul.core.zuul.filter.pre;
 2 
 3 import com.syher.zuul.core.zuul.FilterType;
 4 import com.syher.zuul.core.zuul.filter.AbstractZuulFilter;
 5 
 6 /**
 7  * @author braska
 8  * @date 2018/06/29.
 9  **/
10 public abstract class AbstractPreZuulFilter extends AbstractZuulFilter {
11     @Override
12     public String filterType() {
13         return FilterType.pre.name();
14     }
15 }

接著編寫具體一個具體的過濾器,比如限流。 
RateLimiterFilter.java

 1 package com.syher.zuul.core.zuul.filter.pre;
 2 
 3 import com.google.common.util.concurrent.RateLimiter;
 4 import com.syher.zuul.core.zuul.FilterOrder;
 5 import org.slf4j.Logger;
 6 import org.slf4j.LoggerFactory;
 7 
 8 import javax.servlet.http.HttpServletRequest;
 9 
10 /**
11  * @author braska
12  * @date 2018/06/29.
13  **/
14 public class RateLimiterFilter extends AbstractPreZuulFilter {
15 
16     private static final Logger LOGGER = LoggerFactory.getLogger(RateLimiterFilter.class);
17 
18     /**
19      * 每秒允許處理的量是50
20      */
21     RateLimiter rateLimiter = RateLimiter.create(50);
22 
23     @Override
24     public int filterOrder() {
25         return FilterOrder.RATE_LIMITER_ORDER;
26     }
27 
28     @Override
29     public Object doRun() {
30         HttpServletRequest request = context.getRequest();
31         String url = request.getRequestURI();
32         if (rateLimiter.tryAcquire()) {
33             return success();
34         } else {
35             LOGGER.info("rate limit:{}", url);
36             return fail(401, String.format("rate limit:{}", url));
37         }
38     }
39 }

其他型別的過濾器也一樣。建立不同的抽象類,比如AbstractPostZuulFilter,指定filterType,然後具體的postFilter只要繼承該抽象類即可。

最後,將過濾器託管給spring。 
ZuulConfigure.java

 1 package com.syher.zuul.core.config;
 2 
 3 import com.netflix.loadbalancer.IRule;
 4 import com.netflix.zuul.ZuulFilter;
 5 import com.syher.zuul.core.ribbon.ServerLoadBalancerRule;
 6 import com.syher.zuul.core.zuul.filter.pre.RateLimiterFilter;
 7 import com.syher.zuul.core.zuul.filter.pre.TokenAccessFilter;
 8 import com.syher.zuul.core.zuul.filter.pre.UserRightFilter;
 9 import com.syher.zuul.core.zuul.router.PropertiesRouter;
10 import org.springframework.beans.factory.annotation.Autowired;
11 import org.springframework.boot.autoconfigure.web.ServerProperties;
12 import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
13 import org.springframework.context.annotation.Bean;
14 import org.springframework.context.annotation.Configuration;
15 
16 /**
17  * @author braska
18  * @date 2018/07/05.
19  **/
20 @Configuration
21 public class ZuulConfigure {
22 
23     @Autowired
24     ZuulProperties zuulProperties;
25     @Autowired
26     ServerProperties server;
27 
28     /**
29      * 動態路由
30      * @return
31      */
32     @Bean
33     public PropertiesRouter propertiesRouter() {
34         return new PropertiesRouter(this.server.getServletPrefix(), this.zuulProperties);
35     }
36 
37     /**
38      * 動態負載
39      * @return
40      */
41     @Bean
42     public IRule loadBalance() {
43         return new ServerLoadBalancerRule();
44     }
45 
46     /**
47      * 自定義過濾器
48      * @return
49      */
50     @Bean
51     public ZuulFilter rateLimiterFilter() {
52         return new RateLimiterFilter();
53     }
54 }

二、動態路由

接著寫動態路由。動態路由需要配置可持久化且能動態重新整理。 
zuul預設使用的路由是SimpleRouteLocator,不具備動態重新整理的效果。DiscoveryClientRouteLocator具備重新整理功能,但是需要已有的專案將服務註冊到eureka,這不符合已有專案程式碼零侵入的需求所以排除。那麼還有個辦法就是自定義路由然後實現RefreshableRouteLocator類。

部分程式碼如下: 
AbstractDynamicRouter.java

 1 package com.syher.zuul.core.zuul.router;
 2 
 3 import com.syher.zuul.core.zuul.entity.BasicRoute;
 4 import org.apache.commons.lang.StringUtils;
 5 import org.slf4j.Logger;
 6 import org.slf4j.LoggerFactory;
 7 import org.springframework.beans.BeanUtils;
 8 import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator;
 9 import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
10 import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
11 
12 import java.util.LinkedHashMap;
13 import java.util.List;
14 import java.util.Map;
15 
16 /**
17  * @author braska
18  * @date 2018/07/02.
19  **/
20 public abstract class AbstractDynamicRouter extends SimpleRouteLocator implements RefreshableRouteLocator {
21 
22     private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDynamicRouter.class);
23 
24     public AbstractDynamicRouter(String servletPath, ZuulProperties properties) {
25         super(servletPath, properties);
26     }
27 
28     @Override
29     public void refresh() {
30         doRefresh();
31     }
32 
33     @Override
34     protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
35         LinkedHashMap<String, ZuulProperties.ZuulRoute> routes = new LinkedHashMap<String, ZuulProperties.ZuulRoute>();
36         routes.putAll(super.locateRoutes());
37 
38         List<BasicRoute> results = readRoutes();
39 
40         for (BasicRoute result : results) {
41             if (StringUtils.isEmpty(result.getPath()) ) {
42                 continue;
43             }
44             ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute();
45             try {
46                 BeanUtils.copyProperties(result, zuulRoute);
47             } catch (Exception e) {
48                 LOGGER.error("=============load zuul route info from db with error==============", e);
49             }
50             routes.put(zuulRoute.getPath(), zuulRoute);
51         }
52         return routes;
53     }
54 
55     /**
56      * 讀取路由資訊
57      * @return
58      */
59     protected abstract List<BasicRoute> readRoutes();
60 }

由於本人比較懶。不想每次寫個demo都要重新配置一大堆資料庫資訊。所以本文很多資料比如路由資訊、比如負載策略。要麼寫在文字里面,要麼直接java程式碼構造。 
本demo的路由資訊就是從properties裡面讀取。嗯,繼承AbstractDynamicRouter即可。 
PropertiesRouter.java

 1 package com.syher.zuul.core.zuul.router;
 2 
 3 import com.google.common.collect.Lists;
 4 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 5 import com.syher.zuul.common.Context;
 6 import com.syher.zuul.core.zuul.entity.BasicRoute;
 7 import org.apache.commons.lang.StringUtils;
 8 import org.slf4j.Logger;
 9 import org.slf4j.LoggerFactory;
10 import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
11 
12 import java.io.File;
13 import java.io.IOException;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Properties;
18 import java.util.concurrent.Executors;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.stream.Collectors;
21 
22 /**
23  * @author braska
24  * @date 2018/07/02.
25  **/
26 public class PropertiesRouter extends AbstractDynamicRouter {
27 
28     private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesRouter.class);
29     public static final String PROPERTIES_FILE = "router.properties";
30     private static final String ZUUL_ROUTER_PREFIX = "zuul.routes";
31 
32 
33     public PropertiesRouter(String servletPath, ZuulProperties properties) {
34         super(servletPath, properties);
35     }
36 
37     @Override
38     protected List<BasicRoute> readRoutes() {
39         List<BasicRoute> list = Lists.newArrayListWithExpectedSize(3);
40         try {
41             Properties prop = new Properties();
42             prop.load(
43                     this.getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE)
44             );
45 
46             Context context = new Context(new HashMap<>((Map) prop));
47             Map<String, String> data = context.getSubProperties(ZUUL_ROUTER_PREFIX);
48             List<String> ids = data.keySet().stream().map(s -> s.substring(0, s.indexOf("."))).distinct().collect(Collectors.toList());
49             ids.stream().forEach(id -> {
50                 Map<String, String> router = context.getSubProperties(String.join(".", ZUUL_ROUTER_PREFIX, id));
51 
52                 String path = router.get("path");
53                 path = path.startsWith("/") ? path : "/" + path;
54 
55                 String serviceId = router.getOrDefault("serviceId", null);
56                 String url = router.getOrDefault("url", null);
57 
58                 BasicRoute basicRoute = new BasicRoute();
59                 basicRoute.setId(id);
60                 basicRoute.setPath(path);
61                 basicRoute.setUrl(router.getOrDefault("url", null));
62                 basicRoute.setServiceId((StringUtils.isBlank(url) && StringUtils.isBlank(serviceId)) ? id : serviceId);
63                 basicRoute.setRetryable(Boolean.parseBoolean(router.getOrDefault("retry-able", "false")));
64                 basicRoute.setStripPrefix(Boolean.parseBoolean(router.getOrDefault("strip-prefix", "false")));
65                 list.add(basicRoute);
66             });
67         } catch (IOException e) {
68             LOGGER.info("error to read " + PROPERTIES_FILE + " :{}", e);
69         }
70         return list;
71     }
72 }

既然是動態路由實時重新整理,那肯定需要一個定時器定時監控properties檔案。所以我在啟動類SpringBootZuulApplication加了個定時器監控properties是否發生過變更(之前有疑問的現在可以解惑了)。一旦檔案被修改過就重新發布一下, 然後會觸發routeLocator的refresh方法。

1 public void publish() {
2         if (isPropertiesModified()) {
3             publisher.publishEvent(new RoutesRefreshedEvent(routeLocator));
4         }
5     }

當然,如果是從資料庫或者其他地方比如redis讀取就不需要用到定時器,只要在增刪改的時候直接publish就好了。

最後,記得PropertiesRouter類交由spring託管(在ZuulConfigure類中配置bean)。

router.properties檔案:

1 zuul.routes.dashboard.path=/**
2 zuul.routes.dashboard.strip-prefix=true
3 
4 ##不使用動態負載需指定url
5 ##zuul.routes.dashboard.url=http://localhost:9000/
6 ##zuul服務部署後,動態增加閘道器對映,無需重啟即可實時路由到新的閘道器
7 ##zuul.routes.baidu.path=/**

三、動態負載

負載也算比較簡單,複雜點的是寫負載演算法。 
動態負載主要分兩個步驟: 
1、根據閘道器專案配置的host和port去資料庫(我是java直接造的資料)查詢負載策略,比如輪詢、比如隨機、比如iphash等等。 
2、根據策略結合每臺伺服器分配的權重選出合適的服務。

實現動態負載需要自定義rule類然後繼承AbstractLoadBalancerRule類。 
首先看負載策略的選擇: 
ServerLoadBalancerRule.java

 1 package com.syher.zuul.core.ribbon;
 2 
 3 import com.google.common.base.Preconditions;
 4 import com.netflix.client.config.IClientConfig;
 5 import com.netflix.loadbalancer.AbstractLoadBalancerRule;
 6 import com.netflix.loadbalancer.ILoadBalancer;
 7 import com.netflix.loadbalancer.Server;
 8 import com.syher.zuul.common.util.SystemUtil;
 9 import com.syher.zuul.core.ribbon.balancer.LoadBalancer;
10 import com.syher.zuul.core.ribbon.balancer.RandomLoadBalancer;
11 import com.syher.zuul.core.ribbon.balancer.RoundLoadBalancer;
12 import com.syher.zuul.entity.GatewayAddress;
13 import com.syher.zuul.service.GatewayService;
14 import org.apache.commons.lang.StringUtils;
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17 import org.springframework.beans.factory.annotation.Autowired;
18 import org.springframework.beans.factory.annotation.Value;
19 
20 /**
21  * @author braska
22  * @date 2018/07/05.
23  **/
24 public class ServerLoadBalancerRule extends AbstractLoadBalancerRule {
25 
26     private static final Logger LOGGER = LoggerFactory.getLogger(ServerLoadBalancerRule.class);
27 
28     @Value("${server.host:127.0.0.1}")
29     private String host;
30     @Value("${server.port:8080}")
31     private Integer port;
32 
33     @Autowired
34     private GatewayService gatewayService;
35 
36     @Override
37     public void initWithNiwsConfig(IClientConfig iClientConfig) {
38     }
39 
40     @Override
41     public Server choose(Object key) {
42         return getServer(getLoadBalancer(), key);
43     }
44 
45     private Server getServer(ILoadBalancer loadBalancer, Object key) {
46         if (StringUtils.isBlank(host)) {
47             host = SystemUtil.ipList().get(0);
48         }
49         //Preconditions.checkArgument(host != null, "server.host must be specify.");
50         //Preconditions.checkArgument(port != null, "server.port must be specify.");
51 
52         GatewayAddress address = gatewayService.getByHostAndPort(host, port);
53         if (address == null) { //這裡的邏輯可以改,找不到閘道器配置資訊可以指定預設的負載策略
54             LOGGER.error(String.format("must be config a gateway info for the server[%s:%s].", host, String.valueOf(port)));
55             return null;
56         }
57 
58         LoadBalancer balancer = LoadBalancerFactory.build(address.getFkStrategyId());
59 
60         return balancer.chooseServer(loadBalancer);
61     }
62 
63     static class LoadBalancerFactory {
64 
65         public static LoadBalancer build(String strategy) {
66             GatewayAddress.StrategyType type = GatewayAddress.StrategyType.of(strategy);
67             switch (type) {
68                 case ROUND:
69                     return new RoundLoadBalancer();
70                 case RANDOM:
71                     return new RandomLoadBalancer();
72                 default:
73                     return null;
74             }
75         }
76     }
77 }

然後是負載演算法介面程式碼。 
LoadBalancer.java

 1 package com.syher.zuul.core.ribbon.balancer;
 2 
 3 import com.netflix.loadbalancer.ILoadBalancer;
 4 import com.netflix.loadbalancer.Server;
 5 
 6 /**
 7  * @author braska
 8  * @date 2018/07/06.
 9  **/
10 public interface LoadBalancer {
11 
12     /**
13      * choose a loadBalancer
14      * @param loadBalancer
15      * @return
16      */
17     Server chooseServer(ILoadBalancer loadBalancer);
18 }

定義抽象類,實現LoadBalancer介面 
AbstractLoadBalancer.java

 1 package com.syher.zuul.core.ribbon.balancer;
 2 
 3 import com.netflix.loadbalancer.ILoadBalancer;
 4 import com.netflix.loadbalancer.Server;
 5 import com.syher.zuul.core.SpringContext;
 6 import com.syher.zuul.service.ServerService;
 7 import org.slf4j.Logger;
 8 import org.slf4j.LoggerFactory;
 9 
10 /**
11  * @author braska
12  * @date 2018/07/06.
13  **/
14 public abstract class AbstractLoadBalancer implements LoadBalancer {
15     private static final Logger LOGGER = LoggerFactory.getLogger(AbstractLoadBalancer.class);
16     protected ServerService serverService;
17 
18     @Override
19     public Server chooseServer(ILoadBalancer loadBalancer) {
20         this.serverService = SpringContext.getBean(ServerService.class);
21         Server server = choose(loadBalancer);
22         if (server != null) {
23             LOGGER.info(String.format("the server[%s:%s] has been select.", server.getHost(), server.getPort()));
24         } else {
25             LOGGER.error("could not find any server.");
26         }
27         return server;
28     }
29 
30     public abstract Server choose(ILoadBalancer loadBalancer);
31 }

輪詢負載演算法 
RoundLoadBalancer.java

 1 package com.syher.zuul.core.ribbon.balancer;
 2 
 3 import com.netflix.loadbalancer.ILoadBalancer;
 4 import com.netflix.loadbalancer.Server;
 5 import com.syher.zuul.common.Constant;
 6 import com.syher.zuul.core.GlobalCache;
 7 import com.syher.zuul.core.ribbon.LoadBalancerRuleUtil;
 8 import com.syher.zuul.entity.ServerAddress;
 9 
10 import java.util.List;
11 
12 /**
13  * 權重輪詢
14  * 首次使用取最大權重的伺服器。而後通過權重的不斷遞減,尋找適合的伺服器。
15  * @author braska
16  * @date 2018/07/06.
17  **/
18 public class RoundLoadBalancer extends AbstractLoadBalancer {
19 
20     private Integer currentServer;
21     private Integer currentWeight;
22     private Integer maxWeight;
23     private Integer gcdWeight;
24 
25     @Override
26     public Server choose(ILoadBalancer loadBalancer) {
27         List<ServerAddress> addressList = serverService.getAvailableServer();
28         if (addressList != null && !addressList.isEmpty()) {
29             maxWeight = LoadBalancerRuleUtil.getMaxWeightForServers(addressList);
30             gcdWeight = LoadBalancerRuleUtil.getGCDForServers(addressList);
31             currentServer = Integer.parseInt(GlobalCache.instance().getOrDefault(Constant.CURRENT_SERVER_KEY, -1).toString());
32             currentWeight = Integer.parseInt(GlobalCache.instance().getOrDefault(Constant.CURRENT_WEIGHT_KEY, 0).toString());
33 
34             Integer serverCount = addressList.size();
35 
36             if (1 == serverCount) {
37                 return new Server(addressList.get(0).getHost(), addressList.get(0).getPort());
38             } else {
39                 while (true) {
40                     currentServer = (currentServer + 1) % serverCount;
41                     if (currentServer == 0) {
42                         currentWeight = currentWeight - gcdWeight;
43                         if (currentWeight <= 0) {
44                             currentWeight = maxWeight;
45                             if (currentWeight == 0) {
46                                 GlobalCache.instance().put(Constant.CURRENT_SERVER_KEY, currentServer);
47                                 GlobalCache.instance().put(Constant.CURRENT_WEIGHT_KEY, currentWeight);
48                                 Thread.yield();
49                                 return null;
50                             }
51                         }
52                     }
53 
54                     ServerAddress address = addressList.get(currentServer);
55                     if (address.getWeight() >= currentWeight) {
56                         GlobalCache.instance().put(Constant.CURRENT_SERVER_KEY, currentServer);
57                         GlobalCache.instance().put(Constant.CURRENT_WEIGHT_KEY, currentWeight);
58                         return new Server(address.getHost(), address.getPort());
59                     }
60                 }
61             }
62 
63         }
64         return null;
65     }
66 }

最後,ServerLoadBalancerRule交由spring託管。

至此,springboot+zuul實現自定義過濾器、動態路由、動態負載就都完成了。 
原始碼:https://github.com/rxiu/study-on-road/tree/master/trickle-gateway

相關文章