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

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.mzph.cn/news/180999.shtml

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

相关文章

笔记-PC端wireshark采集FPGA数据的操作

wireshark采集FPGA的数据 目录 一、准备工作二、操作步骤 一、准备工作 1、软件&#xff1a;wireshark 2、平台&#xff1a;PC&#xff08;本人是win11&#xff09;、带有以太网功能的zynq平台 3、网线: 用网线连接zynq板子和PC的以太口端口 二、操作步骤 1、打开任务管理器…

android开发:安卓13Wifi和热点查看与设置功能

近日对安卓热点功能做了一些技术验证&#xff0c;目的是想利用手机开热点给设备做初始化&#xff0c;用的是安卓13&#xff0c;简言之&#xff1a; 热点设置功能不可用&#xff0c;不可设置SSID和密码&#xff0c;不可程序控制开启关闭&#xff0c;网上的代码统统都过时了Loca…

JVM执行引擎

目录 &#xff08;一&#xff09;执行引擎概述 &#xff08;二&#xff09;Java代码编译和执行过程 &#xff08;三&#xff09;机器码&#xff0c;指令&#xff0c;汇编语言&#xff0c;字节码 1、机器码 2、指令 3、指令集 4、汇编 5、字节码 &#xff08;四&#x…

在Visual Studio Code中安装加速TypeScript程序开发的插件

在Visual Studio Code中安装加速TypeScript程序开发的插件 Install Extensions on Visual Studio Code for TypeScript Application Development By Jackson 2023-11-28 众所周知&#xff0c;微软的Visual Studio Code是一款轻量级、功能强大的集成开发环境。它支持各种编程语…

linux CentOS MobaXterm 通过X11 Forwarding 在本地开启图形可视化窗口

第一步 操作系统安装图形界面 X11 Forwarding dnf install xorg-x11-xauth xorg-x11-fonts-* xorg-x11-font-utils xorg-x11-fonts-Type1 xclock第二步 修改参数&#xff0c;启用X11 Forwarding vim /etc/ssh/sshd_config修改参数X11Forwarding yes和X11UseLocalhost no #Al…

AIGC系列之:GroundingDNIO原理解读及在Stable Diffusion中使用

目录 1.前言 2.方法概括 3.算法介绍 3.1图像-文本特征提取与增强 3.2基于文本引导的目标检测 3.3跨模态解码器 3.4文本prompt特征提取 4.应用场景 4.1结合生成模型完成目标区域生成 4.2结合stable diffusion完成图像编辑 4.3结合分割模型完成任意图像分割 1.前言 …

Docker容器常用命令

文章目录 启动类命令帮助类命令镜像命令列出本地主机上的镜像在远程仓库中搜索镜像下载镜像保存镜像加载 tar 包为镜像查看占据的空间删除镜像 虚悬镜像命令自动补全新建启动容器启动交互式容器启动守护式容器 列出正在运行的容器容器其他启停操作启动已经停止的容器重启容器停…

c++|类与对象(中)

目录 一、类的6个默认成员函数 二、构造函数 2.1概念 2.2七大特性 三、析构函数 3.1概念 3.2特性 四、拷贝构造函数 4.1概念 4.2特性 五、赋值运算符重载 5.1运算符重载 5.2赋值运算符重载 5.3前置和后置重载 六、const成员函数 七、取地址及const取地址操作符重…

【算法】算法题-20231129

这里写目录标题 一、15. 三数之和二、205. 同构字符串三、383. 赎金信 一、15. 三数之和 提示 中等 6.5K 相关企业 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] …

Redis中的缓存穿透、雪崩、击穿(详细)

目录 一、概念 1. 缓存穿透&#xff08;Cache Penetration&#xff09; 解决方案&#xff1a; 2. 缓存雪崩&#xff08;Cache Avalanche&#xff09; 解决方案&#xff1a; 3. 缓存击穿&#xff08;Cache Breakdown&#xff09; 解决方案&#xff1a; 二、三者出现的根本原…

中英双语大模型ChatGLM论文阅读笔记

论文传送门&#xff1a; [1] GLM: General Language Model Pretraining with Autoregressive Blank Infilling [2] Glm-130b: An open bilingual pre-trained model Github链接&#xff1a; THUDM/ChatGLM-6B 目录 笔记AbstractIntroductionThe design choices of GLM-130BThe …

Mac苹果视频剪辑:Final Cut Pro Mac

Final Cut Pro是一款由Apple公司开发的专业视频非线性编辑软件&#xff0c;是业界著名的视频剪辑软件之一。它最初发布于1999年&#xff0c;是Mac电脑上的一款独占软件。Final Cut Pro具有先进的剪辑工具、丰富的特效和颜色分级、音频处理等功能&#xff0c;使得用户可以轻松地…

elasticsearch 实战

文章目录 项目介绍导入项目 Elasticsearch Java API 查询文档快速入门发起查询请求解析响应完整代码 match查询精确查询布尔查询排序、分页高亮高亮请求构建高亮结果解析 项目介绍 本项目是一个由spring boot 3.0.2在gradle 8.4和java 21的环境下搭建的elasticsearch项目demo&…

JSP forEach 标签遍历map集合

之前我们说了 普通list 单纯按数量循环 bean类型list的遍历方式 那么 我们forEach标签 也能循环map语法非常简单&#xff0c;和循环list基本是一样的 我们直接上jsp代码 <% page import"java.util.Map" %> <% page import"java.util.HashMap" %…

JAVA基础进阶(十)

一、File类常用的API File类的对象可以用来表示文件或者文件夹,下面是File类常用的API。 1.1、File类判断文件类型、获取文件信息功能 代码中的体现: 1.2、File类的创建和删除方法 代码中的体现: 1.3、文件夹遍历方法 代码中的体现: 二、字符集 字符集&#xff08;Character…

C#常用运算符的优先级

前言 运算符在C#编程语言中扮演着重要的角色&#xff0c;用于执行各种计算和操作。了解运算符的优先级是编写高效和正确代码的关键。本文将深入探讨C#中38个常用运算符的优先级划分和理解&#xff0c;并提供详细的说明和示例&#xff0c;以帮助读者更好地理解运算符的使用。 目…

Stable Video Diffusion重磅发布:基于稳定扩散模型的AI生成视频

最近&#xff0c;stability.ai发布了稳定视频扩散&#xff0c;这是stability.ai第一个基于图像模型稳定扩散的生成视频基础模型。现在可以在研究预览中看到&#xff0c;这个最先进的生成人工智能视频模型代表着stability.ai在为每种类型的人创建模型的过程中迈出了重要的一步。…

FFA 2023|字节跳动 7 项议题入选

Flink Forward 是由 Apache 官方授权的 Apache Flink 社区官方技术大会&#xff0c;作为最受 Apache Flink 社区开发者期盼的年度峰会之一&#xff0c;FFA 2023 将持续集结行业最佳实践以及 Flink 最新技术动态&#xff0c;是中国 Flink 开发者和使用者不可错过的的技术盛宴。 …

中小型工厂如何进行数字化转型

随着科技的快速发展和市场竞争的日益激烈&#xff0c;中小型工厂面临着诸多挑战。为了提高生产效率、降低成本、优化资源配置&#xff0c;数字化转型已成为中小型工厂发展的必经之路。中小型工厂如何进行数字化转型呢&#xff1f; 一、明确数字化转型目标 在进行数字化转型之前…

怎么把dwg格式转换pdf?

怎么把dwg格式转换pdf&#xff1f;DWG是一种由AutoCAD开发的二维和三维计算机辅助设计&#xff08;CAD&#xff09;文件格式&#xff0c;它的名称是“绘图&#xff08;Drawing&#xff09;”的缩写。DWG文件通常包含了设计图纸、模型和元数据等信息&#xff0c;并且被广泛用于工…