Spring Cloud Ribbon source parsing

Box Contents

  1. Spring Cloud OpenFeign source parsing
  2. Spring Cloud Ribbon source parsing
  3. Spring Cloud Alibaba Sentinel Source resolve
  4. Spring Cloud Gatway source parsing
  5. Spring Cloud Alibaba Nacos source parsing

Code ready

Dependencies

+------------+              +------------+
|            |              |            |
|            |              |            |
|            |              |            |
|            |              |            |
|  consumer  +------------> |   provider |
|            | RestTemplate |            |
|            |              |            |
|            |              |            |
|            |              |            |
+------------+              +------------+
复制代码

pom-dependent

Join nacos service discovery can be, internal references spring-cloud-ribbonrelated to dependent

<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
复制代码

Call the client

We here in the most simple RestTemplatecall to start usingRibbon

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
	return new RestTemplate();
}

// Controller 使用restTemplate 调用服务提供方接口
ResponseEntity<String> forEntity = restTemplate.getForEntity("http://provider/req", String.class);

复制代码

Source resolve

Create a call interceptor

1. Get all of @LoadBalancedmarkRestTemplate

public class LoadBalancerAutoConfiguration {
	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();
}
复制代码

2. Increase LoadBalancerInterceptorthe processing logic

  • Not introduced spring-retryusing
@Bean
public LoadBalancerInterceptor ribbonInterceptor() {
	return new LoadBalancerInterceptor();
}
复制代码
  • Introduced spring-retryusing
@Bean
@ConditionalOnMissingBean
public RetryLoadBalancerInterceptor ribbonInterceptor() {
	return new RetryLoadBalancerInterceptor();
}
复制代码
  • LoadBalancerInterceptor business logic
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept() {
        final URI originalUri = request.getURI();
        // http://demo-provider/req 截取 demo-provider 服务名称
        String serviceName = originalUri.getHost();

        // 默认注入的 RibbonAutoConfiguration.RibbonLoadBalancerClient
        return this.loadBalancer.execute(serviceName,
                // 创建请求对象
                this.requestFactory.createRequest(request, body, execution));
    }
}
复制代码

Execution interceptor

3. RibbonLoadBalancerClient execution

//RibbonAutoConfiguration默认注入的RibbonLoadBalancerClient
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
  return new RibbonLoadBalancerClient(springClientFactory());
}
复制代码

4.execute execution

public class RibbonLoadBalancerClient implements LoadBalancerClient {
    public <T> T execute(){
        //获取具体的ILoadBalancer实现
        ILoadBalancer loadBalancer = getLoadBalancer(serviceId);

        // 调用ILoadBalancer 实现获取Server
        Server server = getServer(loadBalancer, hint);
        RibbonServer ribbonServer = new RibbonServer(serviceId, server,
                isSecure(server, serviceId),
                serverIntrospector(serviceId).getMetadata(server));

        //获取状态记录器,保存此次选取的server
        RibbonLoadBalancerContext context = this.clientFactory
                .getLoadBalancerContext(serviceId);
        RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
        T returnVal = request.apply(serviceInstance);
        statsRecorder.recordStats(returnVal);
        return returnVal;
    }
}
复制代码

Get ILoadBalancer

5 SpringClientFactory

// bean 工厂生成LoadBalancer 的实现
protected ILoadBalancer getLoadBalancer(String serviceId) {
	return this.springClientFactory.getLoadBalancer(serviceId);
}

// 具体生成逻辑看 RibbonClientConfiguration,这个Bean 只有工厂调用的时候才会创建
@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
	return new ZoneAwareLoadBalancer<>();
}
复制代码

6. Create LoadBalancer dependent elements

name The default implementation effect
IClientConfig DefaultClientConfigImpl ribbon client configuration parameters such as: time-out setting, compression set, etc.
ServerList NacosServerList Examples Example table target services, specific service discovery client implementation
ServerListFilter ZonePreferenceServerListFilter Logic filtering processing for the list of examples ServerList
IRule ZoneAvoidanceRule Server load balancing of selection rules
IPing DummyPing The method of testing services are available to achieve
ServerListUpdater PollingServerListUpdater Achieve operational update for ServerList

Above default implementation reference RibbonClientConfiguration. ZoneAwareLoadBalancer

Examples of access to services

//Server server = getServer(loadBalancer, hint);  4. excute 方法
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
	return loadBalancer.chooseServer(hint != null ? hint : "default");
}
复制代码

7. ZoneAwareLoadBalancer

public class ZoneAwareLoadBalancer{
    public ZoneAwareLoadBalancer() {
        // 调用父类初始化方法。 这里会开启实例维护的定时任务等 (具体解析参考 扩展部分)
        super(clientConfig, rule, ping, serverList, filter, serverListUpdater);
    }
    @Override
    public Server chooseServer(Object key) {
        // 若是使用的 Nacos 服务发现,则没有 Zone 的概念,直接调用父类的实现
        if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
            return super.chooseServer(key);
        }
        // 以下为有 Zone 的概念 例如 Eureka  (具体)
        ...
        return server;
    }
}
复制代码
  • Call the parent class IRuleimplementation choices Server
public Server chooseServer(Object key) {
	return rule.choose(key);
}
复制代码

8.PredicateBasedRule selection rules

public abstract class PredicateBasedRule {
    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
        // 获取断言配置
        Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
        if (server.isPresent()) {
            return server.get();
        } else {
            return null;
        }
    }
}
复制代码

9. ZoneAvoidancePredicate list of services assertion

public class ZoneAvoidancePredicate {
    @Override
    public boolean apply(@Nullable PredicateKey input) {
        if (!ENABLED.get()) {
            return true;
        }
        // 还是获取区域配置,如是使用的 Nacos 直接返回true
        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;
        }
        // 区域高可用判断
        ...
    }
}
复制代码

Extended: ServerList maintenance

Initialization ServerList

In the above created LoadBalancer dependent elements 6 , the ServerListtarget service instance instance table, specific service discovery client implementation. We look at the realization of Nacos

public class NacosServerList extends AbstractServerList<NacosServer> {
    @Override
    public List<NacosServer> getInitialListOfServers() {
        return getServers();
    }

    @Override
    public List<NacosServer> getUpdatedListOfServers() {
        return getServers();
    }

    private List<NacosServer> getServers() {
        String group = discoveryProperties.getGroup();
        //调用nacos-sdk 查询实例列表
        List<Instance> instances = discoveryProperties.namingServiceInstance()
                .selectInstances(serviceId, group, true);
        // 类型转换
        return instancesToServerList(instances);

    }
}
复制代码

Update ServerListUpdater

  • After initializing the update operation by ServerList PollingServerListUpdater
public class PollingServerListUpdater implements ServerListUpdater {
    @Override
    public synchronized void start(final UpdateAction updateAction) {
        // 更新任务 交给updateAction 具体实现
        final Runnable wrapperRunnable = () -> {
            updateAction.doUpdate();
            lastUpdated = System.currentTimeMillis();
        };

        // 开启后台线程定时执行  updateAction
        scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
                wrapperRunnable,
                initialDelayMs,
                refreshIntervalMs,
                TimeUnit.MILLISECONDS
        );
    }
}
复制代码
  • updateAction achieve
public void doUpdate() {
	DynamicServerListLoadBalancer.this.updateListOfServers();
}
复制代码
public class PollingServerListUpdater implements ServerListUpdater {
    public void updateListOfServers() {
        List<T> servers = new ArrayList();
        
        // 调用NacosServiceList 获取全部服务列表
        servers = this.serverListImpl.getUpdatedListOfServers();
        
        // 如果配置实例过滤器在执行过滤
        if (this.filter != null) {
            servers = this.filter.getFilteredListOfServers((List)servers);
        }
        
        // 更新LoadBalancer 服务列表
        this.updateAllServerList((List)servers);
    }
}
复制代码

Extended: Server maintenance status

  • LoadBalancer will trigger the initial construction setupPingTask()
public BaseLoadBalancer() {
  this.name = DEFAULT_NAME;
  this.ping = null;
  setRule(DEFAULT_RULE);
  // 开启ping 检查任务
  setupPingTask();
  lbStats = new LoadBalancerStats(DEFAULT_NAME);
}
复制代码
  • setup ping task
void setupPingTask() {
  // 是否可以ping, 默认的DummyPing 直接 跳过不执行
  if (canSkipPing()) {
    return;
  }
  // 执行PingTask
  lbTimer.schedule(new BaseLoadBalancer.PingTask(), 0, pingIntervalSeconds * 1000);
  // 开启任务
  new BaseLoadBalancer.Pinger(pingStrategy).runPinger();
}
复制代码
  • SerialPingStrategy serial execution logic
// 串行调度执行 Iping 逻辑
private static class SerialPingStrategy implements IPingStrategy {
  @Override
  public boolean[] pingServers(IPing ping, Server[] servers) {
    int numCandidates = servers.length;
    boolean[] results = new boolean[numCandidates];

    for (int i = 0; i < numCandidates; i++) {
      results[i] = false; /* Default answer is DEAD. */
      if (ping != null) {
        results[i] = ping.isAlive(servers[i]);
      }

    }
    return results;
  }
}
复制代码
  • Url judgment call availability
public class PingUrl implements IPing {
    public boolean isAlive(Server server) {
        urlStr = urlStr + server.getId();
        urlStr = urlStr + this.getPingAppendString();
        boolean isAlive = false;
        HttpClient httpClient = new DefaultHttpClient();
        HttpUriRequest getRequest = new HttpGet(urlStr);
        String content = null;

        HttpResponse response = httpClient.execute(getRequest);
        content = EntityUtils.toString(response.getEntity());
        isAlive = response.getStatusLine().getStatusCode() == 200;
        return isAlive;
    }
}
复制代码

Extended: RibbonClient lazy loading process

From the above, Ribbon at the time the request will default to create LoadBalancerthis lazy loading mechanism will lead the service started, the first call to service latency problem, even in the time-out fuse integrated circuit breaker (hystrix), etc. .

To solve this problem, we will configure the load Ribbon of hunger

ribbon:
  eager-load:
    clients:
      - provider
复制代码
  • RibbonApplicationContextInitializer After the service starts automatically call the plant in advance of the need to create a ribbon clients
public class RibbonApplicationContextInitializer
        implements ApplicationListener<ApplicationReadyEvent> {
    private final List<String> clientNames;

    protected void initialize() {
        if (clientNames != null) {
            for (String clientName : clientNames) {
                this.springClientFactory.getContext(clientName);
            }
        }
    }
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        initialize();
    }

}
复制代码

Next Steps

I welcome the attention, behind update Ribbon, Hystrix, Sentinel, Nacosand other components of the source graphic resolution.

Another Note: The above picture material (omnigraffle & Figure billion) in public numbers can JAVA架构日记get

"★★★★★" Based on Spring Boot 2.2, Spring Cloud Hoxton & Alibaba, OAuth2 of RBAC rights management system

Guess you like

Origin juejin.im/post/5e5c598551882549052f49e4