Python多进程/多线程并发任务
通过 multi_process、multi_thread、single_thread 把多进程、多线程、单线程执行任务封装成统一调用格式,方便开发调试。
重构时可以先将原有的循环代码改造成调用single_thread,本质上应该和原来没有区别,然后将single_thread替换成multi_thread/multi_process即可切换到多线程或多进程。
concurrent_task.py
1 | # -*- coding: utf-8 -*- |
通过 multi_process、multi_thread、single_thread 把多进程、多线程、单线程执行任务封装成统一调用格式,方便开发调试。
重构时可以先将原有的循环代码改造成调用single_thread,本质上应该和原来没有区别,然后将single_thread替换成multi_thread/multi_process即可切换到多线程或多进程。
1 | # -*- coding: utf-8 -*- |
作为Linux命令三剑客之一的awk
功能非常强大,用法也较为复杂,最擅长是按列提取值,awk
结合grep
结合xargs
可以完成非常多的事,网上教程很多,我就以解析一个csv文件为例演示awk
最实用的一些功能。
姓名 | 成绩 |
---|---|
张三 | 95 |
李四 | |
王五 | 98 |
平均分 | 96.5 |
1 | 姓名,成绩 |
打印除了表头(第一行)和汇总(最后一行)外所有行的第一列和最后一列,但如果最后一列是空的则这行不打印。
对于上例,打印出来应该是
1 | 张三 95 |
1 | cat test.csv|awk 'NR>1'|awk 'NR!=1 {print prev} {prev=$0}'|awk -F, '$NF ~ /[^\t\r\n ]/ {print $1,$NF}' |
其中用到了3次awk:
第一个awk,awk 'NR>1'
,实现去掉首行
第二个awk,awk 'NR!=1 {print prev} {prev=$0}'
,实现去掉最后一行
第三个awk,awk -F, '$NF ~ /[^\t\r\n ]/ {print $1,$NF}'
,指定了,
为分隔符,当最后一列不空时打印第一列和最后一列
最长用的就是 -F
用来指定列分割符
类似if
条件,可以用()括起来也可以不括,多个条件与
用&&
连接,多个条件或
用||
连接,只有满足条件才会做后面的动作,省略pattern相当于条件为真,即对每一行做action动作。action可以和{}一起省略,省略时执行默认的action输出整行。action可以是多条语句,语句间用;
分割。
pattern {action}
整体可以重复多次,表示多一行执行多组pattern {action}
。
行号:NR
列数:NF
NF是一个数字,而$NF是最后一列的值。
即需要处理文件的文件名,通常使用awk处理来自管道的文本,所以file通常是省略的。
获取各个网口配置的IPv4
1 | ifconfig|awk 'prev ~ /.+:/ && $1=="inet" {print prev,$2} {prev=$1}' |
ifconfig的输出类似
1 | ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 |
处理后的输出为
1 | ens33: 192.168.1.242 |
默认情况Linux添加用户需要等待New password:
提示在输入密码,无法通过一句命令直接添加用户并设置密码,这导致在Shell脚本中添加用户或者批量添加变得困难,我们可以通过expect自制一个非交互式的添加用户并设置密码的命令。
expect是一直可执行工具,通过执行expect脚本来把shell交互的行为提前设置在expect脚本中。
如果没有安装expect,首先安装expect
1 | apt intall expect |
expect脚本主要有spawn
/expect
/send
三个命令 :
1 | #!/usr/bin/expect |
例如保存成 adduserwithpw.sh
,执行./adduserwithpw.sh user1 pass
就可以添加名为user1
密码为pass
的用户了。
官网 https://grpc.io/
中文文档 http://doc.oschina.net/grpc/
1 | apt install -y build-essential autoconf libtool pkg-config cmake |
1 | git clone --recurse-submodules -b v1.56.0 --depth 1 --shallow-submodules https://github.com/grpc/grpc |
1 | export MY_INSTALL_DIR=$HOME/.local |
这里我们安装了gRPC的头文件和库文件,以及一个工具protoc
。
protoc
)1 | pip install grpcio |
.proto
文件用于定义数据(请求参数message和返回值message)和接口(service)
1 | syntax = "proto3"; // 表示这个文件遵循的proto语法的版本 |
protoc
工具用于将使用ProtoBuf
定义的.proto
文件编译成特定编程语言的源代码。
.proto
文件描述了数据和接口的定义。
例如通过.proto生成C++代码的示例:
1 | protoc --cpp_out=./output_directory ./protos/proto_file.proto |
会在output_directory中生成一个protos文件夹,里面有.proto同名的*.pb.h
和*.pb.cc
文件。
这里有一个细节,生成protos文件夹是因为指定的.proto文件带有路径,生成的路径和指定.proto的路径相同,如果想把.h和.cc文件直接生成在指定的文件夹中,可以通过-I参数指定.proto文件的路径而不在指定.proto文件时携带路径。即
1 | protoc --cpp_out=./output_directory -I./protos proto_file.proto |
如果使用cmake构建项目,通常将这个生成过程写在CMakeList.txt中,用add_custom_command命令完成。
这里生成的.h和.cc中只包含了对数据(.proto中的message)的封装,而不包含接口(.proto中的service)的黏合剂,要生成service的代码需要利用grpc_cpp_plugin
,可以通过在protoc
命令中增加参数一并生成接口和数据的代码。
1 | protoc --cpp_out=./output_directory --grpc_out=./output_directory --plugin=protoc-gen-grpc=/bin/grpc_cpp_plugin -I./protos proto_file.proto |
会在output_directory中生成.proto同名的*.pb.h
和*.pb.cc
文件,以及*.grpc.pb.h
和*.grpc.pb.cc
,*.grpc.pb.*
里面就包含了根据service生成的代码。
Python版的protoc
是已模块的形式安装的,使用方法
1 | python -m grpc_tools.protoc --python_out=./output_directory --pyi_out=./output_directory --grpc_out=./output_directory --plugin=protoc-gen-grpc=/bin/grpc_python_plugin -I./protos proto_file.proto |
另外其实上面通过C源码编译安装的protoc工具也是支持生成python代码的,但python的protoc模块不能生成C版本的代码。
不是必须的,我感觉这样存放会比较方便,protos
和protos_gen
是客户端和服务器共用的,而且必须一致,所以只存统一的一份,这里的protos_gen
就是由protos
生成的意思也就是上面的output_directory
目录。
1 | . |
1 | cmake_minimum_required(VERSION 3.10) |
服务端和客户端的CMakeLists.txt
都可以套用上面的模板
1 | #include <iostream> |
在启动时可以指定端口号替换默认的端口号
1 | ./calc-server --port=12345 |
1 | #include <iostream> |
同样地,在启动时可以指定服务器IP和端口号替换默认参数
1 | ./calc-client --target=127.0.0.1:12345 |
1 | from concurrent import futures |
1 | import grpc |
对于新写的代码,最好不要用下面的方式,而是在配置文件中定义BASE_PATH
,路径的连接都使用os.path.join
,这样代码自然就是跨平台的,对于遗留代码,可以通过下面的to_os_path
快速提供平台兼容性。
1 | import sys |
1 | Windows Registry Editor Version 5.00 |
保存成.reg
双击合并进注册表
首先在代理服务器上安装squid
yum install squid
或apt install squid
编辑
/etc/squid/squid.conf
在上面增加一条acl定义
1 | acl trustedhost src 47.103.123.187/32 |
在下面适当的位置增加一条允许访问
1 | # |
确保squid服务器处于运行状态
1 | service squid status |
如果服务没有运行则启动即可
1 | service squid start |
如果已经启动则重新加载配置
1 | squid -k rec |
squid默认运行在3128
端口,有必要的话把它通过网关路由器或通过ssh隧道映射到公网。
编辑/etc/apt/apt.conf.d/proxy.conf
文件
添加条目
1 | Acquire::http::Proxy "http://proxy-IP-address:proxyport/"; |
编辑/etc/yum.conf
文件
添加条目
1 | http_proxy=http://proxy-IP-address:proxyport |
在Linux上我们可以很方便地实现端口转发,比如通过SSH-Tunnel,使用
1 | ssh -CfNg -L 监听IP:监听端口:目标IP:目标端口 localhost |
甚至可以通过目标局域网暴露出来的一个ssh端口转发到另一个局域网的内部的服务器上,更详细的参考另一篇之前写的文章ssh-tunnel
通过ssh开启的端口转发在系统重启后需要重新建立,而且如果需要建立很多端口转发这样不方便管理,这时候我们可以使用rinetd
工具
apt install rinetd
会以服务的形式安装rinetd
service rinetd status
可以查看运行状态
配置文件在/etc/rinetd.conf
修改配置后可以通过service rinetd reload
重新加载配置而不必重启服务
日志文件在/var/log/rinetd.log
在Windows上其实也有个内置的工具可以完成端口转发,而且也很方便
1 | netsh interface portproxy add v4tov4 listenaddress=监听IP listenport=监听端口 connectaddress=目标IP connectport=目标端口 |
其中监听IP可以填0.0.0.0
表示接收来着任意网卡接口的数据。
1 | netsh interface portproxy show v4tov4 |
重启后也会保留设置,除非主动关闭
1 | netsh interface portproxy delete v4tov4 listenaddress=本地IP listenport=本地端口 |