金沙国际官网_金沙国际平台登录

因为这个金沙国际官网_金沙国际平台登录网站与很多的大型澳门赌场都有合作,金沙国际官网_金沙国际平台登录尽职尽责,高效执行,保持好奇心,不断学习,追求卓越,点击进入金沙国际官网_金沙国际平台登录马上体验吧,所以现在也正式地开始了营业。

您的位置:金沙国际官网 > 编程 > 为什么Python为这么慢,如何解析HTTP请求报文

为什么Python为这么慢,如何解析HTTP请求报文

发布时间:2019-11-19 08:33编辑:编程浏览(172)

    Python语言 近年来 人气爆棚 。它广泛应用于网络开发运营,数据科学,网络开发,以及网络安全问题中。

    这个http server的实现源代码我放在了我的github上,有兴趣的话可以点击查看哦。

    1.导入第三方插件(GDalaXMLNode)

    然而, Python 在速度上完全没有优势可言。

    在上一篇文章中,讲述了如何编写一个最简单的server,但该程序只是接受到请求之后马上返回响应,实在不能更简单。在正常的开发中,应该根据不同的请求做出不同的响应。要做到上述的功能,首先要解析客户端发来的请求报文。

     

    在速度上,Java如何同C,C++,C#或者Python相比较?答案几乎完全取决于要运行的应用。在这个问题上,没有完美的评判标准,然而The Computer Language Benchmarks Game 是一个不错的方法。

    报文在不同的上下文情景下有不同的理解,本文所说的报文都是在HTTP上下文中描述的名词。

    2.第三方插件配置

    链接:

    HTTP报文是什么

    在HTTP程序中,报文就是HTTP用来搬运东西的包裹,也可以理解为程序之间传递信息时发送的数据块。这些数据块以一些文本形式的元信息开头,这些信息描述了报文的内容和含义,后面跟着可选的数据部分。

     

    报文的流动

    HTTP使用属于流入和流出来描述报文的传递方向。HTTP报文会像合水一样流动。不管时请求报文还是响应报文,都会向下游流动,所有报文的发送者都在接受者的上游。下图展示了报文向下游流动的例子。

    图片 1

    libxml/tree.h 路径

    基于我对The Computer Language Benchmarks Game超过十年的观察,相比于Java,C#,Go,JavaScript, C++等,Python是最慢的语言之一。其中包括了 JIT (C#, Java) 和 AOT (C, C++)编译器,以及解释型语言,例如JavaScript。

    报文的组成

    报文由三个部分组成:

    • 对报文进行描述的起始行
    • 包含属性的首部块
    • 可选的、包含数据的主体部分

    起始行和首部是由行分隔的ASCII文本。每行都以一个由两个字符(回车符--ASCII码13和换行符--ASCII码10)组成的行终止序列结束。可以写做CRLF

    尽管规范说明应该用CRLF来表示行终止,但稳健的应用程序也应该接受单个换行作为行的终止。笔者仅支持以CRLF换行的解析,因为我觉得既然有了规范,那就需要遵循,遵循相同的协议的程序才能互相通信。

    实体是一个可选的数据块。与起始行和首部不同的是,主体中可以包含主体或二进制数据,也可以为空(比如仅仅GET一个页面或文件)。

    下面来看看报文的语法的格式和规则。

     

    动态编译:

    报文的语法

    请求报文的语法:

    <method> <request-URL> <version>
    <headers>
    
    <entity-body>
    

    响应报文的语法:

    <version> <status-code> <reason-phrase>
    <headers>
    
    <entity-body>
    

    method,方法

    客户端希望服务器对资源执行的操作。比如GET、POST

    request-URL,请求URL

    请求资源,或者URL路径组件的完整URL。

    version,版本

    报文所使用的HTTP版本。格式:HTTP/.。其中major(主要版本号)和minor(次要版本号)都是整数。

    status-code,状态码

    描述请求过程所发生的情况的数字。

    reason-phrase,原因短语

    数字状态码的文字描述版本。

    headers,首部

    每个首部包含一个名字,后面跟着一个冒号(:),然后是一个可选的空格,接着是一个值,最后是一个CRLF。可以有零个或多个首部。首部由一个CRLF结束,表示首部结束和实体主体开始。

    entity-body,实体的主体部分

    包含一个由任意数据组成的数据块。可以没有,此时是以一个CRLF结束。

    在项目属性中--Bulid Settings中搜索 Search

    请求行

    请求报文的起始行称为请求行。所有的HTTP报文都以一行起始行作为开始。请求行包含一个方法和一个请求URL以及HTTP的版本三个字段。每个字段都以空格分隔。

    比如:GET / HTTP/1.1

    请求方法为GET,请求URL为/,HTTP版本为HTTP/1.1。

     

    静态编译:

    响应行

    响应报文的起始行称为响应行。响应行包含HTTP版本、数字状态码以及描述操作状态的文本形式的原因短语。三个字段也是以空格分隔。

    比如:HTTP/1.1 200 OK

    HTTP版本为HTTP/1.1,数字状态码是200,原因短语是OK。表示请求成功。

    --Search Paths 中--Headr Search Paths 添加路径(/usr/include/libxml2)

    首部

    首部是是包含在请求和响应报文的一些附加信息。本质上,他们只是一些键值对的列表。

    比如:Content-Length: 19

    表示返回内容长度为19。

     

    注意:当我提到“Python”时,我指的是CPython这个官方的解释器。我也将在本文中提及其他的解释器。

    实体的主体部分

    简单地说,这部分就是HTTP要传输的内容。

    非ARC

    我想要回答这样一个问题:当运行同一个程序时,为什么Python会 比其他语言慢2到10倍?为什么我们无法将它变得更快?

    解析请求报文

    了解了报文是如何组成和各部分代表的内容之后,就对如何解析请求报文心里有数了。

     

    以下是最主要的原因:

    核心代码

        /* 解析请求行 */
        int parse_start_line(int sockfd, char *recv_buf, req_pack *rp)
        {
            char *p = recv_buf;
            char *ch = p;
            int i = 0;
            enum parts { method, url, ver } req_part = method;
            char *method_str;
            char *url_str;
            char *ver_str;
            int k = 0;
    
            if (*ch < 'A' || *ch > 'Z') {
                return -1;
            }
    
            while (*ch != CR) {
                if (*ch != BLANK) {
                    k++;
                } else if (req_part == method) {
                    method_str = (char *)malloc(k * sizeof(char *));
                    memset(method_str, 0, sizeof(char *));
                    strncpy(method_str, recv_buf, k);
                    k = 0;
                    req_part = url;
                } else if (req_part == url) {
                    url_str = (char *)malloc(k * sizeof(char *));
                    memset(url_str, 0, sizeof(char *));
                    strncpy(url_str, recv_buf + strlen(method_str) + 1, k);
                    k = 0;
                    req_part = ver;
                }
                ch++;
                i++;
            }
    
            if (req_part == url) {
                if (k != 0) {
                    url_str = (char *)malloc(k * sizeof(char));
                    memset(url_str, 0, sizeof(char));
                    strncpy(url_str, recv_buf + strlen(method_str) + 1, k);
                    k = 0;
                } else {
                    return -1;
                }
            }
    
            if (k == 0) {
                ver_str = (char *)malloc(8 * sizeof(char));
                memset(ver_str, 0, sizeof(char));
                strcpy(ver_str, "HTTP/1.1");
            } else {
                ver_str = (char *)malloc(k * sizeof(char));
                memset(ver_str, 0, sizeof(char));
                strncpy(ver_str,
                        recv_buf + strlen(method_str) + strlen(url_str) + 2, k);
            }
    
            rp->method = method_str;
            rp->url = url_str;
            rp->version = ver_str;
    
            return (i + 2);
        }
    
        /* 解析首部字段 */
        int parse_header(int sockfd, char *recv_buf, header headers[])
        {
            char *p = recv_buf;
            char *ch = p;
            int i = 0;
            int k = 0;
            int v = 0;
            int h_i = 0;
            bool is_newline = false;
            char *key_str;
            char *value_str;
            header *tmp_header = (header *)malloc(sizeof(header *));
            memset(tmp_header, 0, sizeof(header));
    
            while (1) {
                if (*ch == CR && *(ch + 1) == LF) {
                    break;
                }
                while (*ch != COLON) {
                    ch++;
                    i++;
                    k++;
                }
                if (*ch == COLON) {
                    key_str = (char *)malloc(k * sizeof(char *));
                    memset(key_str, 0, sizeof(char *));
                    strncpy(key_str, recv_buf + i - k, k);
                    k = 0;
                    ch++;
                    i++;
                }
                while (*ch != CR) {
                    ch++;
                    i++;
                    v++;
                }
                if (*ch == CR) {
                    value_str = (char *)malloc(v * sizeof(char *));
                    memset(value_str, 0, sizeof(char *));
                    strncpy(value_str, recv_buf + i - v, v);
                    v = 0;
                    i++;
                    ch++;
                }
                i++;
                ch++;
                headers[h_i].key = key_str;
                headers[h_i].value = value_str;
                h_i++;
            }
    
            return (i + 2);
        }
    

    在项目属性中--Bulid Phases

    • “它是GIL(Global Interpreter Lock全局解释器锁)”
    • “它是解释型语言而非编译语言”
    • “它是动态类型语言”

    解析思想

    遍历recv接受到的请求字符串,检查是否遇到回车符r判断一行数据。

    对于起始行,检查是否遇到空格分隔不同的字段;对于首部,检查是否遇到冒号分隔键值对的字段值;对于实体的主体部分,则先判断是否遇到CRLF字符串,然后将剩余内容全部作为实体的主体部分。

    返回值是告知程序下一次遍历的起始位置。

    如果遇到非法请求行则返回400的响应。

     

    那么以上哪种原因对性能影响最大呢?

    总结

    解析报文的过程就是遵循HTTP协议规定的内容去解析报文,获取报文包含的信息。

    由于基础知识较薄弱,代码还有很多错误以及很多地方需要优化。如果有看到错误的地方或有其它建议望各位大侠不吝赐教。^_^

    这个http server的实现源代码我放在了我的github上,有兴趣的话可以点击查看哦。

    原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

    如果本文对你有帮助,请点下推荐吧,谢谢^_^

    --Compile Sources 中--GDataXMLNode.m 添加参数(-fno-objc-arc)

    “它是全局解释器锁”

     

    现代计算机的CPU通常是多核的,并且有些拥有多个处理器。为了充分利用多余的处理能力,操作系统定义了一种低级的结构叫做线程:一个进程(例如Chrome浏览器)可以产生多个线程并且指导内部系统。

    第三方库文件加载

    如果一个进程是CPU密集型,那么其负载可以被多核同时处理,从而有效提高大多数应用的速度。

     

    当我写这篇文章时,我的Chrome浏览器同时拥有44个线程。注意,基于POSIX(比如MacOS和Linux)和Windows操作系统相比,线程的结构和API是不同的。操作系统也会处理线程的调度问题。

    在项目属性中--Bulid Phases--Link Binary With Libraies--libxml2 文件

    如果你之前没有做过多线程编程,你需要快速熟悉锁的概念。区别于单线程进程,你需要确保当内存中的变量被修改时,多线程不会同时试图访问或者改变同一个存储地址。

     

    当CPython创建变量时,它会预先分配存储空间,然后计算当前变量的引用数目。这个概念被称为引用计数。如果引用计数为零,那么它将从系统中释放对应存储区域。

    3.使用第三方插件

    这就是为什么在CPython中创造“临时”变量不会使应用占用大量的存储空间——尤其是当应用中使用了for循环这一类可能大量创建“临时”变量的结构时。

     

    当存在多个线程调用变量时,CPython如何锁住引用计数成为了一个挑战。而“全局解释锁”应运而生,它能够谨慎控制线程的执行。无论有多少的线程,解释器每次只能执行一个操作。

    //设置URL

    这对Python的性能意味着什么呢?

     

    如果你的应用基于单线程、单解释器,那么讨论速度这一点就毫无意义,因为去掉GIL并不会影响代码性能。

    NSURL * url=[NSURL URLwithString:@"XML远程路径"];

    如果你想使用线程在单解释器(Python 进程)中实现并发,并且你的线程为IO密集型(例如网络IO或磁盘IO),你就会看到GIL争用的结果。

     

    如果你有一个网络应用(例如Django)并且使用WSGI,那么每一个对于你的网络应用的请求将是一个独立的Python解释器,因此每个请求只有一个锁。因为Python解释器启动很慢,一些WSGI便集成了能够使保持Python进程的“守护进程”  。

    //会话

    那么其他Python解释器的速度又如何呢?

     

    PyPy拥有GIL,通常比CPython快至少三倍。

    NSURLSession * session=[NSURLSession sharedSession];

    Jython没有GIL,因为在Jython中Python线程是用Java线程表示的,这得益于JVM内存管理系统。

     

    JavaScript是如何做到这一点的呢?

    //任务

    本文由金沙国际官网发布于编程,转载请注明出处:为什么Python为这么慢,如何解析HTTP请求报文

    关键词: