過程筆記和總結
嘗試一、locust 測試百萬Tcp並發
另一種方式是使用jmeter
基礎環境
- 服務端
虛擬機:Centos7.2
jdk 1.8
- 客戶端
虛擬機: Centos7.2
python : 3.7.3Anaconda3
locust : 0.14.5
基礎知識:
- tcp協議:三次握手進行連接,四次揮手斷開,穩定長連接,比Http更占用資源,比udp協議更穩定,保證數據不丟失,但速度比較慢。
- 每個tcp連接大概占用4kb內存,且斷開連接后默認兩分鍾之后才會釋放資源
- linux打開文件限制(open-files)
- linux端口限制
- tcp-socket 網絡編程
- python中類繼承 super 用法
- python中發送16進制數據包
Q&A
客戶端調優
- 客戶端端口限制導致的tcp連接不超過4000,通過修改打開文件限制來增加可使用的端口數量,注意修改后需要重啟生效,具體配置參考
-- /etc/sysctl.conf
# 系統級別最大打開文件
fs.file-max = 100000
# 單用戶進程最大文件打開數
fs.nr_open = 100000
# 是否重用, 快速回收time-wait狀態的tcp連接
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
# 單個tcp連接最大緩存byte單位
net.core.optmem_max = 8192
# 可處理最多孤兒socket數量,超過則警告,每個孤兒socket占用64KB空間
net.ipv4.tcp_max_orphans = 10240
# 最多允許time-wait數量
net.ipv4.tcp_max_tw_buckets = 10240
# 從客戶端發起的端口范圍,默認是32768 61000,則只能發起2w多連接,改為一下值,可一個IP可發起差不多6.4w連接。
net.ipv4.ip_local_port_range = 1024 65535
-- /etc/security/limits.conf
# 最大不能超過fs.nr_open值, 分別為單用戶進程最大文件打開數,soft指軟性限制,hard指硬性限制
* soft nofile 100000
* hard nofile 100000
root soft nofile 100000
root hard nofile 100000
服務端調優
2. 服務端需要接受的鏈接數需要超過100w,根據需要修改虛擬機配置;可參考文章https://www.jianshu.com/p/490e2981545c
- 優化局部句柄限制
查看 ulimit-n
soft和hard為兩種限制方式,其中soft表示警告的限制,hard表示真正限制,nofile表示打開的最大文件數。整體表示任何用戶一個進程能夠打開1000000個文件。注意語句簽名有 號 表示任何用戶
-- /etc/security/limits.conf
# 文末加兩行
*hard nofile 1000000
soft nofile 1000000
shutdown -r now 重啟並查看生效
- 突破全局句柄的限制
cat /proc/sys/fs/file-max
file-max 表示在linux 中最終所有x線程能夠打開的最大文件數
sudo vi /etc/sysctl.conf
# 在文末增加
fs.file-max=1000000
讓文件生效
sudo sysctl -p
Locust分布式說明
因單台node最大tcp連接6w,故測試百萬連接需要對Locust做分布式處理;
Locust分布式注意點:
每台機器上都要有client文件,且必須相同
slave虛擬機環境、Locust版本、python版本必須相同,否則預期出現未知錯誤
slave節點較多時需要並行管理工具pdsh的使用嗎,同時啟動多個slave節點,減少人工啟停時間成本
遇到的問題
locust 分布式執行問題
單機啟動可發送60000,兩台120000
集群啟動發送60000 服務端ip無法超過60000 限制
已解決:
locust master 節點不作為負載機進行發送數據,slave 一個節點最多可以增加6w個長連接(linux共有端口65535, 一般1-1023 端口是系統保留的,1024-65535 是用戶使用的,可用端口有6w多個)
運維管理工具 pdsh
作用:快速分發命令,啟動slave節點
1、 配置ssh信任
修改主機名稱 vim /etc/hostname
master 可設為node0,slave 可設為 node1~16
參考:https://blog.csdn.net/ctypyb2002/article/details/80572181
2、 安裝pdsh
參考:https://www.cnblogs.com/liwanliangblog/p/9194146.html
tar -jxvf **.tag.bz2
./configure --with-ssh --with-rsh --with-mrsh--with-mqshell --with-qshell --with-dshgroups--with-machines=/etc/pdsh/machines --without-pam
make
make install
客戶端代碼
## locustfile.py
# coding:utf-8
import time
import random
# from socket import socket, AF_INET, SOCK_STREAM
import socket
from locust import Locust, TaskSet, events, task
class TcpSocketClient(socket.socket):
# locust tcp client
# author: ShenDu
def __init__(self, af_inet, socket_type):
super(TcpSocketClient, self).__init__(af_inet, socket_type)
def connect(self, addr):
start_time = time.time()
try:
super(TcpSocketClient, self).connect(addr)
except Exception as e:
total_time = int((time.time() - start_time) * 1000)
events.request_failure.fire(request_type="tcpsocket", name="connect", response_time=total_time, exception=e)
else:
total_time = int((time.time() - start_time) * 1000)
events.request_success.fire(request_type="tcpsocket", name="connect", response_time=total_time,
response_length=0)
def send(self, msg):
start_time = time.time()
try:
super(TcpSocketClient, self).send(msg)
except Exception as e:
total_time = int((time.time() - start_time) * 1000)
events.request_failure.fire(request_type="tcpsocket", name="send", response_time=total_time, exception=e)
else:
total_time = int((time.time() - start_time) * 1000)
events.request_success.fire(request_type="tcpsocket", name="send", response_time=total_time,
response_length=0)
def recv(self, bufsize):
recv_data = ''
start_time = time.time()
try:
recv_data = super(TcpSocketClient, self).recv(bufsize)
except Exception as e:
total_time = int((time.time() - start_time) * 1000)
events.request_failure.fire(request_type="tcpsocket", name="recv", response_time=total_time, exception=e)
else:
total_time = int((time.time() - start_time) * 1000)
events.request_success.fire(request_type="tcpsocket", name="recv", response_time=total_time,
response_length=0)
return recv_data
class TcpSocketLocust(Locust):
"""
This is the abstract Locust class which should be subclassed. It provides an TCP socket client
that can be used to make TCP socket requests that will be tracked in Locust's statistics.
"""
def __init__(self, *args, **kwargs):
super(TcpSocketLocust, self).__init__(*args, **kwargs)
self.client = TcpSocketClient(socket.AF_INET, socket.SOCK_STREAM)
ADDR = (self.host, self.port)
self.client.connect(ADDR)
class TcpTestUser(TcpSocketLocust):
host = "192.168.5.58"
port = 6667
min_wait = 100
max_wait = 1000
class task_set(TaskSet):
@task
def send_data(self):
data = "7e0200003f000004021895000b00000000000400030158ccaa06cb7" \
"9f50095000000001601051654150104000069740202000003020000" \
"2504000000002b040000000030010031010b3201467c7e"
# self.client.send(data.encode("utf-8"))
self.client.send(bytes.fromhex(data))
data = self.client.recv(2048).decode("utf-8")
print(data)
if __name__ == "__main__":
import os
os.system("locust -f sendTcp.py")
單節點啟動
locust -f locustfile.py
分布式啟動
# master執行
locust -f locustfile.py --master
# slave執行
locust -f locustfile.py --slave --master-host = 127.0.0.1
# pdsh 同時啟動slave節點, node 可使用ip代替
pdsh -w node[1-16] "locust -f /root/loadtest/locustfile.py --slave --master-host=node0"
python編寫的簡易服務端
調試程序使用的服務端代碼
#coding: utf-8
from __future__ import print_function
from gevent.server import StreamServer
import gevent
# sleeptime = 60
'''
python 編寫的簡單tcp socket server,作為服務端測試使用
'''
def handle(socket, address):
# print(address)
# data = socket.recv(1024)
# print(data)
while True:
gevent.sleep(sleeptime)
try:
socket.send("ok")
except Exception as e:
print(e)
if __name__ == "__main__":
import sys
port = 80
if len(sys.argv) > 2:
port = int(sys.argv[1])
sleeptime = int(sys.argv[2])
else:
print("需要兩個參數!!")
sys.exit(1)
# default backlog is 256
server = StreamServer(('0.0.0.0', port), handle, backlog=4096)
server.serve_forever()
參考文章:
- 百萬並發:
https://blog.csdn.net/jackliu16/article/details/81294741
- bytes和hex字符串之間的轉換:
https://blog.csdn.net/wowocpp/article/details/79701739
- 性能測試 - Locust TCP socket client:
https://blog.csdn.net/max229max/article/details/79277295
- 深入淺出Locust:
https://juejin.im/post/58c29457128fe10060297328
- 官文:
https://docs.locust.io/en/latest/quickstart.html
- locust&jmeter比較:
https://www.jianshu.com/p/dd0fcfdfa561