Web 请求之--性能相关
本博客代码运行环境
python : Python 3.7.1rc1 version
pip : pip 19.1.1 version
Scrapy: scrapy 1.6.0 version
asyncio: asyncio 3.4.3 version
aiohttp: aiohttp 3.5.4 version
gevent: gevent 1.4.0 version
grequests: grequests 0.3.0 version
greenlet: greenlet 0.4.15 version
requests: requests 2.21.0 version
urllib3: urllib3 1.24.1 version
tornado: tornado 6.0.2 version
twisted: twisted 19.2.0 version
以下所有代码都以 爬虫 为例, 发送 http 请求,获取页面源码。
概念回顾
操作系统(OS):
是一个用来协调、管理和控制计算机硬件和软件资源的系统程序,它位于硬件和应用程序之间。
进程(Process):------------》》是 OS 进行资源分配和调度的一个基本独立单位
进程就是一个程序在一个数据集上的一次动态执行过程。
进程一般由程序、数据集、进程控制块三部分组成。
程序: 由我们编写用来描述进程要完成哪些功能以及如何完成,是指令、数据及其组织形式的描述,进程是程序的实体
数据集: 则是程序在执行过程中所需要使用的资源;
进程控制块: 用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志。
多进程:
指操作系统能同时运行多个任务(程序)。
总结来说:
进程是线程的容器
每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。(进程是资源分配的最小单位
适用于: 计算密集型
线程(Thread):------------》》是cpu调度和分派的最小单位
有时被称为轻量级进程(Lightweight Process,LWP)
为了降低上下文切换的消耗,提高系统的并发性,并突破一个进程只能干一样事的缺陷,使到进程内并发成为可能。
是程序执行流的最小单元。 一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成
线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。
由于线程之间的相互制约,致使线程在运行中呈现出间断性。
线程也有就绪、阻塞和运行三种基本状态:
就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待 CPU 调度;
阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
运行状态是指线程占有 CPU 正在运行;
多线程:
在单个程序中同时运行多个线程完成不同的工作,即具有多个顺序流在执行。
总结来说:
线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。
同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)
适用于: IO 密集型
GIL全局解释器锁(Global Interpreter Lock):
无论你启多少个线程,你有多少个cpu, Python在执行一个进程的时候会淡定的在同一时刻只允许一个线程运行
机制,一个基本的要求就是需要实现不同线程对共享资源访问的互斥,所以引入了GIL。
GIL缺点:多处理器退化为单处理器;优点:避免大量的加锁解锁操作
协程(Coroutine):
又称微线程,纤程。协程是一种用户态的轻量级线程。
线程/进程:操作系统提供的一种并发处理任务的能力,操作是由程序触发系统接口, 操作者:系统;
协程:程序员通过高超的代码能力,在代码执行流程中人为的实现多任务并发,是单个线程内的任务调度技巧。操作者:程序员;
没有切换的开销。协程不需要多线程的锁机制,因为都是在同一个线程中运行,所以没有同时访问数据的问题,执行效率比多线程高很多
协程存在的意义:对于多线程应用,CPU通过切片(轮询)的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。
协程切换时,因为只使用一个线程,是由程序自身控制的,体现的是程序员的流程控制能力,通过一定的语法规定在一个线程中所有代码块的执行顺序。
协程的适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程, 即: IO 密集型 综上: 最佳方案: 协程 + 多进程
线程与进程的共同点和区别
(1)共同点: 线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。 (2)区别: 线程和进程的区别在于,子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文。多线程主要是为了充分利用 CPU,提高效率。
线程与进程的区别可以归纳为以下几点:
1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。 2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。 3)调度和切换:线程上下文切换比进程上下文切换要快得多。故所消耗的资源比进程要少的多多。 4)在多线程OS中,进程不是一个可执行的实体。
(3)线程的属性(特点):
1)轻型实体
线程中的实体基本上不拥有系统资源,只是有一点必不可少的、能保证独立运行的资源,比如:
在每个线程中都具有一个用于控制线程运行的线程控制块TCB,用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等的一组寄存器和堆栈。 2)独立调度和分派的基本单位。
在多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小。 3)可并发执行。
在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;同样,不同进程中的线程也能并发执行。 4)共享进程资源。
在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:
所有线程都具有相同的地址空间(进程的地址空间),这意味着,线程可以访问该地址空间的每一个虚地址;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。
HTTP 协议
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。 HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。 HTTP是一个属于应用层的面向对象的协议,目前在WWW中使用的是 HTTP/1.0的第六版, HTTP协议工作于客户端-服务端架构为上。基于 : 请求-----响应 ,模式
HTTP 特点
1、简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。
每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。 2、灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。 3.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。 4.无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。
缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
HTTP 协议 数据发送格式
HTTP 请求协议
请求首行; // 请求方式 请求路径 协议和版本,例如:GET /index.html HTTP/1.1
请求头信息; // 请求头名称:请求头内容,即为key:value格式,例如:Host:localhost
空行; // 用与分隔 请求头 和 请求体(\r\n\r\n)
请求体。 // GET没有请求体,只有POST有请求体。
GET:
HTTP默认的请求方法就是GET
* 没有请求体
* 数据量有限制!
* GET请求数据会暴露在浏览器的地址栏中
GET请求常用的操作:
1. 在浏览器的地址栏中直接给出URL,那么就一定是GET请求
2. 点击页面上的超链接也一定是GET请求
3. 提交表单时,表单默认使用GET请求,但可以设置为POST
例如:
GET 请求:
GET /338.jpg HTTP/1.1\r\nHost:img.mukewang.com\r\nContent-Type:multipart/form-data\r\nReferer:http://www.imooc.com/\r\n\r\n
POST 请求:
POST / HTTP/1.1\r\nHost: img.mukewang.com\r\nConnection: Keep-Alive\r\n\r\nname=Professional&publisher=Wiley
请求头:
1、Host 请求的web服务器域名地址 2、User-Agent HTTP客户端运行的浏览器类型的详细信息。通过该头部信息,web服务器可以判断出http请求的客户端的浏览器的类型。 3、Accept 指定客户端能够接收的内容类型,内容类型的先后次序表示客户都接收的先后次序 4、Accept-Lanuage 指定HTTP客户端浏览器用来展示返回信息优先选择的语言 5、Accept-Encoding 指定客户端浏览器可以支持的web服务器返回内容压缩编码类型。表示允许服务器在将输出内容发送到客户端以前进行压缩,以节约带宽。
而这里设置的就是客户端浏览器所能够支持的返回压缩格式。 6、Accept-Charset HTTP客户端浏览器可以接受的字符编码集 7、Content-Type 显示此HTTP请求提交的内容类型。一般只有post提交时才需要设置该属性 有关Content-Type属性值有如下两种编码类型: (1)“application/x-www-form-urlencoded”: 表单数据向服务器提交时所采用的编码类型,默认的缺省值就是“application/x-www-form-urlencoded”。
然而,在向服务器发送大量的文本、包含非ASCII字符的文本或二进制数据时这种编码方式效率很低。 (2)“multipart/form-data”: 在文件上载时,所使用的编码类型应当是“multipart/form-data”,它既可以发送文本数据,也支持二进制数据上载。 当提交为表单数据时,可以使用“application/x-www-form-urlencoded”;当提交的是文件时,就需要使用“multipart/form-data”编码类型。
HTTP 响应协议
HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
例如:
HTTP/1.1 200 OK
Date: Fri, 22 May 2009 06:07:21 GMT
Content-Type: text/html; charset=UTF-8 <html>
<head></head>
<body>
<!--body goes here-->
</body>
</html>
例:
HTTP/1.1 200 OK\r\nDate:Fri, 22 May 2009 06:07:21 GMT\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n<html><head</head><body>shiwei</body></html>
第一部分:状态行,由HTTP协议版本号, 状态码, 状态消息 三部分组成。
第一行为状态行,(HTTP/1.1)表明HTTP版本为1.1版本,状态码为200,状态消息为(ok)
第二部分:消息报头,用来说明客户端要使用的一些附加信息
第二行和第三行为消息报头,
Date:生成响应的日期和时间;Content-Type:指定了MIME类型的HTML(text/html),编码类型是UTF-8
第三部分:空行,消息报头后面的空行是必须的
第四部分:响应正文,服务器返回给客户端的文本信息。
响应状态码:
状态代码有三位数字组成,第一个数字定义了响应的类别,共分五种类别:
1xx:指示信息--表示请求已接收,继续处理
2xx:成功--表示请求已被成功接收、理解、接受
3xx:重定向--要完成请求必须进行更进一步的操作
4xx:客户端错误--请求有语法错误或请求无法实现
5xx:服务器端错误--服务器未能实现合法的请求
常见状态码:
200 OK //客户端请求成功
400 Bad Request //客户端请求有语法错误,不能被服务器所理解
401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
403 Forbidden //服务器收到请求,但是拒绝提供服务
404 Not Found //请求资源不存在,eg:输入了错误的URL
500 Internal Server Error //服务器发生不可预期的错误
503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常
准备好了吗?基础知识很重要!!
编写爬虫程序,性能的消耗主要在 IO 请求中,当单进程单线程模式下请求URL时必然会引起等待,效率极低!
import requests def task(url):
response = requests.get(url)
return response url_list = ['http://www.github.com', 'http://www.bing.com'] for url in url_list:
task(url)
同步执行
from concurrent.futures import ThreadPoolExecutor
import requests def task(url):
response = requests.get(url)
print(response)
# print(response.text) # 获取 页面 源码 字符串数据
print("\033[1;30;47murl==%-70s----------response==%-s\033[0m"%(url,response.status_code))
return response url_list = [
"http://tuijian.hao123.com/",
"http://www.163.com/",
"http://www.eastmoney.com/",
] pool = ThreadPoolExecutor(5) for url in url_list:
v = pool.submit(task,url) pool.shutdown(wait=True)
多线程执行
from concurrent.futures import ThreadPoolExecutor
import requests def task(url):
""" 返回页面 源码数据 """
response = requests.get(url)
print("\033[1;30;47murl==%-70s----------response==%-s\033[0m"%(url,response.status_code))
return response def done(future,*args, **kwargs):
""" 解析页面 源码 """
print(future.result()) url_list = [
"http://tuijian.hao123.com/",
"http://www.163.com/",
"http://www.eastmoney.com/",
] pool = ThreadPoolExecutor(5) for url in url_list:
v = pool.submit(task,url)
v.add_done_callback(done) pool.shutdown(wait=True)
多线程 + 回调函数
from concurrent.futures import ProcessPoolExecutor
import requests pool = ProcessPoolExecutor(5)
def task(url):
response = requests.get(url)
print(response)
print("\033[1;30;47murl==%-70s----------response==%-s\033[0m"%(url,response.status_code))
return response url_list = [
"http://tuijian.hao123.com/",
"http://www.163.com/",
"http://www.eastmoney.com/",
] if __name__ == "__main__": # 必须要加 此 判断语句
for url in url_list:
v = pool.submit(task, url) pool.shutdown(wait=True)
多进程执行
from concurrent.futures import ProcessPoolExecutor
import requests
pool = ProcessPoolExecutor(5) def task(url):
response = requests.get(url)
# print("\033[1;30;47murl==%-70s----------response==%-s\033[0m"%(url,response.status_code))
return response def done(*args, **kwargs):
print(args[0].result())
print("\033[1;31;40m %s-----%s \033[0m"%(args, kwargs))
url_list = [
"http://tuijian.hao123.com/",
"http://www.163.com/",
"http://www.eastmoney.com/",
] if __name__ == "__main__": # 必须要加 此 判断语句
for url in url_list:
v = pool.submit(task, url)
v.add_done_callback(done) pool.shutdown(wait=True)
多进程 + 回调函数
以上代码 均 可提升请求性能, 但多线程 和 多进程 的缺点是 在 IO 阻塞时 依然会造成线程 和 进程的 浪费,
故 异步 IO 是 首选:
遇到 IO , 则 立即转而操作其他, IO 完成时,则自动回调运行处理 IO 的 函数。
import asyncio
# 不支持 HTTP 请求, 但支持 tcp 请求,socket
@asyncio.coroutine # 不可缺少
def task():
print('before ......Task')
yield from asyncio.sleep(5) # 不可写为 time.sleep(5)
print('after ...... Task') tasks = [task(), task(), task()] # 任务列表
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(*tasks))
loop.close()
asyncio 示例
asyncio + 模拟 http :
import asyncio
@asyncio.coroutine
def task(host,url='/'):
# 创建 链接
reader, writer = yield from asyncio.open_connection(host, 80) request_header_content = """GET %s HTTP/1.0\r\nHost: %s\r\n\r\n"""%(url,host,)
request_header_content = bytes(request_header_content, encoding='utf-8') writer.write(request_header_content)
yield from writer.drain()
text = yield from reader.read()
print('ending ',host, url, text)
writer.close() tasks = [
task('www.cnblogs.com', '/wupeiqi/'),
task('www.cnblogs.com', '/shiwei1930/'),
task('www.cnblogs.com', '/alex3714/'),
] loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*tasks))
loop.close()
asyncio + aiohttp :
import aiohttp
import asyncio @asyncio.coroutine
def task(url):
print(url)
resp = aiohttp.ClientSession().get(url)
response = yield from resp
print(url, response)
response.close() tasks = [task('http://www.baidu.com/'), task('http://www.chouti.com/')] loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*tasks))
loop.close()
asyncio + requests
import requests
import asyncio
@asyncio.coroutine
def task(func,*args):
loop = asyncio.get_event_loop()
future = loop.run_in_executor(None,func,*args)
response = yield from future
print('\033[1;31;40m url=%-30s\n\033[0mcontents=%-30s'%(response.url,response.content)) tasks = [
task(requests.get, 'http://www.baidu.com/'),
task(requests.get, 'http://www.cnblogs.com/'),
task(requests.get, 'http://autohome.com.cn'),
] loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*tasks))
print(results)
loop.close()
gevent + requests
import requests
import gevent
from gevent import monkey
monkey.patch_all() # 不可缺少 def task(method,url,req_kwargs):
print('method=%s,url=%s,kwargs=%s'%(method,url,req_kwargs))
response = requests.request(method=method,url=url, **req_kwargs)
print('url=%s\n\t\tcontent=%s'%(response.url,response.content)) gevent.joinall([
gevent.spawn(task,method='get', url='https://www.github.com/', req_kwargs={}),
gevent.spawn(task, method='get', url='https://www.yahoo.com/', req_kwargs={}),
gevent.spawn(task, method='get', url='https://www.python.org/', req_kwargs={}),
])
grequests = gevent + requests
import grequests request_list = [
grequests.get('http://www.cnblogs.com/'),
grequests.get('https://www.yahoo.com/'),
grequests.get('https://www.github.com/'),
] response_list = grequests.map(request_list)
print('result=',response_list)
for item in response_list:
print('content=',item.content)
Twisted
from twisted.web.client import getPage,defer
from twisted.internet import reactor def callback(contents):
print(contents) def all_done(arg):
reactor.stop() # 全部完成之后 stop @defer.inlineCallbacks
def task(url):
defered = getPage(bytes(url, encoding='utf8'))
defered.addCallback(callback)
yield defered
defered_list = [] url_list = ['http://www.cnblogs.com/', 'https://github.com/', 'http://baidu.com/']
for url in url_list:
defered = task(url)
defered_list.append(defered) dlist = defer.DeferredList(defered_list)
dlist.addBoth(all_done) # 事件循环
reactor.run() # 去 defered_list 中 检测 谁回来了, 是个死 循环
Tornado
from tornado.httpclient import AsyncHTTPClient
from tornado.httpclient import HTTPRequest
from tornado import ioloop COUNT = 0
def handler_response(response):
global COUNT
COUNT -= 1
if response.error:
print('error:',response.error)
else:
print('body:',response.body)
if COUNT == 0:
ioloop.IOLoop.current().stop() def task():
url_list = [
'http://www.baidu.com/',
'http://www.cnblogs.com/',
'https://www.github.com',
]
global COUNT
COUNT = len(url_list)
for url in url_list:
print("url===",url)
http_client = AsyncHTTPClient()
http_client.fetch(HTTPRequest(url),handler_response)
if __name__ == '__main__':
ioloop.IOLoop.current().add_callback(task)
ioloop.IOLoop.current().start() # # 事件循环, 是一个 死循环
自定义 异步 IO 框架
最基本的 网络编程的 本质:客户端
import socket
sk = socket.socket()
# 链接
sk.connect(('www.baidu.com',80)) # IO 阻塞 # 设置 非阻塞
# sk.setblocking(False) # 请求是 发过去了 print('链接成功!') sk.send(b'GET / HTTP/1.0\r\nHost: baidu.com\r\n\r\n')
# sk.send(b'POST / HTTP/1.0\r\nHost: baidu.com\r\n\r\n fdkfjdkjfkdfj') buffer_size = 1024
# 等待 着 服务端响应
data = sk.recv(buffer_size) # IO 阻塞 print(data)
# 关闭链接
sk.close()
自定义 异步 IO 框架-----》客户端
import select
import socket # IO 多路复用: 监听多个 Socket 对象 ,死循环, 事件循环
# - 利用此特性 可以开发 异步 IO 模块 等等 # 异步 IO : 遇到 IO , 则 立即转而操作其他, IO 返回时,则自动回调处理 IO 的 函数。
# 回答: 非阻塞的 socket(setblocking(False)) + IO 多路复用 # 此时 为客户端
class HttpRequest:
def __init__(self, sk, host, callback):
self.socket = sk
self.host = host
self.callback = callback
def fileno(self):
return self.socket.fileno() Buffer_size = 4096 class HttpResponse:
def __init__(self, recv_data):
self.recv_data = recv_data
self.header_dict = {}
self.body = ""
self.initialize() def initialize(self):
headers, body = self.recv_data.split(b'\r\n\r\n', 1)
header_list = headers.split(b'\r\n')
for item in header_list:
item_str = str(item, encoding='utf-8')
sp = item_str.split(':', 1)
if len(sp) == 2:
self.header_dict[sp[0]] = sp[1] class AsyncRequest:
def __init__(self):
self.conn = []
self.connection = [] # 用于检测 是否链接成功 def add_request(self, host,callback):
try:
sk = socket.socket()
sk.setblocking(False)
sk.connect((host, 80))
except BlockingIOError as e:
pass
request = HttpRequest(sk, host, callback)
self.conn.append(request)
self.connection.append(request) def run(self):
# 事件 循环
while True:
rlist, wlist, elist = select.select(self.conn, self.connection, self.conn, 0.05)
for w in wlist:
print(w.host,'链接成功')
# 表示 socket 和服务端链接 成功
txt = "GET / HTTP/1.0\r\nHost:%s\r\n\r\n"%(w.host)
w.socket.send(bytes(txt, encoding='utf8'))
self.connection.remove(w)
print('write over')
for r in rlist:
print(r.host, '有数据返回 ')
# r = HttpRequest object
recv_data = bytes()
while True:
try:
chunck = r.socket.recv(Buffer_size)
recv_data += chunck
except Exception as e:
break response = HttpResponse(recv_data) r.callback(response)
print('host=%s,返回数据为: recv_data=%s'%(r.host,recv_data))
self.conn.remove(r) if not self.conn:
break self.connection
self.conn def f1(response):
print('保存到文件,', response.header_dict)
def f2(response):
print('保存到数据库,', response.header_dict)
url_list = [
# 'cn.bing.com',
{"host": 'www.baidu.com', 'callback': f1},
{'host': 'www.cnblogs.com', 'callback': f2},
] req = AsyncRequest()
for item in url_list:
req.add_request(item['host'], item['callback']) req.run() # 等待 服务端的 链接成功 信号
Web 请求之--性能相关的更多相关文章
- ANTS Performance Profiler 8:支持对Web请求、异步代码和WinRT的性能剖析
下载与激活:http://download.csdn.net/detail/lone112/6734291 离线激活 位于英国的Red Gate Software有限公司最近发布了ANTS Per ...
- benchmark pm2的cluster模式发布web app的性能与相关问题解决方法
pm2以cluster集群方式发布app,可以高效地利用多核cpu,有效提升吞吐量.在上周对公司的redmine服务器进行性能调优后,深感ruby on rails的性能低下,这次测试nodejs的s ...
- H5 缓存机制浅析 移动端 Web 加载性能优化
腾讯Bugly特约作者:贺辉超 1 H5 缓存机制介绍 H5,即 HTML5,是新一代的 HTML 标准,加入很多新的特性.离线存储(也可称为缓存机制)是其中一个非常重要的特性.H5 引入的离线存储, ...
- Http协议简单解析及web请求过程
HTTP协议: HTTP是一个属于应用层的面向对象的协议,由于其简捷.快速的方式,适用于分布式超媒体信息系统. 基于HTTP协议的客户端/服务器请求响应机制的信息交换过程包含下面几个步骤: 1) ...
- 高性能MySQL第2,3章性能相关 回顾笔记
1. 基准测试(benchmark) 不管是新手还是专家都要熟悉基准测试,benchmark测试是对系统的一种压力测试,目标是为了掌握在特定压力下系统的行为.也有其他原因:如重现系统状态,或者是 ...
- 与JavaWeb有关的故事(web请求与Java I/O)
作为一名后端屌丝程序员,对算法.并发.性能乐此不疲.但是,随着年龄和阅历的增加,显然叶落而不知秋的心态是不太能混了.尤其是,某T面试官在明知我是后端,且明确表示对HTTP协议不太熟的情况下,强行让我解 ...
- Python 爬虫性能相关
性能相关 在编写爬虫时,性能的消耗主要在IO请求中,当单进程单线程模式下请求URL时必然会引起等待,从而使得请求整体变慢. import requests def fetch_async(url): ...
- 007-优化web请求三-异步调用【WebAsyncTask】
一.什么是同步调用 浏览器发起请求,Web服务器开一个线程处理,处理完把处理结果返回浏览器.好像没什么好说的了,绝大多数Web服务器都如此般处理.现在想想如果处理的过程中需要调用后端的一个业务逻辑服务 ...
- 《深入分析Java Web技术内幕》读书笔记 - 第1章 深入Web请求过程
第1章 深入Web请求过程 1 1.1 B/S网络架构概述 2 基于统一的应用层协议HTTP来交互数据. 1.2 如何发起一个请求 4 HTTP连接本质是建立Socket连接.请求实现方式:工具包如H ...
随机推荐
- MySQL执行外部sql脚本文件命令报错:unknown command '\'
由于编码不一致导致的 虽然大部分导出是没有问题的 但是数据表中存储包含一些脚本(富文本内容)会出现该问题,强制指定编码即可解决. mysql导入时指定编码: mysql -u root -p --de ...
- BZOJ 2565 最长回文串
传送门 回文自动机! 正着跑一遍 记录以每个点作为回文子串的右端点的最大长度 倒过来跑一遍 记录每个点作为左端点的最大长度 求个和就好啦 附代码. #include<cstdio> #in ...
- CentOS7.5 安装部署Apache+Mysql+Php
系统:CentOS7.5 安装Apache 安装 yum -y install httpd 开启apache服务 systemctl start httpd.service 设置apache服务开机启 ...
- Testng报错:method-selectors?,parameter*,groups?,packages?,classes?
以上的报错信息有两种可能的原因: 1.xml的格式确实有误 2.xml的格式正确,但是工程中testng的jar包不止一个,从而导致有歧义或者冲突(这个很可能发生在同一个workspace有多个工程的 ...
- 【leetcode】472. Concatenated Words
题目如下: Given a list of words (without duplicates), please write a program that returns all concatenat ...
- 【leetcode】553. Optimal Division
题目如下: 解题思路:这是数学上的一个定理.对于x1/x2/x3/..../xN的序列,加括号可以得到的最大值是x1/(x2/x3/..../xN). 代码如下: class Solution(obj ...
- POJ 3130 How I Mathematician Wonder What You Are! (半平面相交)
Description After counting so many stars in the sky in his childhood, Isaac, now an astronomer and a ...
- jquery设置、判断、获取input单选标签选中状态
1.设置某项单选input为选中状态: $("input[type='radio']").eq(1).attr('checked',true); ②也可设其属性checked为'c ...
- scrapy工作流程
整个scrapy流程,我们可以用去超市取货的过程来比喻一下 两个采购员小王和小李开着采购车,来到一个大型商场采购公司月饼.到了商场之后,小李(spider)来到商场前台,找到服务台小花(引擎)并对她说 ...
- python中*args和**kargs得用法总结
前言: 一个很简单的函数说起: def Jiafa(x, y): z = x + y return z print(Jiafa(1,2)) 这个很简单,一看就知道输出等于3. 那下一个问题是,如果我要 ...