首页 > 其他分享 >性能工具之Jmeter小白入门系列之四

性能工具之Jmeter小白入门系列之四

时间:2023-04-06 13:08:54浏览次数:51  
标签:定时器 入门 Timer 添加 线程 new 之四 参数 Jmeter

性能工具之Jmeter小白入门系列之四_数据


       青,取之于蓝,而青于蓝;冰,水为之,而寒于水。木直中绳,輮以为轮,其曲中规。虽有槁暴,不复挺者,輮使之然也。故木受绳则直,金就砺则利,君子博学而日参省乎己,则知明而行无过矣。

   

一、Jmeter中参数取值

1、Test Plan中添加变量

Test Plan中设置好变量名,变量名可以在任意的位置引用,比如说在线程组中直接用${变量名}方式引用变量,步骤如下:

性能工具之Jmeter小白入门系列之四_正则表达式_02

1、添加线程组

性能工具之Jmeter小白入门系列之四_线程组_03

2、添加请求

性能工具之Jmeter小白入门系列之四_数据_04

3、添加结果查看树

性能工具之Jmeter小白入门系列之四_数据_05

2、User Defined Variables

性能工具之Jmeter小白入门系列之四_线程组_06

注意:User Defined Variables定义的变量和Test Plan中定义的变量一样,不管这个组件在任何位置,在整个test plan中都可以引用这些变量。

1、添加线程组

性能工具之Jmeter小白入门系列之四_数据_07

2、添加请求User Defined Variables

性能工具之Jmeter小白入门系列之四_线程组_08

3、添加请求

性能工具之Jmeter小白入门系列之四_数据_09

4、添加结果查看树

性能工具之Jmeter小白入门系列之四_正则表达式_10

性能工具之Jmeter小白入门系列之四_线程组_11

二、Jmeter中CSV Data Set Config

     在做性能测试或者接口测试中,文件参数化最常用的工具摸过如此,咱们这次聊一聊这个工具怎么使用

性能工具之Jmeter小白入门系列之四_正则表达式_12

作用:从文件中逐行读取数据,按指定的分隔符分割,赋给指定的变量。适合处理大量数据的情况

参数说明

  • Filename:文件路径。可以是相对路径也可以是绝对路径。可以是.txt文件也可以是.csv文件
  • File encoding:文件编码,默认问ANSI,其它编码根据实际情况配置
  • Variables Names(comma-delimited):变量名,表示将每一列赋值给一个变量,有多个列时同逗号分隔,后续通过${}引用
  • Ignore first line:忽略首行
  • Delimiter(use "\t" for tab):分隔符,默认为逗号
  • Allow quoted data?:数据是否带引号,默认为false,如果数据中有引号(双引号)的话就会把数据连同引号一起赋值给变量,也就是原封不动的取值;如果为true的话就会把引号去掉,将引号中的值赋给标量。
  • Recycle on EOF?:遇到文件结束符是否再次循环,默认为True,也就是继续从文件开头取值
  • Stop thread on EOF?:遇到文件结束符是否结束线程,默认为False也就是不停止。注意:当【Recycle on EOF?】设置为True时,此项设置无效;当【Recycle on EOF?】设置为False,此项也设置为False,那么到达文件最后在引用变量就会变为<EOF>
  • Sharing mode:共享模式。默认为all threads,还支持current thread group /current thread /edit

共享模式总结:

all treatds:csv文件中的数据是共享的,不管是线程数还是循环次数,都会触发接口请求参数来使用csv文件中不同行的变量数据

current thread group:在线程组内,线程数和循环次数都会触发接口请求参数变化

current thread:循环了数据就会变化,线程数不能决定更新csv文件中的数据,循环次数才可以;

以下简单的演示:

1、添加线程组

性能工具之Jmeter小白入门系列之四_线程组_13

2、添加请求CSV Data Set Config

性能工具之Jmeter小白入门系列之四_数据_14

附件内容:

性能工具之Jmeter小白入门系列之四_正则表达式_15

3、添加http request请求

性能工具之Jmeter小白入门系列之四_线程组_16

4、添加结果查看树

性能工具之Jmeter小白入门系列之四_正则表达式_17

三、Timer:定时器

  • Constant Timer 固定定时器
  • Uniform Random Timer 均匀随机定时器
  • Constant Throughput Timer 固定吞吐量定时器
  • Gaussian Random Timer 高斯随机定时器
  • JSR223 Timer JSR223定时器
  • Poisson Random Timer 泊松随机定时器
  • Synchronizing Timer 同步定时器
  • BeanShell Timer BeanShell脚本编写定时器
  • Precise Throughput Timer 精准吞吐量定时器

1、Constant Timer 固定定时器

性能工具之Jmeter小白入门系列之四_线程组_18


     作用:通过ThreadDelay设定每个线程请求之前的等待时间(单位为毫秒)。



2、Uniform Random Timer 均匀随机定时器

性能工具之Jmeter小白入门系列之四_正则表达式_19


作用:它产生的延迟时间是个随机值,而各随机值出现的概率均等。总的延迟时间等于一个随机延迟时间加上一个固定延迟时间,用户可以设置随机延迟时间和固定延迟时间。


总延迟时间=指定范围内的随机时间+固定延迟时间


3、Constant Throughput Timer 固定吞吐量定时器

性能工具之Jmeter小白入门系列之四_数据_20


作用:  按指定的吞吐量执行,以每分钟为单位。计算吞吐量依据是最后一次线程的执行时延。


Target throughput(in samples per minute):目标吞吐量。注意这里是每分钟发送的请求数,可以选择作用的线程:当前线程、当前线程组、所有线程组等,具体含义如下:


  • this thread only: 设置每个线程的吞吐量。总的吞吐量=线程数*该值。
  • all active threads in current thread group:吞吐量被分摊到当前线程组所有的活动线程上。每个线程将根据上次运行时间延迟。
  • all active threads:吞吐量被分配到所有线程组的所有活动线程的总吞吐量。每个线程将根据上次运行时间延迟。在这种情况下,每个线程组需要一个具有相同设置的固定吞吐量定时器。(不常用)
  • all active threads in current thread group (shared):同上,但是每个线程是根据组中的线程的上一次运行时间来延迟。相当于线程组组内排队。(不常用)
  • all active threads (shared):同上,但每个线程是根据线程的上次运行时间来延迟。相当于让所有线程组整体排队。(不常用)

4、Gaussian Random Timer 高斯随机定时器

性能工具之Jmeter小白入门系列之四_线程组_21


作用:每个线程的延迟时间是符合标准正态分布的随机时间停顿,那么使用这个定时器,总延迟 = 高斯分布值(平均0.0和标准偏差1.0)* 指定的偏差值+固定延迟偏移(Math.abs((this.random.nextGaussian() * 偏差值) + 固定延迟偏移))


5、JSR223 Timer JSR223定时器

性能工具之Jmeter小白入门系列之四_线程组_22


JSR223计时器可以使用JSR223脚本语言生成延迟;

参考帮助文档:

https://jmeter.apache.org/usermanual/component_reference.html#JSR223_Timer


6、Poisson Random Timer 泊松随机定时器

性能工具之Jmeter小白入门系列之四_线程组_23

这个定时器在每个线程请求之前按随机的时间停顿,总的延迟就是泊松分布值和偏移值之和。

上面表示暂停时间会分布在100到400毫秒之间:

(1)Lambda(in milliseconds):兰布达值

(2)Constant Delay Offset(in milliseconds):暂停的毫秒数减去随机延迟的毫秒数


7、Synchronizing Timer 同步定时器

性能工具之Jmeter小白入门系列之四_数据_24

作用:用来设置集合点,其作用是:阻塞线程,直到指定的线程数量到达后,再一起释放,可以瞬间产生很大的压力

(1)Number of Simulated Users to Group by:模拟用户的数量,即指定同时释放的线程数数量,若设置为0,等于设置为线程组中的线程数量;

(2)Timeout in milliseconds:超时时间,即超时多少毫秒后同时释放指定的线程数;如果设置为0,该定时器将会等待线程数达到了设置的线程数才释放,若没有达到设置的线程数会一直死等。如果大于0,那么如果超过Timeout inmilliseconds中设置的最大等待时间后还没达到设置的线程数,Timer将不再等待,释放已到达的线程。默认为0

同步定时器(Synchronizing Timer)的超时时间设置要求:超时时间 > 请求集合数量 * 1000 / (线程数 / 线程加载时间)


8、BeanShell Timer BeanShell脚本编写定时器


性能工具之Jmeter小白入门系列之四_数据_25

参数说明:

  • Reset Interpreter:每次迭代是否重置解析器,默认为false;在长时间运行的脚本中建议设置为true。
  • Parameters:BeanShell脚本的入参。入参可以是单个变量;也可以是数组,若是字符串数组,两个元素之间用空格隔开;也可以是常量。
  • File Name:BeanShell脚本可以从脚本文件中读取。
  • Script:在Script区直接写BeanShell脚本。


简单写一demo增加一个sleep等待一分钟:

性能工具之Jmeter小白入门系列之四_正则表达式_26

try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


增加一个Java Request请求,并且增加时间验证是否按照自己设定的定时运行脚本: 

Java Request ${__time(yyyy-MM-dd HH:mm:ss:SSS,)}

性能工具之Jmeter小白入门系列之四_线程组_27

增加结果查看树:

性能工具之Jmeter小白入门系列之四_线程组_28

结果显示按之前设置的每个1秒钟运行



9、Precise Throughput Timer 精准吞吐量定时器

性能工具之Jmeter小白入门系列之四_线程组_29


  • Target Throught:目标吞吐量
  • Throught Period:表示在多长时间内发送Target Throught指定的请求数(以秒为单位)
  • Test Druation:指定测试运行时间(以秒为单位)
  • Number of threads in the bath:用来设置集合点,等到指定个数的请求后并发执行其它参数默认即可。

三、Jmeter处理器

1、前置处理器Pre Processors

用于对于采集器进行处理,且是在sampler启动之前

性能工具之Jmeter小白入门系列之四_线程组_30

JSR223 PreProcessor

性能工具之Jmeter小白入门系列之四_正则表达式_31

参数配置:

  1. Name:名称,随意设定;
  2. Language:使用的JSR223语言,可根据需要选择;
  3. Parameters:传递给脚本的参数;
  4. Script file:脚本文件;
  5. Script compilation caching:存储编译的脚本,默认勾选;
  6. Script:要运行的脚本;


User Parameters

   用户参数,用于做几组参数给线程组的各个线程使用,如果线程数多于用户参数组数,则多出来的线程则从第一组用户参数开始依次调用参数组;

性能工具之Jmeter小白入门系列之四_数据_32

参数配置:
Name:此前置处理器的名称;
Update Once Per Iteration:标识是否每轮迭代更新一次元素;



HTML Link Parser

此处理器为HTML链接解析器,用于从前一个sampler返回的html页面中按照规则解析链接和表单,再根据此处理器所在的sampler中的规则进行匹配修改,而后该sampler会执行;

性能工具之Jmeter小白入门系列之四_数据_33

HTTP URL Re-writing Modifier

   HTTP URL重写修改器,此处理器与HTTP Link Parser类似,但专用于使用url重写来存储sessionId而非cookie的http request,在线程组级别添加此修改器则应用于所有sample,若为单个sample添加则只适用该sample;

性能工具之Jmeter小白入门系列之四_线程组_34

参数配置:

Session Argument Name:会话参数名称,用于搜索sessionId,其他sample也可通过此参数来 调用其获取的sessionId;

Path Extension:路径扩展,如url添加了分号作为分割,则勾选此项;

Do not use equals in path extension:用于url不用等号来分割key和value的类型;

Do not use questionmark in path extension:用于不带?的类型;

Cache Session Id?:勾选此项则会存储在其挂载的sample上获取到的sessionId供后边的其他sample使用;

URL Encode:是否使用url编码;



JDBC PreProcessor

数据库预处理器,用于在sample开始前查询数据库并获取一些值;

性能工具之Jmeter小白入门系列之四_正则表达式_35

性能工具之Jmeter小白入门系列之四_线程组_36

参数配置:

Variable Name of Pool declared in JDBC Connection Configuration:连接池名称,需与JDBC链接配置中的Variable Name相同(此预处理器需要一个JDBC Connection Configuration,此配置器在配置元件中);

Query Type:数据库查询类型,根据需要自行选择;

Query:数据库语句输入框,根据需要输入,注意结尾不要加”;”;

Parameter values:参数名称,如果Query的语句中有”?”则此处填值,可以使用调用参数方式;

Parameter types:参数类型,与Parameter values对应,设置参数类型,与sql字段类型相同;

Variable names:设定此项可以获取固定列的所有值;

Result variable name:随意设定一个名称,则此名称会被作为一个参数并对应Query出来的内容;可以使用参数调用的方法来获取此设置的名称对应的值;

Query timeout(s):超时时间;

Handle ResultSet:有四个选项,结果保存的方式;



RegEx User Parameters

正则表达式,使用正则表达式为从另一个HTTP请求中提取的HTTP参数指定动态值

性能工具之Jmeter小白入门系列之四_数据_37

参数配置:

name:此前置处理器名称;

Regular Expression Reference Name:调用的正则表达式提取器中的引用名称;

Parameter names regexp group number:用于提取参数名称的正则表达式的组编号;

Parameter values regex group number:用于提取参数值的正则表达式的组编号;


Sample Timeout

超时器,用于设定sample的超时时间,如果完成时间过长,此预处理器会调度计时器任务以中断样本;

性能工具之Jmeter小白入门系列之四_线程组_38


参数配置:
name:超时器名称;
Sample timeout:超时时间;

BeanShell PreProcessor

性能工具之Jmeter小白入门系列之四_正则表达式_39

参数配置:

Name:名称,随意设定;

Language:使用的JSR223语言,可根据需要选择;

Parameters:传递给脚本的参数;

Script file:脚本文件;

Script compilation caching:存储编译的脚本,默认勾选;

Script:要运行的脚本;


四、综合脚本小练习

一、在造数据时候,需要获取结果到保存到本地
有两种方式:第一种直接通过获取结果保存到本地,第二种通过数据库导出
今天介绍第一种,通过jmeter中正则表达式获取:
步骤一:

性能工具之Jmeter小白入门系列之四_数据_40

第二步:

性能工具之Jmeter小白入门系列之四_线程组_41

第三步

性能工具之Jmeter小白入门系列之四_数据_42

参考代码

FileOutputStream fps=new FileOutputStream("${outfile_online}",true);
OutputStreamWriter osw=new OutputStreamWriter(fps);
BufferedWriter bw=new BufferedWriter(osw);


bw.append("${mobile}\t${User}\t${User}\t${fd}\n");


if(bw!=null){bw.close();}
if(osw!=null){osw.close();}
if(fps!=null){fps.close();}

第四步

性能工具之Jmeter小白入门系列之四_正则表达式_43

获取结果

性能工具之Jmeter小白入门系列之四_正则表达式_44


2、后置处理

性能工具之Jmeter小白入门系列之四_正则表达式_45

     从上面可以看出后置处理可以插件挺多,在我工作生涯中常用的就是几个组件,

1、Regular Expression Extractor(正则表达式提取器)

性能工具之Jmeter小白入门系列之四_线程组_46

Apply to:应用范围

  1. Main sample and sub-samples:作用于主节点的取样器及对应子节点的取样器
  2. Main sample only:仅作用于主节点的取样器
  3. Sub-samples only:仅作用于子节点的取样器
  4. JMeter Variable:作用于jmeter变量(输入框内可输入jmeter的变量名称),从指定变量值中提取需要的值。


Field to check 要检查的响应字段:

1、Body 主体:响应报文的主体,最常用

2、Body(unescaped):主体,是替换了所有的html转义符的响应主体内容,注意html转义符处理时不考虑上下文,因此可能有不正确的转换,不太建议使用

3、Body as a Document:从不同类型的文件中提取文本,注意这个选项比较影响性能

4、Response Headers:响应信息头(如果你使用的是中文版的Jmeter,会看到这一项是信息头,这是中文翻译问题,应以英文为准)

5、Request Headers:请求信息头

6、URL:请求url

7、Response Code:响应状态码,比如200、404等

8、Response Message:响应信息


Name of created variable 引用名称:其他地方引用时的变量名称,我这里写的phone,可自定义设置,引用方法:${引用名称}

Regular Expression 正则表达式:数据提取器,()括号里为你要获取的的值。"mobilephone":"( 相当于LR左边界, )","leaveamount"相当于LR右边界。而括号里\d+为正则表达式,用来匹配所需要获取的数据,何谓正则表达式文章末尾会附上说明

Template 模板:用于从找到的匹配项创建字符串的模板。这是一个带有特殊元素的任意字符串,用于引用正则表达式中的组。引用组的语法是:' $ 1 $ '引用组1,' $ 2 $ '引用组2,等等。$ 0 $引用整个表达式匹配的内容。

Match No. 匹配数字:正则表达式匹配数据的所有结果可以看做一个数组,匹配数字即可看做是数组的第几个元素。-1表示全部,0随机,1第一个,2第二个,以此类推。若只要获取到匹配的第一个值,则填写1

Default value 缺省值:匹配失败时的默认值。可以不写。若需用于后续逻辑判断,可简单写为 ERROR。

简单示例:

1、添加线程组

性能工具之Jmeter小白入门系列之四_线程组_47

2、添加请求

性能工具之Jmeter小白入门系列之四_数据_48

3、添加正则提取器

首先请求一次,在结果查看树中调试正则表达式,增加正则提取器,把正则表达式放上去

性能工具之Jmeter小白入门系列之四_正则表达式_49

增加正则表达式

性能工具之Jmeter小白入门系列之四_数据_50

增加调试器

性能工具之Jmeter小白入门系列之四_数据_51

4、添加结果查看树

性能工具之Jmeter小白入门系列之四_线程组_52

2、JSON Extractor(JSON表达式提取器)

性能工具之Jmeter小白入门系列之四_正则表达式_53


3、Regular Expression Extractor(正则表达式提取器)

性能工具之Jmeter小白入门系列之四_正则表达式_54

说明:

1.Variable name :自定义参数名,引用的时候${} 格式

2.Json path expression:json表达式,根据上面的json,表达式为:$.开始

简单样例说明:

1、添加线程组

性能工具之Jmeter小白入门系列之四_线程组_55

2、添加请求

1、使用springboot新建工具简单写好Controller代码如:

@GetMapping("/weixin")
    @ResponseBody
    public Msg getWeixint() {
        HashMap<String, ArrayList<Skills>> hashMap1 = new HashMap<>();
        HashMap<String, ArrayList<Gender>> hashMap = new HashMap<>();
        HashMap<String, String> hashMap3 = new HashMap<>();


        hashMap3.put("name", "数据下发");
        hashMap3.put("opinion", "get获取得数据");


        ArrayList<Gender> list = new ArrayList<>();
        list.add(new Gender("男", "0", true));
        list.add(new Gender("女", "1", true));


        ArrayList<Skills> list1 = new ArrayList<>();
        list1.add(new Skills("HTML", "html", true));
        list1.add(new Skills("CSS", "css", false));
        list1.add(new Skills("JavaScript", "js", false));
        list1.add(new Skills("Photoshop", "ps", true));
        list1.add(new Skills("python", "py", true));


        hashMap.put("gender", list);
        hashMap1.put("skills", list1);


        HashMap<String, Object> hashMap2 = new HashMap<>();


        hashMap2.putAll(hashMap);
        hashMap2.putAll(hashMap1);
        hashMap2.putAll(hashMap3);


        System.out.println(hashMap2);


        return Msg.success().add("infopage", hashMap2);
    }

启动成功如:

性能工具之Jmeter小白入门系列之四_线程组_56

页面响应如:

性能工具之Jmeter小白入门系列之四_数据_57

添加请求

性能工具之Jmeter小白入门系列之四_正则表达式_58

3、添加结果查看树调试

性能工具之Jmeter小白入门系列之四_数据_59


4、添加JSON Extractor

性能工具之Jmeter小白入门系列之四_正则表达式_60

5、添加Debug Sampler

性能工具之Jmeter小白入门系列之四_正则表达式_61

6、查看结果

性能工具之Jmeter小白入门系列之四_数据_62

3、工程结构如下:

性能工具之Jmeter小白入门系列之四_线程组_63


     

    很多插件需要在工作不断练习才能掌握,掌握学习方法,并且自己动手就能把很多事情做出来;


观书有感

【作者】朱熹 【朝代】南宋

半亩方塘一鉴开,天光云影共徘徊。

问渠那得清如许?为有源头活水来。


性能工具之Jmeter小白入门系列之四_正则表达式_64


标签:定时器,入门,Timer,添加,线程,new,之四,参数,Jmeter
From: https://blog.51cto.com/u_15181572/6172653

相关文章

  • 性能工具之Jmeter小白入门系列之二
         在上一讲中简单的介绍了Jmeter的安装与HttpRequet请求,在实际工作,咱们对工具的使用还需咱们进一步学习与操作才能掌握;  话说【间单的事情重复做,就会成为行家;简单的事情用心做,就会成为赢家。简单的法坚持修,就会成为大法,每天持之以恒做简单的事情,这个人就会不简单】......
  • jmeter模拟多IP地址访问
    1.前言:今天一同事在压测时提到怎么用jmeter里虚拟多个ip来发送请求,我想了一下以前用LR时用过虚拟ip地址,jmeter还没有使用过。想着原理应该是相通的,既然LR都能支持的话,那Jmeter应该也是支持,于是就有了jmeter虚拟化IP地址的研究。在网上也查找了相应的资料,摸索参考着实践了一把,坑吃......
  • 深度学习基础入门篇[三]:优化策略梯度下降算法:SGD、MBGD、Momentum、Adam、AdamW
    1.梯度下降算法(优化器)1.1原理解释如果我们定义了一个机器学习模型,比如一个三层的神经网络,那么就需要使得这个模型能够尽可能拟合所提供的训练数据。但是我们如何评价模型对于数据的拟合是否足够呢?那就需要使用相应的指标来评价它的拟合程度,所使用到的函数就称为损失函数(LossFu......
  • jmeter模拟多IP地址访问
    1.前言:今天一同事在压测时提到怎么用jmeter里虚拟多个ip来发送请求,我想了一下以前用LR时用过虚拟ip地址,jmeter还没有使用过。想着原理应该是相通的,既然LR都能支持的话,那Jmeter应该也是支持,于是就有了jmeter虚拟化IP地址的研究。在网上也查找了相应的资料,摸索参考着实践了一把,坑吃......
  • python入门到实战系列一
         学习 pyhton 语言首先需要掌握它的基本规则,还有它支持什么数据类型,下面画一张图来了解它支持的数据类型有哪些?  上面这几个数据类型在工作中经常使用,下面不分先后介绍每一种数据类型基本使用。一、字符串  第一,字符串基础对于它的定义就不在这里说明,下面介绍......
  • 深度学习基础入门篇[二]:机器学习常用评估指标:AUC、mAP、IS、FID、Perplexity、BLEU、
    A.深度学习基础入门篇[二]:机器学习常用评估指标:AUC、mAP、IS、FID、Perplexity、BLEU、ROUGE等详解1.基础指标简介机器学习的评价指标有精度、精确率、召回率、P-R曲线、F1值、TPR、FPR、ROC、AUC等指标,还有在生物领域常用的敏感性、特异性等指标。在分类任务中,各指标的计算......
  • VsCode开发工具的入门及基本使用
    (VsCode开发工具的入门及基本使用)一、VsCode介绍1.VsCode简介VisualStudioCode(简称“VSCode”)是Microsoft在2015年4月30日Build开发者大会上正式宣布一个运行于MacOSX、Windows和Linux之上的,针对于编写现代Web和云应用的跨平台源代码编辑器,可用于Windows,macOS和Lin......
  • 题目 1016: [编程入门]水仙花数判断
    打印出所有"水仙花数",所谓"水仙花数"是指一个三位数,其各位数字立方和等于该本身。例如:153是一个水仙花数,因为153=1^3+5^3+3^3。 #include<stdio.h>intmain(){inta,b,c,i;for(i=100;i<1000;i++){a=i/100;b=i/10%10;c=i......
  • 题目 1014: [编程入门]阶乘求和
    求Sn=1!+2!+3!+4!+5!+…+n!之值,其中n是一个数字(n不超过20)。 1.因为这是阶乘求和,一般的int类型可能就不够,因此我们利用longlongint类型(不用unsignedlong因为当n为20时,Sn=2561327494111820313)了,unsignedlong的范围不够。2.求Sn的和,我·用的是两个for循环嵌套for(in......
  • 全网最详细中英文ChatGPT-GPT-4示例文档-会议笔记文档智能转摘要从0到1快速入门——官
    目录Introduce简介setting设置Prompt提示Sampleresponse回复样本APIrequest接口请求python接口请求示例node.js接口请求示例curl命令示例json格式示例其它资料下载ChatGPT是目前最先进的AI聊天机器人,它能够理解图片和文字,生成流畅和有趣的回答。如果你想跟上AI时代的潮流......