Box Contents
- Spring Cloud OpenFeign source parsing
- Spring Cloud Ribbon source parsing
- Spring Cloud Alibaba Sentinel Source resolve
- Spring Cloud Gatway source parsing
- Spring Cloud Alibaba Nacos source parsing
Code ready
Dependencies
+------------+ +------------+
| | | |
| | | |
| | | |
| | | |
| consumer +------------> | provider |
| | RestTemplate | |
| | | |
| | | |
| | | |
+------------+ +------------+
复制代码
pom-dependent
Join nacos service discovery can be, internal references spring-cloud-ribbon
related 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 RestTemplate
call 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 @LoadBalanced
markRestTemplate
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
}
复制代码
2. Increase LoadBalancerInterceptor
the processing logic
- Not introduced
spring-retry
using
@Bean
public LoadBalancerInterceptor ribbonInterceptor() {
return new LoadBalancerInterceptor();
}
复制代码
- Introduced
spring-retry
using
@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
IRule
implementation 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 ServerList
target 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 LoadBalancer
this 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
, Nacos
and other components of the source graphic resolution.
Another Note: The above picture material (omnigraffle & Figure billion) in public numbers can JAVA架构日记
get