上海网站建设公司兴田德润放心,厦门seo关键词,做足球行业深度内容的网站,黄页号码怎么取消标记文章目录 一、目的概述二、验证步骤1、源码下载2、导入IDE3、运行前修改配置4、策略说明5、修改策略 三、最终结论四、改进措施1. 思路分析2. 核心代码3. 测试页面 一、目的概述
为了验证Ribbon客户端负载均衡策略在负载节点失效的情况下#xff0c;是否具有故障转移的功能是否具有故障转移的功能进行了以下代码验证
二、验证步骤
1、源码下载
git clone https://gitee.com/00fly/microservice-all-in-one.githttps://gitee.com/00fly/microservice-all-in-one/tree/master/ribbon-demo-simple
2、导入IDE 3、运行前修改配置
根据调用关系我们需要启动2个user服务为了方便调试我们这边分别启动8081、8082端口的user服务并在movie模块中设置负载节点地址为127.0.0.1:8081,127.0.0.1:8082 #mermaid-svg-VsDDc5ELdUAq65D6 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-VsDDc5ELdUAq65D6 .error-icon{fill:#552222;}#mermaid-svg-VsDDc5ELdUAq65D6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VsDDc5ELdUAq65D6 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-VsDDc5ELdUAq65D6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VsDDc5ELdUAq65D6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VsDDc5ELdUAq65D6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VsDDc5ELdUAq65D6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VsDDc5ELdUAq65D6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VsDDc5ELdUAq65D6 .marker.cross{stroke:#333333;}#mermaid-svg-VsDDc5ELdUAq65D6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VsDDc5ELdUAq65D6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VsDDc5ELdUAq65D6 .cluster-label text{fill:#333;}#mermaid-svg-VsDDc5ELdUAq65D6 .cluster-label span{color:#333;}#mermaid-svg-VsDDc5ELdUAq65D6 .label text,#mermaid-svg-VsDDc5ELdUAq65D6 span{fill:#333;color:#333;}#mermaid-svg-VsDDc5ELdUAq65D6 .node rect,#mermaid-svg-VsDDc5ELdUAq65D6 .node circle,#mermaid-svg-VsDDc5ELdUAq65D6 .node ellipse,#mermaid-svg-VsDDc5ELdUAq65D6 .node polygon,#mermaid-svg-VsDDc5ELdUAq65D6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VsDDc5ELdUAq65D6 .node .label{text-align:center;}#mermaid-svg-VsDDc5ELdUAq65D6 .node.clickable{cursor:pointer;}#mermaid-svg-VsDDc5ELdUAq65D6 .arrowheadPath{fill:#333333;}#mermaid-svg-VsDDc5ELdUAq65D6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VsDDc5ELdUAq65D6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VsDDc5ELdUAq65D6 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-VsDDc5ELdUAq65D6 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-VsDDc5ELdUAq65D6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VsDDc5ELdUAq65D6 .cluster text{fill:#333;}#mermaid-svg-VsDDc5ELdUAq65D6 .cluster span{color:#333;}#mermaid-svg-VsDDc5ELdUAq65D6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VsDDc5ELdUAq65D6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 微服务movie 微服务user 微服务user 以eclipse为例简要说明 查看环境配置 打开Dashboard选择Duplicate config 选择open Config 选择Profile设置为dev 全部启动 docker部署相对简单编排文件为 https://gitee.com/00fly/microservice-all-in-one/blob/master/ribbon-demo-simple/docker/docker-compose.yml
version: 3.8
services:#负载均衡节点ribbon-user-simple-0:image: registry.cn-shanghai.aliyuncs.com/00fly/ribbon-user-simple:0.0.1container_name: ribbon-user-simple-0deploy:resources:limits:cpus: 1memory: 200Mreservations:memory: 180Mrestart: on-failurelogging:driver: json-fileoptions:max-size: 5mmax-file: 1#负载均衡节点ribbon-user-simple-1:image: registry.cn-shanghai.aliyuncs.com/00fly/ribbon-user-simple:0.0.1container_name: ribbon-user-simple-1deploy:resources:limits:cpus: 1memory: 200Mreservations:memory: 180Mrestart: on-failurelogging:driver: json-fileoptions:max-size: 5mmax-file: 1#调用方ribbon-movie-simple:image: registry.cn-shanghai.aliyuncs.com/00fly/ribbon-movie-simple:0.0.1container_name: ribbon-movie-simpledeploy:resources:limits:cpus: 1memory: 200Mreservations:memory: 180Mports:- 8090:8082environment:USER_SERVERS: ribbon-user-simple-0:8081,ribbon-user-simple-1:8081restart: on-failurelogging:driver: json-fileoptions:max-size: 5mmax-file: 14、策略说明
RandomRule 实现从服务实例清单中随机选择一个服务实例的功能。RoundRobinRule 实现了按照线性轮询的方式依次选择每个服务实例的功能。RetryRule 实现了一个具备重试机制的实例选择功能。WeightedResponseTimeRule是对 RoundRobinRule 的拓展增加了根据实例的运行情况来计算权重并根据权重来挑选实例。ClientConfigEnableRoundRobinRule 通过继承该策略在子类中做一些高级策略时有可能会存在一些无法实施的情况那么就可以用父类的实现作为备选线性轮询机制。BestAvailableRule 通过遍历负载均衡器中维护的所有服务实例会过滤掉故障的实例并找出并发请求数最小的一个所以该策略的特性是可选出最空闲的实例。PredicateBasedRule 先通过子类实现中的 Predicate 逻辑来过滤一部分服务实例然后再以线性轮询的方式从过滤后的实例清单中选出一个。AvailabilityFilteringRule 通过线性抽样的方式直接尝试寻找可用且较空闲的实例来使用。ZoneAvoidanceRule 根据负载情况选择可用区
5、修改策略
修改这边的负载均衡策略 打开页面 停止8081或8082端口服务重新调试返回结果如下
三、最终结论
RandomRule、RoundRobinRule 策略不具备故障转移能力 RetryRule、WeightedResponseTimeRule等虽然具有故障转移但是故障转移的时间太长并且故障恢复后重新选中该恢复的节点所需时间也较长。
各种策略的表现。大家可以自行研究测试。
四、改进措施
1. 思路分析
采用多线程多个节点同时检测返回最快响应的节点采用多线程定义超时时间返回超时时间之内有响应的节点, 后续根据规则选择1个节点
2. 核心代码
NodeController.java import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;import com.itmuch.cloud.study.user.entity.User;import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;Slf4j
Api(tags 负载均衡节点)
RestController
RequestMapping(/node)
public class NodeController
{Autowiredprivate WebClient webClient;Value(${microservice-ribbon-user.ribbon.listOfServers})private ListString listOfServers;private ExecutorService executorService Executors.newFixedThreadPool(10);ApiOperation(查询用户)GetMapping(/user/{id})public ListUser findById(PathVariable Long id)throws InterruptedException{// WebClient支持异步ListUser users new CopyOnWriteArrayListUser();listOfServers.stream().forEach(hostWithPort - webClient.get().uri(String.format(http://%s/%s, hostWithPort, id))// URI.acceptCharset(StandardCharsets.UTF_8).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class).subscribe(resp - users.add(resp)));int index 0;while (users.isEmpty() (index) 100){TimeUnit.MILLISECONDS.sleep(10);log.info(index:{}, waitting......, index);}if (users.isEmpty()){throw new RuntimeException(查询超时无返回值);}return users;}ApiOperation(查询用户 by execute)GetMapping(/v0/user/{id})public ListUser findByExecute(PathVariable Long id)throws InterruptedException{// ListUser users new ArrayListUser();// TODO ArrayList users一定概率有null值// 原因:通过new ArrayList()初始化的大小是0首次插入触发扩容并发可能导致出现null值ListUser users new CopyOnWriteArrayListUser();listOfServers.stream().forEach(hostWithPort - executorService.execute(() - webClient.get().uri(String.format(http://%s/%s, hostWithPort, id))// URI.acceptCharset(StandardCharsets.UTF_8).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class).subscribe(resp - users.add(resp))));int index 0;while (users.isEmpty() (index) 100){TimeUnit.MILLISECONDS.sleep(10);log.info(index:{}, waitting......, index);}if (users.isEmpty()){throw new RuntimeException(查询超时无返回值);}return users;}ApiOperation(查询用户 by submit)GetMapping(/v1/user/{id})public ListUser findBySubmit(PathVariable Long id)throws InterruptedException{ListUser users new CopyOnWriteArrayListUser();listOfServers.stream().forEach(hostWithPort - executorService.submit(() - webClient.get().uri(String.format(http://%s/%s, hostWithPort, id))// URI.acceptCharset(StandardCharsets.UTF_8).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class).subscribe(resp - users.add(resp)), users));int index 0;while (users.isEmpty() (index) 100){TimeUnit.MILLISECONDS.sleep(10);log.info(index:{}, waitting......, index);}if (users.isEmpty()){throw new RuntimeException(查询超时无返回值);}return users;}ApiOperation(查询用户 by invokeAny)GetMapping(/v2/user/{id})public User findByInvokeAny(PathVariable Long id)throws InterruptedException, ExecutionException, TimeoutException{return executorService.invokeAny(listOfServers.stream().map(hostWithPort - new CallableUser(){Overridepublic User call(){MonoUser mono webClient.get().uri(String.format(http://%s/%s, hostWithPort, id))// URI.acceptCharset(StandardCharsets.UTF_8).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class);return mono.block();}}).collect(Collectors.toList()), 1000, TimeUnit.MILLISECONDS);}ApiOperation(查询用户 by invokeAll)GetMapping(/v3/user/{id})public ListUser findByInvokeAll(PathVariable Long id)throws InterruptedException{ListFutureUser futures executorService.invokeAll(listOfServers.stream().map(hostWithPort - new CallableUser(){Overridepublic User call(){MonoUser mono webClient.get().uri(String.format(http://%s/%s, hostWithPort, id))// URI.acceptCharset(StandardCharsets.UTF_8).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User.class);return mono.block();}}).collect(Collectors.toList()), 1000, TimeUnit.MILLISECONDS);ListUser users new ArrayListUser();for (FutureUser future : futures){try{users.add(future.get());}catch (Exception e){log.error(e.getMessage(), e);}}return users;}
}3. 测试页面 有任何问题和建议都可以向我提问讨论,大家一起进步谢谢!
-over-