gateway官網文件解讀(六) 彙總

habazhu1110發表於2020-09-30

終於整完了, 看了兩天,中間還穿插各種面試和會議. 看了我的英語水平...或者說google的英譯漢能力著實可以的.

看完之後有幾個感受.

gateway本身分成三個元件

routes: 路由, 也是最小的顆粒元件

predicates: 斷言, 就是滿足什麼樣的條件

filter: 過濾器, 裡面可以對請求做一些處理

application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - Cookie=mycookie,mycookievalue
        filters:
        - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin

這就是一個最簡單的標準的一個路由裡面有自己的

id(唯一標識),

predicates用來標記哪些請求進來,

filter代表著請求進來以後你要做什麼,

這裡面他提供了大批量的類庫;

包括, 時間, cookie, url, 引數, header, 基本你能想到的東西都可以放到predicates和filter裡面.他也比較希望你用它的類庫.

從1~6 其實他就是在各種介紹他的類庫.....不過我說實話.....太多了,整的我都不想用了,不是僥倖, 純粹習慣問題.

Spring Cloud Gateway Diagram

這幅圖要從上往下讀: 請求進來, 進來以後經過兩個handle,然後經過filter逐層的返回, 應該用的是責任鏈, 這個我還沒有細看,後續去解讀原始碼時候看看.

Example 59. ExampleConfiguration.java

@Bean
public GlobalFilter customFilter() {
    return new CustomGlobalFilter();
}

public class CustomGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("custom global filter");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

我喜歡這個:全域性filter, 裡面我們可以隨意的寫程式碼,各種搞事情,比如jwt. 

這時候有個問題了,如果多個filter他究竟是咋執行的

7.10. Marking An Exchange As Routed

閘道器路由ServerWebExchange之後,通過將gatewayAlreadyRouted新增到交換屬性來將交換標記為“已路由”。 將請求標記為已路由後,其他路由篩選器將不會再次路由請求,實質上會跳過該過濾器。 您可以使用多種便捷方法將交換標記為已路由,或者檢查交換是否已路由。

  • ServerWebExchangeUtils.isAlreadyRouted takes a ServerWebExchange object and checks if it has been “routed”.

  • ServerWebExchangeUtils.setAlreadyRouted takes a ServerWebExchange object and marks it as “routed”

也就是是說一個路由以後他就關閉了.其他的就不執行了,注意這裡說的並不包括全域性的,因為我建立了兩個globalFilter都是執行的.那globalFilter呢,他的執行順序是按照order執行的, 然後每個請求都必須執行.

我目前呢寫了兩個globalFilter, 一個是用來列印日誌的, 另外一個是用來做jwt鑑權的.還有就是直接加一個@Component標籤就行,不用非得宣告一個@Bean,他的意思大概是希望你寫的更加顯式一點. 這個在

17. Developer Guide 導讀更加明顯.

我們看一下他的建議

17.1. Writing Custom Route Predicate Factories 編寫自定義路由工廠

MyRoutePredicateFactory.java

public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<HeaderRoutePredicateFactory.Config> {

    public MyRoutePredicateFactory() {
        super(Config.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        // grab configuration from Config object
        return exchange -> {
            //grab the request
            ServerHttpRequest request = exchange.getRequest();
            //take information from the request to see if it
            //matches configuration.
            return matches(config, request);
        };
    }

    public static class Config {
        //Put the configuration properties for your filter here
    }

}

17.2. Writing Custom GatewayFilter Factories

To write a GatewayFilter, you must implement GatewayFilterFactory. You can extend an abstract class called AbstractGatewayFilterFactory. The following examples show how to do so:

Example 76. PreGatewayFilterFactory.java

public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory<PreGatewayFilterFactory.Config> {

    public PreGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        // grab configuration from Config object
        return (exchange, chain) -> {
            //If you want to build a "pre" filter you need to manipulate the
            //request before calling chain.filter
            ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
            //use builder to manipulate the request
            return chain.filter(exchange.mutate().request(builder.build()).build());
        };
    }

    public static class Config {
        //Put the configuration properties for your filter here
    }

}

PostGatewayFilterFactory.java

public class PostGatewayFilterFactory extends AbstractGatewayFilterFactory<PostGatewayFilterFactory.Config> {

    public PostGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        // grab configuration from Config object
        return (exchange, chain) -> {
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                ServerHttpResponse response = exchange.getResponse();
                //Manipulate the response in some way
            }));
        };
    }

    public static class Config {
        //Put the configuration properties for your filter here
    }

}

他把rotes,prediscates 和filter給分開了.而且可以prefilter和postFilter

下面我們結合我們立下的flag, 看看怎麼搞重定向

現在我想把所有/action/*****的請求都變成/business

Example 8. application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment},/blue/{segment}

巴巴的說了好久了,停了好幾天.因為在忙別的業務.在這裡說一下最終解決方案

cloud:
  gateway:
    locator:
      enabled: true
    default-filters:
      - AddResponseHeader=X-Response-Default-Foo, Default-Bar
    # 服務自動發現,取第一個擷取詞匹配consul
    discovery:
      locator:
        lower-case-service-id: true
        enabled: true #開啟根據微服務名稱自動轉發
        filters:
          - StripPrefix=1
    routes:
    - id: action
      uri: lb://business
      predicates:
      - Path= /action/**

沒錯,就是加了這麼一個路由,這裡加了一個斷言predicates: 所有 /action/ 的請求 都被轉發到註冊中心的 business服務上.

比如http://gateway.com/action/abcServer 會被轉譯為 http://gateway.com/business/action/abcServer

-----------------------------------------------------

上邊解決了路由轉發,我們在這裡在從頭梳理一下我的需求

1.我有一個gateway, 一個consul 和一個business的主專案

2.我有一堆歷史的債務有個叫做static的專案需要被整合到business裡面,原因是裡面就只有兩個介面,一個2B同事搞得,個人感覺就是拿公司的服務玩.

3.我有個php的老專案api.com,原來有公網域名,現在需要整合進geteway,我需要把原有域名也可以正常通過gateway能訪問到服務

4.我需要做一個負載均衡實現灰度

-------------------------------------

1.業務合併,上邊已經解決了

2.現在我們做負載均衡和灰度

那麼我們首先設定一個負載均衡的flag:假設我有兩臺服務, (ip分別是83,84),我這裡是本地用埠91和92代替.91和92都已經註冊到了consul(註冊中心)上面.

  • 實現所有的請求20%在91上, 80%在92上
  • 實現header中所有請求studentId=123的學員請求都在92上
  • 實現header中所有請求按照studentId分群,將20%的比例固定的分配到91上,其他在92上
  • 實現header中url=abc的請求按照studentId分群,將20%的比例固定的分配到91上,其他在92上

回過來我們梳理一下無非就是按照url,studentId兩個維度將流量分配給不同的服務. 好了現在我們需求有了,開始做個設計,

這個明顯的是一個策略的模式,而且策略間應該是有優先順序的.比如一個我們策略一是按照studentId%100<20在91,上策略2是studentId=123的在91上, 1,2明顯是衝突的. 所以涉及到了優先順序.

這個策略明顯是不定長度的, 意思就是說比如url=abc的,後面還有url=bcd的,而且以後還可能有其他的專案.

最後初步設計這裡採用責任鏈的方式,因為最後我們可以很明確的抽象出來幾種規則有幾個共性, 入參是studentId和url,回參是服務地址.

話不多說直接上程式碼:

-----------------------------兜兜轉轉,寫完這個程式碼半個月了才想起來部落格還沒收尾--------------------------

首先yml的配置

#負載均衡
mybalance:
  open : true
  #灰度
  grayscale:
    - order: -129
      id: ver等於2.0.0的queryPort請求,投射到9091,ver不等於2.0.0的queryPort分發到其他服
      ip: 192.168.0.225:9091
      ver: '=2.0.0'
      url: '/statistics/testServer/queryPort'
      exclusive : 'trueUrl'
    - order: -128
      id: ver>=2.4.0的所有請求,投射到9091,ver小於2.4.0分發到其他服
      ip: 192.168.0.225:9092
      ver: '>2.4.0'
      url: '*'
      exclusive : 'trueVer'
    - order: -127
      id: studentId=123的,url=queryPort 投射到9091,其他請求隨機投放
      ip: 192.168.0.225:9091
      studentId: 123
      url: '/statistics/testServer/queryPort2'
      exclusive : trueUrl
    - order: -125
      id: studentId=123的,url=queryPort 投射到9091,其他請求隨機投放
      ip: 192.168.0.225:9091
      studentId: 123
      url: '*'
      exclusive : '*'

其次我們看看這個策略的解析類

@Api("灰度策略")
public class Grayscale {
    @ApiParam("優先順序,值越小優先順序越高,當出現了高優先順序的負載均衡以後低優先順序的就不再執行")
    private Integer order;
    @ApiParam("策略的唯一標識")
    private String id;
    @ApiParam("策略的ip標識,正則  consul中的address  ip或者ip+埠")
    private String ip;
    @ApiParam("header中studentId,正則,如果是*就代表所有")
    private String studentId;
    @ApiParam("請求的url,正則,如果是*就代表所有")
    private String url;
    @ApiParam("強制,true如果找不到對應ip就丟擲異常,false:如果找不到對應的ip就隨機返回一臺 ")
    private boolean enforce=false;
    @ApiParam("權重0~100 當ip 有內容的時候本欄位不生效")
    private Integer weight;
    @ApiParam("排他 如果之前的條件沒有完全命中,那麼就會執行exclusive過濾, 比如 trueStu 代表如果stu判斷是true就會從服務列表摘除, falseStu代表如果stu是false就從列表摘除  ")
    private String exclusive;
    @ApiParam("版本號")
    private String ver;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getIp() {
        return ip;
    }

    public void setIp(String ip) {
        this.ip = ip;
    }

    public String getStudentId() {
        return studentId;
    }

    public void setStudentId(String studentId) {
        this.studentId = studentId;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public boolean isEnforce() {
        return enforce;
    }

    public void setEnforce(boolean enforce) {
        this.enforce = enforce;
    }

    public Integer getWeight() {
        return weight;
    }

    public void setWeight(Integer weight) {
        this.weight = weight;
    }

    public Integer getOrder() {
        return order;
    }

    public void setOrder(Integer order) {
        this.order = order;
    }

    public String getExclusive() {
        return exclusive;
    }

    public void setExclusive(String exclusive) {
        this.exclusive = exclusive;
    }

    public String getVer() {
        return ver;
    }

    public void setVer(String ver) {
        this.ver = ver;
    }

}

 

@Component
@ConditionalOnProperty( matchIfMissing = true ,prefix = "mybalance",name="open",havingValue = "true" )
@Api("負載均衡")
@ConfigurationProperties(prefix = "mybalance")
public class MyBalanceEntity {
    @ApiParam("灰度")
    private List<Grayscale> grayscale;

    @Constructor
    @ApiParam("所有策略")
    public void sort() {
        if (grayscale != null && grayscale.size() > 1) {
            grayscale.sort((g1, g2) -> {
                if (g2.getOrder() > g1.getOrder()) return 1;
                if (g2.getOrder() < g1.getOrder()) return -1;
                return 0;
            });
        }
    }

    public List<Grayscale> getGrayscale() {
        return grayscale;
    }

    public void setGrayscale(List<Grayscale> grayscale) {
        this.grayscale = grayscale;
    }
}

----------------------------------------這個可以看出來策略就是先按照id升序然後相同的按照前後順序

/**
 * https://blog.csdn.net/zhou1124/article/details/103773835
 */
@Api("負載均衡,先執行MyLoadBalancerClientFilter,再執行MyLoadBalanceRule")
@Component
public class MyLoadBalancerClientFilter extends LoadBalancerClientFilter {
    public static ThreadLocal<ServerWebExchange> exchange = new ThreadLocal<>();
    private static Logger log = LoggerFactory.getLogger(DaishuCloudGatewayApplication.class);

    @Autowired
    private MyBalanceEntity myBalanceEntity;
    public MyLoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties) {
        super(loadBalancer, properties);
    }

    @Override
    protected ServiceInstance choose(ServerWebExchange exchange) {
        //如果沒有任何策略就使用
        if(myBalanceEntity==null||myBalanceEntity.getGrayscale()==null||myBalanceEntity.getGrayscale().size()==0){
            return super.choose(exchange);
        }
        //這裡可以拿到web請求的上下文,可以從header中取出來自己定義的資料。
        MyLoadBalancerClientFilter.exchange.set(exchange);
        //獲得真實的請求路徑lb://statistics/testServer/queryPort
        URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
        HttpHeaders httpHeaders = exchange.getRequest().getHeaders();

        BalanceDto balanceDto=new BalanceDto(httpHeaders,uri);
        balanceDto.setHttpHeaders(httpHeaders);
        balanceDto.setUri(uri);

        log.info("步驟1");
        //如果在已有的ThreadLocal中沒有連線
        if (MyLoadBalanceRule.originHost.get() == null) {
            //獲得所屬ip
            List<String> originHostHeader = httpHeaders.get(MyLoadBalanceRule.originHostHeader);
            if (originHostHeader == null || originHostHeader.size() == 0) {
                String host = exchange.getRequest().getURI().getHost();
                //設定請求頭
                exchange.getRequest().mutate().header(MyLoadBalanceRule.originHostHeader, host).build();
                //設定本機地址
                MyLoadBalanceRule.originHost.set(host);
            } else {
                MyLoadBalanceRule.originHost.set(originHostHeader.get(0));
            }
        }
        //開始路由
        if (this.loadBalancer instanceof RibbonLoadBalancerClient) {
            RibbonLoadBalancerClient client = (RibbonLoadBalancerClient) this.loadBalancer;
            String serviceId = ((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost();
            //這裡使用userId做為選擇服務例項的key, 呼叫的是MyLoadBalanceRule的choose, balanceDto 就是那邊接收到的key
            return client.choose(serviceId, balanceDto);
        }
        return super.choose(exchange);
    }

}
@Component
@Api("路由規則")
public class MyLoadBalanceRule extends BestAvailableRule {
    private static Logger log = LoggerFactory.getLogger(DaishuCloudGatewayApplication.class);
    @Autowired
    @Qualifier("grayscaleBalance")
    private Balance grayscaleBalance;

    public static ThreadLocal<String> originHost=new ThreadLocal<>();
    public static  String originHostHeader="originHost";
    @Autowired
    private MyBalanceEntity myBalanceEntity;
    public Server choose(ILoadBalancer lb, Object key) {

        //log.info("步驟2"+key);
        if (lb == null) {
            log.error("MyLoadBalanceRule Exception no load balancer");
            return null;
        }
        if(myBalanceEntity==null||myBalanceEntity.getGrayscale()==null||myBalanceEntity.getGrayscale().size()==0){
            return  grayscaleBalance.loadRandomServer(lb.getReachableServers());
        }
        BalanceDto balanceDto=(BalanceDto) key;
        //consul 上的註冊192.168.0.225:9091 192.168.0.225:9092 consul中服務對應的address專案
        List<Server> reachableServers = lb.getReachableServers();
        if(reachableServers==null ||reachableServers.size()==0){
            log.error("MyLoadBalanceRule Exception 沒有可用的服務");
            return null;
        }
        balanceDto.setReachableServers(reachableServers);
        BalanceContext balanceContext=new BalanceContext(balanceDto);
        //進行負載均衡
        grayscaleBalance.chooseServer(balanceContext);
        //log.info("balanceContext:" + JSONObject.toJSONString(balanceContext));
        return  balanceContext.getServer();

    }
    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        // TODO Auto-generated method stub

    }

}

//這兩個類是負載均衡

先執行

MyLoadBalancerClientFilter

在執行

MyLoadBalanceRule
@Api("負載均衡的抽象規範,暫時沒有其他用途")
public abstract class Balance {
    private static Logger log = LoggerFactory.getLogger(DaishuCloudGatewayApplication.class);
    @ApiParam("返回一臺服務")
    public abstract void chooseServer(BalanceContext balanceContext);
    @ApiParam("隨機返回一臺服務")
    public abstract Server loadRandomServer(List<Server> serverList);


    @ApiParam("隨機返回一臺服務")
    protected void loadRandomServer(final @ApiParam("入參") BalanceContext balanceContext) {
        Server server= loadRandomServer(new ArrayList(balanceContext.getBalanceDto().getReachableServerMap().values()));
        balanceContext.setServer(server);
        balanceContext.setPolicyId("loadRandomServer");
    }

    @ApiParam("根據策略載入服務到context")
    protected void loadServer(final @ApiParam("灰度策略") Grayscale grayscale, final @ApiParam("入參") BalanceContext balanceContext) {
        if (Collections.isEmpty(balanceContext.getBalanceDto().getReachableServerMap())) {
            throw new DsException(10009);
        }
        MatchMap matchMap = new MatchMap();
        //判斷是否命中
        effectiveStu(grayscale, balanceContext, matchMap);
        effectiveUrl(grayscale, balanceContext, matchMap);
        effectiveVer(grayscale, balanceContext, matchMap);
        //命中ip
        if (matchMap.isAllMatch()) {
            //構建context
            buildContextServer(grayscale, balanceContext);
        } else {
            //移除server
            exclusiveContextServer(grayscale, balanceContext, matchMap);
        }
    }

    @ApiParam("移除sever")
    private void exclusiveContextServer(Grayscale grayscale, BalanceContext balanceContext, @ApiParam("匹配結果") MatchMap matchMap) {
        //如果只剩下一臺服務並且是一個有效的移除策略才開始移除
        if (balanceContext.getBalanceDto().getReachableServerMap().size() > 1 && DsStringUtil.isNotEmpty(grayscale.getExclusive()) && !grayscale.getExclusive().equals("*")) {
            String[] exp = grayscale.getExclusive().split(",");
            for (String str : exp) {
                switch (str) {
                    case "falseVer":
                        if (!matchMap.isVerMatch())
                            balanceContext.getBalanceDto().getReachableServerMap().remove(grayscale.getIp());
                        break;
                    case "trueVer":
                        if (matchMap.isVerMatch())
                            balanceContext.getBalanceDto().getReachableServerMap().remove(grayscale.getIp());
                        break;
                    case "trueUrl":
                        if (matchMap.isUrlMatch())
                            balanceContext.getBalanceDto().getReachableServerMap().remove(grayscale.getIp());
                        break;
                    case "falseUrl":
                        if (!matchMap.isUrlMatch())
                            balanceContext.getBalanceDto().getReachableServerMap().remove(grayscale.getIp());
                        break;
                    case "trueStu":
                        if (matchMap.isStuMatch())
                            balanceContext.getBalanceDto().getReachableServerMap().remove(grayscale.getIp());
                        break;
                    case "falseStu":
                        if (!matchMap.isStuMatch())
                            balanceContext.getBalanceDto().getReachableServerMap().remove(grayscale.getIp());
                        break;
                    default:
                }


            }
        }

    }

    @ApiParam("構建context")
    private void buildContextServer(Grayscale grayscale, BalanceContext balanceContext) {
        if (balanceContext.getBalanceDto().getReachableServerMap().get(grayscale.getIp()) != null) {
            balanceContext.setOrder(grayscale.getOrder());
            balanceContext.setPolicyId(grayscale.getId());
            balanceContext.setServer(balanceContext.getBalanceDto().getReachableServerMap().get(grayscale.getIp()));
        } else {
            if (grayscale.isEnforce()) {
                log.info("策略:" + grayscale.getId() + "沒有找到服務==" + grayscale.getIp() + "強制執行失敗");
                throw new DsException(10010, "策略id:" + grayscale.getId());
            } else {
                log.info("策略:" + grayscale.getId() + "沒有找到服務==" + grayscale.getIp() + "跳過策略");
            }
        }
    }

    @ApiParam("匹配版本")
    private void effectiveVer(Grayscale grayscale, BalanceContext balanceContext, MatchMap matchMap) {
        //如果策略有但是header沒有就不通過
        if (effective(grayscale.getVer()) && DsStringUtil.isEmpty(balanceContext.getBalanceDto().getHttpHeaders().getFirst("ver"))) {
            matchMap.setAllMatch(false);
            matchMap.setVerMatch(false);
            return;
        }
        if (effective(grayscale.getVer())) {
            DsHeader dsHeader = new DsHeader(balanceContext.getBalanceDto().getHttpHeaders());
            String ver = grayscale.getVer().replaceAll(">", "").replaceAll("=", "").replaceAll("<", "");
            //判斷版本情況  header內容小於輸入version版本返回-1    0 等於  header內容大於version返回1
            Integer i = dsHeader.afterVer(ver);
            //結果是小於
            if (i == -1 && !grayscale.getVer().startsWith("<")) {
                matchMap.setAllMatch(false);
                matchMap.setVerMatch(false);
            }
            //結果是大於
            if (i == 1 && !grayscale.getVer().startsWith(">")) {
                matchMap.setAllMatch(false);
                matchMap.setVerMatch(false);
            }
            //如果結果相等但是判斷條件是不等於,或者不包含=
            if (i == 0 && (grayscale.getVer().startsWith("!=") || !grayscale.getVer().contains("="))) {
                matchMap.setAllMatch(false);
                matchMap.setVerMatch(false);
            }
        }
    }

    @ApiParam("判斷地址匹配結果")
    private void effectiveUrl(Grayscale grayscale, BalanceContext balanceContext, MatchMap matchMap) {
        //如果需要檢測url
        if (effective(grayscale.getUrl()) && !match(grayscale.getUrl(), balanceContext.getBalanceDto().getUri().toString())) {
            matchMap.setAllMatch(false);
            matchMap.setUrlMatch(false);
        }
    }

    @ApiParam("判斷學生匹配結果")
    private void effectiveStu(Grayscale grayscale, BalanceContext balanceContext, MatchMap matchMap) {
        //如果策略有但是header沒有
        if (effective(grayscale.getStudentId()) && DsStringUtil.isEmpty(balanceContext.getBalanceDto().getStudentId())) {
            matchMap.setAllMatch(false);
            matchMap.setStuMatch(false);
            return;
        }
        if (effective(grayscale.getStudentId()) && !match(grayscale.getStudentId(), balanceContext.getBalanceDto().getStudentId().toString())) {
            matchMap.setAllMatch(false);
            matchMap.setStuMatch(false);
        }
    }


    @ApiParam("是否是有效欄位")
    private boolean effective(String str) {
        if (DsStringUtil.isEmpty(str) || str.trim().equals("*")) {
            return false;
        }
        return true;
    }

    @ApiParam("正規表示式是否匹配")
    private boolean match(@ApiParam("正規表示式") String exp, @ApiParam("內容") String str) {
        // 忽略大小寫的寫法
        Pattern pattern = Pattern.compile(exp, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(str.replaceAll("lb://","/"));
        boolean rs = matcher.matches();
        return rs;
    }

}
@Api("灰度負載均衡")
@Component
public class GrayscaleBalance extends Balance {
    @Autowired
    private MyBalanceEntity myBalanceEntity;

    @Override
    public void chooseServer(BalanceContext balanceContext) {
        if (myBalanceEntity.getGrayscale() != null && myBalanceEntity.getGrayscale().size() > 0) {
            for (Grayscale grayscale : myBalanceEntity.getGrayscale()) {
                //如果還沒有產生有效策略
                if (balanceContext.getServer() == null) {
                    //根據策略載入服務到context
                    super.loadServer(grayscale, balanceContext);
                } else {
                    //因為本身就已經進行過
                    break;
                }
            }
            //如果所有的都執行完了還沒有拿到有效的策略
            if (balanceContext.getServer() == null) {
                super.loadRandomServer(balanceContext);
            }
        }
    }

    @ApiParam("隨機返回一臺服務")
    @Override
    public Server loadRandomServer(List<Server> serverList) {
        Random random = new Random();
        int index = random.nextInt(serverList.size());
        return serverList.get(index);
    }

}

//這兩個類是實際的邏輯

這裡面用了繼承的方式倒不是說必須的,主要是考慮以後萬一有擴充套件

通過這些配置就可以實現負載均衡的灰度, 其實如果需求不這麼複雜的話還是建議用自帶的斷言和filter,或者自帶的ribbon.可讀性更好,效能也高.而且權重什麼的也不用自己去做.主要還是看需求吧.

 

 

 

 

 

 

 

 

 

 

相關文章