第2篇:nacos与feign性能测试

架构性能瓶颈

制定架构并不只是画个图, 装个软件然后就开工. 最重要的是分析找出这个架构的性能瓶颈, 然后或者用硬件, 或者用优化代码的方式加以改善提高, 最终达到整个架构支持高并发的目标.

这是上一篇博客写到的秒杀架构草稿图:


image.png

那么它的性能瓶颈在哪呢? 大家可以注意到几个中间件的颜色是不一样的, 分别表示.

绿色: 作为流量入口的负载均衡器, 性能是最优越最不容易出问题的. 软件的代表自然是大名鼎鼎的nginx, 基本上一台4核16G的服务器, 承担个数万并发没有问题. 硬件的代表有F5, 小肥爬爬曾经在一个项目用过F5, 对它支撑20多万用户的表现非常震惊....

土黄色: 微服务的接口层实现有好多方式, java系的protocalbuf/dubbo/feign, go系的gin, python的tornado / falcon.... 理论上当然是go语言最好, java系的pb/dubbo最好. 但我习惯了用feign, 所以要实测nacos+feign的性能, 同时也给团队内部一个量化数据頂心丸.

红色: 应用系统的性能瓶颈最终都会是mysql, 确切地说, 是一切OLTP系统. 这其实是可以理解的, 写操作, 特别是数据库的操作涉及到事务同步的概念, 在开发语言的层面就是资源的竞争, 加锁, 等待... 同时这些系统一般都是SOCKET程序, 还记得操作系统概念的同学都知道, 资源竞态和IO场景效率都不会太高. 所以mysql 会成为架构的性能瓶颈, 应该是程序员的一个普遍从业经验.

我们的计划是除nginx外, 先单独对某个部件组合进行测试, 结合各方面的测试数据, 然后再制定业务工程的划分和代码级别的优化.

搭建微服务工程

工程代码在这里(记得选择分支) , 如下:

git clone https://gitee.com/xiaofeipapa/springcloud-adv -b high-concurrency

建议先下载代码, 然后对照代码看博客.

spring boot 版本选择

出于习惯, 我还是使用alibaba全家桶来搭建微服务工程. 核心的版本号如下:


        <!-- spring / spring cloud / spring boot-->
        <!-- https://github.com/alibaba/spring-cloud-alibaba/wiki/版本说明 -->
        <spring-boot.version>2.6.13</spring-boot.version>
        <spring.version>5.3.25</spring.version>
        <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
        <spring-cloud.version>2021.0.4</spring-cloud.version>
        <nacos.version>2.2.0</nacos.version>

feign的实现

image.png

feign工程代码不复杂, 里面只有2个方法: 无参的和带1个参数的. 带参数方法主要用于后面的jmeter 测试.

/**
 * 作者: 小肥爬爬
 * 简书: https://www.jianshu.com/u/db796a501972
 * 邮箱: imyunshi@163.com
 * <p>
 * 您可以任意转载, 恳请保留我作为原作者, 原创不易, 跪求, 谢谢.
 **/
@FeignClient(name = "sample-feign")
public interface ITestFeign {

    @GetMapping("/test/time")
    String time() throws UnknownHostException;

    @GetMapping("/test/hello")
    String hello(@RequestParam String name);
}
/**
 * 作者: 小肥爬爬
 * 简书: https://www.jianshu.com/u/db796a501972
 * 邮箱: imyunshi@163.com
 * <p>
 * 您可以任意转载, 恳请保留我作为原作者, 原创不易, 跪求, 谢谢.
 *
 * 秒杀入口
 **/
@RestController
@Slf4j
public class SampleFeignController implements ITestFeign {

    @Value("${server.port}")
    int port;

    @Override
    public String time() throws UnknownHostException {
        InetAddress localHost = InetAddress.getLocalHost();
        return "from: " + localHost.getHostAddress() + ":" + port + " , at: " + LocalDateTime.now();
    }

    @Override
    public String hello(String name) {
        return "hello, " + name;
    }
}

主要配置文件:

server:
  port: 17000
  tomcat:
    threads:
      max: 4000
      # ---------- springboot 旧版本配置
#    # 最大工作线程数, 默认200
#    max-threads: 2000
#    # 最大连接数 从 100 变成 200
#    max-connections: 2200
#    # 等待队列
#    accept-count: 100
#    min-spare-threads: 500

这里把tomcat的并发设成4000.

gateway 工程

配置gateway工程的主要要点是: 引入gateway的包, 排除 spring-boot-starter-web . 因为springcloud gateway 用的是 reactor 写法, 和web 写法不兼容.


        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

下载nacos 并启动

nacos下载地址在github, 经常出现访问不畅. 所以我上传了一个2.2.2 版本到百度云盘, 请自由下载:
链接: https://pan.baidu.com/s/1Jp7OOKAoOBWZTVBADx6hTg?pwd=27x8 提取码: 27x8

启动命令如下:

cd nacos目录/bin
./startup.sh -m standalone   

启动gateway 工程和feign工程

依次启动gatway 工程 和feign 工程. 如果想形成feign工程的第二个部署实例怎么办? 有两个办法: 要么用docker部署, 要么用idea 直接启动.

作为一篇博客不想引入太多其他技术, 这里介绍下怎么用idea 启动第二个实例. 右上角点一下启动命令旁边的下拉小箭头, 然后选择 Edit Configurations

image.png

在打开的界面选择 StartFeignSample, 然后点击上面的小工具图标, 其中有一个是复制命令. 将命令改名, 然后在 Environment variables 设置环境变量为 server.port=17001, 如下:

image.png

再增加一个实例, 端口是17002. 即最后这个服务总共有3个实例. 如nacos的下图:

image.png

用ab进行简单测试

接下来我们用ab测试nacos+feign集群的性能. 测试命令如下:

# 让操作系统打开更多句柄
ulimit -n 65536

ab -n 20000 -c 2000 http://localhost:10000/sample-feign/test/time/

以上命令应该在mac / linux 都能正常运行. 如果你是用windows 系统..... 咳咳, 可能要找找其他帖子看看怎么运行.

-n: 表示ab 总共发送 2万个请求.
-c: 表示并发数量2000.

最终在我的电脑的表现如下:

Concurrency Level:      2000
Time taken for tests:   0.977 seconds
Complete requests:      20000
Failed requests:        70
   (Connect: 0, Receive: 0, Length: 70, Exceptions: 0)
Total transferred:      3719720 bytes
HTML transferred:       1019720 bytes
Requests per second:    20462.68 [#/sec] (mean)
Time per request:       97.739 [ms] (mean)
Time per request:       0.049 [ms] (mean, across all concurrent requests)
Transfer rate:          3716.57 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   39   5.3     40      50
Processing:    11   54  11.4     51     107
Waiting:        1   39   9.7     37      99
Total:         56   94  10.7     92     138

Percentage of the requests served within a certain time (ms)
  50%     92
  66%     99
  75%    102
  80%    103
  90%    107
  95%    110
  98%    114
  99%    119
 100%    138 (longest request)

从上数据可以看到, 所有请求都在146ms内完成, 可以看到即使是单机, 轻松抗住2000个并发毫无问题. 事实上即使n再加大, 性能也是刚刚的. 这是我电脑的数据:
(-n 50000 )

-c 完成所有请求的总时间(s) 请求的最长时间(ms)
3000 2.323 187
4000 2.367 260
5000 2.369 332
6000 2.345 409
7000 2.406 500
8000 2.378 561
9000 2.368 569
10000 2.467 666

我们怎么来解读这个数据? 大家可以回想下个人购买产品的体验, 当点击"下单购买"按钮的时候, 一般会看到个loading框在转转转, 然后会提示你购买成功或者失败, 这个过程一般在2-4秒. 也就是说只要处理时间在2-4秒, 都是可以接受的.

所以这个数据的结论就是: 所有请求都能在3秒内完成. 并且耗费时间最多的请求也不足1秒, 我们的并发测试达到了效果. 当然这个方法非常简单, 没有实际方法, 没有调用数据库. 但这足以说明性能刚刚的了.

用go性能会更刚. 不过这个性能指标对大多数公司已经够用了.

feign vs dubbo
feign仅是http请求的包装, 模型更简单, 很多老项目的代码都用得上. dubbo的性能大概比feign强一倍. 但我个人不怎么喜欢用. 读者也可以换成dubbo 进行自己的测试.

用jmeter 进行带参数的链接测试

jmeter 和 ab的测试结论其实差不多, 但jmeter 的测试功能更多更强大. 接下来我们看看怎么用jmeter 测试带参数的链接和返回测试报告.

增加测试方法

// feign的测试方法: 

@FeignClient(name = "sample-feign")
public interface ITestFeign {

// ... 其他方法

    @GetMapping("/test/hello")
    String hello(@RequestParam String phone, @RequestParam Integer actId, @RequestParam Integer num);
}

// controller的方法

    @Override
    public String hello(String phone, Integer actId, Integer num) {
        log.info("---- 活动id: {}, 手机号: {}, 购买数量: {}" , actId, phone, num);
        return "hello, " + phone;
    }

这个方法表示会接受3个参数: 秒杀活动的id, 用户的手机号, 以及用户购买商品的数量.

生成模拟数据文件

我一般把一些小功能程序扔到junit执行, 以下是代码:


/**
 * 作者: 小肥爬爬
 * 简书: https://www.jianshu.com/u/db796a501972
 * 邮箱: imyunshi@163.com
 * <p>
 * 您可以任意转载, 恳请保留我作为原作者, 原创不易, 跪求, 谢谢.
 **/
@Slf4j
public class GenPhoneDataTest extends BaseFeignSampleTests{


    ConcurrentHashMap<String, Integer> cache = new ConcurrentHashMap<>();

    // 生成1w个用户并保存到csv
    @Test
    public void doTest() throws Exception {

        int max = 10000;
        int start = 0;
        int activityId = 1000;      // 活动id

        while (start < max){

            // 生成随机手机号
            String phone = PersonInfoSource.getInstance().randomChineseMobile();
            // 保证不重复
            while (cache.contains(phone)){
                phone = PersonInfoSource.getInstance().randomChineseMobile();
            }

            // 随机购买数量, 不超过3件
            int productCount = NumberSource.getInstance().randomInt(1, 4);
            cache.put(phone, productCount);

            start++;
        }

        this.writeFile(activityId);

        log.info("---- 已保存 10000个手机号");
    }

    private void writeFile(int activityId) throws IOException {
        String path = "/tmp/phone_data.txt";
        File file = new File(path);
        //如果文件不存在,创建文件
        if (!file.exists()) {
            file.createNewFile();
        }
        FileWriter writer = new FileWriter(file);
        for (String name : cache.keySet()){
            writer.write(name + "," + cache.get(name) + "," + activityId);
            writer.write("\n");
        }
        writer.flush();
        writer.close();
    }

}

以上代码会生成10000行数据, 保存到 /tmp/phone_data.txt 文件, 数据格式类似:

17349473799,3,1000
15863934940,2,1000
19601404246,1,1000
19419729371,3,1000

这个文件是为jmeter预备的, 接下来看看jmeter 的操作.

下载执行jmeter

下载jmeter, 我打包了一个在百度云盘: https://pan.baidu.com/s/1HmMfHKoLeJyRXeKNJrImrA?pwd=f6if 提取码: f6if

解压之后, 在bin目录下 打开 jmeter 即可.

增加线程组

在Test Plan那里打开鼠标右键:


image.png
image.png

这表示我们用4000个线程进行测试, 即模拟4000个并发用户, 循环10次, 即总共4万个请求.

增加参数文件

image.png

image.png

这表示每个发出的http请求都会从 phone_data.txt 文件读取参数. 第一个参数名是phone, 第二个参数名是num, 第三个是actId, 它们用,分隔. 参数的顺序不能乱, 要和txt文件里的内容一致.

增加http请求

image.png

image.png

以上是http请求的设置, 它的链接是 http://localhost:10000/sample-feign/test/hello , 会带上3个参数. 格式是: 参数名: xxx , 值: ${xxx} . 这些参数名对应着上一节的 Config data set .

增加报告

image.png

View result tree 和 summary report 都要点上.

最终看起来是这样的:


image.png

启动工程, 点击 jmeter工具栏那个绿色的按钮, 然后静待测试结果即可.

观察测试结果

image.png

image.png

测试结论和ab 测试差不多. 但是jmeter 可以设置参数, 保存测试报告, 返回每一个请求的结果. 这就很有用了.

建议大家都熟练掌握jmeter, 通过它测试掌握自己代码每个接口的性能.

总结

在这一节我们动手测试了nacos+feign的性能, 证明足以满足大多数项目的并发请求. 下一章我们会测mysql, 大多数项目的真正性能瓶颈.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容