网络性能优化

高并发网络编程的性能测试一直是个非常难的问题
困难点:
1 纯网络性能测试会把业务逻辑最小化,需要一定的改造。
2测试虚要给服务器足够的压力,这样对于测试用的客户端的QPS就有较高的要求
解决方案,
1 将业务逻辑跟网络框架拆分
2 我们可以开启多个实例
 
我们用来测试的代码库就是https://www.cnblogs.com/nerdlerss/p/9035928.html
中的库,在这个库中,我们把业务逻辑跟网络框架进行了拆分

我们的业务代码就是

counter = 0   
if __name__ == '__main__':
              
    def logic(d_in):
        global counter
        counter += 1
        if counter %100000 ==0:   
            print counter,time.time()
        return(d_in[::-1])
    reverseD = nbNet('0.0.0.0', 9099, logic)
    reverseD.run()

  说白了就是 每响应10w下答应一下时间

我们测试端代码

#!/usr/bin/env python

import socket, sys, os

HOST = '127.0.0.1'
PORT = 9099
CNT = int(sys.argv[2])

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

cmd = sys.argv[1]
data = "%010d%s"%(len(cmd), cmd)
while True:
    s.send(data * CNT)
    s.recv(len(data) * CNT)

但是我们的客户端是不可能打到server端的性能瓶颈的,我们可以开启多个终端,启动多个实例,这样达到我们性能要求

我们首先看下没有优化之前的性能

我们虚拟机的性能是:一核心CPU没有超线程 2.5G 内存2G

我们从300w次请求之后选取后面100w次的时间

3000000 1527159028.98

3100000 1527159031.62

3200000 1527159034.16

3300000 1527159036.72

3400000 1527159039.27

3500000 1527159041.82

3600000 1527159044.38

3700000 1527159047.01

3800000 1527159049.56

3900000 1527159052.12

4000000 1527159054.69

 

我们计算下每10w次请求的时间(取小数点后两位)

[0] 2.63s

[1]  2.54s 

[2] 2.56s 

[3] 2.55s

[4] 2.56s

[5]2.62s

[6] 2.55s

[7] 2.56s

[8] 2.57s

[9]2.56s

最差 2.63s  QPS 约等于 38022

最好 2.54s QPS 约等于 39370

==============================================================

在python内建了一个 profiler工具,可以帮我们定位性能瓶颈

 服务端调用方法

  python -m cProfile -s cumulative nbNetFramework.py
  -s cumulative 的意思是按照系统调用的总体耗时排序

 客户端调用方法

  python loadrun.py a 100

  python loadrun.py a 100

 执行一段时间后 结束server的时候会输出这段时间代码中的方法执行时间情况

root@work:/home/rico/python/rebootMon/nbNet# python  -mcProfile -s cumulative  nbNetFramework.py
100000 1527160075.61
200000 1527160079.19
300000 1527160082.8
^C         11986059 function calls (11985241 primitive calls) in 14.675 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.001    0.001   14.675   14.675 nbNetFramework.py:4(<module>)
        1    1.693    1.693   14.654   14.654 nbNetFramework.py:166(run)
  1088050    0.615    0.000   10.292    0.000 nbNetFramework.py:192(state_machine)
   725366    0.622    0.000    5.588    0.000 nbNetFramework.py:271(read2process)
   362683    0.670    0.000    4.089    0.000 nbNetFramework.py:307(write2read)
   725366    1.722    0.000    2.981    0.000 nbNetFramework.py:66(read)
  1088050    2.669    0.000    2.669    0.000 {method 'poll' of 'select.epoll' objects}
   362683    1.209    0.000    1.985    0.000 nbNetFramework.py:232(process)
   362683    0.605    0.000    1.691    0.000 nbNetFramework.py:124(write)
   362685    0.719    0.000    1.382    0.000 nbNetFramework.py:21(setFd)
   725366    1.070    0.000    1.070    0.000 {method 'recv' of '_socket.socket' objects}
   362683    0.894    0.000    0.894    0.000 {method 'send' of '_socket.socket' objects}
   725365    0.740    0.000    0.740    0.000 {method 'modify' of 'select.epoll' objects}
   362692    0.303    0.000    0.423    0.000 socket.py:227(meth)
   362683    0.320    0.000    0.320    0.000 nbNetFramework.py:339(logic)
2905468/2905319    0.250    0.000    0.250    0.000 {len}
   362685    0.240    0.000    0.240    0.000 nbNetUtils.py:164(__init__)
   363892    0.192    0.000    0.192    0.000 {isinstance}
   362725    0.067    0.000    0.067    0.000 {getattr}
   362688    0.053    0.000    0.053    0.000 {method 'fileno' of '_socket.socket' objects
ncalls -- 调用多少次
#自己调用的时间,不包括其他十年
tottime 调用时耗
percall 每次调用的时间
#包括子函数时间
cumtime调用耗时
percall 每次调用耗时

 这样我们就知道那个方法比较耗费时间了。

然后通过 line_profiler来优化具体的行

#pip install line_profiler 安装

在比较耗时的方法上面加上@profile 这个装饰(这里我们看到之前的read方法比较耗时)
# kernprof -l -v ./nbNet.py 执行命令

然后我们执行客户端代码

然后等一段时间结束

Function: read at line 45

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    45                                               @profile
    46                                               def read(self, fd):
    47                                                   """fd is fileno() of socket"""
    48     87910     118605.0      1.3      7.1          try:
    49     87910     104105.0      1.2      6.2              sock_state = self.conn_state[fd]
    50     87910      71153.0      0.8      4.2              conn = sock_state.sock_obj
    51     87910      79448.0      0.9      4.7              if sock_state.need_read <= 0:
    52                                                           raise socket.error
    53     87910     433116.0      4.9     25.8              one_read = conn.recv(sock_state.need_read)
    54     87910     118447.0      1.3      7.1              if len(one_read) == 0:
    55                                                           raise socket.error
    56     87910     122770.0      1.4      7.3              sock_state.buff_read += one_read
    57     87910      92267.0      1.0      5.5              sock_state.have_read += len(one_read)
    58     87910      99366.0      1.1      5.9              sock_state.need_read -= len(one_read)
    59                                                        
    60                                           
    61     87910      78578.0      0.9      4.7              if sock_state.have_read == 10:
    62     43955     126373.0      2.9      7.5                  header_said_need_read = int(sock_state.buff_read)
    63     43955      41639.0      0.9      2.5                  if header_said_need_read <= 0:
    64                                                               raise socket.error
    65     43955      45788.0      1.0      2.7                  sock_state.need_read += header_said_need_read
    66     43955      39130.0      0.9      2.3                  sock_state.buff_read = ''
    67     43955      33228.0      0.8      2.0                  return "readcontent"
    68     43955      37326.0      0.8      2.2              elif sock_state.need_read == 0:
    69     43955      35561.0      0.8      2.1                  return "process"
    70                                                       else:
    71                                                           return "readmore"
    72                                                   except (socket.error, ValueError), msg:
    73                                                       try:
    74                                                           if msg.errno == 11:
    75                                                               return "retry"
    76                                                       except:
    77                                                           pass
    78                                                       return 'closing'

  

 

原文地址:https://www.cnblogs.com/nerdlerss/p/9084337.html