[Python]再学 socket 之非阻塞 Server

2.1.2 进程

存在硬盘中的叫做‘程序’(*.py),当程序运转加载到内部存款和储蓄器中的时候叫做‘进度’。系统会分配给各个进程二个唯一ID,
以此 ID 叫做:PID
,进度还分为父进度和子进度,父进度(PPID)创设子进程(PID)。关系如下图:

图片 1

能够由此ps指令来查看过程的信息:每一日三个linux命令(四1):ps命令

亟需留意:

  • 子进度一定要关门
  • 子过程关闭一定要文告父进程,否则会冒出‘僵尸进度’
  • 自然要先甘休父进度,再截至子进度,不然会油不过生‘孤儿进度’

僵尸进度:叁个进度使用fork创设子进度,即使子进度退出,而父进度并从未调用wait或waitpid获取子进度的情状音讯,那么子进度的过程描述符如故保存在系统中。那种进度称之为僵尸进度。(系统所能使用的经过号是有限定的,假诺大气的发生僵死进程,将因为未有可用的长河号而导致系统不能够产生新的长河。则会抛出OSError: [Errno 35] Resource temporarily unavailable异常)

孤儿过程:两个父进度退出,而它的三个或八个子进度还在运营,那么那三个子进程将改为孤儿进度。孤儿进程将被init进度(进度号为一)所收养,并由init进度对它们形成情形收集工作。(未有挫伤)

 

一.2 为啥会冒出堵塞?

因为服务器处理请求是内需耗时的,正如笔者上边的梗塞 Server
代码中的time.sleep(10),用于模拟
服务器处理请求消耗的时辰。

在拍卖完上一个请求(再次来到给 Client
数据)的那段日子中,服务器无法处理其余的请求,只可以让任何的 Client
等待。那样的频率是
最棒低下的,所以下边就介绍怎么样创立2个非阻塞的 Server

           
那段是MSDN上的,那的意味是按不相同的急需,重新分区(那里是安分守己SalesOrderID字段分区),获取相应数额。聚合函数就不多说了。。亮点是,比子查询质量高(MSDN说的)。

1.1 阻塞 Server 示例

上面就经过C/S模型,体现阻塞状态:

  • 选择别的 socket 请求的 socket 叫做:Server(S)
  • 请求 Server 的 socket 叫做:Client(C)

该代码片段分别是:阻塞的 Server 和测试用的 Client:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#   
#   Author  :   XueWeiHan
#   Date    :   17/2/25 上午10:39
#   Desc    :   阻塞 server
import socket
import time

SERVER_ADDRESS = (HOST, PORT) = '', 50007
REQUEST_QUEUE_SIZE = 5


def handle_request(client_connection):
    """
    处理请求
    """
    request = client_connection.recv(1024)
    print('Server recv: {request_data}'.format(request_data=request.decode()))
    time.sleep(10)  # 模拟阻塞事件
    http_response = "Hello, I'm server"
    client_connection.sendall(http_response)


def server():
    listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    listen_socket.bind(SERVER_ADDRESS)
    listen_socket.listen(REQUEST_QUEUE_SIZE)
    print('Server on port {port} ...'.format(port=PORT))

    while 1:
        client_connection, client_address = listen_socket.accept()
        handle_request(client_connection)
        client_connection.close()

if __name__ == '__main__':
    server()
  • REQUEST_QUEUE_SIZE:在 sever
    阻塞的时,允许挂起多少个接二连三。便于能够拍卖时一向从该队列中拿走三番五次,裁减建立连接的时光
  • time.sleep:用于模拟阻塞

测试用的 Client

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#   
#   Author  :   XueWeiHan
#   Date    :   17/2/25 上午11:13
#   Desc    :   测试 client

import socket

SERVER_ADDRESS = (HOST, PORT) = '', 50007


def send_message(s, message):
    """
    发送请求
    """
    s.sendall(message)


def client():
    message = "Hello, I'm client"
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(SERVER_ADDRESS)
    send_message(s, message)
    print 'Client is Waiting response...'
    data = s.recv(1024)
    s.close()
    print 'Client recv:', repr(data)  # 打印从服务器接收回来的数据

if __name__ == '__main__':
    client()

打开多少个极点,先运转 Server,在其余两个极端运营Client(分别起名称为client壹、client贰),会意识
服务器先接到 client一 的数码,然后重返响应。再此在此之前 client2平素处于等候的事态。唯有等 Server
拍卖完 client一 的央浼后,才会接到 client二 的数额。

图片 2

诸如此类2个个地接过请求、处理请求的 Server 就叫做 阻塞 Server。

        row_number() over(partition by name order by counts desc) rn

二、非阻塞 Server

  • 亟待了然的1对基本概念
  • 非阻塞 Server 示例(多进程)

前面会用多进程贯彻
非阻塞socket,在此之前须要明白部分基本知识和定义,便于明白后边的代码。

     含义:

再学 socket 之非阻塞 Server

正文是依照 python二.7 达成,运维于 Mac 系统下

本篇小说是上一篇初探
socket
的续集,
上一篇文章介绍了:怎么样树立起多个主干的 socket 连接、TCP 和 UDP
的概念、socket 常用参数和措施

Socket
是用来通讯、传输数据的指标,上一篇已经济研讨究了如若展开基本的直通和传输数据。因为,在这一个互
联网发生的时代,做为 Server 的 socket 要同时收取很多的央求。

经过翻阅:地址,强烈推荐阅读原来的书文。

整治了下边包车型地铁文字,怎么着:创设四个 非阻塞的 server。

      意思是 把表Table1 中的数据按照 name列进行分区,每个区按照counts进行排序。

二.一.3 文件讲述符

在UNIX中1切都是八个文本,当操作系统打开一存在的个文件的时候,便会回去一个‘文件讲述符’,进度经过
操作该公文操作符,从而达成对文本的读写。Socket
是贰个操作文件讲述符的长河,Python 的 socket
模块提供了这几个操作系统底层的完毕。大家只需求调用socket对象的点子就足以了。

亟需注意

  • 文件讲述符的回收机制是使用引用计数方式
  • 历次操作完文件讲述符要求调用close()方式,关闭文件讲述符。道理和进程一样,操作系统都会最多可创制的文书描述符的限制,假若直白不关门文本描述符的话,导致数据太多不能创造新的,就会抛出OSError: [Errno 24] Too many open file异常。

MSDN上语法:

参考

 

②.一.壹 Socket 处理请求的进度

参考上边写的围堵 Server
的代码,能够看看:服务器端的socket对象,listen_socket
从不和客户端沟通数据。它只会通过accept艺术接受连接。然后,创立2个新的socket对象,client_connection用来和客户端通讯。

所以,服务器端的socket 分为:经受请求的socket(listen_socket)
与客户端传输数据的socket(client_connection)

正如上边聊起的,真正封堵地方是:与客户端传输数据的socket(client_connection
要求静观其变处理请求的结果,然后返还给客户端,结束这一次通讯,才能处理后边的呼吁。

 

2.一.四 怎么着查看进程和用户能源极限

电脑的测算和仓库储存能力都是不难的,统称为电脑能源。

地方说了经过和文书讲述符号都以有个最大数量(极限),上边就是用来查看和改动用户能源限制的命令——ulimit

-a  列出所有当前资源极限。
-c  以 512 字节块为单位,指定核心转储的大小。
-d  以 K 字节为单位指定数据区域的大小。
-f  使用 Limit 参数时设定文件大小极限(以块为单位),或者在未指定参数时报告文件大小极限。缺省值为 -f 标志。
-H  指定设置某个给定资源的硬极限。如果用户拥有 root 用户权限,可以增大硬极限。任何用户均可减少硬极限。
-m  以 K 字节为单位指定物理内存的大小(驻留集合大小)。系统未强制实施此限制。
-n  指定一个进程可以拥有的文件描述符数的极限。
-r  指定对进程拥有线程数的限制。
-s  以 K 字节为单位指定堆栈的大小。
-S  指定为给定的资源设置软极限。软极限可增大到硬极限的值。如果 -H 和 -S 标志均未指定,极限适用于以上二者。
-t  指定每个进程所使用的秒数。
-u  指定对用户可以创建的进程数的限制。

常用命令如下:

  • ulimit -a:查看
  • ulimit -n:设置1个历程可具备文件讲述符数量
  • ulimit -u:最多能够创设多少个进度

           

一、阻塞 Server

  • 阻塞 Server 示例
  • 怎么会并发堵塞

   意思是  每种分区排序后,获取第一行数据,其余行吐弃。

最后

该非阻塞 Server 是由此操作系统级别的 fork
实现的,用到了多进度和功率信号机制。

因为多进度化解非阻塞的标题,很好精晓,可是那些消耗计算机财富的,后边会介绍越发轻量级的——利用事件循环达成非阻塞
Server。

挖个坑~

1 SELECT SalesOrderID, ProductID, OrderQty
2     ,SUM(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Total'
3     ,AVG(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Avg'
4     ,COUNT(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Count'
5     ,MIN(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Min'
6     ,MAX(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'Max'
7 FROM Sales.SalesOrderDetail 
8 WHERE SalesOrderID IN(43659,43664);

2.1 供给知道的一对基本概念

  • Socket 处理请求的经过
  • 进程
  • 文本讲述符
  • 什么查看进度和用户财富
over(partition by name order by counts desc)

二.2 Fork 格局的非阻塞 Server

使用 fork 的方法贯彻非阻塞 Server,首要原理正是当 socket
接受到(accept)1个呼吁,就 fork 出3个子历程
去处理那些请求。然后父进度继续接受请求。从而达成产出的拍卖请求,不必要处理上一个伸手才能经受、处理下一个请求。

import errno
import os
import signal
import socket

SERVER_ADDRESS = (HOST, PORT) = '', 8888
REQUEST_QUEUE_SIZE = 1024


def grim_reaper(signum, frame):
    while True:
        try:
            pid, status = os.waitpid(
                -1,          # Wait for any child process
                 os.WNOHANG  # Do not block and return EWOULDBLOCK error
            )
        except OSError:
            return

        if pid == 0:  # no more zombies
            return


def handle_request(client_connection):
    request = client_connection.recv(1024)
    print(request.decode())
    http_response = b"""\
HTTP/1.1 200 OK

Hello, World!
"""
    client_connection.sendall(http_response)


def serve_forever():
    listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    listen_socket.bind(SERVER_ADDRESS)
    listen_socket.listen(REQUEST_QUEUE_SIZE)
    print('Serving HTTP on port {port} ...'.format(port=PORT))

    signal.signal(signal.SIGCHLD, grim_reaper)

    while True:
        try:
            client_connection, client_address = listen_socket.accept()
        except IOError as e:
            code, msg = e.args
            # restart 'accept' if it was interrupted
            if code == errno.EINTR:
                continue
            else:
                raise

        pid = os.fork()
        if pid == 0:  # child
            listen_socket.close()  # close child copy
            handle_request(client_connection)
            client_connection.close()
            os._exit(0)
        else:  # parent
            client_connection.close()  # close parent copy and loop over

if __name__ == '__main__':
    serve_forever()

如读书代码时出现的题目,能够参照上边包车型大巴要害字:

  1. Python os.fork,文件句柄(引用计数)、子进度(pid==0)
  2. linux ulimt命令
  3. 僵尸进度何以制止僵尸进程,采用os.wait
  4. python
    signal模块
  5. error.EINTR(慢系统调用:大概永远阻塞的类别调用,例如:socket)
  6. 因为过多的子进度并发先河,同时终止,会油但是生的产生截至的频域信号,父进度的
    signal
    壹弹指间收下过多的时限信号,导致了有的复信号丢失,那种境况依然会残留一些僵尸进度。这年就供给写1个handle频限信号的法子。选取waitpidos.WHOHANG挑选,举办死循环。以保障获得到具有
    signal
  7. OSError
    因为waitpidos.WNOHANG慎选,不会卡住,然而假使未有子进度退出,会抛出OSError,须求catch
    到那一个可怜,保险父进度接收到了各类子进度的终止消息,从而确认保障未有僵尸进度。

waitpid()函数的options选项:

os.WNOHANG - 如果没有子进程退出,则不阻塞waitpid()调用

os.WCONTINUED - 如果子进程从stop状态变为继续执行,则返回进程自前一次报告以来的信息。

os.WUNTRACED - 如果子进程被停止过而且其状态信息还没有报告过,则报告子进程的信息。

共计二种选拔场景。

          按某列进行重新分区,然后区内排序后,取当中某条数据。例:

     

场景1:

select * from (  
     select id,name,counts,row_number() over(partition by name order by counts desc) rn  
     from Table1  
    ) t where t.rn =1 

    

             结合聚合函数,获取分区聚合后的值,品质比子查询还要高。

  

 

   场景二:

     

拍卖部分分组后,该组依照某列排序后 ,取中间某条完整数据的标题。 或
遵照内部分裂列分组后的聚合 比如 sum,avg之类。

  MSDN的链接地址:https://msdn.microsoft.com/zh-cn/library/ms189461(v=sql.105).aspx

 

     意思是 每一种区排序后
取到个中排序后的类别号 。并起名字rn 

 

1   select * from (  
2    select id,name,counts,row_number() over(partition by name order by counts desc) rn  
3    from Table1  
4   ) t where t.rn <=1  
Ranking Window Functions 
< OVER_CLAUSE > :: =
    OVER ( [ PARTITION BY value_expression , ... [ n ] ]
           <ORDER BY_Clause> )

Aggregate Window Functions 
< OVER_CLAUSE > :: = 
    OVER ( [ PARTITION BY value_expression , ... [ n ] ] )

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图