1. restTemplate是spring实现的,基于restful风格的http请求模板。使用restTemplate可以简化请求操作的复杂性,同时规范了代码风格。
2. restTemplate不加@Loadbalanced注解,会根据url去请求,需要明确域名或者ip地址,如果写的是服务名称,例如:http://SERVICE_ORDER/list, 会报错:java.net.UnknownHostException
3. restTemplate加了@Loadbalanced注解,就会通过loadbalacer去将SERVICE_ORDER解析成相应的ip+端口号,并且实现负载均衡
加下来就来分析,
定义restTemplate的bean
@Bean
@LoadBalanced//追加ribbon负载功能
public RestTemplate createRestTemplate(){
return new RestTemplate();
}
接着来分析@LoadBalanced注解
位于spring-cloud-commons包下
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class) // 有TestTemplate.class才会去加载这个configuration
@ConditionalOnBean(LoadBalancerClient.class) //有loadBalancerClient.class 才会去加载
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
private List restTemplates = Collections.emptyList();
@Autowired(required = false)
private List transformers = Collections.emptyList();
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
// 构建loadBalancerInterceptor拦截器
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List list = new ArrayList<>(
restTemplate.getInterceptors());
// 将LoadBalancerInterceptor保存到restTemplate的拦截器中
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
...
}
如果加了@LoadBalanced注解,就会往restTemplate里添加一个拦截器LoadBalancerInterceptor
以restTemplate.getForObject()为例
@Override
@Nullable
public T getForObject(String url, Class responseType, Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
@Override
@Nullable
public T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor responseExtractor, Object... uriVariables) throws RestClientException {
URI expanded = getUriTemplateHandler().expand(url, uriVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
@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;
try {
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
// 加了@LoadBalanced注解之后,request为:InterceptingClientHttpRequest
response = request.execute();
handleResponse(url, method, response);
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
catch (IOException ex) {
String resource = url.toString();
String query = url.getRawQuery();
resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
throw new ResourceAccessException("I/O error on " + method.name() +
" request for \"" + resource + "\": " + ex.getMessage(), ex);
}
finally {
if (response != null) {
response.close();
}
}
}
org.springframework.http.client.support.HttpAccessor#createRequest
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
// 创建request,
// 如果restTemplate中有拦截器,factory为:InterceptingClientHttpRequestFactory
// 没有拦截器的话,就是SimpleClientHttpRequestFactory
ClientHttpRequest request = getRequestFactory().createRequest(url, method);
initialize(request);
if (logger.isDebugEnabled()) {
logger.debug("HTTP " + method.name() + " " + url);
}
return request;
}
org.springframework.http.client.support.HttpAccessor#getRequestFactory
private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
public ClientHttpRequestFactory getRequestFactory() {
return this.requestFactory;
}
RestTemplate extends InterceptingHttpAccessor --> abstract class InterceptingHttpAccessor extends HttpAccessor
InterceptingHttpAccessor覆盖了HttpAccessor的getRequestFactory()
org.springframework.http.client.support.InterceptingHttpAccessor#getRequestFactory
@Override
public ClientHttpRequestFactory getRequestFactory() {
// 获取拦截器,如果拦截器为空,就走父类的getRequestFactory,即返回SimpleClientHttpRequestFactory
// 如果拦截器不为空,就走InterceptingClientHttpRequestFactory
List interceptors = getInterceptors();
if (!CollectionUtils.isEmpty(interceptors)) {
ClientHttpRequestFactory factory = this.interceptingRequestFactory;
if (factory == null) {
factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
this.interceptingRequestFactory = factory;
}
return factory;
}
else {
return super.getRequestFactory();
}
}
前面分析了@LoadBalanced注解,会往restTemplate里添加一个拦截器LoadBalancerInterceptor,所以这里返回的是InterceptingClientHttpRequestFactory
org.springframework.http.client.InterceptingClientHttpRequestFactory#createRequest
protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
// 创建的时候,将拦截器传入进去
return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
}
经过分析,加了@LoadBaclanced注解之后,request变成了InterceptingClientHttpRequest,
接下来分析:
response = request.execute();
org.springframework.http.client.AbstractClientHttpRequest#execute
@Override
public final ClientHttpResponse execute() throws IOException {
assertNotExecuted();
ClientHttpResponse result = executeInternal(this.headers);
this.executed = true;
return result;
}
org.springframework.http.client.AbstractBufferingClientHttpRequest#executeInternal(org.springframework.http.HttpHeaders)
@Override
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
byte[] bytes = this.bufferedOutput.toByteArray();
if (headers.getContentLength() < 0) {
headers.setContentLength(bytes.length);
}
ClientHttpResponse result = executeInternal(headers, bytes);
this.bufferedOutput = new ByteArrayOutputStream(0);
return result;
}
org.springframework.http.client.InterceptingClientHttpRequest#executeInternal
protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();
return requestExecution.execute(this, bufferedOutput);
}
org.springframework.http.client.InterceptingClientHttpRequest.InterceptingRequestExecution#execute
@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
// 遍历拦截器,前面分析了@LoadBalanced注解,会往restTemplate里添加一个拦截器LoadBalancerInterceptor
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}
else {
HttpMethod method = request.getMethod();
Assert.state(method != null, "No standard HTTP method");
ClientHttpRequest delegate = 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();
}
}
org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor#intercept
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null,
"Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));
}
通过调用loadBalancer来执行execute()
开始来分析@LoadBalanced注解
spring-cloud-netflix-ribbon:2.2.5-RELEASE
@Configuration
@Conditional({RibbonAutoConfiguration.RibbonClassesConditions.class})
@RibbonClients
@AutoConfigureAfter(
name = {"org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration"}
)
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {
...
@Bean
@ConditionalOnMissingBean({LoadBalancerClient.class})
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(this.springClientFactory());
}
...
}
new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
loadBalancerInterceptor里面的loadBalancer就是这个RibbonLoadBalancerClient了
org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient#execute(java.lang.String, org.springframework.cloud.client.loadbalancer.LoadBalancerRequest
@Override
public T execute(String serviceId, LoadBalancerRequest request)
throws IOException {
return execute(serviceId, request, null);
}
public T execute(String serviceId, LoadBalancerRequest request, Object hint)
throws IOException {
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonServer ribbonServer = new RibbonServer(serviceId, server,
isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
return execute(serviceId, ribbonServer, request);
}
protected ILoadBalancer getLoadBalancer(String serviceId) {
return this.clientFactory.getLoadBalancer(serviceId);
}
public ILoadBalancer getLoadBalancer(String name) {
return getInstance(name, ILoadBalancer.class);
}
@Override
public C getInstance(String name, Class type) {
C instance = super.getInstance(name, type);
if (instance != null) {
return instance;
}
IClientConfig config = getInstance(name, IClientConfig.class);
return instantiateWithConfig(getContext(name), type, config);
}
这里其实就是找ILoadBalancer 的bean
org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration#ribbonLoadBalancer
@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
ServerList serverList, ServerListFilter serverListFilter,
IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
return this.propertiesFactory.get(ILoadBalancer.class, config, name);
}
return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
serverListFilter, serverListUpdater);
}
最终返回的是ZoneAwareLoadBalancer
但是还有一些参数
IClientConfig
org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration#ribbonClientConfig
@Bean
@ConditionalOnMissingBean
public IClientConfig ribbonClientConfig() {
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
config.loadProperties(this.name);
config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
return config;
}
serverList
@Bean
@ConditionalOnMissingBean
@SuppressWarnings("unchecked")
public ServerList ribbonServerList(IClientConfig config) {
if (this.propertiesFactory.isSet(ServerList.class, name)) {
return this.propertiesFactory.get(ServerList.class, config, name);
}
ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
serverList.initWithNiwsConfig(config);
return serverList;
}
serverListFilter
org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration#ribbonServerListFilter
@Bean
@ConditionalOnMissingBean
@SuppressWarnings("unchecked")
public ServerListFilter ribbonServerListFilter(IClientConfig config) {
if (this.propertiesFactory.isSet(ServerListFilter.class, name)) {
return this.propertiesFactory.get(ServerListFilter.class, config, name);
}
ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
filter.initWithNiwsConfig(config);
return filter;
}
rule
org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration#ribbonRule
@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
if (this.propertiesFactory.isSet(IRule.class, name)) {
return this.propertiesFactory.get(IRule.class, config, name);
}
ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
rule.initWithNiwsConfig(config);
return rule;
}
ping
org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration#ribbonPing
@Bean
@ConditionalOnMissingBean
public IPing ribbonPing(IClientConfig config) {
if (this.propertiesFactory.isSet(IPing.class, name)) {
return this.propertiesFactory.get(IPing.class, config, name);
}
return new DummyPing();
}
serverListUpdater
org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration#ribbonServerListUpdater
@Bean
@ConditionalOnMissingBean
public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
return new PollingServerListUpdater(config);
}
但是我这里用的是nacos,
spring-cloud-starter-alibaba-nacos-discovery
覆盖了serverList
com.alibaba.cloud.nacos.ribbon.NacosRibbonClientConfiguration#ribbonServerList
@Bean
@ConditionalOnMissingBean
public ServerList> ribbonServerList(IClientConfig config,
NacosDiscoveryProperties nacosDiscoveryProperties) {
if (this.propertiesFactory.isSet(ServerList.class, config.getClientName())) {
ServerList serverList = this.propertiesFactory.get(ServerList.class, config,
config.getClientName());
return serverList;
}
NacosServerList serverList = new NacosServerList(nacosDiscoveryProperties);
serverList.initWithNiwsConfig(config);
return serverList;
}
继续分析:RibbonLoadBalancerClient.execute()
public T execute(String serviceId, LoadBalancerRequest request, Object hint)
throws IOException {
// ZoneAwareLoadBalancer
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonServer ribbonServer = new RibbonServer(serviceId, server,
isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
return execute(serviceId, ribbonServer, request);
}
// hint = null
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
if (loadBalancer == null) {
return null;
}
// Use 'default' on a null hint, or just pass it on?
return loadBalancer.chooseServer(hint != null ? hint : "default");
}
//ENABLE 默认为true
private static final DynamicBooleanProperty ENABLED = DynamicPropertyFactory.getInstance().getBooleanProperty("ZoneAwareNIWSDiscoveryLoadBalancer.enabled", true);
@Override
public Server chooseServer(Object key) {
// 默认没有分区,所以走if里面的super.chooseServer(key)
if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
logger.debug("Zone aware logic disabled or there is only one zone");
return super.chooseServer(key);
}
// 多个分区的时候
...
}
com.netflix.loadbalancer.BaseLoadBalancer#chooseServer
public Server chooseServer(Object key) {
if (counter == null) {
counter = createCounter();
}
counter.increment();
// rule=ZoneAvoidanceRule
if (rule == null) {
return null;
} else {
try {
return rule.choose(key);
} catch (Exception e) {
logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e);
return null;
}
}
}
之前分析了rule=ZoneAvoidanceRule
ZoneAvoidanceRule extends PredicateBasedRule
com.netflix.loadbalancer.PredicateBasedRule#choose
@Override
public Server choose(Object key) {
// ZoneAwareLoadBalancer
ILoadBalancer lb = getLoadBalancer();
// lb.getAllServers()获取所有的实例列表
Optional server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
if (server.isPresent()) {
return server.get();
} else {
return null;
}
}
DynamicServerListLoadBalancer
com.netflix.loadbalancer.BaseLoadBalancer#getAllServers
@Override
public List getAllServers() {
return Collections.unmodifiableList(allServerList);
}
@Monitor(name = PREFIX + "AllServerList", type = DataSourceType.INFORMATIONAL)
protected volatile ListallServerList = Collections
.synchronizedList(new ArrayList());
allServerList是空的,回到之前初始化ZoneAwareLoadBalancer的时候,传入了一个serverListUpdater
public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
ServerList serverList, ServerListFilter filter,
ServerListUpdater serverListUpdater) {
super(clientConfig, rule, ping);
this.serverListImpl = serverList;
this.filter = filter;
this.serverListUpdater = serverListUpdater;
if (filter instanceof AbstractServerListFilter) {
((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
}
restOfInit(clientConfig);
}
void restOfInit(IClientConfig clientConfig) {
// 默认false
boolean primeConnection = this.isEnablePrimingConnections();
// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
this.setEnablePrimingConnections(false);
// 开启定时任务,更新服务实例列表
// 延迟1s后开始,周期是30s一次
enableAndInitLearnNewServersFeature();
// 立即更新一次服务实例列表
updateListOfServers();
if (primeConnection && this.getPrimeConnections() != null) {
this.getPrimeConnections()
.primeConnections(getReachableServers());
}
this.setEnablePrimingConnections(primeConnection);
LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
}
public void enableAndInitLearnNewServersFeature() {
LOGGER.info("Using serverListUpdater {}", serverListUpdater.getClass().getSimpleName());
// PollingServerListUpdater
serverListUpdater.start(updateAction);
}
com.netflix.loadbalancer.PollingServerListUpdater#start
@Override
public synchronized void start(final UpdateAction updateAction) {
// cas设置状态
if (isActive.compareAndSet(false, true)) {
final Runnable wrapperRunnable = new Runnable() {
@Override
public void run() {
if (!isActive.get()) {
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
return;
}
try {
// 执行更新操作
updateAction.doUpdate();
lastUpdated = System.currentTimeMillis();
} catch (Exception e) {
logger.warn("Failed one update cycle", e);
}
}
};
// 提交定时任务
scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
wrapperRunnable,
// 延迟1s执行,
initialDelayMs,
// 更新周期,默认30s
refreshIntervalMs,
TimeUnit.MILLISECONDS
);
} else {
logger.info("Already active, no-op");
}
}
// updateAction
com.netflix.loadbalancer.DynamicServerListLoadBalancer#updateAction
protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {
@Override
public void doUpdate() {
updateListOfServers();
}
};
com.netflix.loadbalancer.DynamicServerListLoadBalancer#updateListOfServers
public void updateListOfServers() {
List servers = new ArrayList();
if (serverListImpl != null) {
// 通过serverListImpl来获取服务列表
// nacos --> NacosServerList
servers = serverListImpl.getUpdatedListOfServers();
LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
getIdentifier(), servers);
// filter -> ZonePreferenceServerListFilter
if (filter != null) {
servers = filter.getFilteredListOfServers(servers);
LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
getIdentifier(), servers);
}
}
updateAllServerList(servers);
}
com.alibaba.cloud.nacos.ribbon.NacosServerList#getUpdatedListOfServers
@Override
public List getUpdatedListOfServers() {
return getServers();
}
private List getServers() {
try {
// DEFAULT_GROUP
String group = discoveryProperties.getGroup();
// 通过namingServiceInstace来获取serviceId的实例列表
// selectInstances就是nacos的api了,true代表只拉取健康的实例
List instances = discoveryProperties.namingServiceInstance()
.selectInstances(serviceId, group, true);
// 转成nacosServer
return instancesToServerList(instances);
}
catch (Exception e) {
throw new IllegalStateException(
"Can not get service instances from nacos, serviceId=" + serviceId,
e);
}
}
org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter#getFilteredListOfServers
@Override
public List getFilteredListOfServers(List servers) {
// 默认output=servers
List output = super.getFilteredListOfServers(servers);
// 默认zone = null
if (this.zone != null && output.size() == servers.size()) {
List local = new ArrayList<>();
for (Server server : output) {
if (this.zone.equalsIgnoreCase(server.getZone())) {
local.add(server);
}
}
if (!local.isEmpty()) {
return local;
}
}
return output;
}
super.getFilteredListOfServers() --> com.netflix.loadbalancer.ZoneAffinityServerListFilter#getFilteredListOfServers
@Override
public List getFilteredListOfServers(List servers) {
// 默认zone=null, zoneAffinity=false, zoneExclusive=false
if (zone != null && (zoneAffinity || zoneExclusive) && servers !=null && servers.size() > 0){
List filteredServers = Lists.newArrayList(Iterables.filter(
servers, this.zoneAffinityPredicate.getServerOnlyPredicate()));
if (shouldEnableZoneAffinity(filteredServers)) {
return filteredServers;
} else if (zoneAffinity) {
overrideCounter.increment();
}
}
return servers;
}
com.netflix.loadbalancer.DynamicServerListLoadBalancer#updateAllServerList
protected void updateAllServerList(List ls) {
// other threads might be doing this - in which case, we pass
if (serverListUpdateInProgress.compareAndSet(false, true)) {
try {
for (T s : ls) {
// 设置为可用状态
s.setAlive(true); // set so that clients can start using these
// servers right away instead
// of having to wait out the ping cycle.
}
// 保存实例列表
setServersList(ls);
// 检查ping
super.forceQuickPing();
} finally {
serverListUpdateInProgress.set(false);
}
}
}
com.netflix.loadbalancer.DynamicServerListLoadBalancer#setServersList
public void setServersList(List lsrv) {
// 调用父类的方法,主要是复制给allServerList和upServerList
super.setServersList(lsrv);
List serverList = (List) lsrv;
Map> serversInZones = new HashMap>();
for (Server server : serverList) {
// make sure ServerStats is created to avoid creating them on hot
// path
// 主要确保serverStatsCache有server状态的缓存
getLoadBalancerStats().getSingleServerStat(server);
// 接下来就主要是吧server按zone划分一下,但是默认zone=UNKONW
// 所以各个服务都在一个zone下面
String zone = server.getZone();
// 默认UNKNOW
if (zone != null) {
zone = zone.toLowerCase();
List servers = serversInZones.get(zone);
if (servers == null) {
servers = new ArrayList();
serversInZones.put(zone, servers);
}
servers.add(server);
}
}
setServerListForZones(serversInZones);
}
supersetServerList() --> com.netflix.loadbalancer.BaseLoadBalancer#setServersList
public void setServersList(List lsrv) {
// 获取读锁
Lock writeLock = allServerLock.writeLock();
logger.debug("LoadBalancer [{}]: clearing server list (SET op)", name);
ArrayList newServers = new ArrayList();
writeLock.lock();
try {
ArrayList allServers = new ArrayList();
for (Object server : lsrv) {
if (server == null) {
continue;
}
// nacos --> NacosServer extends Server
if (server instanceof String) {
server = new Server((String) server);
}
if (server instanceof Server) {
logger.debug("LoadBalancer [{}]: addServer [{}]", name, ((Server) server).getId());
allServers.add((Server) server);
} else {
throw new IllegalArgumentException(
"Type String or Server expected, instead found:"
+ server.getClass());
}
}
boolean listChanged = false;
if (!allServerList.equals(allServers)) {
listChanged = true;
// changeListeners =[] 默认
if (changeListeners != null && changeListeners.size() > 0) {
List oldList = ImmutableList.copyOf(allServerList);
List newList = ImmutableList.copyOf(allServers);
for (ServerListChangeListener l: changeListeners) {
try {
l.serverListChanged(oldList, newList);
} catch (Exception e) {
logger.error("LoadBalancer [{}]: Error invoking server list change listener", name, e);
}
}
}
}
//默认false
if (isEnablePrimingConnections()) {
for (Server server : allServers) {
if (!allServerList.contains(server)) {
server.setReadyToServe(false);
newServers.add((Server) server);
}
}
if (primeConnections != null) {
primeConnections.primeConnectionsAsync(newServers, this);
}
}
// This will reset readyToServe flag to true on all servers
// regardless whether
// previous priming connections are success or not
// 赋值给 allServerList
allServerList = allServers;
// 默认true
if (canSkipPing()) {
for (Server s : allServerList) {
// 设置服务为健康状态
s.setAlive(true);
}
// 赋值给健康的服务列表
upServerList = allServerList;
} else if (listChanged) {
forceQuickPing();
}
} finally {
// 释放写锁
writeLock.unlock();
}
}
public void forceQuickPing() {
if (canSkipPing()) {
return;
}
logger.debug("LoadBalancer [{}]: forceQuickPing invoking", name);
try {
new Pinger(pingStrategy).runPinger();
} catch (Exception e) {
logger.error("LoadBalancer [{}]: Error running forceQuickPing()", name, e);
}
}
private boolean canSkipPing() {
if (ping == null
|| ping.getClass().getName().equals(DummyPing.class.getName())) {
// default ping, no need to set up timer
return true;
} else {
return false;
}
}
// nacos api
com.alibaba.nacos.client.naming.NacosNamingService#selectInstances(java.lang.String, java.lang.String, boolean)
public List selectInstances(String serviceName, String groupName, boolean healthy) throws NacosException {
// 最后一个true代表订阅
return selectInstances(serviceName, groupName, healthy, true);
}
到这里就完成了服务的定时拉取了,默认30s一次,拉取完,就交给rule进去server的选取了
回到之前的rule.choose()
ZoneAvoidanceRule extends PredicateBasedRule
com.netflix.loadbalancer.PredicateBasedRule#choose
@Override
public Server choose(Object key) {
// ZoneAwareLoadBalancer
ILoadBalancer lb = getLoadBalancer();
// lb.getAllServers()获取所有的实例列表
Optional server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
if (server.isPresent()) {
return server.get();
} else {
return null;
}
}
com.netflix.loadbalancer.ZoneAvoidanceRule#getPredicate
@Override
public AbstractServerPredicate getPredicate() {
return compositePredicate;
}
public ZoneAvoidanceRule() {
super();
ZoneAvoidancePredicate zonePredicate = new ZoneAvoidancePredicate(this);
AvailabilityPredicate availabilityPredicate = new AvailabilityPredicate(this);
compositePredicate = createCompositePredicate(zonePredicate, availabilityPredicate);
}
private CompositePredicate createCompositePredicate(ZoneAvoidancePredicate p1, AvailabilityPredicate p2) {
return CompositePredicate.withPredicates(p1, p2)
.addFallbackPredicate(p2)
.addFallbackPredicate(AbstractServerPredicate.alwaysTrue())
.build();
}
CompositePredicate.chooseRoundRobinAfterFiltering
CompositePredicate extends AbstractServerPredicate
com.netflix.loadbalancer.AbstractServerPredicate#chooseRoundRobinAfterFiltering(java.util.List
public Optional chooseRoundRobinAfterFiltering(List servers, Object loadBalancerKey) {
List eligible = getEligibleServers(servers, loadBalancerKey);
if (eligible.size() == 0) {
return Optional.absent();
}
return Optional.of(eligible.get(incrementAndGetModulo(eligible.size())));
}
com.netflix.loadbalancer.CompositePredicate#getEligibleServers
@Override
public List getEligibleServers(List servers, Object loadBalancerKey) {
List result = super.getEligibleServers(servers, loadBalancerKey);
Iterator i = fallbacks.iterator();
while (!(result.size() >= minimalFilteredServers && result.size() > (int) (servers.size() * minimalFilteredPercentage))
&& i.hasNext()) {
AbstractServerPredicate predicate = i.next();
result = predicate.getEligibleServers(servers, loadBalancerKey);
}
return result;
}
supper.getEligibleServers() --> com.netflix.loadbalancer.AbstractServerPredicate#getEligibleServers(java.util.List
public List getEligibleServers(List servers, Object loadBalancerKey) {
// loadBalancerKey = "default"
if (loadBalancerKey == null) {
return ImmutableList.copyOf(Iterables.filter(servers, this.getServerOnlyPredicate()));
} else {
List results = Lists.newArrayList();
for (Server server: servers) {
//
if (this.apply(new PredicateKey(loadBalancerKey, server))) {
results.add(server);
}
}
return results;
}
}
com.netflix.loadbalancer.CompositePredicate#apply
@Override
public boolean apply(@Nullable PredicateKey input) {
return delegate.apply(input);
}
com.netflix.loadbalancer.CompositePredicate.Builder#Builder(com.netflix.loadbalancer.AbstractServerPredicate...)
Builder(AbstractServerPredicate ...primaryPredicates) {
toBuild = new CompositePredicate();
Predicate chain = Predicates.and(primaryPredicates);
toBuild.delegate = AbstractServerPredicate.ofKeyPredicate(chain);
}
-->com.netflix.loadbalancer.ZoneAvoidanceRule#ZoneAvoidanceRule
public ZoneAvoidanceRule() {
super();
ZoneAvoidancePredicate zonePredicate = new ZoneAvoidancePredicate(this);
AvailabilityPredicate availabilityPredicate = new AvailabilityPredicate(this);
compositePredicate = createCompositePredicate(zonePredicate, availabilityPredicate);
}
com.netflix.loadbalancer.AbstractServerPredicate#ofKeyPredicate
public static AbstractServerPredicate ofKeyPredicate(final Predicate p) {
return new AbstractServerPredicate() {
@Override
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP")
public boolean apply(PredicateKey input) {
return p.apply(input);
}
};
}
com.netflix.loadbalancer.ZoneAvoidancePredicate#apply
public boolean apply(@Nullable PredicateKey input) {
// ENABLE 默认true
if (!ENABLED.get()) {
return true;
}
// zone=UNKONWN
String serverZone = input.getServer().getZone();
if (serverZone == null) {
// there is no zone information from the server, we do not want to filter
// out this server
return true;
}
LoadBalancerStats lbStats = getLBStats();
if (lbStats == null) {
// no stats available, do not filter
return true;
}
// lbStats.getAbailableZones() = ["unknown"]
if (lbStats.getAvailableZones().size() <= 1) {
// only one zone is available, do not filter
return true;
}
//--------------------------应该不会走到这里
Map zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
if (!zoneSnapshot.keySet().contains(serverZone)) {
// The server zone is unknown to the load balancer, do not filter it out
return true;
}
logger.debug("Zone snapshots: {}", zoneSnapshot);
Set availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
logger.debug("Available zones: {}", availableZones);
if (availableZones != null) {
return availableZones.contains(input.getServer().getZone());
} else {
return false;
}
}
com.netflix.loadbalancer.AvailabilityPredicate#apply
public boolean apply(@Nullable PredicateKey input) {
LoadBalancerStats stats = getLBStats();
if (stats == null) {
return true;
}
// shouldSkipServer() = false
return !shouldSkipServer(stats.getSingleServerStat(input.getServer()));
}
private boolean shouldSkipServer(ServerStats stats) {
if ((CIRCUIT_BREAKER_FILTERING.get() && stats.isCircuitBreakerTripped())
|| stats.getActiveRequestsCount() >= activeConnectionsLimit.get()) {
return true;
}
return false;
}
继续看
public Optional chooseRoundRobinAfterFiltering(List servers, Object loadBalancerKey) {
List eligible = getEligibleServers(servers, loadBalancerKey);
if (eligible.size() == 0) {
return Optional.absent();
}
return Optional.of(eligible.get(incrementAndGetModulo(eligible.size())));
}
private int incrementAndGetModulo(int modulo) {
for (;;) {
int current = nextIndex.get();
int next = (current + 1) % modulo;
if (nextIndex.compareAndSet(current, next) && current < modulo)
return current;
}
}
实现了轮询
--------------
接下来ping
public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
ServerList serverList, ServerListFilter filter,
ServerListUpdater serverListUpdater) {
super(clientConfig, rule, ping);
this.serverListImpl = serverList;
this.filter = filter;
this.serverListUpdater = serverListUpdater;
if (filter instanceof AbstractServerListFilter) {
((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
}
restOfInit(clientConfig);
}
super() --> com.netflix.loadbalancer.BaseLoadBalancer#BaseLoadBalancer(com.netflix.client.config.IClientConfig, com.netflix.loadbalancer.IRule, com.netflix.loadbalancer.IPing)
public BaseLoadBalancer(IClientConfig config, IRule rule, IPing ping) {
initWithConfig(config, rule, ping, createLoadBalancerStatsFromConfig(config));
}
void initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping, LoadBalancerStats stats) {
this.config = clientConfig;
String clientName = clientConfig.getClientName();
this.name = clientName;
// ping 周期,默认30s
int pingIntervalTime = Integer.parseInt(""
+ clientConfig.getProperty(
CommonClientConfigKey.NFLoadBalancerPingInterval,
Integer.parseInt("30")));
//
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 ...
setRule(rule);
setPing(ping);
setLoadBalancerStats(stats);
rule.setLoadBalancer(this);
if (ping instanceof AbstractLoadBalancerPing) {
((AbstractLoadBalancerPing) ping).setLoadBalancer(this);
}
logger.info("Client: {} instantiated a LoadBalancer: {}", name, this);
boolean enablePrimeConnections = clientConfig.get(
CommonClientConfigKey.EnablePrimeConnections, DefaultClientConfigImpl.DEFAULT_ENABLE_PRIME_CONNECTIONS);
if (enablePrimeConnections) {
this.setEnablePrimingConnections(true);
PrimeConnections primeConnections = new PrimeConnections(
this.getName(), clientConfig);
this.setPrimeConnections(primeConnections);
}
init();
}
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() {
// 默认ping, 返回true
if (canSkipPing()) {
return;
}
if (lbTimer != null) {
lbTimer.cancel();
}
lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + name,
true);
lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000);
forceQuickPing();
}
到这里,已经执行完getServer(),server已经是一个具体的服务实例了
public T execute(String serviceId, LoadBalancerRequest request, Object hint)
throws IOException {
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonServer ribbonServer = new RibbonServer(serviceId, server,
isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
return execute(serviceId, ribbonServer, request);
}
private ServerIntrospector serverIntrospector(String serviceId) {
ServerIntrospector serverIntrospector = this.clientFactory.getInstance(serviceId,
ServerIntrospector.class);
if (serverIntrospector == null) {
serverIntrospector = new DefaultServerIntrospector();
}
return serverIntrospector;
}
private boolean isSecure(Server server, String serviceId) {
IClientConfig config = this.clientFactory.getClientConfig(serviceId);
ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
return RibbonUtils.isSecure(config, serverIntrospector, server);
}
isSecure()其实是从服务实例instance里面的meta中读取
这里用的是nacos --> NacosServerIntrospector
com.alibaba.cloud.nacos.ribbon.NacosServerIntrospector
public class NacosServerIntrospector extends DefaultServerIntrospector {
@Override
public Map getMetadata(Server server) {
if (server instanceof NacosServer) {
return ((NacosServer) server).getMetadata();
}
return super.getMetadata(server);
}
@Override
public boolean isSecure(Server server) {
if (server instanceof NacosServer) {
return Boolean.valueOf(((NacosServer) server).getMetadata().get("secure"));
}
return super.isSecure(server);
}
}
但是request已经变化了,成了ServiceRequestWrapper,回到拦截器处理的地方
org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor#intercept
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null,
"Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));
}
rquestFactory是LoadBalancerInterceptor构造函数传进来的
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration#loadBalancerRequestFactory
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
}
org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory#createRequest
public LoadBalancerRequest createRequest(
final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) {
return instance -> {
HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance,
this.loadBalancer);
if (this.transformers != null) {
for (LoadBalancerRequestTransformer transformer : this.transformers) {
serviceRequest = transformer.transformRequest(serviceRequest,
instance);
}
}
return execution.execute(serviceRequest, body);
};
}
execution是:org.springframework.http.client.InterceptingClientHttpRequest.InterceptingRequestExecution
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 {
// request --> InterceptingClientHttpRequest
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;
}
request.apply()--->LoadBalancerRequestFactory#createRequest()
-->
return instance -> {
HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance,
this.loadBalancer);
if (this.transformers != null) {
for (LoadBalancerRequestTransformer transformer : this.transformers) {
serviceRequest = transformer.transformRequest(serviceRequest,
instance);
}
}
return execution.execute(serviceRequest, body);
};
--> execution.execute()
org.springframework.http.client.InterceptingClientHttpRequest.InterceptingRequestExecution#execute
@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
// 默认iterator里面就1个LoadBalancerInterceptor
// 并且这个方法在之前就执行过一次,
// iterator.hasNext() = false
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}
else {
HttpMethod method = request.getMethod();
Assert.state(method != null, "No standard HTTP method");
// request-->ServiceRequestWrapper
ClientHttpRequest delegate = 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();
}
}
org.springframework.cloud.client.loadbalancer.ServiceRequestWrapper#getURI
@Override
public URI getURI() {
// loadBalancer --> RibbonLoadBalancerClient
URI uri = this.loadBalancer.reconstructURI(this.instance, getRequest().getURI());
return uri;
}
org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient#reconstructURI
@Override
public URI reconstructURI(ServiceInstance instance, URI original) {
Assert.notNull(instance, "instance can not be null");
String serviceId = instance.getServiceId();
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
URI uri;
Server server;
// instance instanceof RibbonServer == true
if (instance instanceof RibbonServer) {
RibbonServer ribbonServer = (RibbonServer) instance;
server = ribbonServer.getServer();
uri = updateToSecureConnectionIfNeeded(original, ribbonServer);
}
else {
server = new Server(instance.getScheme(), instance.getHost(),
instance.getPort());
IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
uri = updateToSecureConnectionIfNeeded(original, clientConfig,
serverIntrospector, server);
}
return context.reconstructURIWithServer(server, uri);
}
com.netflix.loadbalancer.LoadBalancerContext#reconstructURIWithServer
public URI reconstructURIWithServer(Server server, URI original) {
String host = server.getHost();
int port = server.getPort();
// http/https
String scheme = server.getScheme();
//
if (host.equals(original.getHost())
&& port == original.getPort()
&& scheme == original.getScheme()) {
return original;
}
if (scheme == null) {
scheme = original.getScheme();
}
if (scheme == null) {
scheme = deriveSchemeAndPortFromPartialUri(original).first();
}
try {
StringBuilder sb = new StringBuilder();
sb.append(scheme).append("://");
if (!Strings.isNullOrEmpty(original.getRawUserInfo())) {
sb.append(original.getRawUserInfo()).append("@");
}
sb.append(host);
if (port >= 0) {
sb.append(":").append(port);
}
sb.append(original.getRawPath());
if (!Strings.isNullOrEmpty(original.getRawQuery())) {
sb.append("?").append(original.getRawQuery());
}
if (!Strings.isNullOrEmpty(original.getRawFragment())) {
sb.append("#").append(original.getRawFragment());
}
URI newURI = new URI(sb.toString());
return newURI;
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
到这里就完成了服务名到ip地址的转换。
这里execution会执行两次,这个需要特别注意一下。
nacos 的源码分析请参考文章:https://blog.csdn.net/zxh240651200/category_11544733.html
小结:
loadBalance体系,通过nacos的api获取服务列表,维持在本地里;然后通过serverlistUpater去定时更新一下服务实例列表,默认是30s。