导语 | Tars 是由腾讯主导开源,并捐献给Linux基金会的微服务RPC框架。而TarsBenchmark的推出是对Tars生态的进一步完善,它支持的在线压测功能极大降低开发测试人员在线评测服务性能使用门槛。本文是对腾讯专家工程师陈林峰在云+社区沙龙online的分享整理,希望与大家一同交流。

点击视频查看完整直播回放

一、TarsBenchmark是什么 

1. 常见测压工具

我们在服务后台的一些APP上线之前,通常会做一些性能的评估,然后会评测一下。例如开发的项目大概可以服务多少用户,以及能够承担多大的并发量?

(1)Apache Bench

经典的性能评测工具AB,也叫Apche Beach,它是一个http协议服务的压测工具,也是众多WebAPP 的经典压测工具,采用C语言开发,方便易用也具备较好的性能。

但是它也有不足的地方,主要在于它采用的是单线程模式,而我们现在后台的服务器很多都是多核,因此它不能发挥我们服务器的全部性能。

(2)Wrk

新的压测工具Wrk,它弥补了AB单线程设计的不足。它采用的网络是事件驱动的方式,在网络IO方面有比较好的表现。

另外它支持脚本语言。可以在lua生成一些随机内容。现在大部分后台的服务都是采用分布式的部署,如果仅对某一个请求不断的重放,容易导致单机高负载,而不能反应真实集群的服务能力。而wrk在这两方面进行了改进,因此它的应用非常地广泛。

(3)GHZ

在微服务里还有一个很典型的压测工具GHZ,他是GRPC框架下的一个压测工具,开发语言是golong,因此具备很好的并发能力。

它的用例采用JSON的格式组织,它利用PB良好的反射特性很容易将JSON的用例转换成网络传输所需要的二进制方式,因此使用起来也非常方便。

(4)JMeter

最后给大家介绍一下JMeter工具,它是采用Java语言开发,提供了图形界面交互形式,支持分布式部署,可以弥补单机性能的不足。

但是因为JMeter相当于采用线程的模型,用一个线程模拟一个用户请求,实际上当线程积累到一定的数量之后,线程调度和竞争会带来一些额外的CPU开销,所以它的单机性能存在一些不足。

2. TarsBenchmark是什么? 

常见的压测工具可能还有很多,这里就不一一展开,下文主要介绍TarsBenchmark,看看这款压测工具到底有哪些特性?

(1)单机运行的压测工具 

TarsBenchmark是基于Tars生态的一个压测工具,它主要服务Tars Service,而Tars是腾讯开源的多语言、高性能、易运维的微服务框架,它的通讯协议采用Tars,而Tars是类似于PB二进制的一种传输的协议,并且它是由腾讯完全自主研发的。目前参与的开发人数已经快接近300人了,在 Github 上你输入TarsCloud 就可以进入官方介绍文章了解我们的Tars。

Tars支持C++等多种开发语言,Tars相对其他开发框架比如PRPC等,还提供一个服务平台,并且可以帮助开发者和企业快速构建稳定可靠的分布式微服务应用,开发人员只需要关注业务逻辑,从而提高研发和运营效率。

(2)一个支持云端压测平台

TarsBenchmark不仅可以在单机上运行,它还提供一个云端的压测Web平台,即可以支持分布式压测,让开发者可以很方便从容进行TarsService压测,来评估Tars服务的性能。

(3)不仅仅ForTarsService

当你的团队里面使用非Tars协议服务,它也可以很容易满足。在后面的TarsBenchmark使用中,我会为大家介绍怎样去开发非Tars协议,用我们的工具很容易满足第三方协议的压测。

3. 解决了哪些问题 ?

那么TarsBenchmark到底解决了哪些问题呢?主要在于以下三点:

  • 高性能:充分发挥多核CPU计算能力,8核机器可输出40Wtps能力;

  • 高扩展性:支持任意一个Tars接口压测,友好支持非Tars协议;

  • 简单易用:用例采用JSON格式,编写便利,支持线上云压测 。

二、TarsBenchmark原理 

1. 工具压测原理 

(1)多进程

首先来介绍高性能设计的实现,我们在上文也分析了AB和Wrk,其中Wrk对AB的升级主要在于采用多线程实现,而TarsBenchmark也是采用多进程的方式。

在主进程上我们会根据服务器的物理核数去fork相等数量的压测进程,各压测进程之间是完全隔离的,也就是独立去运行,避免了进程与进程之间互相争夺临界资源。当然这个压测进程数是可以通过参数指定的,默认会根据CPU的有效核数去Fork对应的一个进程。

(2)网络处理

在网络方面采用的是事件驱动的方式,就是通过定时发包器发包,基于网络事件收发,有效避免网络IO的阻塞。

另外在连接方面我们采用连接池,在连接池对每个连接采用连接复用的方式。在我们分析AB压测原理的时候发现:当AB链接建立起来并发完一个报文出去的时候,收到这个报文之后才会发起下一个报文,因此在这个链接中它会有等待操作,不能完全的把链接利用起来和匀速产生QPS。

而TarsBenchmark是基于连接复用方式,不依赖于对端是否返回。对于外部服务返回这个场景,当我们在用AB压测的时候,有时候会发现服务器的返回非常及时,那么这个时候AB就可以达到很高的一个输出能力;但是当服务端返回耗时较高的时候,AB输出能力就会出现乘数级的下降。

例如: CGI在20ms的返回和200ms的返回,相同链接数会影响AB十倍的性能输出差异,因此我们觉得链接设计方式是一种浪费。

在TarsBenchmark设计的时候考虑到这一点,它基于协议包的序号可以做到不依赖服务端回包时机去选择下一个报文发送时间,通过返回包序号去定位到发包的时间,从而计算压测过程当中的状态信息。

(3)多维监控

我们的压测进程和主进程之间有一些通讯,是通过无锁队列来完成信息交互,包括上文提到的耗时的统计、错误码的统计都是通过无锁队列来完成的。

(4)协议扩展

另外在协议这一块,我们采用的是协议代理工厂这种模式,TarsBenchmark默认提供的是Tars协议的压测。但是如果你有一些私有协议,可以参考Tars协议的实现,也可以集成进来,代理工厂可以自动识别新增加的协议。

(5)支持随即数据

在Tars协议压测中,TarsBenchmark可以支持随机数据的生成,另外也提供随机函数,它可以有效避免压测过程中对唯一的请求进行重放。

(6)自动生成用例

在Tars协议这一块,我们还提供一个工具,用以自动生成Tars服务所需的测试用例。TarsBenchmark的测试用例采用的是JSON格式,用户只需要编辑Value可以很轻松得发起压测。

2. 分布式压测原理

分布式压测主要是解决同时多人使用平台的问题。它的主要部件由四个部分组成。

第一部分是WebUI入口,它集成在Tarsweb上面,提供了一套非常简单的交互。

第二部分是CGI,主要做测试用例的管理,其中包括数据库的CRUD操作、权限的控制。

第三、四部分是后台服务,包含一个压测Admin服务和Node服务(压测node)。Node服务负责执行具体的压测任务,使用工具压测结束之后随系统自动销毁,而在分布式压测的时候,是依赖压测admin来进行销毁的。

为降低复杂度,TarsBenchmark在设计的时候采用的是多线程方式(linux系统调度CPU资源是以线程为单位)。压测admin会接受CGI的指令,比如用户在输入的QPS调度所需的压测node时,因为压测node调度最小单元是线程,一个线程可以设置3~5万的输出能力,然后根据这种单线程能力我们再计算需要的线程数,最后把它分配到我们的压测node里面去执行压测。

TarsBenchmark会把这些数据会暂存在压测Admin里面,压测Admin服务一般推荐主备部署的模式,分布式压测用例编写也是采用JSON格式,后台有个工具对每个接口帮助用户自动生成demo用例。用例的编写也非常的简单。TarsBenchmark也会提供测试功能,方便用户压测的时候去验证当前功能是否正常。

3. 协议转换

我们知道 Tars 是一种二进制、可扩展的跨语言的协议,它支持C++、PHP、Java和Node,其本质是 TLV 的协议。但是整个压测,如果采用 JSON 这种组织,如何转成二进制?这可能是整个压测最大的一个难点,那么TarsBenchmark是怎么去解决这个问题的呢?

首先我们分析一下Tars的协议编解码原理,T包含Tag和Type,传输的时候二进制前面有四个 bit ,表现为一个 Tag。接下来是表示数据的Type, Tars 协议最多可以支持 16 种数据类型,目前已经编到 14 种。这是Tars最开始的时候就这样设计的,目前我们数据的类型并没有超出我们的编码,说明当时的这种设计还是具备一定的前瞻性。

这其中有七种基本类型,还有三类复杂类型。举个例子,以很常见的两个结构体嵌套为例,它编出来的二进制结果如上图所示,最右边是最终编码出来的一个效果,首先我们看到这个是B结构体二进制编码的效果,从第一个 0 开始,代表了它就是这个 a 变量的一个 Tag,我们查找 a,发现它是结构体的起始,进入到a结构体之后第一位也是 0 ,tag 对应的是 int 数据,然后是两个字节的数据,4 进 2 的话对应 1、2、3 和 4。

16 我们可以看到 1 是它的 bits1,对应 s 的变量,6 是代表它的实俊,它的字节长度是 4,我们可以看到是跟的 616、63 和 64,对应就是abcd,然后0p结束代表这个结构体已经结束了。接下来就是 14,我们发现 1 是代表这种 float 数据类型,然后4对应的是四个bits的数据,反序列化以后对应这样的一个结构体,跟我们中间这个结构体对应起来。

如果采用编辑二进制去压测我们的Tars,我想大部分人都会崩溃,一个简单的结构体可能还可以写出来,但是当你结构体的成员很多或者超过了十个,或者结构体里面又嵌套结构体,估计大部分开发都会抓狂,因此利用这种方式编辑显然是不可取的。

那么TarsBenchmark是怎么做的呢?我们会根据 Tars 的接口文件,利用 IDL工具,首先将这种语法数生成一个描述文件,描述文件包含三个部分:Tag、Type和 Name。对于Web平台会存到DB里面去,采用RPC 调用的Tars在body之外还有一层head,它有四个关键信息: 一个请求 id, 服务名称,函数名称,以及参数二进制body。

在实现的TarsBenchmark会去查这个描述文件的Name,然后通过Name获得Tag和Type,再从用例里面获得Value,再通过Tars的编解码规则写入二进制Buffer里面,最终完成了JSON to Tars的这种转换。

它的逆向转换其实道理是相通的,我们首先把它从二进制还原成Tars的Tag和Type,然后我们通过Tag在描述文件中查到Type和Name,然后通过Name和Value还原JSON的格式,以上是一个RPC调用过程在客户端的协议转换流程。

4. 第三方Service压测 

如果采用第三方协议其实也很简单,实现四个函数接口就可以完成对于非Tars协议的支持。

这四个函数接口分别是是初始化接口,断包接口(采用TCP长链接的流式格式,响应包到哪截止,在这个input函数里面去实现就可以),编码接口,解码接口。

请求编码接口的实现,这里面有一个很重要的参数id,我们系统每次请求会生成一个id,如果这个接口支持就可以把id返回回来。

以我们的Tars为例,我们在head里面有一个id填充进去,RPC响应回来的时候会携带同样的id,TarsBenchmark会根据这个请求应答,计算这个请求的耗时和成功率。如果第三方服务不支持返回序号,需要在isSupportSeq()返回false,它就进入无序模式,无法做到链接复用。

以上就是TarsBenchmark的设计原理。

三、TarsBenchmark的使用 

1. 代码目录结构 

源码路径:

https://github.com/TarsCloud/TarsBenchmark

首先看一下TarsBenchmark的代码结构,主要分为四个部分,从下往上看最开始的是工具,服务,公共模块和资源文件。重点介绍一下公共模块,主要是3部分:协议,网络和监控。

  • 协议支持: 默认会支持两种协议,一种是HTTP协议,一种是Tars的协议。

  • 监控: 压测共同的数据都是通过监控器暂存,无锁队列通信也在此实现。

  • 网络: 采用事件驱动方式,非阻塞Socket模式设计。

2. 代码编译 

代码编译主要是两个部分,一个是TarsCpp,如果有云端压测的需求,TarsWeb也是依赖项。

编译的步骤也很简单,首先将代码克隆下来,新建一个临时目录通过cmake完成编译,就可以生成三个程序,分别是tb、nodeserver和adminserver。很多时候最小的需求只需要一个工具tb,工具可以直接在单机执行,而当你有云端压测需求,执行一下install安装脚本,一键安装到Tarsweb中去。

这里主要有几个参数,一个是web的host,adminserver推荐和Tars基础服务部署在一起,因为它本身不会消耗太多的CPU,但是nodeserver不同,推荐单独部署,并且它是可以水平扩展的。

3. 单机工具压测 

对于单机工具的压测使用,有如下参数可以根据实际情况调整:

  • -c是压测的链接数,-s是最大的qbs的限制,如果没有指定-s就会尝试探测服务端最大性能去实现压测;

  • -D、-P压测目标服务器的端口,目标服务器可以执行多个地址,通过逗号进行区隔;

  • -n参数: 指定压测的进程数。-T指定TCP或者UDP,默认TCP;

  • -p参数: 接口通讯协议,如果是自己的私有协议,可以改成私有协议的名字。

每次压测的周期里面,都会有一些耗时的分布、成功率的统计、平均耗时的P99或者P90的这种统计信息,可以周期性的打印出来,避免出现压测过程当中不了解当前服务的情况,还有就是持续的这种压测时间也是可以通过-i参数去指定的。

4. 云端分布式压测 

云端压测使用十分简单,在TarsWeb接口调试里有一个压测入口,用例可以看出来,你新增的时候会自动生成这些用例,当你压测的时候,你只需要指定这些压测目标IP,QPS就可以随时随地发起压测,并可以看到压测过程中的服务表现。

四、TarsBenchmark成果 

1. 发展历程 

我们是在2016年的时候在腾讯内部开源,最开始它只是一个工具,支持的协议也很少,主要是Tars,而Tars在内部是叫做taf协议。

在2018年之后支持协议数变得非常丰富,之前很古老的服务都可以通过这个工具支持到,目前在内部使用还是十分广泛的。另外它支持云端Web压测,每周大概使用的人数也超过了数百人,不需要登录到IDC机器上,随时随地都可以发起。

今年4月份我们也把它贡献给开源社区TarsCloud,造福社区的小伙伴,欢迎大家多多提出指教。

2. 同类型对比

目前使用的反响非常不错,相对于其他的一些开源工具,这个工具是采用c++语言开发的,有两种模式,一种是多进程,也就是工具采用多进程的模式,在云端压测是采用线程池的模式,支持分布式的压测,单机性能相对AB和Wrk也有一个很好的表现。

最重要的是TarsBenchmark支持匀速的发包,因为它基于连接复用的方式,在连续发送上不依赖请求返回,因此可以做到匀速的发包,也支持随机内容的生成。另外它还支持线上的压测,这也是TarsBenchmark的一个很重要的亮点。

以上就是今天的分享内容,欢迎各位小伙伴们前来参与生态贡献。另外如果对我们的岗位有需求,可以直接发简历到我的邮箱里面来:linfengchen@tencent.com

五、Q&A

Q:wrk和TarsBenchmark之间的区别是什么?如何做压测选型?

A:这个问题很好,刚才我们说wrk相对来说跟我们的TarsBenchmark的网络模型其实是一样的,它是基于多线程以及网络附用的模式。但是wrk是纯http的协议压测工具,http协议可以理解为无序的模式,无法做到链接复用,性能还有一些制约,也没法做到匀速发送。

另外一方面,wrk主要是一个纯HTTP协议,它并不能支持到私有的协议,私有的协议用起来还是有一些费劲,我们的TarsBenchmark在协议扩展性方面会表现的会更好一点。

Q:我们现在TarsBenchmark代码量是多少?

A:我们还是很轻量的,我们统计有效代码是几千行左右。

我印象当中好像3000到4000行之间,是代码量非常少的工程,学习曲线也是非常平缓的,不会太复杂,只要有一些C++语言基础就可以很快的上手。

Q:TarsBenchmark以及TarsJMeter的区别?

A:刚才暖场的时候也说,我们的JMeter是采用线程的模式去模拟用户,在一个线程里面执行一个用户的请求响应,这种线程的调度开销还是非常大的,你试想一下在一个机器上起一万个线程的规模,线程之间因为线程调度依赖操作系统,因此像JMeter的网络模型的性能不会很好。

当然也有一些人对这种JMeter做了一个扩展,因为JMeter最大的应用是具备很好的扩展性,它支持分布式压测,单机不行,可以分布式弥补,这是JMeter比较大的优点。

另外它的上报都是插件的设计思想,还是蛮先进的,比如说我们一些结果的数据,我们可以上报到其他的一些UI语言上面做一些呈现,这一块是JMeter很大的一个优势,我们在做市面上常见的几款压测工具调研的时候,把它们架构的长处融合在一起就最终形成我们的TarsBenchmark。

总结下来,TarsBenchmark有更好的单机性能表现。

Q:是为什么不用golong语言开发

A:这个有一些背景,我们最开始是在2015到2016年做的这款产品,Tars那个时候的生态,在我们内部主要是以C++为主,golong在网络这一块其实做了很多底层的能力支持,我们内源版本也有golong的实现,但是目前没有开源出来。

通过对比发现C++的性能会比golong的网络模型好一点点,主要是体现在编解码这一块效率会更高一点。

Q:Tars支持哪些接口,http支持吗?

A:这个是支持的,你用最新版的Tars开发框架是可以写自己的接口,所以我们的TarsBenchmark本身的话也是支持http协议的压测。

Q:是不是支持这种kafka做一些压测?

A:kafka这些组件基本上都有自己的一些压测工具,其实我个人建议,你用它框架组件的一些压测工具就可以满足了,当然你自己有兴趣去研究一下我们的TarsBenchmark,按我刚才说的思路实现三个函数、四个函数,也可以去完成对你kafka提取的一个压测。

Q:为什么效率会这么高?还可以提高吗?

A:刚才在工具里面我也跟大家介绍了一下,主要是四个方面,一个就是多进程,充分发挥CPU的计算效率。第二方面是我们采用这种非组装的网络模型,第三是基于这种网络链接附用的方式避免链接在那里闲置,第四是所谓的这种通讯都是采用非阻塞的。

还有没有办法继续提高?我们现在也发现了,比如说我们在复杂的Tars接口的编解码效率这块还是会有一些问题,所以如果说还有什么提高的话,主要就是在编解码这块要考虑零拷贝的思想,我们的操作系统和压缩工具之间尽量零拷贝去缓冲区的操作,包括这种二进制的协议以Tars为例,我们把它转换成Tars尽量采用更高效的编解码的效率工具,目前我们在尝试做进一步的提升。

Q:TarsBenchmark目前在腾讯云上面看到相关的产品和解决方案,未来会不会上腾讯云?

A:目前,主要是以开源的这种形式去共建,以开源的方式提供,目前没有上云的计划,如果采用Tars的生态,我们可以免费提供给广大的用户去使用,包括企业和个人。

Q:采用的是 C++ 11 标准设计的吗?

A:这个是对的,我们是采用 C++ 11 的标准去完成的。另外我也受 Tarscloud 官方委托,如果大家对Tars的比较关注,可以关注它的官网,它的源码就是在刚刚的PPT里面,大家可以关注云+社区的公众号回复线上沙龙就可以获取。代码结构可以在我们这个上一期节目,就是Tarscloud能力以及它的参数的一个目录,你可以自己去找相应的你感兴趣的资料去学习或尝试。

Q:大数据产品是否可以压测?

A:你说的大数据产品指的是idp的压测?目前像这种大数据也有自己的一套通讯协议,如果你抓住这个通讯协议的规律,或者你了解它的一些协议设计的方式,其实也是可以的。

讲师简介 

陈林峰

腾讯专家工程师

陈林峰,腾讯专家工程师,TarsBenchmark项目负责人。深入过多次QQ春节红包项目的开发,在高性能后台服务设计方面积累了丰富的经验,在服务性能评测有比较深刻的体会。目前负责QQ社交娱乐业务的后台开发和团队管理。

文章推荐

腾讯看点视频推荐索引构建方案

压测利器:TarsBenchmark正确打开方式相关推荐

  1. 【TARS】压测工具TarsBenchmark

    目录 0.学习链接 1.初识TarsBenchmark 2.TarsBenchmark的框架结构 3.安装部署 3.1 前置条件 3.1.1 TarsCpp版本要求 3.1.2 软件依赖 3.1.3 ...

  2. python论文参考文献名称_Word的正确打开方式(附毕业论文模板)

       ----点击蓝字关注我呀---- 三年前的我搞毕业设计,第一次接触这玩意儿,一脸懵逼 好在我朱哥搞过大创(还是国家级的),当时给我各种科普单片机的知识 搞大创的好处就是当我们不知道是画机械图还是 ...

  3. 【解决方案】谈公众号红包的正确打开方式--传奇创世

    红包,红包.每次群发红包为什么只能是200?原来这是腾讯做出的一个举措.群发有红包?那如果想要微信公众号的红包怎么办? 其实,也是可以实现的.接下来的解决方案就是针对公众号红包做出的,让我们一起来看它 ...

  4. 微信小程序里使用weui的正确打开方式

    自己写微信小程序,发现有weui这样的东西.第一次使用,网上搜到到用法是这样的: 到https://weui.io/#input查看例子,右键查看源码,然后再对照界面和源码使用. 但是这样看到的源码是 ...

  5. win10系统rar文件的正确打开方式

    rar文件怎么打开?基本上所有的windows电脑用户都会有接触到rar文件,特别是我们在传输大文件夹的时候rar文件可以说是必备的.但是最近很多升级到win10系统的用户发现自己的rar文件夹打不开 ...

  6. 万用表测试软件,工具||万用表的正确打开方式!

    原标题:工具||万用表的正确打开方式! 仪表之家,实际可行的为仪表工/自控/热工解决检修时问题 一.引言 数字万用表:一种多用途电子测量仪器,一般包含安培计.电压表.欧姆计等功能,有时也称为万用计.多 ...

  7. jquery.ztree 打开父节点_增额终身寿险的正确打开方式

    寿险,想必大部分人都理解它的含义:被保人身故或者全残就能获得赔偿的保险,终身寿险自然就是无论何时身故或者全残都会赔偿你的保险.但我们今天要讲的增额终身寿险有点不太一样,它的正确打开方式又是什么呢? 增 ...

  8. 倾情力荐,智能访客系统的正确打开方式

    任何科学的进步,都是以人为本的.无论时代怎么变迁,生活的脚步不停向前.世界的每一个角落,每天都在上演着不同的故事.人多人少,每一处既是别人眼中的风景,也是自己的轨迹.在人口流量多的地方,总是聚集着不同 ...

  9. opengl 贴图坐标控制_材质贴图正确打开方式

    哈喽,各位观众朋友们好鸭~欢迎来到讲道理画图的地方,我是黄玮宁. 最近呀经常有小伙伴来问我那些不同通道的材质贴图该怎么用,而且频率不是一般的高,所以我觉得有必要来说说这些通道贴图的用法了. 视频版(B ...

最新文章

  1. mysql创建外键级联更新_MySQL中利用外键实现级联删除、更新
  2. linux语言windows 语言,作业系统一般用什么编码语言程式设计?如:Windows,Linux,是组合语言吗?还是自己开发的程式码?...
  3. 转行学Java,如何才能成为年薪50万的Java程序员呢?
  4. min里所有的参数都不存在_高中生物所有的考点难点,其实都在你不仔细看的课本里,必修1-3超强记忆手册!...
  5. mysql 表与表之间的条件比对_值得收藏 | 一份最完整的MySQL规范
  6. 远程桌面无法找到计算机不属于指定网络,远程桌面找不到计算机
  7. 手机号正则表达 php,php 手机号码正则表达试程序代码_PHP教程
  8. 计算机操作员五级知识点,计算机操作员五级.doc
  9. adadelta算法_(学习率自适应的梯度下降算法)ADADELTA: AN ADAPTIVE LEARNING RATE METHOD(2012)...
  10. 【idea插件开发】从0入门idea插件开发,idea插件开发教程,如何开发idea插件
  11. Windows API、SDK和CRT的关系
  12. excel 根据某单元格的值设置整行颜色(条件格式)
  13. 关于页面请求发起后,通过F12查看到,被挂起页面中stalled花费很长时间问题的追查...
  14. html中diy的背景怎么透明,自制复古几何无缝纹案背景_html/css_WEB-ITnose
  15. 用python计算圆周率_用python计算圆周率π
  16. R语言GAM(广义相加模型)对物业耗电量进行预测
  17. 记录一个阿里云OSS图片上传错误
  18. PPC E500内核寄存器
  19. 火狐浏览器使用拼写检查
  20. 小目标一、平均数的分类及计算方式

热门文章

  1. 安徽三连学院计算机考试,安徽三联学院2017年3月计算机等级考试报名时间
  2. 3分钟透彻了解人工智能!原来AI还有强弱之分?!
  3. ArcGIS API for Javascript学习
  4. 人工智能期末模拟沙场秋点兵(部分题,有些太模糊就不做了)
  5. MySQL的DML常用语法格式
  6. webpack导出方法
  7. android依赖重复处理办法
  8. 学习环境搭建(一)服务器安装
  9. vb CommonDialog 属性
  10. 中国超高分子量聚乙烯产业调研与投资前景报告(2022版)