最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring Cloud Feign內(nèi)部實(shí)現(xiàn)代碼細(xì)節(jié)

 更新時(shí)間:2021年05月21日 10:42:04   作者:Javachichi  
Feign 的英文表意為“假裝,偽裝,變形”, 是一個(gè)http請(qǐng)求調(diào)用的輕量級(jí)框架,可以以Java接口注解的方式調(diào)用Http請(qǐng)求,而不用像Java中通過封裝HTTP請(qǐng)求報(bào)文的方式直接調(diào)用。接下來通過本文給大家分享Spring Cloud Feign內(nèi)部實(shí)現(xiàn)代碼細(xì)節(jié),感興趣的朋友一起看看吧

1. 概述

Feign用于服務(wù)間調(diào)用,它的內(nèi)部實(shí)現(xiàn)是一個(gè)包含Ribbon(負(fù)載均衡)的**JDK-HttpURLConnection(Http)**調(diào)用。雖然調(diào)用形式是類似于RPC,但是實(shí)際調(diào)用是Http,這也是為什么Feign被稱為偽RPC調(diào)用的原因。
內(nèi)部調(diào)用過程如下:

在這里插入圖片描述

2. 代碼細(xì)節(jié)

1) BaseLoadBalancer.java配置初始化

重點(diǎn)功能: 1. 初始化負(fù)載均衡策略 2. 初始化取服務(wù)注冊(cè)列表調(diào)度策略

void initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping, LoadBalancerStats stats) {
    ...
    // 每隔30s Ping一次
    int pingIntervalTime = Integer.parseInt(""
            + clientConfig.getProperty(
                    CommonClientConfigKey.NFLoadBalancerPingInterval,
                    Integer.parseInt("30")));
    // 每次最多Ping 2s
    int maxTotalPingTime = Integer.parseInt(""
            + clientConfig.getProperty(
                    CommonClientConfigKey.NFLoadBalancerMaxTotalPingTime,
                    Integer.parseInt("2")));
    setPingInterval(pingIntervalTime);
    setMaxTotalPingTime(maxTotalPingTime);

    // cross associate with each other
    // i.e. Rule,Ping meet your container LB
    // LB, these are your Ping and Rule guys ...
    // 設(shè)置負(fù)載均衡規(guī)則
    setRule(rule);
    // 初始化取服務(wù)注冊(cè)列表調(diào)度策略
    setPing(ping);

    setLoadBalancerStats(stats);
    rule.setLoadBalancer(this);
    ...
}

2) 負(fù)載均衡策略初始化

重點(diǎn)功能: 1. 默認(rèn)使用輪詢策略

BaseLoadBalancer.java

public void setRule(IRule rule) {
    if (rule != null) {
        this.rule = rule;
    } else {
        /* default rule */
        // 默認(rèn)使用輪詢策略
        this.rule = new RoundRobinRule();
    }
    if (this.rule.getLoadBalancer() != this) {
        this.rule.setLoadBalancer(this);
    }
}

RoundRobinRule.java

private AtomicInteger nextServerCyclicCounter;

public Server choose(ILoadBalancer lb, Object key) {
    if (lb == null) {
        log.warn("no load balancer");
        return null;
    }

    Server server = null;
    int count = 0;
    while (server == null && count++ < 10) {
        List<Server> reachableServers = lb.getReachableServers();
        List<Server> allServers = lb.getAllServers();
        int upCount = reachableServers.size();
        int serverCount = allServers.size();

        if ((upCount == 0) || (serverCount == 0)) {
            log.warn("No up servers available from load balancer: " + lb);
            return null;
        }
        // 輪詢重點(diǎn)算法
        int nextServerIndex = incrementAndGetModulo(serverCount);
        server = allServers.get(nextServerIndex);

        if (server == null) {
            /* Transient. */
            Thread.yield();
            continue;
        }

        if (server.isAlive() && (server.isReadyToServe())) {
            return (server);
        }

        // Next.
        server = null;
    }

    if (count >= 10) {
        log.warn("No available alive servers after 10 tries from load balancer: "
                + lb);
    }
    return server;
}

private int incrementAndGetModulo(int modulo) {
    for (;;) {
        int current = nextServerCyclicCounter.get();
        int next = (current + 1) % modulo;
        if (nextServerCyclicCounter.compareAndSet(current, next))
            return next;
    }
}

3) 初始化取服務(wù)注冊(cè)列表調(diào)度策略

重點(diǎn)功能: 1. 設(shè)置輪詢間隔為30s 一次

注意: 這里沒有做實(shí)際的Ping,只是獲取緩存的注冊(cè)列表的alive服務(wù),原因是為了提高性能

BaseLoadBalancer.java

public void setPing(IPing ping) {
    if (ping != null) {
        if (!ping.equals(this.ping)) {
            this.ping = ping;
            setupPingTask(); // since ping data changed
        }
    } else {
        this.ping = null;
        // cancel the timer task
        lbTimer.cancel();
    }
}

void setupPingTask() {
    if (canSkipPing()) {
        return;
    }
    if (lbTimer != null) {
        lbTimer.cancel();
    }
    lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + name,
            true);
    // 這里雖然默認(rèn)設(shè)置是10s一次,但是在初始化的時(shí)候,設(shè)置了30s一次
    lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000);
    forceQuickPing();
}

class Pinger {

    private final IPingStrategy pingerStrategy;

    public Pinger(IPingStrategy pingerStrategy) {
        this.pingerStrategy = pingerStrategy;
    }

    public void runPinger() throws Exception {
        if (!pingInProgress.compareAndSet(false, true)) { 
            return; // Ping in progress - nothing to do
        }
        
        // we are "in" - we get to Ping

        Server[] allServers = null;
        boolean[] results = null;

        Lock allLock = null;
        Lock upLock = null;

        try {
            /*
             * The readLock should be free unless an addServer operation is
             * going on...
             */
            allLock = allServerLock.readLock();
            allLock.lock();
            allServers = allServerList.toArray(new Server[allServerList.size()]);
            allLock.unlock();

            int numCandidates = allServers.length;
            results = pingerStrategy.pingServers(ping, allServers);

            final List<Server> newUpList = new ArrayList<Server>();
            final List<Server> changedServers = new ArrayList<Server>();

            for (int i = 0; i < numCandidates; i++) {
                boolean isAlive = results[i];
                Server svr = allServers[i];
                boolean oldIsAlive = svr.isAlive();

                svr.setAlive(isAlive);

                if (oldIsAlive != isAlive) {
                    changedServers.add(svr);
                    logger.debug("LoadBalancer [{}]:  Server [{}] status changed to {}", 
                        name, svr.getId(), (isAlive ? "ALIVE" : "DEAD"));
                }

                if (isAlive) {
                    newUpList.add(svr);
                }
            }
            upLock = upServerLock.writeLock();
            upLock.lock();
            upServerList = newUpList;
            upLock.unlock();

            notifyServerStatusChangeListener(changedServers);
        } finally {
            pingInProgress.set(false);
        }
    }
}

private static class SerialPingStrategy implements IPingStrategy {

    @Override
    public boolean[] pingServers(IPing ping, Server[] servers) {
        int numCandidates = servers.length;
        boolean[] results = new boolean[numCandidates];

        logger.debug("LoadBalancer:  PingTask executing [{}] servers configured", numCandidates);

        for (int i = 0; i < numCandidates; i++) {
            results[i] = false; /* Default answer is DEAD. */
            try {
                // NOTE: IFF we were doing a real ping
                // assuming we had a large set of servers (say 15)
                // the logic below will run them serially
                // hence taking 15 times the amount of time it takes
                // to ping each server
                // A better method would be to put this in an executor
                // pool
                // But, at the time of this writing, we dont REALLY
                // use a Real Ping (its mostly in memory eureka call)
                // hence we can afford to simplify this design and run
                // this
                // serially
                if (ping != null) {
                    results[i] = ping.isAlive(servers[i]);
                }
            } catch (Exception e) {
                logger.error("Exception while pinging Server: '{}'", servers[i], e);
            }
        }
        return results;
    }
}

4) 最后拼接完整URL使用JDK-HttpURLConnection進(jìn)行調(diào)用

SynchronousMethodHandler.java(io.github.openfeign:feign-core:10.10.1/feign.SynchronousMethodHandler.java)

Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    Request request = this.targetRequest(template);
    if (this.logLevel != Level.NONE) {
        this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
    }

    long start = System.nanoTime();

    Response response;
    try {
        response = this.client.execute(request, options);
        response = response.toBuilder().request(request).requestTemplate(template).build();
    } catch (IOException var13) {
        if (this.logLevel != Level.NONE) {
            this.logger.logIOException(this.metadata.configKey(), this.logLevel, var13, this.elapsedTime(start));
        }

        throw FeignException.errorExecuting(request, var13);
    }
    ...
}

LoadBalancerFeignClient.java

@Override
public Response execute(Request request, Request.Options options) throws IOException {
    try {
        URI asUri = URI.create(request.url());
        String clientName = asUri.getHost();
        URI uriWithoutHost = cleanUrl(request.url(), clientName);
        FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
                this.delegate, request, uriWithoutHost);

        IClientConfig requestConfig = getClientConfig(options, clientName);
        return lbClient(clientName)
                .executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
    }
    catch (ClientException e) {
        IOException io = findIOException(e);
        if (io != null) {
            throw io;
        }
        throw new RuntimeException(e);
    }
}

AbstractLoadBalancerAwareClient.java

public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
    LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);

    try {
        return command.submit(
            new ServerOperation<T>() {
                @Override
                public Observable<T> call(Server server) {
                    // 獲取真實(shí)訪問URL
                    URI finalUri = reconstructURIWithServer(server, request.getUri());
                    S requestForServer = (S) request.replaceUri(finalUri);
                    try {
                        return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                    } 
                    catch (Exception e) {
                        return Observable.error(e);
                    }
                }
            })
            .toBlocking()
            .single();
    } catch (Exception e) {
        Throwable t = e.getCause();
        if (t instanceof ClientException) {
            throw (ClientException) t;
        } else {
            throw new ClientException(e);
        }
    }
    
}

FeignLoadBalancer.java

@Override
public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
        throws IOException {
    Request.Options options;
    if (configOverride != null) {
        RibbonProperties override = RibbonProperties.from(configOverride);
        options = new Request.Options(override.connectTimeout(this.connectTimeout),
                override.readTimeout(this.readTimeout));
    }
    else {
        options = new Request.Options(this.connectTimeout, this.readTimeout);
    }
    Response response = request.client().execute(request.toRequest(), options);
    return new RibbonResponse(request.getUri(), response);
}

feign.Client.java

@Override
public Response execute(Request request, Options options) throws IOException {
  HttpURLConnection connection = convertAndSend(request, options);
  return convertResponse(connection, request);
}

Response convertResponse(HttpURLConnection connection, Request request) throws IOException {
  // 使用HttpURLConnection 真實(shí)進(jìn)行Http調(diào)用
  int status = connection.getResponseCode();
  String reason = connection.getResponseMessage();

  if (status < 0) {
    throw new IOException(format("Invalid status(%s) executing %s %s", status,
        connection.getRequestMethod(), connection.getURL()));
  }

  Map<String, Collection<String>> headers = new LinkedHashMap<>();
  for (Map.Entry<String, List<String>> field : connection.getHeaderFields().entrySet()) {
    // response message
    if (field.getKey() != null) {
      headers.put(field.getKey(), field.getValue());
    }
  }

  Integer length = connection.getContentLength();
  if (length == -1) {
    length = null;
  }
  InputStream stream;
  if (status >= 400) {
    stream = connection.getErrorStream();
  } else {
    stream = connection.getInputStream();
  }
  return Response.builder()
      .status(status)
      .reason(reason)
      .headers(headers)
      .request(request)
      .body(stream, length)
      .build();
}

拓展干貨閱讀:一線大廠面試題、高并發(fā)等主流技術(shù)資料

以上就是Spring Cloud Feign內(nèi)部實(shí)現(xiàn)代碼細(xì)節(jié)的詳細(xì)內(nèi)容,更多關(guān)于Spring Cloud Feign的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 200行Java代碼編寫一個(gè)計(jì)算器程序

    200行Java代碼編寫一個(gè)計(jì)算器程序

    本篇文章給大家分享的只用200行java代碼,實(shí)現(xiàn)一個(gè)計(jì)算器程序,不僅能夠計(jì)算加減乘除,還能夠匹配小括號(hào)。實(shí)現(xiàn)代碼超簡(jiǎn)單,需要的朋友參考下吧
    2017-12-12
  • Java實(shí)現(xiàn)常見的排序算法的示例代碼

    Java實(shí)現(xiàn)常見的排序算法的示例代碼

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)常見的排序算法(選擇排序、插入排序、希爾排序等)的相關(guān)資料,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-10-10
  • java實(shí)現(xiàn)模擬USB接口的功能

    java實(shí)現(xiàn)模擬USB接口的功能

    本文主要介紹了java實(shí)現(xiàn)模擬USB接口的功能,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Java通過正則表達(dá)式捕獲組中的文本

    Java通過正則表達(dá)式捕獲組中的文本

    這篇文章主要給大家介紹了關(guān)于利用Java如何通過正則表達(dá)式捕獲組中文本的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧下
    2019-09-09
  • SpringBoot的ResponseEntity類返回給前端具體講解

    SpringBoot的ResponseEntity類返回給前端具體講解

    這篇文章主要給大家介紹了關(guān)于SpringBoot的ResponseEntity類返回給前端的相關(guān)資料,ResponseEntity是Spring框架中用于封裝HTTP響應(yīng)的類,可以自定義狀態(tài)碼、響應(yīng)頭和響應(yīng)體,常用于控制器方法中返回特定數(shù)據(jù)的HTTP響應(yīng),需要的朋友可以參考下
    2024-11-11
  • Netty分布式行解碼器邏輯源碼解析

    Netty分布式行解碼器邏輯源碼解析

    這篇文章主要為大家介紹了Netty分布式行解碼器邏輯源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-03-03
  • Java基本數(shù)據(jù)類型與對(duì)應(yīng)的包裝類(動(dòng)力節(jié)點(diǎn)java學(xué)院整理)

    Java基本數(shù)據(jù)類型與對(duì)應(yīng)的包裝類(動(dòng)力節(jié)點(diǎn)java學(xué)院整理)

    Java是面向?qū)ο蟮木幊陶Z言,包裝類的出現(xiàn)更好的體現(xiàn)這一思想,Java語言提供了八種基本類型。六種數(shù)字類型(四個(gè)整數(shù)型,兩個(gè)浮點(diǎn)型),一種字符類型,還有一種布爾型。 下面通過本文給大家詳細(xì)介紹,感興趣的朋友一起學(xué)習(xí)吧
    2017-04-04
  • Java中的遞增i++與++i的實(shí)現(xiàn)原理詳解

    Java中的遞增i++與++i的實(shí)現(xiàn)原理詳解

    這篇文章主要介紹了Java中的i++與++i的實(shí)現(xiàn)原理詳解,在Java中,i++是一種常見的遞增操作符,用于將變量i的值增加1,它是一種簡(jiǎn)潔且方便的方式來實(shí)現(xiàn)循環(huán)和計(jì)數(shù)功能,i++可以用于各種情況,本文來看一下其實(shí)現(xiàn)原理,需要的朋友可以參考下
    2023-10-10
  • java獲取新insert數(shù)據(jù)自增id的實(shí)現(xiàn)方法

    java獲取新insert數(shù)據(jù)自增id的實(shí)現(xiàn)方法

    這篇文章主要介紹了java獲取新insert數(shù)據(jù)自增id的實(shí)現(xiàn)方法,在具體生成id的時(shí)候,我們的操作順序一般是:先在主表中插入記錄,然后獲得自動(dòng)生成的id,以它為基礎(chǔ)插入從表的記錄,需要的朋友可以參考下
    2019-06-06
  • Java編程實(shí)現(xiàn)五子棋人人對(duì)戰(zhàn)代碼示例

    Java編程實(shí)現(xiàn)五子棋人人對(duì)戰(zhàn)代碼示例

    這篇文章主要介紹了Java編程實(shí)現(xiàn)五子棋人人對(duì)戰(zhàn)代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-11-11

最新評(píng)論

高密市| 马鞍山市| 平乡县| 讷河市| 夏河县| 紫金县| 诸暨市| 阿拉尔市| 德格县| 宜城市| 县级市| 吉林市| 邵武市| 饶河县| 陆河县| 嘉峪关市| 科技| 临西县| 绥江县| 张家川| 潞城市| 绥化市| 陵水| 屯门区| 玉树县| 丰城市| 五常市| 安康市| 衡阳市| 临泽县| 佛山市| 新昌县| 枝江市| 东乌珠穆沁旗| 慈溪市| 布尔津县| 彰化市| 乳山市| 恩施市| 富顺县| 南充市|