1。概要
Spring Cloud Ribbonは、Netflixリボンに基づくクライアント負荷分散ツールのセットです。
簡単に言えば、リボンはNetflixによってリリースされたオープンソースプロジェクトであり、その主な機能はクライアント側の負荷分散アルゴリズムとサービス呼び出しを提供することです。リボンクライアントコンポーネントは、接続タイムアウト、再試行などの一連の完全な構成アイテムを提供します。簡単に言えば、構成ファイルに負荷分散(略してLB)の背後にあるすべてのマシンをリストすることです。リボンは、特定のルール(単純なポーリング、ランダム接続など)に基づいてこれらのマシンを自動的に接続するのに役立ちます。簡単リボンを使用して、カスタムの負荷分散アルゴリズムを実装します。
公式ウェブサイト情報:https://github.com/Netflix/ribbon/wiki/Getting-Started
2. LB(負荷分散)
2.1LBロードバランシングとは
簡単に言えば、システムのHA(高可用性)を実現するために、ユーザーの要求は複数のサーバーに均等に分散されます。一般的な負荷分散には、ソフトウェアnginx、LVS、ハードウェアF5などが含まれます。
2.2リボンローカルゲスト負荷分散クライアントとnginxサーバー負荷分散の違い
Nginxはサーバーの負荷分散です。すべてのクライアントリクエストはnginxに渡され、nginxは転送リクエストを実装します。つまり、負荷分散はサーバーによって実装されます。
リボンローカル負荷分散:マイクロサービスインターフェイスを呼び出すと、登録情報サービスリストがレジストリリストから取得され、JVMにローカルにキャッシュされるため、RPCリモートサービス呼び出しテクノロジがローカルに実装されます。
集中型LB
とは、サービスのコンシューマーとプロバイダーの間で独立したLBファシリティ(F5などのハードウェアまたはnginxなどのソフトウェア)を使用することを指します。ファシリティは、特定の戦略プロバイダーを介してサービスへのアクセス要求を転送する責任があります。
インプロセスLB
は、LBロジックをコンシューマーに統合します。コンシューマーは、サービスレジストリまたは使用可能なアドレスから適切なサーバーを選択します。リボンは、単なるクラスライブラリであるインプロセスLBに属します。コンシューマープロセスに統合され、消費者は、彼を通じてサービスプロバイダーのアドレスを取得します。
3.リボンの負荷分散
3.1アーキテクチャの説明
作業中のリボンは2つのステップに分かれています。
- 最初のステップはEurekaサーバーを選択することです。彼は、同じエリアで負荷の少ないサーバーを選択することを好みます。
- 2番目の部分では、ユーザーが指定した戦略に従ってサーバーが取得したサービスのリストからアドレスを選択します。
その中で、リボンは、ポーリング、ランダム、応答時間に基づく重み付けなど、さまざまな戦略を提供します。
概要:リボンは実際にはソフトロードバランシングのクライアントコンポーネントであり、リクエストを必要とする他のクライアントと組み合わせて使用できます。eurekaとの組み合わせはほんの一例です。
3.2RestTemplateの使用
https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html
getForObjectメソッド/ getForEntityメソッド
postForObject / postForEntity
3.3リボンコアコンポーネントIRule
IRule:特定のアルゴリズムに従ってサービスのリストからアクセスするサービスを選択します
- com.netflix.loadbalancer.RoundRobinRule:ポーリング
- com.netflix.loadbalancer.RandomRule:ランダム
- com.netflix.loadbalancer.RetryRule:最初にRoundRobinRule戦略に従ってサービスを取得します。サービスが失敗した場合、指定された時間内に再試行します
- WeightedResponseTimeRule:RoundRobinRuleの拡張であり、応答速度が速いほど、インスタンス選択の重みが大きくなり、選択が容易になります。
- BestAvailableRule:最初に、複数のアクセス障害が原因でサーキットブレーカーのトリップ状態にあるサービスを除外し、次に同時実行性が最も少ないサービスを選択します。
- AvailabilityFilteringRule:最初に失敗したインスタンスを除外し、次に同時実行性の低いインスタンスを選択します
- ZoneAvoidanceRule:デフォルトのルールであるコンポジットは、サーバーが配置されているエリアのパフォーマンスと、サーバーを選択するためのサーバーの可用性を判断します。
交換方法
modifycloud-consumer-order80
構成の詳細に注意してください。
- 新しいパッケージ
- 新しいMyselfRuleクラスを作成します
package com.lele.myrule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author: lele
* @date: 2021/3/15 7:12
* @description:
*/
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
return new RandomRule(); // 定义为随机
}
}
- コンシューマー80のメインスタートアップクラスに@RibbonClient
ブラウザーアクセスを追加します:http:// localhost / Consumer / Payment / get / 1
4.リボン負荷分散アルゴリズム
4.1原則
負荷分散アルゴリズム:RESTインターフェースへの要求数%サーバークラスターの総数=実際のコールサーバーロケーションインデックス(RESTインターフェースの数は各再起動後に1から始まります)
List instances = discoveryClient.getInstances(“CLOUD-PAYMENT-SERVICE”);
List[0] instances = 127.0.0.1:8001
List[1] instances = 127.0.0.1:8002
8001と8002は1つのクラスターに結合され、合計2台のマシンがあり、クラスターの総数は2です。ポーリングアルゴリズムの原則によると、次のようになります。
リクエストの総数が1の場合、1%2 = 1は添え字の位置1に対応し、取得したサーバーアドレスは127.0.0.1:8002
です。リクエストの総数が2の場合、2%2 = 0はに対応します。添え字位置0、取得されたサーバーアドレスは
、リクエストの総数が3の場合、127.0.0.1:8001であり、3%2 = 1は添え字位置1に対応し、取得されたサーバーアドレスは127.0.0.1です。 :8002
ラベルの位置が0の場合、得られたサーバアドレスは127.0.0.1:8001とき要求の合計数は、4である4%2 = 0に相当次へ
...と
そうで
4.2手書きポーリングアルゴリズム
-
7001/7002クラスターの起動
-
8001/8002マイクロサービスの変革
-
80オーダーのマイクロサービス変換:
-
ApplicationContextBeanは@LoadBalancedを削除します
-
LoadBalancerインターフェース
-
MyLB
package com.lele.springcloud.lb; import org.springframework.cloud.client.ServiceInstance; import org.springframework.stereotype.Component; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * @author: lele * @date: 2021/3/16 7:27 * @description: */ @Component public class MyLB implements LoadBalancer { private AtomicInteger atomicInteger = new AtomicInteger(0); public final int getAndIncrement() { 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 instances(List<ServiceInstance> serviceInstances) { int index = getAndIncrement() % serviceInstances.size(); return serviceInstances.get(index); } }
-
-
80 OrderController
package com.lele.springcloud.controller;
import com.lele.springcloud.entities.CommonResult;
import com.lele.springcloud.entities.Payment;
import com.lele.springcloud.lb.LoadBalancer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.net.URI;
import java.util.List;
/**
* @author: lele
* @date: 2021/3/1 21:33
* @description:
*/
@RestController
@Slf4j
public class OrderController {
// public static final String PAYMENT_URL = "http://localhost:8001";
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";
@Resource
private RestTemplate restTemplate;
@Resource
private LoadBalancer loadBalancer;
@Resource
private DiscoveryClient discoveryClient;
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment) {
return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id) {
return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
}
@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult<Payment> getPayment2(@PathVariable("id") Long id) {
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/payment/get/" + id, CommonResult.class);
if (entity.getStatusCode().is2xxSuccessful()) {
return entity.getBody();
} else {
return new CommonResult<>(444, "操作失败");
}
}
@GetMapping(value = "/consumer/payment/lb")
public String getPaymentLB() {
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
if (instances == null || instances.size() <= 0) {
return null;
}
ServiceInstance serviceInstance = loadBalancer.instances(instances);
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri+"/payment/lb", String.class);
}
}
- T试
http:// localhost / Consumer / Payment / lb