灰度发布专题---5、API网关灰度发布

news2024/11/22 13:20:13

API网关灰度发布

前面说到Dubbo灰度发布,那网关代理层如何实现灰度发布呢,在网关层实现灰度发布,我们可以采用2种方式实现,分别是权重和灰度规则配置。在这之前我们先了解下Gateway的源码,更利于后面灰度分析。

Gateway源码分析

Gateway在执行路由前会先选择指定的服务,指定的服务选择涉及负载均衡算法,我们先对源码进行剖 析一次,然后再模仿源码编写一个过滤器,让过滤器最后执行。

Gateway负载均衡流程

在这里插入图片描述
我们这里只探究SpringCloud Gateway涉及负载均衡中的源码部分,如上图:

1:请求会执行一个过滤器ReactiveLoadBalancerClientFilter,获取一个真实调用的服务实例信息。 2:filter()方法执行过程: 
1):获取url 
2):调用choose()方法获取要调用的真实实例,服务的IP、端口号、服务名字 
3):在url构建出的基础上构建出访问的真实地址         http://192.168.1.103:18081/car 
4):进入下一个调用链路 
3:choose()方法调用RoundRobinLoadBalancer的choose()方法获取实例。 

Gateway负载均衡源码

负载均衡过程中涉及到2个重要对象 ReactiveLoadBalancerClientFilter和 RoundRobinLoadBalancer,我们接下来对这2个对象源码展开分析。

ReactiveLoadBalancerClientFilter
  1. ReactiveLoadBalancerClientFilter创建
    在这里插入图片描述
    创建过程中,会初始化一个负载均衡器工厂对象,通过它可以创建负载均衡器 RoundRobinLoadBalancer。

  2. filter()方法
    在这里插入图片描述
    filter()方法完成了主要的服务筛选过程,筛选过程如下:
    1:调用choose()获取指定实例。
    2:真实实例获取后,把用户请求地址替换成真实服务地址信息。

  3. choose()
    在这里插入图片描述
    choose()方法调用了LoadBalancerClientFactory工厂对象创建了负载均衡器,真实实例选择其实是在 负载均衡器中完成的。

RoundRobinLoadBalancer
  1. choose()
    在这里插入图片描述
    该方法只是获取所有有效的实例,并封装成集合对象,然后再调用getInstanceResponse()方法。
  2. getInstanceResponse()
    在这里插入图片描述
    该方法通过一定算法获取指定实例,并将指定实例封装成DefaultResponse对象,并返回。

网关层灰度发布分析

在网关层实现灰度发布,我们可以采用2种方式实现,分别是权重和灰度规则配置。

1. 权重
在这里插入图片描述
当我们的系统刚发布的时候,还不是很稳定,可以放一部分流量来测试新系统,微服务网关可以采用权 重的方式实现流量控制,如果新系统没有问题,逐步把老系统换成新系统。

2. 灰度规则配置
在这里插入图片描述
灰度规则配置则是网关层常用的灰度发布方式,实现的流程我们分析一下:
1:用户请求到达微服务网关
2:微服务网关执行拦截
3:加载规则配置
4:按照规则配置适配服务
5:执行链路调用
规则配置比较灵活,没有绝对方案,常用的有 区域IP切流 、 固定用户切流 、 指定版本切流 。
区域IP切流: 指定省份或城市的IP使用灰度版本,多用于多城市子站点发布
固定用户切流: 部分内部账号或测试账号或部分指定会员使用灰度版本
指定版本切流: 根据用户当前版本使用指定服务,用户版本信息一般会传递到后台,多用于APP灰度发 布

3. 灰度发布-权重分流
基于Gateway实现灰度发布,首先要做的就是拦截用户请求,再根据规则路由请求。
项目结构:
在这里插入图片描述
权重分流结构

如上图,加入 carv1是稳定版, carv2是测试版本,我们为了测试 carv2是否稳定,可以放部分流量 到 carv2来测试,放部分流量可以采用权重的方式来实现,让 carv1占据90%流量, carv2只占据 10%流量。
权重配置只需要做2个操作:
1:让相关服务归属一个服务组
2:给服务组配置权重

修改 gray-gateway的 bootstrap.yml配置文件,如下:
在这里插入图片描述
配置代码如下:

gateway: 
routes: 
# 通过访问http://localhost:8001/car,来测试权重 - id: car-version1 
uri: lb://car-version1 
predicates: 
- Path=/car/** 
- Weight=group1, 8 
- id: car-version2 
uri: lb://car-version2 
predicates: 
- Path=/car/** 
- Weight=group1, 2 

多策略灰度发布

在这里插入图片描述

多策略灰度发布实现分析

在这里插入图片描述
如上图,实现多策略灰度发布,可以按照这个流程来实现:
1:稳定系统和灰度系统将信息注册到Nacos中,包括版本信息
2:用户执行请求,我们需要根据灰度规则动态筛选指定服务,可以编写过滤器拦截所有请求,再执行服务筛 选
3:服务筛选方法在ReactorServiceInstanceLoadBalancer对象中,叫choose(),我们需要重写它
4:根据当前用户请求信息、IP信息、用户信息进行不同策略的灰度服务筛选
5:筛选后,需要将筛选的服务返回给过滤器,过滤器执行链路调用

多策略主流程实现

我们先把主要运行流程代码实现出来,再编写不同的策略进行切换即可。主流程我们按照如下流程实现 操作:
1:配置服务节点注册到nacos中的版本信息
2:配置服务调用的scheme
3:获取scheme,bootstrap.yml中 uri: scheme://xxx
4:判断当前scheme是否为灰度服务,我们定义灰度服务的scheme为grayLb
5:根据不同策略获取灰度服务实例
6:封装灰度实例地址信息(将 http://car-version1/car 换成
http://192.168.211.1:18082/car)
7:灰度服务链路调用

version和scheme配置

修改 carv1和 carv2的bootstrap.yml,如下操作:
在这里插入图片描述
修改 gray-gateway的bootstrap.yml
在这里插入图片描述
上图代码如下:

gateway: 
routes: 
# 通过访问http://localhost:8001/car,来测试权重 - id: car-version1 
uri: lb://car-version 
predicates: 
- Path=/car/** 
- Weight=group1, 8 
- id: car-version2 
uri: lb://car-version 
predicates: 
- Path=/car/** 
- Weight=group1, 2 
#灰度版本路由配置 
- id: car-gray 
uri: grayLb://car-version 
predicates: 
- Path=/api/** filters: 
- StripPrefix=1 
灰度服务实例获取

基于API网关灰度发布需要拦截用户请求,按照业务需求实现实例服务选择,我们可以使用拦截器拦截 GrayFilter,实现 ReactorServiceInstanceLoadBalancer自定义实例选择规则,由于API网关实现 灰度操作会涉及大量代码,每次添加代码我们都用关系图展示出来,更有助于分析,代码关系图如下:
在这里插入图片描述
我们首先准备一个过滤器 GrayFilter,过滤器中要调用负载均衡器,所以需要 创建2个对象,分别为 LoadBalancerClientFactory和 LoadBalancerProperties,过滤器代码如 下:

@Configuration 
public class GrayFilter implements GlobalFilter, Ordered { 
//负载均衡构建工厂对象 
private LoadBalancerClientFactory clientFactory; 
//负载均衡属性对象 
private LoadBalancerProperties properties; 
public GrayFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) { 
this.clientFactory = clientFactory; 
this.properties = properties; 
} 
/***** 
* 拦截所有请求 
* @param exchange:用户请求响应的所有对象 
* @param chain:链路调用对象 
*/ 
@Override 
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { 
//uri中以grayLb开始的路由都是灰度系统调用 
//灰度系统调用->调用LoadBalancer的choose()方法获取实例信息 
//灰度系统调用->将用户请求的url地址中的域名换成真实服务的IP、端口号 
//灰度系统调用->调用下一个过滤器链路 
//非灰度系统请求,直接调用下一个过滤器链路 
return chain.filter(exchange); 
}
//最后执行 
@Override 
public int getOrder() { 
return Ordered.LOWEST_PRECEDENCE; 
}
} 

我们修改 gray-gateway,后面所有操作都直接操作 gray-gateway服务,添加负载均衡实例筛选对象 GrayLoadBalancer取代默认的负载均衡筛选操作,我们先把该类创建完成, 筛选实例逻辑根据不同策略一步一步实现,代码如下:

public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer { 
/***** 
* 根据不同策略选择指定服务实例 
* @param request:用户请求信息封装对象 
* @return 
*/ 
@Override 
public Mono<Response<ServiceInstance>> choose(Request request) { return null; 
} 

因为我们需要在 GrayLoadBalancer中从服务集合中筛选一个符合调用的服务实例,所以每次需要把服 务集合传到 GrayLoadBalancer中,在 GrayLoadBalancer中创建带参构造函数,创建 ServiceInstanceListSupplier集合对象,该对象封装了单个服务实例,代码如下:
在这里插入图片描述
我们需要在 GrayFilter过滤器中调用真实实例,可以在 GrayFilter编写一个choose方法调用 GrayLoadBalancer中的 choose方法,关系图如下:
在这里插入图片描述
修改GrayFilter添加方法调用 GrayLoadBalancer.choose()方法来获取指定 服务实例信息,该方法会用到 LoadBalancerClientFactory加载服务列表, LoadBalancerProperties可以动态设置服务属性(等会会用到),代码如下:

/**** 
* 调用GrayLoadBalancer中的choose()方法获取服务实例 
* @return 
*/ 
public Mono<Response<ServiceInstance>> choose(ServerWebExchange exchange){ //获取Uri 
URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); 
//创建GrayLoadBalancer 
GrayLoadBalancer grayLoadBalancer = new GrayLoadBalancer( 
clientFactory.getLazyProvider(uri.getHost(), 
ServiceInstanceListSupplier.class),//服务集合封装对象 
uri.getHost()//服务名字 
);
//调用GrayLoadBalancer.choose() IP、ID(header) 
HttpHeaders headers = exchange.getRequest().getHeaders(); 
return grayLoadBalancer.choose(new DefaultRequest<HttpHeaders>(headers)); 
}
服务真实地址更换

我们在Gateway的配置文件中配置地址是 lb://car-version,加上用户访问地址后是 lb://car- version/api/car,但这并不是真实地址,我们需要把它换成真实地址,也就是
http://192.168.211.1/api/car,我们在 GrayFilter中创建一个方法,实现地址替换,

在这里插入图片描述
实现代码如下:

/**** 
* 将请求地址中的域名换成真实服务的IP、端口号 
* @return 
*/ 
public Mono<Response<ServiceInstance>> 
setInstanceInfo(Mono<Response<ServiceInstance>> 
serviceInstanceResponse,ServerWebExchange exchange){ 
return serviceInstanceResponse.doOnNext(new 
Consumer<Response<ServiceInstance>>() { 
@Override 
public void accept(Response<ServiceInstance> serviceInstance) { //获取url 
URI uri = exchange.getRequest().getURI(); 
//获取真实服务地址信息     IP、端口 
DelegatingServiceInstance delegatingServiceInstance = new 
DelegatingServiceInstance(serviceInstance.getServer(),null); 
//将用户请求的url地址换成真实IP、端口 
URI requestURI = 
LoadBalancerUriTools.reconstructURI(delegatingServiceInstance, uri); 
//将requestURI添加到exchange的属性中 
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR,re questURI); 
} 
}); 
} 
主流程实现

所有的主流程已经编写完成,我们可以编写流程调用了,在 GrayFilter中的 filter方法中执行如下 调用:

/***** 
* 拦截所有请求 
* @param exchange:用户请求响应的所有对象 
* @param chain:链路调用对象 
*/ 
@Override 
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //获取scheme,bootstrap.yml中     uri: scheme://xxx 
URI uri = 
exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR); 
String schemePrefix = 
exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR); 
//判断当前scheme是否为灰度服务,我们定义灰度服务的scheme为grayLb 
if(uri != null && ("grayLb".equals(uri.getScheme()) || 
"grayLb".equals(schemePrefix))){ 
//根据不同策略获取灰度服务实例 
Mono<Response<ServiceInstance>> responseInstance = 
this.choose(exchange); 
//封装灰度实例地址信息(将     http://car-version1/car 换成 
http://192.168.211.1:18082/car) 
responseInstance = setInstanceInfo(responseInstance,exchange); 
//灰度服务链路调用 
return  responseInstance.then(chain.filter(exchange)); 
} 
return chain.filter(exchange); 
} 

灰度系统权重分流

用户访问灰度系统的时候,灰度系统也不一定是单个节点,我们很多时候也需要做权重分流,机器性能 好的服务器处理流量多,机器性能弱的服务器处理流量少。
我们假设一个场景: carv1和 carv2都是灰度系统,在他们两个系统之间实现权重控制,该如何做 呢?
自定义权重需要配置Nacos元数据权重节点,在程序中获取元数据权重,并计算权重 getServiceInstanceResponseWithWeight,计算权重可以使用成熟的工具 `WeightMeta和 WeightRandomUtils,对象方法关系图如下:
在这里插入图片描述

自定义权重分析

修改GrayLoadBalancer添加权重实例筛选方法

/****** 
* 权重选择一个实例 
* @return 
*/ 
public Response<ServiceInstance> weight(List<ServiceInstance> instances){ //封装Map<ServiceInstance,Integer>  实例:权重 
Map<ServiceInstance,Integer> weightMap = new HashMap<ServiceInstance, Integer>(); 
for (ServiceInstance instance : instances) { 
//获取元数据中的权重 
Map<String, String> metadata = instance.getMetadata(); 
Integer serviceWeight =Double.valueOf( metadata.get("nacos.weight") 
).intValue(); 
//存储到weightMap中 
weightMap.put(instance,serviceWeight); 
} 
//将服务权重Map对象封装成WeightMeta对象 
WeightMeta<ServiceInstance> weightMeta = 
WeightRandomUtils.buildWeightMeta(weightMap); 
//随机选择一个服务 
ServiceInstance serviceInstance = weightMeta.random(); 
return serviceInstance==null? new EmptyResponse() : new DefaultResponse(serviceInstance); 
} 
权重筛选调用

在 GrayLoadBalancer中创建 getInstanceResponse方法,用于调用不同策略方法获取指定服务实 例,并在 choose方法中实现对 getInstanceResponse的调用,代码如下:

/***** 
* 根据不同策略选择指定服务实例 
* @param request:用户请求信息封装对象 
* @return 
*/ 
@Override 
public Mono<Response<ServiceInstance>> choose(Request request) { 
//获取所有请求头 
HttpHeaders headers = (HttpHeaders) request.getContext(); 
//服务列表不为空 
if (this.serviceInstanceListSupplierProvider != null) { 
//获取有效的实例对象 
ServiceInstanceListSupplier supplier = 
this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListS upplier::new); 
//获取有效实例集合 
Mono<List<ServiceInstance>> next = supplier.get().next(); 
//按照指定路由规则查找符合的实例对象 
return supplier.get().next().map(list->getInstanceResponse(list)); 
} 
return null; 
} 
/****** 
* 获取实例 
* @param instances 
* @return 
*/ 
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) { 
//找不到实例 
if (instances.isEmpty()) { 
return new EmptyResponse(); 
}else{ 
//权重路由 
return weight(instances); 
} 
} 
  1. 初始化配置
    创建 GrayBalanceConfig执行初始化配置:
@Configuration 
public class GrayBalanceConfig { 
/**** 
* 过滤器配置 
*/ 
@Bean 
@ConditionalOnMissingBean({GrayFilter.class}) 
public GrayFilter grayFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) { 
return new GrayFilter(clientFactory, properties); 
} 
} 
  1. 测试
    启动 carv1和 carv2以及 gray-gateway,并访问 http://localhost:8001/api/car,可以看到访问 结果会出现如下结果,并且出现比例为1:1 ,权重比例可以在Nacos中调整
    Nacos中权重调整
    在这里插入图片描述
    调整后服务调用比例为6:1
    在这里插入图片描述

灰度发布版本分流

版本灰度发布一般适用于App,用户每次请求后台会将版本号携带过来,版本号可以在每次向后台发起 请求的时候把版本号一起塞到请求头中,后台服务获取请求头中的版本信息,再根据版本信息匹配 Nacos中注册的服务元数据的版本号,如果匹配上了,则进行服务权重筛选找到最符合的服务实例信 息。
在微服务网关这里实现版本分流,我们可以先在程序配置文件中向Nacos元数据写入版本号,并在实例 选择出创建方法 getServiceInstanceResponseByVersion获取Nacos中的版本,指定当前服务需要调 用的版本,并按照权重规则下沉调用,关系图如下:
在这里插入图片描述

  1. 版本号路由筛选
    在 GrayLoadBalancer中添加版本筛选方法,确认版本后,多个版本的服务可能是集群,因此还要做权 重筛选,代码如下:
/*** 
* 版本号选择 
*/ 
public Response<ServiceInstance> version(List<ServiceInstance> instances,String version){ 
//存储所有有效服务 
List<ServiceInstance> serviceInstances = new ArrayList<ServiceInstance>(); //循环所有服务 
for (ServiceInstance instance : instances) { 
//对比元数据中是否包含版本号信息 
Map<String, String> metadata = instance.getMetadata(); 
//如果包含,则将服务添加到被调用的服务集合中 
for (Map.Entry<String, String> entry : metadata.entrySet()) { 
String key = entry.getKey(); 
String value = entry.getValue(); 
if(key.equals("version") && value.equals(version)){ serviceInstances.add(instance); 
continue; 
} 
} 
} 
//根据权重选择 
return weight(serviceInstances); 
} 

在 GrayLoadBalancer.choose和 GrayLoadBalancer.getInstanceResponse中分别把请求头信息传 递过来:
GrayLoadBalancer.choose:

return supplier.get().next().map(list->getInstanceResponse(list,headers)); 

GrayLoadBalancer.getInstanceResponse:

/******
* 获取实例 
* @param instances 
* @return 
*/ 
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances,HttpHeaders headers) { 
//找不到实例 
if (instances.isEmpty()) { 
return new EmptyResponse(); 
}else{ 
//获取版本号 
String versionNo = headers.getFirst("version"); 
//权重路由、         根据版本号+权重路由 
return StringUtils.isEmpty(versionNo)? weight(instances) : 
version(instances,versionNo); 
} 
} 
  1. 测试
    在这里插入图片描述
    拷贝 carv2一份,改名 carv3,端口 18083, version=v2,进行测试,效果如下:
    在这里插入图片描述
    此时测试,只会调用 carv2和 carv3服务。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1266369.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Python3】【力扣题】367. 有效的完全平方数

【力扣题】题目描述&#xff1a; 【Python3】代码&#xff1a; 1、解题思路&#xff1a;Python函数。num的平方根 或者 num的0.5次幂。 知识点&#xff1a;float.is_integer(...)&#xff1a;判断浮点数的值是否等于整数。也可以&#xff1a;浮点数.is_integer()。 pow(a,b)&…

婴儿专用洗衣机哪个牌子比较好?宝宝洗衣机推荐

对于宝妈来说有一款小型可以自动杀菌的洗衣机真的非常重要。我们日常生活中一般会把一些贴身衣物和较特殊的面料的衣物用于手洗&#xff0c;而手洗的过程会相对来说比较麻烦&#xff0c;而且还可能洗不干净&#xff0c;而传统的洗衣机采用了双缸式容易滋生霉菌&#xff0c;容易…

P8A012-A016组策略安全

账户策略 【预备知识】 组策略&#xff08;Group Policy&#xff09;是Microsoft Windows系统管理员为用户和计算机定义并控制程序、网络资源及操作系统行为的主要工具。通过使用组策略可以设置各种软件、计算机和用户策略。 【实验步骤】 网络拓扑&#xff1a;server2008A…

Python hashlib库解析:数据安全加密必备指南

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 哈希函数在计算机科学中扮演着重要的角色。它是一种能够将任意长度的数据转换成固定长度的唯一值的算法。Python提供了hashlib库&#xff0c;用于生成哈希摘要&#xff0c;提供了常见的哈希算法&#xff0c;如MD…

查看mysql 或SQL server 的连接数,mysql超时、最大连接数配置

1、mysql 的连接数 1.1、最大可连接数 show variables like max_connections; 1.2、运行中连接数 show status like Threads_connected; 1.3、配置最大连接数&#xff0c; mysql版本不同可配置的最大连接数不同&#xff0c;mysql8.0的版本默认151个连接数&#xff0c;…

超声波清洗机器哪个品牌好用?这四款都夸的超声波清洗机

超声波清洗是一种先进的清洗技术&#xff0c;它利用高频振动产生微小气泡来对物体进行清洗。这些微小气泡在物体表面不断振动&#xff0c;使得污垢和油脂被震碎并脱落。这种方法可以有效地去除眼镜上的污垢、油脂和细菌&#xff0c;从而提高眼镜的清洁度。与传统的清洗方法相比…

Vue2 若依框架头像上传 全部代码

<template><div><div class"user-info-head" click"editCropper()"><img v-bind:src"options.img" title"点击上传头像"class"img-circle img-lg" /></div><el-dialog :title"title&…

不同路径(力扣LeetCode)动态规划

不同路径 题目描述 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&…

VT-MSPA1-12-1X/V0直动式比例压力阀放大器

适用于控制不带电位移反馈的比例压力阀、比例流量阀、比例方向阀的控制;差动输入;1个脉冲输出端口;函数发生器;带斜坡时间可调的斜坡生器&#xff08;可上升和下降斜坡&#xff09;; 可调电流调节器;电源带错极保护;LED 电磁铁动作显示;&#xff08;LED 的亮度与流过电磁铁的电…

Zip压缩包密码如何取消?有密码?无密码?

Zip压缩包可以进行加密&#xff0c;相信大家都很了解&#xff0c;那么zip压缩包取消密码&#xff0c;大家了解多少呢&#xff1f;有密码的情况下&#xff0c;有哪些方法可以取消密码&#xff1f;无密码又该如何取消密码&#xff1f;今天将方法总结分享给大家。 最原始的方法&a…

【100个Cocos实例】完蛋,你看我在刮刮乐中刮到了什么?

引言 什么&#xff1f;还能刮出这个&#xff1f; 相信大家对刮刮乐都不陌生&#xff0c;昨天听到小伙伴说花了20买了张刮刮乐&#xff0c;结果刮中了20&#xff0c;白忙活一场。 假如我们要在Cocos游戏开发中实现刮刮乐效果&#xff0c;应该怎么做呢&#xff1f; 本文将介绍…

Angular的Ng-Zorro组件库通知提醒框知识点

目录 系列文章目录 一、在angular中引入通知提醒框服务 二、创建通知提醒框 提示&#xff1a;在实际操作中根据id移除通知提醒框的方法尚未熟悉&#xff0c;根据此文档进行巩固 一、在angular中引入通知提醒框服务 constructor(private notifition: NzNotificationService) {}…

pip 安装软件出现 [No space left on device]

问题&#xff1a; /home文件下空间满了&#xff0c;安装软件可能会出现这个问题 解决方法&#xff1a; pip install --no-cache-dir 安装包名

跨链原子交换

原子交换的想法于 2013 年首次在 BitcoinTalk 论坛上提出&#xff0c;它可以实现两个区块链之间的代币交换。 这些交换是原子的&#xff0c;因为双方要么收到对方的硬币&#xff0c;要么都保留自己的硬币。 一方不可能欺骗另一方。 它不依赖任何可信赖的第三方&#xff0c;消除…

TOD和PPS精确时间同步技术

介绍 PPS和TOD PPS和TOD是两种用于精确时间同步的技术&#xff0c;它们在许多领域都有广泛的应用&#xff0c;总的来说&#xff0c;PPS和TOD被广泛应用于各种需要高度精确时间同步的领域&#xff0c;包括通信、测量、测试、系统集成和计算机网络等。 一、PPS PPS&#xff08…

HCIP-十二、BGP常用属性

十二、BGP常用属性 实验拓扑实验需求及解法1.IP 地址已配置&#xff0c;自行测试直连。2.AS100 中运行 OSPF3.AS200 中运行 ISIS4.运行 BGP5.发布 BGP 路由6.修改起源属性 Origin7.修改 AS-path8.修改本地优先 Local-preference9.修改 MED 实验拓扑 实验需求及解法 本实验模拟…

java编程:数组代表着苹果期货在 9 天内的价格变化,其中第 n 个元素是苹果在第 n 天的 价格,你可以进行最多三次交易,设计算法,9 天内赚到最多的钱

1 题目 编程题&#xff1a;下面的数组代表着苹果期货在 9 天内的价格变化&#xff0c;其中第 n 个元素是苹果在第 n 天的 价格&#xff0c;你可以进行最多三次交易&#xff0c;设计一个算法&#xff0c;9 天内如何赚到最多的钱&#xff1f;价格数组&#xff1a; [8, 9, 2, 5, …

SSM训练营管理系统开发mysql数据库web结构java编程计算机网页源码eclipse项目

一、源码特点 SSM 训练营管理系统是一套完善的信息系统&#xff0c;结合springMVC框架完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数据库&#xff0c;系 统主要采用B/S模…

【EasyExcel实践】导出多个sheet到多个excel文件,并压缩到一个zip文件

文章目录 前言正文一、项目依赖二、封装表格实体和Sheet实体2.1 表格实体2.2 Sheet实体 三、核心实现3.1 核心实现之导出为输出流3.2 web导出3.3 导出为字节数组 四、调试4.1 构建调试用的实体类4.2 控制器调用4.3 测试结果 五、注册大数转换器&#xff0c;长度大于15时&#x…

台灯哪种灯光对眼睛好?精选高品质的护眼台灯

近几年越来越多家长开始重视孩子的视力健康问题&#xff0c;要知道如今我国儿童青少年的近视率已经达到了52.7%&#xff0c;也就是说平均每十个儿童青少年中就有五个是存在视力问题的&#xff0c;而且近视的人群是越往高年级的学生占比越多。不过目前也发现了不少低年级的孩子出…