aiohttp + asyncio 异步网络请求基本操作

作者:matrix 发布时间:2019年11月26日星期二 分类:Python

asyncio异步操作需要关键字async,await
async用来声明方法函数,await用来声明耗时操作。
但是await关键字后面要求为awaitable对象 且只能在async方法内部使用,不能在外部入口中使用。asyncio的语法其实是系统内部实现了yield from协程。

aiohttp用来代替requests的请求库,且支持异步操作。
主要优点体现在并发请求多个耗时任务时,自动安排耗时时的操作,避免cpu等待一个一个请求。

单个请求操作

import aiohttp
import asyncio

#get 请求
async def get():
  async with aiohttp.request('GET','https://api.github.com/users/Ho',params={'arg1':123}) as response:
    # response.request_info # 请求信息
    return await response.json()

rel = asyncio.run(get())

# 或者使用下面方式 手动关闭异步事件循环
# loop = asyncio.get_event_loop()
# rel = loop.run_until_complete(get())
# loop.close()

print(rel)

多个并发请求操作

主要区别在于异步任务的添加操作,运行。

请求测试url:

http://link/await/1 # delay 1sec
http://link/await/2 # delay 2sec
...

请求测试:

import aiohttp
import asyncio

#get 请求
async def get():
  async with aiohttp.request('GET','http://link/await/1') as response:
    return await response.text()

# 所有请求任务
async def all_req():
#async with asyncio.Semaphore(5): 设置并发的连接数
# https://docs.python.org/zh-cn/3/library/asyncio-sync.html#asyncio.Semaphore

  task = []
  #添加请求任务
  for i in range(5):
    task.append(asyncio.create_task(get()))
  #create_task 方法等同于  ensure_future()方法
  #手册建议首选 create_task方法 
  # https://docs.python.org/zh-cn/3/library/asyncio-future.html?highlight=ensure_future#asyncio.ensure_future

  return await  asyncio.gather(*task)#传入参数 tuple类型 作为位置参数
  # 等同于 asyncio.gather(get(),get())
  # gather()方法用于收集所有任务完成的返回值,如果换成wait()方法会返回任务tuple对象,(done,pending)

rel = asyncio.run(all_req())
print(rel)

# 总共5个请求任务返回:
# 总耗时1秒多,相比同步的5秒+好N多。
"""
['sleep 1 second is done', 'sleep 1 second is done', 'sleep 1 second is done', 'sleep 1 second is done', 'sleep 1 second is done']

[Done] exited with code=0 in 1.955 seconds
"""

tell why??

测试发现Semaphore方法设置的请求并发数量跟本不起作用,nginx的access.log以及Proxifier看到的一次性请求量都不是代码中设置的数量。

使用uvloop优化异步操作

uvloop用于提升协程的速度。
uvloop使用很简单,直接设置异步策略就好了。

import asyncio
import uvloop

#声明使用 uvloop 事件循环
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

测试遇到很多报错,基本上都是await和async使用的问题。

异步请求的分块chunk并发控制

自行chunk操作
自己按照所有任务的list列表进行chunk切割,然后分块进行请求,每块中固定chunk数量的任务。基本可以实现想要的并发限制操作

async def _bulk_task(num,current_page = 1):
  """批量创建异步任务
  """
  task = []
  for i in range(num):# 每次10个连接并发进行请求
    task.append(asyncio.create_task(get(current_page)))
    current_page += 1
  return await asyncio.gather(*task) 

# 主要进行chunk操作的函数
def run_task(total,chunk,offset_start_page = 1):
    """运行分块处理的批量任务

    Arguments:
        total int 总请求数
        chunk int 每次并发请求数
        offset_start_page int 初始分块开始的页数(偏移页数),正常默认为1

    Yields:
        返回收集的异步任务运行结果
    """

    length = math.ceil(total/chunk)
    for i in range(length):
        start_page = i * chunk + offset_start_page # 当前分块开始的页数
        haldle_num = chunk# 当前需要并发处理的数量

        #处理结尾的块
        if i == length - 1:
            # print(':::',chunk,start_page + chunk - offset_start_page)
            haldle_num = min(chunk,total + offset_start_page - start_page)

        # print('当前分块下标:{},当前分块需要处理的总数:{},当前分块开始页数:{}'.format(i,haldle_num,start_page))
        rel = asyncio.run(_bulk_task(haldle_num,start_page))
        yield rel


rel  = run_task(123,10)# 123总任务 每10条并发请求
for i in rel:
  print(i)

TODO

参考:

https://hubertroy.gitbooks.io/aiohttp-chinese-documentation/content/aiohttp%E6%96%87%E6%A1%A3/ClientUsage.html#%E6%84%89%E5%BF%AB%E5%9C%B0%E7%BB%93%E6%9D%9F

https://docs.Python.org/zh-cn/3/library/asyncio-eventloop.html#asyncio.get_running_loop

https://segmentfault.com/q/1010000008663962

http://www.ruanyifeng.com/blog/2019/11/Python-asyncio.html

https://blog.csdn.net/qq_37144341/article/details/89471603

https://www.jianshu.com/p/8f65e50f39b4

awk入门小记

作者:matrix 发布时间:2019年11月6日星期三 分类:command Linux

图片5055-awk入门小记

awk用于unix的文本处理,经常出现和使用。读取每一行文本进行格式化输出。

测试环境 Macos

awk简单操作

默认使用空格进行字符串分割

echo '1 2 3' |awk '{print $0}' # 1 2 3

echo '1 2 3' |awk '{print $3}' # 3

echo -e "1\n2\n3" |awk '/[23]/' #正则条件查找

echo -e "abc\n1a2\n33" |awk '/a/' #正则条件查找字母a 

awk '{print $3}' file_path #指定文件

ls -alh|awk '/^d/' #输出目录
# drwxr-xr-x    3 panc  staff    96B  7 16 23:32 pt-program

echo '1-2-3' |awk -F '-' '{print $2}' #指定分割符号 -  输出 2

$0表示当前行
$1表示第一个分割的字段

awk命令后面跟操作的语句字符串''包裹,每个语句段落用花括号{}包裹,语句段落中用分号;分隔语句。

echo -e '1 2 3\n4 5 6' |awk '{print $0;print("-end-")}'
echo -e '1 2 3\n4 5 6' |awk '{print $0;}{print("-end-")}'
#上面输出结果相同
#1 2 3
#-end-
#4 5 6
#-end-


echo -e '1 2 3\n4 5 6' |awk '{print $1,$3}'
#输出
#1 3
#4 6

使用内置变量

NF表示当前行有多少个分割字段

echo  '1-2-3' |awk -F '-' '{print $(NF)}' # 3  获取最后一个分割字符
echo  '1-2-3' |awk -F '-' '{print $(NF-1)}' # 2  获取倒数第二个分割字符

其他变量
NR表示当前处理的是第几行

BEGIN预处理

在执行处理之前提前设置一些信息
比如之前的-F参数可以使用BEGIN来设置

echo -e "a-b-c" |awk 'BEGIN{FS="-"} /^a/{print $0}' #设置分割符为-

echo -e "a-b-c" |awk 'BEGIN{FS="-";print "this title::"} {print $0}' #设置输出的首行标题

echo -e "1122\n3344" |awk 'BEGIN{FIELDWIDTHS="2 2";} {print $1,$2}' #固定字段宽度  (mac os中测试失败)
#输出
#11 22
#33 44

内置变量
FS字段分隔符
FIELDWIDTHS指定字段宽度
ORS指定输出的行分隔符

if逻辑处理

awk还可以支持函数和复杂的逻辑处理,完全可以在里面定义函数 调用 判断

例子:
显示红色error
绿色seccess
白色log


echo -e 'success\nerror\nsuccess\nloginfo' | awk \ '{ if (/error/) { print "\033[91m" $0 "\033[0m" } else if (/success/){ print "\033[32m" $0 "\033[0m"; }else{ print $0 } next; }'

上面语句有换行的段落方便阅览。一行简写也是可以
例子:
判断值大小

echo -e '12\n14\n232\n90' |awk '{if($0>=90) print $0}' #输出>=90的数据
#232
#90

for循环

使用for循环计算求和


echo '5' |awk '{ \ total = 0 for (var = 1; var < $0; var++) { total += var } print "total:",total }'

Built-In Functions函数使用

awk中语义中支持函数的使用

echo -e 'H htjim\nDot\ncom\n009' |awk '{print toupper($0)}'#转换为大写

内置函数
toupper 转换为大写
tolower 转换为小写
rand 随机数

其他函数参考手册:
https://www.gnu.org/software/gawk/manual/html_node/Built_002din.html#Built_002din

自定义函数

自定义彩色文字输出函数awk内部执行调用


echo -e 'success\nerror\nsuccess\nloginfo' | awk \ ' function red(s) { printf "\033[1;31m" s "\033[0m " } function green(s) { printf "\033[1;32m" s "\033[0m " } function blue(s) { printf "\033[1;34m" s "\033[0m " } { if (/error/) { print red($0) } else if (/success/){ print green($0) }else{ print blue($0) } }'

其他例子

结合tail -f log显示红色报错提示

 tail -f test.log |  awk '{
 if (/success/) {
 print "\033[91m" $0 "\033[0m"
 } else {
 print "\033[32m" $0 "\033[0m"; 
 }
 next;
}';

next用于直接跳到下一行文本进行执行 有点continue的意思

参考:
https://likegeeks.com/awk-command/

http://www.ruanyifeng.com/blog/2018/11/awk.html

https://www.cnblogs.com/Linuxprobe/p/5745381.html