1. リボンの負荷分散
1.1 リボンの紹介
Spring Cloud Ribbon は、Netflix リボンに基づくクライアント ロード バランシング ツールのセットです。
簡単に言うと、リボンは Netflix がリリースしたオープン ソース プロジェクトで、その主な機能は、クライアント側のソフトウェア負荷分散アルゴリズムとサービス呼び出しを提供することです。リボン クライアント コンポーネントは、接続タイムアウト、再試行などの一連の包括的な構成項目を提供します。簡単に言うと、ロード バランサー (略して LB) の背後にあるすべてのマシンを構成ファイルにリストすることです。リボンは、特定のルール (単純なポーリング、ランダム接続など) に基づいてこれらのマシンを自動的に接続するのに役立ちます。リボンを使用して、カスタムの負荷分散アルゴリズムを簡単に実装できます
注: リボンは現在メンテナンス中であり、基本的に更新する準備ができていません。
1.2 リボン機能
- 集中LB
つまり、独立した LB ファシリティ (F5 などのハードウェア、または Nginx などのソフトウェア) がサービス コンシューマとプロバイダーの間で使用され、ファシリティはアクセス要求をサービス プロバイダーに転送する役割を果たします。とある作戦
- インプロセス LB
LB ロジックをコンシューマーに統合すると、コンシューマーはサービス レジストリから利用可能なアドレスを学習し、これらのアドレスから適切なサーバーを選択します。
リボンはインプロセス LB であり、コンシューマ プロセスに統合された単なるクラス ライブラリであり、コンシューマはそれを通じてサービス プロバイダのアドレスを取得します。
2つの違い:
-
簡単に言えば、システムの HA (高可用性) を実現するために、ユーザー要求を複数のサービスに均等に分散することです。
一般的な負荷分散ソフトウェアには、Nginx、LVS、ハードウェア F5 などがあります。 -
リボン ローカル負荷分散クライアントと Nginx サーバー負荷分散の違い
Nginx はサーバー ロード バランサーであり、すべてのクライアント リクエストが Nginx に渡され、Nginx がリクエストを転送します。つまり、負荷分散はサーバーによって実装されます。
リボン ローカル ロード バランシング。マイクロサービス インターフェイスを呼び出すと、登録センターから登録情報サービス リストを取得し、JVM でローカルにキャッシュして、RPC リモート サービス呼び出しテクノロジをローカルで実現します。
リボンは負荷分散 + RestTemplate
リボンは実際にはソフトウェアが負荷分散を実現するためのクライアント コンポーネントです. リクエストを必要とする他のクライアントと組み合わせて使用できます. Eureka との組み合わせはその一例です.
作業中、リボンは 2 つのステップに分かれています。
- 最初のステップは、同じエリアで負荷の少ないサーバーを優先する EurekaSever を選択することです。
- 2 番目のステップは、ユーザーが指定したポリシーに従って、サーバーから取得したサービス登録リスト内のアドレスを選択することです。
その中でも、リボンは、ポーリング、ランダム、応答時間に応じた重み付けなど、さまざまな戦略を提供します。
1.3 リボンの使用:
- デフォルトでは、新しいバージョンの eureka を使用すると、デフォルトでリボンが統合されます。
<!-- 服务注册中心的客户端端 eureka-client -->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
リボンはこの > spring-cloud-starter-netflix-eureka-client に統合されています
手動でリボンを導入して注文モジュールに入れることもできます。オーダーアクセス有料時のみ負荷分散が必要です。
//ribbon依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.1.RELEASE</version>
<scope>compile</scope>
</dependency>
- RestTemplate 拡張機能の説明
== getForObject/getForEntity メソッドのデモ ==
- getForObject メソッドのデモ
getForObject によって返されるオブジェクトは、基本的に JSON として理解できるレスポンス ボディのデータから変換されたオブジェクトです。
@GetMapping("/consumer/getForObject/{id}")
public CommonResult<Payment> getPaymentById1(@PathVariable("id") Long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/"+id, CommonResult.class);
}
- getForEntity メソッドのデモ
getForEntity によって返されるオブジェクトは ResponseEntity オブジェクトであり、応答ヘッダー、応答ステータス コード、応答本文など、応答に重要な情報が含まれています。
@GetMapping("/consumer/getForEntity/{id}")
public CommonResult<Payment> getPaymentById2(@PathVariable("id") Long id){
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/"+id, CommonResult.class);
if(entity.getStatusCode().is2xxSuccessful()){
return entity.getBody();
}else{
return new CommonResult(444,"操作失败");
}
}
1.3.1 リボンで一般的に使用される負荷分散アルゴリズム
IRule: 特定のアルゴリズムに従って、サービス リストからアクセスするサービスを選択します。
Rule インターフェイスには 7 つの実装クラスがあり、各実装クラスは負荷分散アルゴリズムを表します
継承図:
クラス負荷分散アルゴリズムを実装する
1.3.2 リボンの使用
負荷分散アルゴリズムの代替手段
構成に関する注意事項:
公式ドキュメントでは次のように明確に警告されています。
カスタム アルゴリズム構成クラス@ComponentScan によってスキャンされた現在のパッケージとそのサブパッケージの下に配置することはできませんそうしないと、カスタマイズした構成クラスがすべてのリボン クライアントで共有され、特別なカスタマイズの目的を達成できなくなります。
率直に言って、メインのスタートアップ クラスが配置されているパッケージと、それが配置されているパッケージのサブパッケージには配置できません。
- myribbonrule という名前の springcloud と同じレベルのパッケージを作成します。
- 構成クラスを作成し、負荷分散アルゴリズムを指定する
カスタム リボン負荷分散構成クラス:
package com.tigerhhzz.myribbonrule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author tigerhhzz
* @date 2023/4/10 11:24
*/
@Configuration
public class MyselfRibbonRule {
@Bean
public IRule myRule() {
return new RandomRule(); //定义为随机
}
}
デフォルトのポーリング アルゴリズムはランダム アルゴリズムに置き換えられます
- 注釈 @RibbonClient をメインのスタートアップ クラスに追加します。
package com.tigerhhzz.springcloud;
import com.tigerhhzz.myribbonrule.MyselfRibbonRule;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
@Slf4j
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PROVIDER-SERVICE",configuration = MyselfRibbonRule.class)
public class OrderMain80{
public static void main(String[] args){
SpringApplication.run(OrderMain80.class,args);
log.info("OrderMain80启动成功~~~~~~~~~~~~~~~~~~~");
}
}
//name:代表哪个服务提供者要使用我们配置的算法
//configuretion:指定我们的算法配置类
CLOUD-PROVIDER-SERVICE のサービスにアクセスするときに、独自の負荷分散アルゴリズムを使用することを示します
1.3.3 リボンのポーリングアルゴリズム原理
1.3.4 負荷分散ポーリング アルゴリズムの手書き
- 変換モジュール (8001、8002)、コントローラー メソッド
モジュール (8001、8002) のコントローラー メソッドにメソッドを追加して、現在のノード ポートを返します。
@GetMapping("/payment/lb")
public String getPaymentLB(){
return serverPort;
}
- order80 モジュールを変更する
@LoadBalanced アノテーションを削除する
package com.tigerhhzz.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @author tigerhhzz
* @date 2023/4/8 22:52
*/
@Configuration
public class ApplicationContextConfig {
@Bean
//@LoadBalanced //注释掉这个注解
public RestTemplate getRestTemplate(){
return new RestTemplate();
/*
RestTemplate提供了多种便捷访问远程http服务的方法,
是一种简单便捷的访问restful服务模板类,是spring提供的用于rest服务的客户端模板工具集
*/
}
}
- 新しいカスタム リボン負荷分散インターフェイスを作成する
package com.tigerhhzz.myloadbanlance;
import org.springframework.cloud.client.ServiceInstance;
import java.util.List;
/**
* @author tigerhhzz
* @date 2023/4/10 17:24
*/
public interface myloadbanlance {
ServiceInstance instance(List<ServiceInstance> instances);
}
- カスタム リボン負荷分散インターフェイスの実装クラス
package com.tigerhhzz.myloadbanlance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author tigerhhzz
* @date 2023/4/10 17:25
*/
@Component
public class myloadbanlanceImpl implements myloadbanlance{
private AtomicInteger atomicInteger = new AtomicInteger(0);
//获取下一个要调用的服务id
public final int getIncrement() {
int current;
int next;
do {
current = this.atomicInteger.get();
next = current >= 2147483647 ? 0 : current+1;
}while (!this.atomicInteger.compareAndSet(current,next));
System.out.println("------------第几次访问:次数next"+next);
return next;
}
@Override
public ServiceInstance instance(List<ServiceInstance> instances) {
//拿到id,进行取余得到真正要调用服务的下标
int index = getIncrement() % instances.size();
return instances.get(index);
}
}
- 80 モジュールのコントローラーにテスト インターフェイス @GetMapping("payment/lb") を追加します。
package com.tigerhhzz.springcloud.controller;
import com.tigerhhzz.myloadbanlance.myloadbanlance;
import com.tigerhhzz.springcloud.entities.CommonResult;
import com.tigerhhzz.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.net.URI;
import java.util.List;
@RestController
@Slf4j
@RequestMapping("comsumer")
public class OrderController {
//远程调用的 地址
//public static final String PAYMENY_URL = "http://localhost:8001";
//远程调用的 地址
public static final String PAYMENT_URL = "http://cloud-provider-service";
@Resource
private RestTemplate restTemplate;
@Resource
private myloadbanlance myloadbanlance;
@Resource
private DiscoveryClient discoveryClient;
@PostMapping("payment/create")
public CommonResult<Payment> create (@RequestBody Payment payment){
return restTemplate.postForObject(PAYMENT_URL + "/payment/create",//请求地址
payment,//请求参数
CommonResult.class);//返回类型
}
@GetMapping("payment/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id")Long id){
return restTemplate.getForObject(PAYMENT_URL + "/payment/" + id,//请求地址
CommonResult.class);//返回类型
}
@GetMapping("getForObject/{id}")
public CommonResult<Payment> getPaymentById1(@PathVariable("id") Long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/"+id, CommonResult.class);
}
@GetMapping("getForEntity/{id}")
public CommonResult<Payment> getPaymentById2(@PathVariable("id") Long id){
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL+"/payment/"+id, CommonResult.class);
if(entity.getStatusCode().is2xxSuccessful()){
return entity.getBody();
}else{
return new CommonResult(444,"操作失败");
}
}
@GetMapping("payment/lb")
public String getPaymentLB() {
List<ServiceInstance> instances = discoveryClient.getInstances("cloud-provider-service");
if (instances == null || instances.size() == 0) {
return null;
}
ServiceInstance serviceInstance = myloadbanlance.instance(instances);
URI uri = serviceInstance.getUri();
System.out.println(uri+"/payment/lb");
return restTemplate.getForObject(uri+"/payment/lb",String.class);
// return uri+"/payment/lb";
}
}
1.3.5 サービスの開始とテスト
注: 手書きのカスタム ポーリング アルゴリズム パッケージは、スタートアップ クラスと同じディレクトリに配置する必要があります。そうしないと、Spring の開始後に、それ自体で記述されたカスタム ポーリング アルゴリズム クラスをスキャンできません。