Python_服务端性能高并发测试

背景

公司业务处于上升期,但是服务端却low的像个demo,于是乎重构服务端开始了;
关于测试,测试这个行业在大多数互联网公司比较失败,做测试的应该都有这种感觉。但是我感觉我很幸运,因为CTO很重视测试,他的口头禅就是不可测试的程序都是不可靠的,所以我们公司的所有程序都会有配套的测试工具。这篇文章中的工具就是专门测试服务端而模拟的客户端TSP。

业务需求

重构服务端,达到接入百万客户端级别

实现原理

  1. 服务端监听3个端口,这三个端口全部是设备发起
  2. tsp模拟客户端连接服务端逻辑,从而达到设备、用户上线
  3. 采用进程池嵌套线程池方式,达到高并发的目的
  4. 代码中统计发包时间平均值、收包时间平均值、收包字节大小平均值、收包错误率;
  5. 服务端采集硬件配置、cpu负载、内存负载、磁盘io等等,想知道更多参数

具体实现

1.构建主流程,完成基于socket请求的收发包

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#
from socket import socket,AF_INET,SOCK_STREAM

import struct

from log import log
log=log()
class socket_main:

    def __init__(self,who,conn_data):
        self.conn_data = conn_data
        self.who = who
        self.__conn(conn_data)

    def __conn(self,addr):

        try:
            log.debug('{} _conn data:{}'.format(self.who,addr))
            self.tcpCliSock = socket(AF_INET, SOCK_STREAM)
            self.tcpCliSock.connect(addr)
            self.tcpCliSock.setblocking(1)
            self.tcpCliSock.settimeout(1)
            # self.tcpCliSock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,8192)
            log.debug('{} socket data:{}'.format(self.who,'success'))
        except BaseException as msg:
            log.error('{} socket conn_result:{}'.format(self.who,msg))

    def send(self,data):
        try:
            try:
                result = self.tcpCliSock.send(data)
            except BaseException as msg:
                # self.__conn(self.conn_data)
                # result = self.tcpCliSock.send(data)
                log.error(msg)
                raise msg
            log.debug('{} socket send_len:{}'.format(self.who, result))
            return result
        except BaseException as msg:
            raise log.error('{} socket send_error:{}'.format(self.who,msg))

    def recv(self,size,code):
        result =''
        try:
            try:
                result = self.tcpCliSock.recv(int(size))

                while len(result)<int(size):
                    result +=self.tcpCliSock.recv(int(size)-len(result))
            except:
                pass
            if not result:
               log.error('{} socket recv_result ERROR:result is null'.format(self.who))
            log.debug('{} socket recv_result:{}'.format(self.who, len(result)))
            data_struct = struct.unpack(code, result)
            log.debug('{} socket recv_result_unpack:{}'.format(self.who, data_struct))
            return data_struct
        except BaseException as msg:
            log.error('{} socket recv_error:{}'.format(self.who,msg))
            raise msg

    def only_recv(self,size):
        try:
            result = self.tcpCliSock.recv(int(size))
            while len(result) < int(size):
                result += self.tcpCliSock.recv(int(size) - len(result))
            log.debug('{} socket only_recv_result_len:{}'.format(self.who, len(result)))
            log.debug('only_recv:{}'.format(result))
            return result
        except BaseException as msg:
            log.error('{} socket recv_error:{}'.format(self.who,msg))
            raise msg

    def close(self):
        try:
            log.debug('{} socket has been closed'.format(self.who))
            self.tcpCliSock.close()
        except BaseException as msg:
            log.debug('{} socket close_error:{}'.format(self.who,msg))

  1. 建立进程池、线程池套餐
def main():
  print '业务代码'

class MyThread(threading.Thread):
    def __init__(self, func, args, name=''):
        threading.Thread.__init__(self)
        self.name = name
        self.func = func
        self.args = args

    def run(self):
        apply(self.func, self.args)

def main_thread():
    global sn
    threads = []
    nloops = xrange(thread_num)# thread_num并发线程数
    for i in nloops:
        mac, mac_real, sn = getMacSn()
        t = MyThread(main, (mac,mac_real,sn))
        threads.append(t)
    for i in nloops:
        threads[i].start()
    for i in nloops:
        threads[i].join()


if __name__=='__main__':
    result = ''
    pool = multiprocessing.Pool(processes=proc)# processes进程池数量
    log.info("main process(%d) running..." % os.getpid())
    for i in xrange(proc_num):# proc_num 并发进程数量
        result = pool.apply_async(main_thread)
    pool.close()
    pool.join()

    if not result.successful():
        log.error('主进程异常:{}'.format(result.successful()))
    else:
        log.info('goodbye:主进程({})执行完毕'.format(os.getpid()))

  1. 通过在业务代码中计算统计参数,然后放在内存中累计计算,输出到指定级别日志中,用到的主要方法是通过设置全局变量进行统一计算:
class globalMap:
    # 拼装成字典构造全局变量  借鉴map  包含变量的增删改查
    map = {}

    def set_map(self, key, value):
        if(isinstance(value,dict)):
            value = json.dumps(value)
        self.map[key] = value
        log.debug(key + ":" + str(value))


    def set(self, **keys):
        try:
            for key_, value_ in keys.items():
                self.map[key_] = str(value_)
                log.debug(key_+":"+str(value_))
        except BaseException as msg:
            log.error(msg)
            raise msg

    def del_map(self, key):
        try:
            del self.map[key]
            return self.map
        except KeyError:
            log.error("key:'" + str(key) + "'  不存在")


    def get(self,*args):
        try:
            dic = {}
            for key in args:
                if len(args)==1:
                    dic = self.map[key]
                    log.debug(key+":"+str(dic))
                elif len(args)==1 and args[0]=='all':
                    dic = self.map
                else:
                    dic[key]=self.map[key]
            return dic
        except KeyError:
            log.warning("key:'" + str(key) + "'  不存在")
            return 'Null_'

总结

多进程的方式和多线程进行对比,虽然Python全局锁的限制导致线程有点瑕疵,但是跟进程比起并发,还是能甩进程几条街的。通过这种方式进行高密度业务逻辑操作,可以很轻松找到服务端瓶颈。

@雾霾-一个早晨,被窝中-2018-01-14 10:17:13

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

推荐阅读更多精彩内容