봄 클라우드 리본 소스 분석

박스 내용물

  1. 봄 클라우드 OpenFeign 소스 분석
  2. 봄 클라우드 리본 소스 분석
  3. 봄 클라우드 알리바바 센티넬 소스 해결
  4. 봄 클라우드 Gatway 소스 분석
  5. 봄 클라우드 알리바바 Nacos 소스 분석

코드 준비

종속성

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

치어 의존

nacos 발견 할 수있는 서비스, 내부 참조하세요 spring-cloud-ribbon의존과 관련을

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

클라이언트 전화

가장 간단한 여기에 우리 RestTemplate의 호출은 사용 시작Ribbon

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

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

复制代码

소스 해결

전화 요격 만들기

모든 가져 오기 1. @LoadBalanced마크RestTemplate

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

2. 증가 LoadBalancerInterceptor처리 로직을

  • 도입하지 않음 spring-retry사용
@Bean
public LoadBalancerInterceptor ribbonInterceptor() {
	return new LoadBalancerInterceptor();
}
复制代码
  • 도입 spring-retry하여
@Bean
@ConditionalOnMissingBean
public RetryLoadBalancerInterceptor ribbonInterceptor() {
	return new RetryLoadBalancerInterceptor();
}
复制代码
  • LoadBalancerInterceptor 비즈니스 로직
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));
    }
}
复制代码

실행 인터셉터

3. RibbonLoadBalancerClient 실행

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

4.execute 실행

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;
    }
}
复制代码

가져 오기 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)

이름 디폴트의 ​​구현 효과
IClientConfig DefaultClientConfigImpl 등 시간 제한 설정, 압축 영구 변형 : 리본과 같은 클라이언트 구성 파라미터
SERVERLIST NacosServerList 예제 예제 테이블 대상 서비스, 특정 서비스 검색 클라이언트 구현
ServerListFilter ZonePreferenceServerListFilter 논리 예의 목록은 필터링 처리 SERVERLIST
아이 룰 ZoneAvoidanceRule 선택 규칙의 서버로드 밸런싱
IPing DummyPing 서비스를 테스트하는 방법은 달성 할 수 있습니다
ServerListUpdater PollingServerListUpdater SERVERLIST에 대한 운영 업데이 트를 달성

위의 기본 구현 참조 RibbonClientConfiguration. ZoneAwareLoadBalancer

서비스에 대한 액세스의 예

//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;
    }
}
复制代码
  • 부모 클래스 호출 IRule구현 선택 서버
public Server chooseServer(Object key) {
	return rule.choose(key);
}
复制代码

8.PredicateBasedRule 선택 규칙

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 목록

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;
        }
        // 区域高可用判断
        ...
    }
}
复制代码

확장 : SERVERLIST 유지 보수

초기화 SERVERLIST

상기에서 생성 된로드 밸런서 의존 소자 (6)ServerList타겟 서비스 인스턴스 인스턴스 테이블 특정 서비스 디스커버리 클라이언트 구현. 우리는 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);

    }
}
复制代码

업데이트 ServerListUpdater

  • 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
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);
    }
}
复制代码

확장 : 서버 유지 관리 상태

  • 로드 밸런서는 초기 건설을 트리거합니다 setupPingTask()
public BaseLoadBalancer() {
  this.name = DEFAULT_NAME;
  this.ping = null;
  setRule(DEFAULT_RULE);
  // 开启ping 检查任务
  setupPingTask();
  lbStats = new LoadBalancerStats(DEFAULT_NAME);
}
复制代码
  • 설치 핑 작업
void setupPingTask() {
  // 是否可以ping, 默认的DummyPing 直接 跳过不执行
  if (canSkipPing()) {
    return;
  }
  // 执行PingTask
  lbTimer.schedule(new BaseLoadBalancer.PingTask(), 0, pingIntervalSeconds * 1000);
  // 开启任务
  new BaseLoadBalancer.Pinger(pingStrategy).runPinger();
}
复制代码
  • SerialPingStrategy 직렬 실행 논리
// 串行调度执行 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 판단 호출 가능 여부
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;
    }
}
复制代码

확장 : RibbonClient 게으른 로딩 프로세스

위에서 리본이 시간에 요청을 생성하는 기본값으로 LoadBalancer시작 서비스에 대한 첫 번째 호출 이어질 것이 게으른 로딩 메커니즘 서비스 대기 시간 도 시간 제한 퓨즈 집적 회로 차단기 (hystrix)에서, 문제 등을 .

이 문제를 해결하기 위해, 우리는 기아의 부하 리본을 구성합니다

ribbon:
  eager-load:
    clients:
      - provider
复制代码
  • RibbonApplicationContextInitializer 서비스 시작 자동으로 리본 클라이언트를 만들 필요가 사전에 공장을 호출 한 후
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();
    }

}
复制代码

다음 단계

나는 갱신 뒤에, 관심을 환영합니다 Ribbon, Hystrix, Sentinel, Nacos소스 그래픽 해상도 및 기타 구성 요소.

또 다른 참고 : 공공 숫자 위의 그림 자료 (omnigraffle & 그림 억) 할 JAVA架构日记

"★★★★★"봄 부팅 2.2, 봄 클라우드 혹 스톤 & 알리바바, RBAC 권한 관리 시스템의 OAuth2를 바탕으로

추천

출처juejin.im/post/5e5c598551882549052f49e4