Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Code Analysis of Internal implementation in SpringCloud feign

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly introduces the relevant knowledge of SpringCloud feign internal code analysis, the content is detailed and easy to understand, the operation is simple and fast, and has a certain reference value. I believe you will gain something after reading this SpringCloud feign internal code analysis article. Let's take a look at it.

1. Overview

Feign is used for inter-service calls, and its internal implementation is a * * JDK-HttpURLConnection (Http) * * call containing Ribbon (load balancer). Although the form of the call is similar to RPC, the actual call is Http, which is why Feign is called a pseudo-RPC call.

two。 Code details

1) initialization of BaseLoadBalancer.java configuration

Key functions: 1. Initialize load balancing strategy 2. Initialize the scheduling strategy of service registration list

Void initWithConfig (IClientConfig clientConfig, IRule rule, IPing ping, LoadBalancerStats stats) {. / / every 30s Ping int pingIntervalTime = Integer.parseInt ("" + clientConfig.getProperty (CommonClientConfigKey.NFLoadBalancerPingInterval, Integer.parseInt ("30") / / up to Ping 2s int maxTotalPingTime = Integer.parseInt ("" + clientConfig.getProperty (CommonClientConfigKey.NFLoadBalancerMaxTotalPingTime, Integer.parseInt ("2")) each time; 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. / / set the load balancing rule setRule (rule); / / initialize the service registration list scheduling policy setPing (ping); setLoadBalancerStats (stats); rule.setLoadBalancer (this);.}

2) initialization of load balancing policy

Key functions: 1. Polling policy is used by default

BaseLoadBalancer.java

Public void setRule (IRule rule) {if (rule! = null) {this.rule = rule;} else {/ * default rule * / / default uses polling policy 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 reachableServers = lb.getReachableServers(); List 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; } // 轮询重点算法 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) initialize the scheduling strategy of fetching service registration list

Key functions: 1. Set the polling interval to 30s once

Note: there is no actual Ping here, just the alive service that gets the cached registration list, in order to improve performance

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); / / although the default setting is 10s, during initialization, lbTimer.schedule is set for 30s (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 newUpList = new ArrayList (); final List changedServers = new ArrayList () 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进行调用 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 @Overridepublic 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 command = buildLoadBalancerCommand(request, requestConfig); try { return command.submit( new ServerOperation() { @Override public Observable call(Server server) { // 获取真实访问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 @Overridepublic 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 @Overridepublic 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 真实进行Http调用 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 headers = new LinkedHashMap(); for (Map.Entry 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 >

{stream = connection.getErrorStream ();} else {stream = connection.getInputStream ();} return Response.builder () .status (status) .requests (reason) .headers (headers) .request (request) .body (stream, length) .build ();} this is the end of the article on "SpringCloud feign Internal implementation Code Analysis". Thank you for reading! I believe that everyone has a certain understanding of the knowledge of "SpringCloud feign internal code analysis". If you want to learn more knowledge, you are welcome to follow the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report