Java最全十五,高性能缓存Caffeine原理及实战

架构学习资料

准备两个月,面试五分钟,Java中高级岗面试为何越来越难?

准备两个月,面试五分钟,Java中高级岗面试为何越来越难?

准备两个月,面试五分钟,Java中高级岗面试为何越来越难?

准备两个月,面试五分钟,Java中高级岗面试为何越来越难?

准备两个月,面试五分钟,Java中高级岗面试为何越来越难?

由于篇幅限制小编,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

@Override

public T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException {

Server server = null;

if(serviceInstance instanceof RibbonServer) {

server = ((RibbonServer)serviceInstance).getServer();

}

if (server == null) {

throw new IllegalStateException("No instances available for " + serviceId);

}

RibbonLoadBalancerContext context = this.clientFactory

.getLoadBalancerContext(serviceId);

RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

try {

//使用 LoadBalancerRequest 向服务发请求

T returnVal = request.apply(serviceInstance);

statsRecorder.recordStats(returnVal);

return returnVal;

}

// catch IOException and rethrow so RestTemplate behaves correctly

catch (IOException ex) {

statsRecorder.recordStats(ex);

throw ex;

}

catch (Exception ex) {

statsRecorder.recordStats(ex);

ReflectionUtils.rethrowRuntimeException(ex);

}

return null;

}

private ServerIntrospector serverIntrospector(String serviceId) {

ServerIntrospector serverIntrospector = this.clientFactory.getInstance(serviceId,

ServerIntrospector.class);

if (serverIntrospector == null) {

serverIntrospector = new DefaultServerIntrospector();

}

return serverIntrospector;

}

//是否是https请求

private boolean isSecure(Server server, String serviceId) {

IClientConfig config = this.clientFactory.getClientConfig(serviceId);

ServerIntrospector serverIntrospector = serverIntrospector(serviceId);

return RibbonUtils.isSecure(config, serverIntrospector, server);

}

//根据服务ID选择服务

protected Server getServer(String serviceId) {

return getServer(getLoadBalancer(serviceId));

}

//负载均衡器选择服务

protected Server getServer(ILoadBalancer loadBalancer) {

if (loadBalancer == null) {

return null;

}

return loadBalancer.chooseServer(“default”); // TODO: better handling of key

}

//根据服务id得到负载均衡器

protected ILoadBalancer getLoadBalancer(String serviceId) {

return this.clientFactory.getLoadBalancer(serviceId);

}

…省略…

解释一下:

  • 这里的ServiceInstance choose(String serviceId)方法的作用是根据ServideId选择一个服务,底层实现是通过LoadBalancer.chooseServer 负载均衡器LoadBalancer来完成的服务的选择的

  • 选择到服务之后调用execute向选择到的服务发起请求,通过LoadBalancerRequest来完成其请求。

RestTemplate的执行流程

RestTmplate发请求时地址 "http://user-server/user/"+id; 中 user-server是当前服务需要调用的目标服务的服务名,那么Ribbon到底是如何实现负载均衡调用的呢?我们可以从这里跟踪一下RestTemplate的执行流程

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {

…省略…

@Nullable

protected T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor responseExtractor) throws RestClientException {

Assert.notNull(url, “URI is required”);

Assert.notNull(method, “HttpMethod is required”);

ClientHttpResponse response = null;

Object var14;

try {

//创建请求对象,使用SimpleClientHttpRequestFactory创建ClientHttpRequest

ClientHttpRequest request = this.createRequest(url, method);

if (requestCallback != null) {

//设置header和body

requestCallback.doWithRequest(request);

}

response = request.execute();

this.handleResponse(url, method, response);

var14 = responseExtractor != null ? responseExtractor.extractData(response) : null;

} catch (IOException var12) {

String resource = url.toString();

String query = url.getRawQuery();

resource = query != null ? resource.substring(0, resource.indexOf(63)) : resource;

throw new ResourceAccessException("I/O error on " + method.name() + " request for “” + resource + “”: " + var12.getMessage(), var12);

} finally {

if (response != null) {

response.close();

}

}

return var14;

}

请求来到RestTemplate#doExecute方法,首选是通过使用SimpleClientHttpRequestFactory根据url和method创建ClientHttpRequest 请求对象,使用的实现是InterceptingClientHttpRequestFactory,然后使用response = request.execute();去执行请求,一路跟踪,请求来到InterceptingClientHttpRequest#executeInternal

class InterceptingClientHttpRequest extends AbstractBufferingClientHttpRequest {

//headers请求头 , bufferedOutput输出内容

protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {

//创建拦截器执行器

InterceptingClientHttpRequest.InterceptingRequestExecution requestExecution = new InterceptingClientHttpRequest.InterceptingRequestExecution();

return requestExecution.execute(this, bufferedOutput);

}

这里通过InterceptingClientHttpRequest.InterceptingRequestExecution() 拦截器执行器去执行请求,请求来到InterceptingClientHttpRequest.InterceptingRequestExecution#execute

private class InterceptingRequestExecution implements ClientHttpRequestExecution {

private final Iterator iterator;

public InterceptingRequestExecution() {

this.iterator = InterceptingClientHttpRequest.this.interceptors.iterator();

}

public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {

if (this.iterator.hasNext()) {

//[重要]这里取到的正是 LoadBalancerInterceptor

ClientHttpRequestInterceptor nextInterceptor = (ClientHttpRequestInterceptor)this.iterator.next();

return nextInterceptor.intercept(request, body, this);

} else {

HttpMethod method = request.getMethod();

Assert.state(method != null, “No standard HTTP method”);

//如果iterator中没有拦截器了,就创建一个ClientHttpRequest去执行请求

ClientHttpRequest delegate = InterceptingClientHttpRequest.this.requestFactory.createRequest(request.getURI(), method);

request.getHeaders().forEach((key, value) -> {

delegate.getHeaders().addAll(key, value);

});

if (body.length > 0) {

if (delegate instanceof StreamingHttpOutputMessage) {

StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage)delegate;

streamingOutputMessage.setBody((outputStream) -> {

StreamUtils.copy(body, outputStream);

});

} else {

StreamUtils.copy(body, delegate.getBody());

}

}

//执行请求

return delegate.execute();

}

}

}

InterceptingRequestExecution 中维护了一个Iterator<ClientHttpRequestInterceptor> iterator;其中LoadBalancerInterceptor 就在该集合中,所以请求来到LoadBalancerInterceptor #intercept(request, body, this); 方法

//负载均衡拦截器

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

//负载均衡客户端[重要]

private LoadBalancerClient loadBalancer;

//负载均衡请求创建工厂

private LoadBalancerRequestFactory requestFactory;

//初始化

public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {

this.loadBalancer = loadBalancer;

this.requestFactory = requestFactory;

}

//初始化

public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {

// for backwards compatibility

this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));

}

//拦截器核心方法【重要】

//request请求对象

//body 内容

@Override

public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,

final ClientHttpRequestExecution execution) throws IOException {

//请求的URL,格式如:http://user-server/user/1 ,user-server是服务名

final URI originalUri = request.getURI();

//URL中的服务名

String serviceName = originalUri.getHost();

Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);

//通过requestFactory.createRequest(request, body, execution)创建LoadBalancerRequest

//然后调用负载均衡器执行请求,参数:服务名,LoadBalancerRequest

return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));

}

}

这里蛮重要的,请求调用了LoadBalancerInterceptor #intercept负载均衡拦截器的拦截方法,获取到URL,从中获取到主机名即调用的服务名(Ribbon客户端服务名),然后使用LoadBalancerRequestFactory 创建了LoadBalancerRequest请求对象,调用loadBalancer#execute 负载均衡器执行请求

ILoadBalancer 选择服务(负载均衡)

请求来到RibbonLoadBalancerClient#execute

@Override

public T execute(String serviceId, LoadBalancerRequest request) throws IOException {

//获取负载均衡器

ILoadBalancer loadBalancer = getLoadBalancer(serviceId);

//loadBalancer选择服务

Server server = getServer(loadBalancer);

if (server == null) {

throw new IllegalStateException("No instances available for " + serviceId);

}

//选择的服务封装成RibbonServer

RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,

serviceId), serverIntrospector(serviceId).getMetadata(server));

//LoadBalancerRequest对服务执行请求

return execute(serviceId, ribbonServer, request);

}

这里就蛮关键了

  • 首选是通过服务名调用getLoadBalancer方法得到负载均衡器

  • 然后getServer(loadBalancer)是通过负载均衡器选择一个服务,底层会使用IRule的算法

  • 然后将服务封装成RibbonServer 对象,交给LoadBalancerRequest去执行请求

这里的负载均衡器默认会走ZoneAwareLoadBalancer,它是通过SpringClientFactory 从Ribbon上下文对象中获取到的负载均衡器对象,关于这个我们在上一章讨论过

public class RibbonLoadBalancerClient implements LoadBalancerClient {

…省略…

private SpringClientFactory clientFactory;

protected ILoadBalancer getLoadBalancer(String serviceId) {

return this.clientFactory.getLoadBalancer(serviceId);

}

而得到ILoadBalancer之后,调用getServer(loadBalancer)方法选择服务,我们跟踪一下

public class RibbonLoadBalancerClient implements LoadBalancerClient {

…省略…

protected Server getServer(ILoadBalancer loadBalancer) {

if (loadBalancer == null) {

return null;

}

//ZoneAwareLoadBalancer#chooseServer

return loadBalancer.chooseServer(“default”); // TODO: better handling of key

}

这里loadBalancer.chooseServer("default");请求来到ZoneAwareLoadBalancer#chooseServer,源码如下:

public class ZoneAwareLoadBalancer extends DynamicServerListLoadBalancer {

…省略…

@Override

public Server chooseServer(Object key) {

if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {

//如果禁用了zone,或者自由一个zone会走这里

logger.debug(“Zone aware logic disabled or there is only one zone”);

return super.chooseServer(key);

}

//下面就是根据zone选择服务了,默认情况下不会走下面

Server server = null;

try {

LoadBalancerStats lbStats = getLoadBalancerStats();

//得到zone快照

Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);

logger.debug(“Zone snapshots: {}”, zoneSnapshot);

if (triggeringLoad == null) {

triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(

“ZoneAwareNIWSDiscoveryLoadBalancer.” + this.getName() + “.triggeringLoadPerServerThreshold”, 0.2d);

}

if (triggeringBlackoutPercentage == null) {

triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(

“ZoneAwareNIWSDiscoveryLoadBalancer.” + this.getName() + “.avoidZoneWithBlackoutPercetage”, 0.99999d);

}

//得到可用的zone

Set availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());

logger.debug(“Available zones: {}”, availableZones);

if (availableZones != null && availableZones.size() < zoneSnapshot.keySet().size()) {

//随机选择区域

String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);

logger.debug(“Zone chosen: {}”, zone);

if (zone != null) {

BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);

//选择服务

server = zoneLoadBalancer.chooseServer(key);

}

}

} catch (Exception e) {

logger.error(“Error choosing server using zone aware logic for load balancer={}”, name, e);

}

if (server != null) {

return server;

} else {

logger.debug(“Zone avoidance logic is not invoked.”);

return super.chooseServer(key);

}

}

这里做了一个判断,如果没有设置zone或者只有一个zone(默认),这里会调用 return super.chooseServer(key);通过父类的BaseLoadBalancer#chooseServer方法选择服务,这也是默认的执行流程,代码走到了BaseLoadBalancer#chooseServer方法中,源码如下

public class BaseLoadBalancer extends AbstractLoadBalancer implements

PrimeConnections.PrimeConnectionListener, IClientConfigAware {

public Server chooseServer(Object key) {

if (counter == null) {

//创建一个计数器

counter = createCounter();

}

//计数器增加

counter.increment();

//如果负载均衡规则为空,返回空

if (rule == null) {

return null;

} else {

try {

//[重要]调用了负载均衡器算法类的choose方法

return rule.choose(key);

} catch (Exception e) {

logger.warn(“LoadBalancer [{}]: Error choosing server for key {}”, name, key, e);

return null;

}

}

}

BaseLoadBalancer #chooseServer方法中调用了IRule#choose方法进行服务的选择服务,IRule有很多是算法策略实现类,默认会走轮询算法,如果有定义负载均衡算法,这里rule.choose调用的就是定义的算法类

这里我打了个端点,跟踪了一下源码发现默认情况下会从BaseLoadBalancer#chooseServer方法中调用PredicateBasedRule#choose ,PredicateBasedRule本身是继承ClientConfigEnabledRoundRobinRule,也就是说PredicateBasedRule是使用的是轮询算法,同时它扩展了Predicate功能,即:提供了服务器过滤逻辑

/**

一个规则,提供了服务器过滤逻辑,具体使用的是AbstractServerPredicate实现过滤功能。 过滤后,服务器从过滤列表中的循环方式返回。

  • A rule which delegates the server filtering logic to an instance of {@link AbstractServerPredicate}.

  • After filtering, a server is returned from filtered list in a round robin fashion.

  • @author awang

*/

public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule {

/**

抽象函数,返回AbstractServerPredicate,用来对服务做过滤的

  • Method that provides an instance of {@link AbstractServerPredicate} to be used by this class.

*/

public abstract AbstractServerPredicate getPredicate();

/**

  • Get a server by calling {@link AbstractServerPredicate#chooseRandomlyAfterFiltering(java.util.List, Object)}.

  • The performance for this method is O(n) where n is number of servers to be filtered.

*/

@Override

public Server choose(Object key) {

//得到负载均衡器

ILoadBalancer lb = getLoadBalancer();

//通过AbstractServerPredicate的chooseRoundRobinAfterFiltering选出具体的服务实例

//AbstractServerPredicate的子类实现的Predicate逻辑来过滤一部分服务实例

//然后在以线性轮询的方式从过滤后的实例中选出一个

Optional server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);

if (server.isPresent()) {

return server.get();

} else {

return null;

}

}

}

这里使用了AbstractServerPredicate#chooseRoundRobinAfterFiltering来选择服务从lb.getAllServers()得到所有的服务作为参数,继续跟踪下去

/**

  • Choose a server in a round robin fashion after the predicate filters a given list of servers and load balancer key.

*/

public Optional chooseRoundRobinAfterFiltering(List servers, Object loadBalancerKey) {

//得到合格的服务列表,主要根据zone做一个过滤

List eligible = getEligibleServers(servers, loadBalancerKey);

if (eligible.size() == 0) {

//没找到合格的服务

return Optional.absent();

}

//以线性轮询的方式合格的服务列表获取一个实例

//incrementAndGetModulo方法会以轮询的方式计算一个下标值

return Optional.of(eligible.get(incrementAndGetModulo(eligible.size())));

}

…省略…

/**

引用于 RoundRobinRule 算法策略 , 轮询

  • Referenced from RoundRobinRule

  • Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.

  • @param modulo The modulo to bound the value of the counter.

  • @return The next value.

*/

//增量和取模实现轮询

private int incrementAndGetModulo(int modulo) {

for (;😉 {

int current = nextIndex.get();

int next = (current + 1) % modulo;

if (nextIndex.compareAndSet(current, next) && current < modulo)

return current;

}

}

这里首先会通过zone过滤出可用的服务列表,然后使用轮询算法选择一个服务返回,到这里选择服务的流程调用

LoadBalancerRequest 执行服务

代码继续回到 RibbonLoadBalancerClient#execute,选择完服务之后,服务被封装成RibbonServer

分享

这次面试我也做了一些总结,确实还有很多要学的东西。相关面试题也做了整理,可以分享给大家,了解一下面试真题,想进大厂的或者想跳槽的小伙伴不妨好好利用时间来学习。学习的脚步一定不能停止!

薪酬缩水,“裸辞”奋战25天三面美团,交叉面却被吊打,我太难了

Spring Cloud实战

薪酬缩水,“裸辞”奋战25天三面美团,交叉面却被吊打,我太难了

Spring Boot实战

薪酬缩水,“裸辞”奋战25天三面美团,交叉面却被吊打,我太难了

面试题整理(性能优化+微服务+并发编程+开源框架+分布式)

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

ulo(int modulo) {

for (;😉 {

int current = nextIndex.get();

int next = (current + 1) % modulo;

if (nextIndex.compareAndSet(current, next) && current < modulo)

return current;

}

}

这里首先会通过zone过滤出可用的服务列表,然后使用轮询算法选择一个服务返回,到这里选择服务的流程调用

LoadBalancerRequest 执行服务

代码继续回到 RibbonLoadBalancerClient#execute,选择完服务之后,服务被封装成RibbonServer

分享

这次面试我也做了一些总结,确实还有很多要学的东西。相关面试题也做了整理,可以分享给大家,了解一下面试真题,想进大厂的或者想跳槽的小伙伴不妨好好利用时间来学习。学习的脚步一定不能停止!

[外链图片转存中…(img-Gt167eFE-1715340248448)]

Spring Cloud实战

[外链图片转存中…(img-5OVpl8SV-1715340248449)]

Spring Boot实战

[外链图片转存中…(img-zsnsSNUU-1715340248449)]

面试题整理(性能优化+微服务+并发编程+开源框架+分布式)

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值