RIBBON

2 篇文章 0 订阅
1 篇文章 0 订阅

服务端负载均衡RIBBON

-#### Ribbon集成示例

  • 客户端集成
    1. 引入SpringCloud中的ribbon相关依赖
    2. 通过RibbonLoadBlanceClient的choose方法获取到服务实例
    3. 通过服务实例得到 ip + port 去调用服务
      思考:Ribbon如何得到服务实例的详细信息?
Ribbon相关的初始化方式

RibbonAutoConfiguration:ribbon的自动装配类
LoadBalanceClient:定义了各种方法,如URI获取,服务实例选择等。(默认RibbonLoadBalancerClient)
SpringClientFactory:内部包含了各种获取对象–如loadbalancer。Instance,等方法

追踪RibbonAutoConfiguration自动装配类
  • SpringClientFactory spring工厂类 探究如何做到资源隔离的
	@Bean
	public SpringClientFactory springClientFactory() {
		// 定位1 SpringClientFactory
		SpringClientFactory factory = new SpringClientFactory();
		// 在工厂类中将配置文件装载进来了
		factory.setConfigurations(this.configurations);
		return factory;
	}		
  • 定位1 SpringClientFactory 调用父类NamedContextFactory 的方法
public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
		implements DisposableBean, ApplicationContextAware {
		.......... 省略代码
		ApplicationContextAware 类会帮助我们拿到容器的上线文
		
	private ApplicationContext parent;  // 定义了ApplicationContext  取名parent
	private Map<String, C> configurations = new ConcurrentHashMap<>();//创建了一个 ConcurrentHashMap

		.......... 省略代码
	@Override // 直接将 ApplicationContext 拿了进来
	public void setApplicationContext(ApplicationContext parent) throws BeansException {
		this.parent = parent;
	}
	.......... 省略代码
	//在这边定义了一个 createContext方法
	protected AnnotationConfigApplicationContext createContext(String name) {
		// 创建了一个AnnotationConfigApplicationContext 出来  这是一个子容器  
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		// 在这个上下文中传入了 name  name就是我们传入的serviceid
		if (this.configurations.containsKey(name)) { // 判断我们传入的名字在不在
			for (Class<?> configuration : this.configurations.get(name)
					.getConfiguration()) {
				// 如果name存在,便利我们能够拿到的所有name 同时将我们configuration 注册到 AnnotationConfigApplicationContext  中去
				context.register(configuration);
			}
		}
		for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
			// 将 default 开头的configurations.entrySet().getKey().startsWith("default.")开头的注册到AnnotationConfigApplicationContext  上下文中  
			if (entry.getKey().startsWith("default.")) {
				for (Class<?> configuration : entry.getValue().getConfiguration()) {
					context.register(configuration);
				}
			}
		}
		// 同时又进行了一系列的注册
		context.register(PropertyPlaceholderAutoConfiguration.class,
				this.defaultConfigType);
		context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
				this.propertySourceName,
				Collections.<String, Object> singletonMap(this.propertyName, name)));
		// 在这个地方判断 ApplicationContext 是否为空 如果不为空 将AnnotationConfigApplicationContext 注入到父容器 ApplicationContext 中
		// 通过这段代码 可以知道,ribbon的资源隔离是通过父子容器的机制进行隔离的
		// 父子容器:spring(父容器) 和 springMVC(子容器)
		// 父子容器之间的关系: 父容器不能拿到子容器的bean 反观 子容器可以很轻松的拿到父容器的bean
		if (this.parent != null) {
			// Uses Environment from parent as well as beans
			context.setParent(this.parent);
		}
		context.setDisplayName(generateDisplayName(name));
		context.refresh();
		return context;
	}

追踪LoadBalancerClient Bean 通过断点的方式进行调试
	@Bean
	@ConditionalOnMissingBean(LoadBalancerClient.class)
	public LoadBalancerClient loadBalancerClient() {
		return new RibbonLoadBalancerClient(springClientFactory());
	}
  • 断点位置
    @GetMapping("index")
    public Object getIndex(){
        ServiceInstance serviceInstance = loadBalancerClient.choose("hello-server"); // 断点 定位1
        String ip = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        return restTemplate.getForObject("http://"+ip+":"+port,String.class,"");
    }
  • 定位1
	@Override
	public ServiceInstance choose(String serviceId) { // yml 配置的serviceId
		return choose(serviceId, null);
	}
	
	public ServiceInstance choose(String serviceId, Object hint) {  // yml 配置的serviceId
		Server server = getServer(getLoadBalancer(serviceId), hint);	// 定位2
		if (server == null) {
			return null;
		}
		return new RibbonServer(serviceId, server, isSecure(server, serviceId),
				serverIntrospector(serviceId).getMetadata(server));
	}
  • 定位2
	protected ILoadBalancer getLoadBalancer(String serviceId) {
		return this.clientFactory.getLoadBalancer(serviceId); // getLoadBalancer 定位3
	}
  • 定位3
	public ILoadBalancer getLoadBalancer(String name) {
		return getInstance(name, ILoadBalancer.class); // getInstance 定位4
	}
  • 定位4
	@Override
	public <C> C getInstance(String name, Class<C> type) {
		C instance = super.getInstance(name, type); // 调用父类的getInstance 定位5
		if (instance != null) {
			return instance;
		}
		IClientConfig config = getInstance(name, IClientConfig.class);
		return instantiateWithConfig(getContext(name), type, config);
	}
  • 定位5
	public <T> T getInstance(String name, Class<T> type) {
		AnnotationConfigApplicationContext context = getContext(name); // 在此处获取一次context 定位6
		if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
				type).length > 0) {
			return context.getBean(type);
		}
		return null;
	}
  • 定位6
	private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();// map中放入的是刚才构建的自容器
	protected AnnotationConfigApplicationContext getContext(String name) {
		if (!this.contexts.containsKey(name)) { // 判断 serversId存不存在  
			synchronized (this.contexts) {
				if (!this.contexts.containsKey(name)) {
					this.contexts.put(name, createContext(name)); // 如果 serversId 不存在 创建一个容器,放入到contexts 中  容器构建时,调用的是 createContext 方法   定位7
				}
			}
		}
		return this.contexts.get(name);
	}
  • 定位7
	protected AnnotationConfigApplicationContext createContext(String name) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		if (this.configurations.containsKey(name)) {
			for (Class<?> configuration : this.configurations.get(name)
					.getConfiguration()) {
				context.register(configuration);
			}
		}
		for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
			if (entry.getKey().startsWith("default.")) {
				for (Class<?> configuration : entry.getValue().getConfiguration()) {
					context.register(configuration);
				}
			}
		}
		context.register(PropertyPlaceholderAutoConfiguration.class,
				this.defaultConfigType);
		context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
				this.propertySourceName,
				Collections.<String, Object>singletonMap(this.propertyName, name)));
		if (this.parent != null) {
			// Uses Environment from parent as well as beans
			context.setParent(this.parent);
			// jdk11 issue
			// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
			context.setClassLoader(this.parent.getClassLoader());
		}
		context.setDisplayName(generateDisplayName(name));
		context.refresh();
		return context;
	}
  • 仔细追踪调用链路
追踪 getServer 链路
  • 定位1
	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");  // 定位2
	}
  • 定位2 ZoneAwareLoadBalancer (定位)
    public Server chooseServer(Object key) {
        if (ENABLED.get() && this.getLoadBalancerStats().getAvailableZones().size() > 1) {	// 定义了区域
            Server server = null;

            try {
                LoadBalancerStats lbStats = this.getLoadBalancerStats();
                Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
                logger.debug("Zone snapshots: {}", zoneSnapshot);
                if (this.triggeringLoad == null) {
                    this.triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty("ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2D);
                }

                if (this.triggeringBlackoutPercentage == null) {
                    this.triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty("ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999D);
                }

                Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, this.triggeringLoad.get(), this.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 = this.getLoadBalancer(zone);
                        server = zoneLoadBalancer.chooseServer(key);
                    }
                }
            } catch (Exception var8) {
                logger.error("Error choosing server using zone aware logic for load balancer={}", this.name, var8);
            }

            if (server != null) {
                return server;
            } else {
                logger.debug("Zone avoidance logic is not invoked.");
                return super.chooseServer(key);
            }
        } else {	// 没有定义区域
            logger.debug("Zone aware logic disabled or there is only one zone");
            return super.chooseServer(key); // 定位 3
        }
    }
  • 定位3 BaseLoadBalancer(定位)
 private static final IRule DEFAULT_RULE = new RoundRobinRule(); // rule 默认是轮训方式

 public Server chooseServer(Object key) {  // 调用父类的chooseServer方法
        if (this.counter == null) {
            this.counter = this.createCounter(); // 定位5
        }

        this.counter.increment();
        if (this.rule == null) {
            return null;
        } else {
            try {
                return this.rule.choose(key);	// 调用 this.rule.choose(key)  定位5
            } catch (Exception var3) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", new Object[]{this.name, key, var3});
                return null;
            }
        }
    }
  • 定位4 IRule
public interface IRule{  // 规则 可以自定义
    /*
     * choose one alive server from lb.allServers or
     * lb.upServers according to key
     * 
     * @return choosen Server object. NULL is returned if none
     *  server is available 
     */

    public Server choose(Object key); 
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}
  • Ribbon中的负载均衡规则
    负载均衡规则,在ribbon中提供了以下负载均衡规则
名称描述
ZoneAvoidanceRule区域规避策略(默认策略)
AbstractLoadBalanceRule抽象celue,策略的模板
RoundRobinRule轮询策略
BestAvailableRule最小调用数
RandomRule随机数策略
RetryRule重试策略
WeightedResponseTimeRule权重策略
AvailabilityFilteringRule线程抽样策略
    @Bean
    public IRule ribbonRule(){
        //自定义规则
        return new RandomRule();
    }
  • Rule 规则的实现 上接 定位5 RandomRule
	@Override
	public Server choose(Object key) {
		return choose(getLoadBalancer(), key);  // 定位6 getLoadBalancer  choose 定位7 
	}
  • 定位6
    @Override
    public ILoadBalancer getLoadBalancer(){
        return lb;
    }
  • 定位7
    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        Server server = null;

        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> upList = lb.getReachableServers();
            List<Server> allList = lb.getAllServers();

            int serverCount = allList.size();
            if (serverCount == 0) {
                /*
                 * No servers. End regardless of pass, because subsequent passes
                 * only get more restrictive.
                 */
                return null;
            }

            int index = chooseRandomInt(serverCount);
            server = upList.get(index);

            if (server == null) {
                /*
                 * The only time this should happen is if the server list were
                 * somehow trimmed. This is a transient condition. Retry after
                 * yielding.
                 */
                Thread.yield();
                continue;
            }

            if (server.isAlive()) { // 在这边判断服务是否存活 定位8
                return (server);
            }

            // Shouldn't actually happen.. but must be transient or a bug.
            server = null;
            Thread.yield();
        }

        return server;

    }

  • 定位8 Server
    public boolean isAlive() {
        return isAliveFlag; // 在这边如何判断服务是否存活
    }

容错机制
  • Iping机制
    相当于心跳,每隔一段时间判断服务实例是否正常

  • Iping机制的装载流程

    1. 为引入Eureka时,加载配置文件,一旦配置没有指定Iping,默认调用new DummyPing(),全部都返回true
    2. 引入Eureka时,加载EurekaRibbonClientConfiguration装载一个Iping,如果自定义了一个Iping,默认使用NIWSDiscoveryPing,通过Eureka的状态返回服务是否正常。
  • 重试机制
    如果调用后,出现异常,可以通过重试机制,发起重试。

  • 再次追踪 RibbonAutoConfiguration 的工厂类 SpringClientFactory

    @Bean
    public SpringClientFactory springClientFactory() {
        SpringClientFactory factory = new SpringClientFactory();
        factory.setConfigurations(this.configurations);
        return factory;
    }
  • new SpringClientFactory
	public SpringClientFactory() {
		super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name"); // 在加载的时候构建了一个 RibbonClientConfiguration
	}
  • RibbonClientConfiguration
	@Bean
	@ConditionalOnMissingBean
	public IRule ribbonRule(IClientConfig config) { //规则在此处被实例化
		if (this.propertiesFactory.isSet(IRule.class, name)) { // 如果自定义了Rule(或者在配置文件中定义) 在这边会直接返回,否则 启用 ZoneAvoidanceRule 
			return this.propertiesFactory.get(IRule.class, config, name);
		}
		ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
		rule.initWithNiwsConfig(config);
		return rule;
	}
	@Bean
	@ConditionalOnMissingBean
	public IPing ribbonPing(IClientConfig config) { // Iping规则在此处定义
		if (this.propertiesFactory.isSet(IPing.class, name)) { // 如果我们在代码中自定义了 Iping规则,会直接使用 否则,返回DummyPing
			return this.propertiesFactory.get(IPing.class, config, name);
		}
		return new DummyPing(); // 下一步
	}

  • DummyPing
public class DummyPing extends AbstractLoadBalancerPing {

    public DummyPing() {
    }

    public boolean isAlive(Server server) {
        return true; // 没有自定义配置Iping规则时,isAlive会永远返回 true 
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}
  • ribbon中引入Eureka 追踪 RibbonEurekaAutoConfiguration
@Configuration
@EnableConfigurationProperties
@ConditionalOnRibbonAndEurekaEnabled
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@RibbonClients(defaultConfiguration = EurekaRibbonClientConfiguration.class) // 在此处引入了EurekaRibbonClientConfiguration
public class RibbonEurekaAutoConfiguration {

}
  • EurekaRibbonClientConfiguration
	@Bean
	@ConditionalOnMissingBean
	public IPing ribbonPing(IClientConfig config) { // 在此处帮我们修改了IPing 规则 
		if (this.propertiesFactory.isSet(IPing.class, serviceId)) { // 此处判断我们是否自定义 Iping规则,有直接返回,没有则使用 NIWSDiscoveryPing 
			return this.propertiesFactory.get(IPing.class, config, serviceId);
		}
		NIWSDiscoveryPing ping = new NIWSDiscoveryPing();
		ping.initWithNiwsConfig(config);
		return ping;
	}
	
	@Bean
	@ConditionalOnMissingBean
	public ServerList<?> ribbonServerList(IClientConfig config,
			Provider<EurekaClient> eurekaClientProvider) { // 在此处重新定义了 ServerList
		if (this.propertiesFactory.isSet(ServerList.class, serviceId)) {
			return this.propertiesFactory.get(ServerList.class, config, serviceId);
		}
		DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(  // 在此处 Eureka 将自身的 ServerList 给到了 Ribbon 处理
				config, eurekaClientProvider);
		DomainExtractingServerList serverList = new DomainExtractingServerList(
				discoveryServerList, config, this.approximateZoneFromHostname);
		return serverList;
	}

  • NIWSDiscoveryPing
/**
 * "Ping" Discovery Client
 * i.e. we dont do a real "ping". We just assume that the server is up if Discovery Client says so
 * @author stonse
 *
 */
public class NIWSDiscoveryPing extends AbstractLoadBalancerPing {
	        
		BaseLoadBalancer lb = null; 
		

		public NIWSDiscoveryPing() {
		}
		
		public BaseLoadBalancer getLb() {
			return lb;
		}

		/**
		 * Non IPing interface method - only set this if you care about the "newServers Feature"
		 * @param lb
		 */
		public void setLb(BaseLoadBalancer lb) {
			this.lb = lb;
		}

		public boolean isAlive(Server server) {
		    boolean isAlive = true;
		    if (server!=null && server instanceof DiscoveryEnabledServer){ // 在此处获取服务实例 拿到所有的服务实例
	            DiscoveryEnabledServer dServer = (DiscoveryEnabledServer)server;	            
	            InstanceInfo instanceInfo = dServer.getInstanceInfo();
	            if (instanceInfo!=null){	                
	                InstanceStatus status = instanceInfo.getStatus();
	                if (status!=null){ // 在此处返回所有 服务实例为up状态的服务
	                    isAlive = status.equals(InstanceStatus.UP);
	                }
	            }
	        }
		    return isAlive;
		}

        @Override
        public void initWithNiwsConfig(
                IClientConfig clientConfig) {
        }
		
}
如何实现Ribbon的重试机制
  • RibbonAutoConfiguration
    @Bean
    @ConditionalOnClass(
        name = {"org.springframework.retry.support.RetryTemplate"}
    )  // 当 RetryTemplate 存在的时候,会触发重试机制 需要引入对应jar
    @ConditionalOnMissingBean
    public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(final SpringClientFactory clientFactory) {  // 重试机制的加载
        return new RibbonLoadBalancedRetryFactory(clientFactory);
    }
  • 引入的jar
     <dependency>
         <groupId>org.springframework.retry</groupId>
         <artifactId>spring-retry</artifactId>
     </dependency>
@LoadBalanced 注解 restTemplate 是 spring自带的
  • 断点的方式查询调用链路
    @GetMapping("index")
    public Object getIndex(){
//        ServiceInstance serviceInstance = loadBalancerClient.choose("hello-server");
//        String ip = serviceInstance.getHost();
//        int port = serviceInstance.getPort();
//        return restTemplate.getForObject("http://"+ip+":"+port,String.class,"");
        return restTemplate.getForObject("http://hello-server",String.class,""); // 断点  定位1 
    }
  • 定位1
    @Nullable
    public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
        RequestCallback requestCallback = this.acceptHeaderRequestCallback(responseType);
        HttpMessageConverterExtractor<T> responseExtractor = new HttpMessageConverterExtractor(responseType, this.getMessageConverters(), this.logger);
        return this.execute(url, HttpMethod.GET, requestCallback, responseExtractor, (Object[])uriVariables);  // 定位2
    }
  • 定位2
    @Nullable
    public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
        URI expanded = this.getUriTemplateHandler().expand(url, uriVariables);
        return this.doExecute(expanded, method, requestCallback, responseExtractor); // 定位3
    }
  • 定位3
    @Nullable
    protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
        Assert.notNull(url, "URI is required");
        Assert.notNull(method, "HttpMethod is required");
        ClientHttpResponse response = null;

        Object var14;
        try {
            ClientHttpRequest request = this.createRequest(url, method);
            if (requestCallback != null) {
                requestCallback.doWithRequest(request);
            }

            response = request.execute();  // 在此处发起调用  此前都在组装request  定位4
            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;
    }
  • 定位4 AbstractClientHttpRequest
    public final ClientHttpResponse execute() throws IOException {
        this.assertNotExecuted();
        ClientHttpResponse result = this.executeInternal(this.headers);  // 在此处调用了一个执行方法  定位5 
        this.executed = true;
        return result;
    }
  • 定位5
	@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); // 在此处调用  定位6 
		this.bufferedOutput = new ByteArrayOutputStream(0);
		return result;
	}
  • 定位6
	@Override
	protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
		InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();
		return requestExecution.execute(this, bufferedOutput);  // 在此处调用了一个执行方法  定位7
	}
  • 定位7
	@Override
	public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
		if (this.iterator.hasNext()) {
			ClientHttpRequestInterceptor nextInterceptor = this.iterator.next(); // 在此处寻找到了一个拦截去,然后对我们的请求地址进行了一波拦截
			return nextInterceptor.intercept(request, body, this);  // 定位8 对请求地址进行拦截 (重试机制)  定位9 没有配置重试机制
		}
		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();
		}
	}
}
  • 定位8 进入了 RetryLoadBalancerInterceptor 的拦截器中 在这边 利用 拦截器的方式将请求交给ribbon的处理
	@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
		final URI originalUri = request.getURI(); // 在此处拦截到了请求地址 即我们的请求  
		final String serviceName = originalUri.getHost();
		Assert.state(serviceName != null,
				"Request URI does not contain a valid hostname: " + originalUri);
		final LoadBalancedRetryPolicy retryPolicy = this.lbRetryFactory
				.createRetryPolicy(serviceName, this.loadBalancer);
		RetryTemplate template = createRetryTemplate(serviceName, request, retryPolicy); // 在此处创建 createRetryTemplate  在此处 做了一个自己的 template  然后进行了一波请求  
		return template.execute(context -> {
			ServiceInstance serviceInstance = null;
			if (context instanceof LoadBalancedRetryContext) {
				LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
				serviceInstance = lbContext.getServiceInstance();
			}
			if (serviceInstance == null) {
				serviceInstance = this.loadBalancer.choose(serviceName);
			}
			ClientHttpResponse response = RetryLoadBalancerInterceptor.this.loadBalancer
					.execute(serviceName, serviceInstance,
							this.requestFactory.createRequest(request, body, execution));
			int statusCode = response.getRawStatusCode();
			if (retryPolicy != null && retryPolicy.retryableStatusCode(statusCode)) {
				byte[] bodyCopy = StreamUtils.copyToByteArray(response.getBody());
				response.close(); 
				throw new ClientHttpResponseStatusCodeException(serviceName, response,
						bodyCopy);
			}
			return response;
		}, new LoadBalancedRecoveryCallback<ClientHttpResponse, ClientHttpResponse>() {
			// This is a special case, where both parameters to
			// LoadBalancedRecoveryCallback are
			// the same. In most cases they would be different.
			@Override
			protected ClientHttpResponse createResponse(ClientHttpResponse response,
					URI uri) {
				return response;
			}
		});
	}
  • 定位9 没有开启重试机制 LoadBalancerInterceptor
	@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));
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值