@LoadBalanced注釈はRestTemplateは、負荷分散機能を持たせることができますなぜ?[春の雲を楽しむことを学びます]

各1

あなたは考える必要があります。なぜ完璧よりも重要で完了する傾向があるのですか?

序文

ではSpring Cloudマイクロサービスアプリケーションシステム、リモートコールはバランスをロードする必要があります。私たちは、使用RestTemplate:オープン負荷分散が非常に簡単である場合に、クライアントへのリモート呼び出しとして取得するためのコメント私たちは、主に使用信じる行うためにクライアント側:多分あなたと私は同じ気持ち持って、負荷分散を特に強力で使用することは容易ではないものの、リボンを私たちにもその影響を、私は、実際には、その原則のルートをたくさん勉強したり、我々は合理的な説明を与えることはできませんが、いくつかの現象につながる、内部について十分に知らないが、カスタマイズさと拡張しますこのために作られたこの論文の櫛は、私はあなたがこの記事を渡すことができると思います(私たちはそれだけ説明する必要がより明確に理解コンテンツのこの小片を)。@LoadBalanced
RibbonRibbon@LoadBalanced

Open Clientのロードバランシングは一つだけコメント、このような形になります

@LoadBalanced // 标注此注解后,RestTemplate就具有了客户端负载均衡能力
@Bean
public RestTemplate restTemplate(){
    return new RestTemplate();
}

彼は言ったSpringJavaコミュニティホイール、本発明の再発明の最良かつ最も優れた作品は強調しすぎることはできません。オープンなぜあなたは、見つける襟のこの記事に代わっRestTemplate負荷はそれほど単純でバランスをとります。

説明:この論文では、すでにおなじみRestTemplateと理解しRestTemplate、分析の関連コンポーネントの基本的な原則を。この部分はまだ比較的漠然としている場合、あなたがすることをお勧めすることを強制私の記事の前に参照してください。RestTemplate使用し、原則、あなたはまだ胸の中に熟れていますか?[春のMVCを学ぶお楽しみください]

RibbonAutoConfiguration

これはSpring Boot/Cloud、スタートRibbonエントリは自動的にクラスを設定し、あなたは一般的な理解を持っている必要があります:

@Configuration
// 类路径存在com.netflix.client.IClient、RestTemplate等时生效
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class) 
// // 允许在单个类中使用多个@RibbonClient
@RibbonClients 
// 若有Eureka,那就在Eureka配置好后再配置它~~~(如果是别的注册中心呢,ribbon还能玩吗?)
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class })
// 加载配置:ribbon.eager-load --> true的话,那么项目启动的时候就会把Client初始化好,避免第一次惩罚
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration {

    @Autowired
    private RibbonEagerLoadProperties ribbonEagerLoadProperties;
    // Ribbon的配置文件们~~~~~~~(复杂且重要)
    @Autowired(required = false)
    private List<RibbonClientSpecification> configurations = new ArrayList<>();

    // 特征,FeaturesEndpoint这个端点(`/actuator/features`)会使用它org.springframework.cloud.client.actuator.HasFeatures
    @Bean
    public HasFeatures ribbonFeature() {
        return HasFeatures.namedFeature("Ribbon", Ribbon.class);
    }


    // 它是最为重要的,是一个org.springframework.cloud.context.named.NamedContextFactory  此工厂用于创建命名的Spring容器
    // 这里传入配置文件,每个不同命名空间就会创建一个新的容器(和Feign特别像) 设置当前容器为父容器
    @Bean
    public SpringClientFactory springClientFactory() {
        SpringClientFactory factory = new SpringClientFactory();
        factory.setConfigurations(this.configurations);
        return factory;
    }

    // 这个Bean是关键,若你没定义,就用系统默认提供的Client了~~~
    // 内部使用和持有了SpringClientFactory。。。
    @Bean
    @ConditionalOnMissingBean(LoadBalancerClient.class)
    public LoadBalancerClient loadBalancerClient() {
        return new RibbonLoadBalancerClient(springClientFactory());
    }
    ...
}

この設定が完了している最も重要でありRibbon、自動的に関連するコンポーネントを設定し、持っているLoadBalancerClientロードバランシングを行うために(ここで使用して独自の実装クラスですRibbonLoadBalancerClient


@LoadBalanced

ノート自体とそのシンプルさ(すべての木材のプロパティ):

// 所在包是org.springframework.cloud.client.loadbalancer
// 能标注在字段、方法参数、方法上
// JavaDoc上说得很清楚:它只能标注在RestTemplate上才有效
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

その最大の特徴:の付いたヘッド@Qualifierノート、これはその発効の最も重要な要因の一つである、記事の後、私はその発効の時期に友人半分の長さの多くを過ごしました。自動的に有効にするに構成された、我々は、自動設定のカテゴリに来る必要があります。
@LoadBalancedLoadBalancerAutoConfiguration

LoadBalancerAutoConfiguration

// Auto-configuration for Ribbon (client-side load balancing).
// 它的负载均衡技术依赖于的是Ribbon组件~
// 它所在的包是:org.springframework.cloud.client.loadbalancer
@Configuration
@ConditionalOnClass(RestTemplate.class) //可见它只对RestTemplate生效
@ConditionalOnBean(LoadBalancerClient.class) // Spring容器内必须存在这个接口的Bean才会生效(参见:RibbonAutoConfiguration)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class) // retry的配置文件
public class LoadBalancerAutoConfiguration {
    
    // 拿到容器内所有的标注有@LoadBalanced注解的Bean们
    // 注意:必须标注有@LoadBalanced注解的才行
    @LoadBalanced
    @Autowired(required = false)
    private List<RestTemplate> restTemplates = Collections.emptyList(); 
    // LoadBalancerRequestTransformer接口:允许使用者把request + ServiceInstance --> 改造一下
    // Spring内部默认是没有提供任何实现类的(匿名的都木有)
    @Autowired(required = false)
    private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

    // 配置一个匿名的SmartInitializingSingleton 此接口我们应该是熟悉的
    // 它的afterSingletonsInstantiated()方法会在所有的单例Bean初始化完成之后,再调用一个一个的处理BeanName~
    // 本处:使用配置好的所有的RestTemplateCustomizer定制器们,对所有的`RestTemplate`定制处理
    // RestTemplateCustomizer下面有个lambda的实现。若调用者有需要可以书写然后扔进容器里既生效
    // 这种定制器:若你项目中有多个RestTempalte,需要统一处理的话。写一个定制器是个不错的选择
    // (比如统一要放置一个请求拦截器:输出日志之类的)
    @Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
        return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                for (RestTemplateCustomizer customizer : customizers) {
                    customizer.customize(restTemplate);
                }
            }
        });
    }
    
    // 这个工厂用于createRequest()创建出一个LoadBalancerRequest
    // 这个请求里面是包含LoadBalancerClient以及HttpRequest request的
    @Bean
    @ConditionalOnMissingBean
    public LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {
        return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
    }
    
    // =========到目前为止还和负载均衡没啥关系==========
    // =========接下来的配置才和负载均衡有关(当然上面是基础项)==========

    // 若有Retry的包,就是另外一份配置,和这差不多~~
    @Configuration
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    static class LoadBalancerInterceptorConfig {、
    
        // 这个Bean的名称叫`loadBalancerClient`,我个人觉得叫`loadBalancerInterceptor`更合适吧(虽然ribbon是唯一实现)
        // 这里直接使用的是requestFactory和Client构建一个拦截器对象
        // LoadBalancerInterceptor可是`ClientHttpRequestInterceptor`,它会介入到http.client里面去
        // LoadBalancerInterceptor也是实现负载均衡的入口,下面详解
        // Tips:这里可没有@ConditionalOnMissingBean哦~~~~
        @Bean
        public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }
    
        
        // 向容器内放入一个RestTemplateCustomizer 定制器
        // 这个定制器的作用上面已经说了:在RestTemplate初始化完成后,应用此定制化器在**所有的实例上**
        // 这个匿名实现的逻辑超级简单:向所有的RestTemplate都塞入一个loadBalancerInterceptor 让其具备有负载均衡的能力
        
        // Tips:此处有注解@ConditionalOnMissingBean。也就是说如果调用者自己定义过RestTemplateCustomizer类型的Bean,此处是不会执行的
        // 请务必注意这点:容易让你的负载均衡不生效哦~~~~
        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
            return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
        }
    }
    ...
}

この構成コードは少し長く、私は次の処理工程にまとめました:

  1. LoadBalancerAutoConfiguration持っている必要があります効果のクラスパスを取るためにRestTemplateSpringコンテナをしている必要があります、だけでなく、内LoadBalancerClientビーン実現
    1. LoadBalancerClientのみ実装クラスは次のとおりです。org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient
  2. LoadBalancerInterceptorClientHttpRequestInterceptorクライアントの要求インタセプタ。その役割は、クライアントが要求を送信する前に迎撃することであるため、負荷分散クライアントを実現します
  3. restTemplateCustomizer()匿名カスタマイザ戻るRestTemplateCustomizerそれはすべてに使用されているRestTemplateプラス負荷分散インターセプタを(それが注意すべきことは@ConditionalOnMissingBeanコメント〜)

見つけるのは困難ではない、コアの負荷分散実装はインターセプタがあり、インターセプターは、一般的にすることですRestTemplateカウンター攻撃は、負荷分散機能を備えた依頼者となっています

LoadBalancerInterceptor

このクラスは、使用される唯一の場所ですLoadBalancerAutoConfiguration〜コンフィギュレーション・アップに

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

    // 这个命名都不叫Client了,而叫loadBalancer~~~
    private LoadBalancerClient loadBalancer;
    // 用于构建出一个Request
    private LoadBalancerRequestFactory requestFactory;
    ... // 省略构造函数(给这两个属性赋值)

    @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));
    }
}

このインターセプタは、要求をインターセプトした後にされているserviceNameために委託LoadBalancerClientによれば、実行するためにServiceName実際のNの複数に対応することができるServerので、あなたは、多数のサーバから等化アルゴリズムを使用して最適のものを選ぶことができServer、それが保持する(最終的な要求のために真のリクエストアクチュエータClientHttpRequestExecution)。


LoadBalancerClient

要求がブロックされた後、最終的に委託LoadBalancerClientプロセス。

// 由使用负载平衡器选择要向其发送请求的服务器的类实现
public interface ServiceInstanceChooser {

    // 从负载平衡器中为指定的服务选择Service服务实例。
    // 也就是根据调用者传入的serviceId,负载均衡的选择出一个具体的实例出来
    ServiceInstance choose(String serviceId);
}

// 它自己定义了三个方法
public interface LoadBalancerClient extends ServiceInstanceChooser {
    
    // 执行请求
    <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
    <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
    
    // 重新构造url:把url中原来写的服务名 换掉 换成实际的
    URI reconstructURI(ServiceInstance instance, URI original);
}

それは唯一の実装クラスがあるRibbonLoadBalancerClientServiceInstanceChooser〜を実装し、複数のクラスを)。

RibbonLoadBalancerClient

まず第一に、私たちはその心配する必要がありますchoose()方法:

public class RibbonLoadBalancerClient implements LoadBalancerClient {
    
    @Override
    public ServiceInstance choose(String serviceId) {
        return choose(serviceId, null);
    }
    // hint:你可以理解成分组。若指定了,只会在这个偏好的分组里面去均衡选择
    // 得到一个Server后,使用RibbonServer把server适配起来~~~
    // 这样一个实例就选好了~~~真正请求会落在这个实例上~
    public ServiceInstance choose(String serviceId, Object hint) {
        Server server = getServer(getLoadBalancer(serviceId), hint);
        if (server == null) {
            return null;
        }
        return new RibbonServer(serviceId, server, isSecure(server, serviceId),
                serverIntrospector(serviceId).getMetadata(server));
    }

    // 根据ServiceId去找到一个属于它的负载均衡器
    protected ILoadBalancer getLoadBalancer(String serviceId) {
        return this.clientFactory.getLoadBalancer(serviceId);
    }

}

choose方法:着信サービスIDは、その後、SpringClientFactoryロードバランサを取得しcom.netflix.loadbalancer.ILoadBalancer、最終的にによってそれに委託chooseServer()を選択する方法com.netflix.loadbalancer.Serverは本当に完了したことを、インスタンスをServerある選ばれましたILoadBalancer

ILoadBalancerそして、その関連クラスは、比較的大規模なシステムで、我々は拡大して複数の操作を行いますが、唯一の私たちのプロセスに焦点を当てていません

LoadBalancerInterceptor実行が行われる場合には、直接信頼loadBalancer.execute()このメソッドを:

RibbonLoadBalancerClient:

    // hint此处传值为null:一视同仁
    // 说明:LoadBalancerRequest是通过LoadBalancerRequestFactory.createRequest(request, body, execution)创建出来的
    // 它实现LoadBalancerRequest接口是用的一个匿名内部类,泛型类型是ClientHttpResponse
    // 因为最终执行的显然还是执行器:ClientHttpRequestExecution.execute()
    @Override
    public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
        return execute(serviceId, request, null);
    }
    // public方法(非接口方法)
    public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
        // 同上:拿到负载均衡器,然后拿到一个serverInstance实例
        ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
        Server server = getServer(loadBalancer, hint);
        if (server == null) { // 若没找到就直接抛出异常。这里使用的是IllegalStateException这个异常
            throw new IllegalStateException("No instances available for " + serviceId);
        }

        // 把Server适配为RibbonServer  isSecure:客户端是否安全
        // serverIntrospector内省  参考配置文件:ServerIntrospectorProperties
        RibbonServer ribbonServer = new RibbonServer(serviceId, server,
                isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server));

        //调用本类的重载接口方法~~~~~
        return execute(serviceId, ribbonServer, request);
    }

    // 接口方法:它的参数是ServiceInstance --> 已经确定了唯一的Server实例~~~
    @Override
    public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
    
        // 拿到Server)(说白了,RibbonServer是execute时的唯一实现)
        Server server = null;
        if (serviceInstance instanceof RibbonServer) {
            server = ((RibbonServer) serviceInstance).getServer();
        }
        if (server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        }

        // 说明:执行的上下文是和serviceId绑定的
        RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
        ... 
        // 真正的向server发送请求,得到返回值
        // 因为有拦截器,所以这里肯定说执行的是InterceptingRequestExecution.execute()方法
        // so会调用ServiceRequestWrapper.getURI(),从而就会调用reconstructURI()方法
            T returnVal = request.apply(serviceInstance);
            return returnVal;
        ... // 异常处理
    }

returnValClientHttpResponse、最終的ににhandleResponse()見つかり抽出を言及するのに例外なく(存在する場合)、例外条件を処理するための方法:responseExtractor.extractData(response)そうでも全体の要求を完了しました。

細部

以下のため@LoadBalancedの下でRestTemplateの使用、私は詳細が参照するために以下のように要約しました:

  1. 着信StringタイプのURLは(絶対にする必要がありhttp://...そうでない場合は例外をスローし、):java.lang.IllegalArgumentException: URI is not absolute
  2. serviceId大文字と小文字を区別しませんhttp://user/...效果同http://USER/...()
  3. serviceIdポートのポート番号を教えないでください~~~

付い:最後に、あることを指摘しなければならないだけ書き込みが、書き込むことはできません要求を送信します。プロジェクトは、例2種類を持っている必要がある場合は、複数定義してくださいアドレス異なる使用シナリオだった〜@LoadBalancedRestTemplateserviceIdIP地址/域名RestTemplate

ローカルテスト

必要に応じて現地試験後にその実行プロセスを理解する(レジストリに依存しない)、あなたがそうすることができます。

// 因为自动配置头上有@ConditionalOnMissingBean注解,所以自定义一个覆盖它的行为即可
// 此处复写它的getServer()方法,返回一个固定的(访问百度首页)即可,方便测试
@Bean
public LoadBalancerClient loadBalancerClient(SpringClientFactory factory) {
    return new RibbonLoadBalancerClient(factory) {
        @Override
        protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
            return new Server("www.baidu.com", 80);
        }
    };
}

このように、この訪問の結果を、以下のことは自分自身をHTMLコンテンツのBaiduのホームページです。

@Test
public void contextLoads() {
    String obj = restTemplate.getForObject("http://my-serviceId", String.class);
    System.out.println(obj);
}

ここにはmy-serviceId確かに存在するが、上記の私のカスタム設定のおかげではありませんLoadBalancerClient

どのような、死んで書き込みインスタンスをエレガントではないでしょうか?確かに、あなたは常に最前線にも、コードのこの部分は、それの場合は複数のインスタンス、それをコメントアウトすることができませんか?私たちは、自分の負荷分散アルゴリズムを行う書かなければなりませんでしたか?それは明らかである我々はこの点を考慮するとして早くから:ユーレカからクライアントの負荷分散スケジューリングのための設定listOfServersを使用して(returnServerSpring Cloud<clientName>.<nameSpace>.listOfServers=<comma delimited hostname:port strings>

たとえば、私はちょうどそれを設定するには、設定ファイルをマスターする必要があります。

# ribbon.eureka.enabled=false # 若没用euraka,此配置可省略。否则不可以
my-serviceId.ribbon.listOfServers=www.baidu.com # 若有多个实例请用逗号分隔

効果は完全に上記のです。

ヒント:この方法は、完全な構成の絶対パスを必要としない、http://(省略することができるnew Server()方法も)

実現可能な独自のインターセプタを追加するためにログ要求?

もちろん、それは可能ですが、私は次の例を与えます:

@LoadBalanced
@Bean
public RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    List<ClientHttpRequestInterceptor> list = new ArrayList<>();
    list.add((request, body, execution) -> {
        System.out.println("当前请求的URL是:" + request.getURI().toString());
        return execution.execute(request, body);
    });
    restTemplate.setInterceptors(list);
    return restTemplate;
}

:だから、各クライアントの要求は、これらの言葉に印刷されます当前请求的URI是:http://my-serviceId(それは、最終的な要求を実行する必要があるため)一般的な状況(デフォルト)カスタムインターセプタは、インターセプタのロードバランシングの前に実行されます。あなたはインターセプタと制御シーケンスを複数定義する必要がある場合はできるOrderedシリアルインターフェースを実装します-


最後に、最後には、私は非常に、非常に重要な質問を投げます:

    @LoadBalanced
    @Autowired(required = false)
    private List<RestTemplate> restTemplates = Collections.emptyList();

@Autowired+ @LoadBalancedを設定できRestTemplate、それをカスタマイズすることで来て使用される自動注入を?コア原則は何ですか?

>ヒント:原則の内容が属するSpring Framworkコア技術は、gulpingなしで深く考えることをお勧めします。ダウト私にメッセージを与えることができ、私はまた、次の記事で詳細な回答が得られます(考えることをお勧めします)

推奨読書

RestTemplate使用し、あなたはまだ胸の中に熟れている原則?[春のMVCを学ぶお楽しみください]
カテゴリ別に一括依存性の注入は[春]学習楽しむ--- @Qualifier高度なアプリケーション

概要

本論文では、おなじみ@LoadBalancedRestTemplateエントリーポイントとしては、説明しRibbon、実装プロセスの負荷分散を、当然のことながら、この部分のRibbon負荷系の氷山の先端全体の知識は知識の中核であるが、それは足がかりとして理にかなって、私はこれがあなたを喚起することを願ってRibbonシステムへの関心、〜それを理解します

== 春、SpringBoot、MyBatisのソースコード解析およびその他の利害私にWXを追加することができため場合:fsx641385712、手動でグループに離陸することを勧め ==
== 春、SpringBoot、MyBatisのソースコード解析および他のために興味が私を追加WXことができる場合:fsx641385712、手動で離陸したグループにあなたを招待 ==

おすすめ

転載: www.cnblogs.com/fangshixiang/p/11532729.html