Python3 RSA+AES混合加密(一)

为啥要用RSA+AES混合加密?安全和速度的妥协呗,RSA更安全但是速度实在太慢

一般逻辑顺序

加密过程

  1. 准备好RSA公钥(假设为key_rsa_pub)
  2. 随机生成AES加密密钥:key_aes_rand(这个会经过RSA加密发送给接收方)
  3. 使用key_aes_rand对需要发送的数据msg进行AES加密(加密时需要对数据进行填充),得到encrypted_msg
  4. 第3步aes加密后的数据有些是不可见数据,网络传输时可能会出错,此时会再进一步进行base64编码,得到base64ed_encrypted_msg
  5. 对key_aes_rand使用key_rsa_pub进行RSA加密、base64,得到base64ed_encrypted_key_aes_rand
  6. 把base64ed_encrypted_msg和base64ed_encrypted_key_aes_rand发送给接收方即可

解密过程

  1. 准备好RSA私钥(key_rsa_pri)
  2. 对接收到的数据拆分,得到base64ed_encrypted_msg和base64ed_encrypted_key_aes_rand
  3. 对base64ed_encrypted_key_aes_rand进行base64解密,得到encrypted_key_aes_rand
  4. 对encrypted_key_aes_rand,使用RSA私钥key_rsa_pri解密,得到key_aes_rand
  5. 对base64ed_encrypted_msg进行base64解密,得到encrypted_msg
  6. 对encrypted_msg,使用key_aes_rand进行AES解密得到msg

说明

  1. 以上流程实际上就是用随机生成的AES密钥对原始信息加密,然后用RSA公钥对AES的随机密钥加密,将两个加密后的数据发给接收端
  2. 相比较直接AES加密的方式,混合加密时AES的密钥时随机生成的。(通过RSA加密后再发送给接收端)
  3. RSA加密使用的公钥时公开的

上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import base64
import os

# Crypto安装命令:`pip3 install -i https://pypi.douban.com/simple pycryptodome`
from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.Cipher import AES


# AES根据16位对齐(即AES128)
BLOCK_SIZE = 16


# 伪随机数生成器
random_generator = Random.new().read


# RSA公钥,发送方使用
public_key = b"""-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDW1aA3SG/czhFO+57bFVIMx390
TMWXUY1ZTHW6gXTaK1gBfd7rHVaxLhmOsfIBaxBFfXg6lOfBQ+9mOZDY0LPzRRP2
1pmW+DW/QvrAO1xL1t04zKzPNkslCpjQ3fSwwIyJnfQsRQxUxUv9j/OJdWh97XNc
K4EyACqRC9eO9vJ18QIDAQAB
-----END PUBLIC KEY-----"""


# RSA私钥,请保护好
private_key = b"""-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQDW1aA3SG/czhFO+57bFVIMx390TMWXUY1ZTHW6gXTaK1gBfd7r
HVaxLhmOsfIBaxBFfXg6lOfBQ+9mOZDY0LPzRRP21pmW+DW/QvrAO1xL1t04zKzP
NkslCpjQ3fSwwIyJnfQsRQxUxUv9j/OJdWh97XNcK4EyACqRC9eO9vJ18QIDAQAB
AoGARzKGZbaJ7AFsVw1TY4PjV1Izo9VgPnp2f7uz4IV6tmWwRX5I3F6IFoa9TZRy
LD970FZ5UTYmwDQa03lhzu5g/t4MBGZ+dEJ7hmcR4PJuLZSl59E2TzkEo9hE3DQ7
bq4wSIeBcY61hiwGf8tCnWetJbAvj+HS29HIB8sl1fu0jekCQQDgwiJm3HkgkoNq
mx5WliKvcGi+/eB9FXar9jn6762+mkge7htZbrM4tpIHl+5Vx+bn8E159FTcmgCS
My/WCb37AkEA9LJbn9GLvxupAcXq35snnq5xkNAsgHJHQaeu/VT0T2Gz5pUO3iB4
GLpWlia/E9eIDWj399Zs089AT72dAab0AwJAPtTmqxy9W+a5iEbe/1OvVJ43GhV8
+VrTtxT5dnYkeyFEQilMSf8RaSxYvHizrxVYLsTV098DDjybJkPa/pnwmwJAXeJk
5y/l92AseyKt2DdWfzqdFhvZRzsRfe5RZJ+I0UBCXxEH0FAS5CHygM/C9mD2sXZ5
1ZxuyuG04iN1LyIYcwJAULISlX5ZR8y+V6B1E/vndPu4BdfX5QORqzIwTAWsAt8k
uMsm5ubz+EEf4klTSVLhRZrxquVnTaBgiib6ngc9iA==
-----END RSA PRIVATE KEY-----"""


# 填充函数,填充模式:PKCS5Padding(填充内容等于缺少的字节数)
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * \
chr(BLOCK_SIZE - len(s) % BLOCK_SIZE).encode()
# 返填充函数
unpad = lambda s: s[:-ord(s[len(s) - 1:])]


# 生成随机密钥
def get_random_key_readable(key_size=16):
ulen = int(key_size/4*3)
key = base64.b64encode(os.urandom(ulen))
return key


def encrypt(msg: bytes, key_rsa_pub: bytes):
# 生成随机AES密钥
key_aes_rand = get_random_key_readable(BLOCK_SIZE)

# 对原始信息进行AES加密再base64(需要网络传输选择urlsafe_b64encode)
encrypted_msg = AES.new(key_aes_rand, AES.MODE_ECB).encrypt(pad(msg))
base64ed_encrypted_msg = base64.urlsafe_b64encode(encrypted_msg)

# 对AES随机密钥进行RSA加密
encrypted_key_aes_rand = Cipher_pkcs1_v1_5.new(
RSA.importKey(key_rsa_pub)).encrypt(key_aes_rand)

base64ed_encrypted_key_aes_rand = base64.urlsafe_b64encode(encrypted_key_aes_rand)
return [base64ed_encrypted_msg, base64ed_encrypted_key_aes_rand]


def decrypt(source: list, key_rsa_pri: bytes):
# 对接收到的数据拆分,得到base64ed_encrypted_msg和base64ed_encrypted_key_aes_rand
base64ed_encrypted_msg, base64ed_encrypted_key_aes_rand = source[0], source[1]

# 对base64ed_encrypted_key_aes_rand进行base64解密,得到encrypted_key_aes_rand
encrypted_key_aes_rand = base64.urlsafe_b64decode(base64ed_encrypted_key_aes_rand)

# 对encrypted_key_aes_rand,使用RSA私钥key_rsa_pri解密,得到key_aes_rand
key_aes_rand = Cipher_pkcs1_v1_5.new(RSA.importKey(key_rsa_pri)).decrypt(
encrypted_key_aes_rand, random_generator)

encrypted_msg = base64.urlsafe_b64decode(base64ed_encrypted_msg)
msg = unpad(AES.new(key_aes_rand, AES.MODE_ECB).decrypt(encrypted_msg))
return msg


if __name__ == "__main__":
message = "这是一个测试数据"
value = encrypt(message.encode("utf-8"), key_rsa_pub=public_key)
msg = decrypt(value, key_rsa_pri=private_key).decode()

print("原始数据: {origin_msg}".format(origin_msg=message))
print("密文数据: {encrypt_msg}".format(encrypt_msg=value))
print("解密数据: {decrypt_msg}".format(decrypt_msg=msg))

运行上面代码

RSA/AES混合加密

ApacheJmeter各Element介绍(二):Thread Group

Thread Group(线程组)介绍

  1. Thread Group是测试的起点,也就是说,TestPlan下面必须要有Thread Group才可以开始测试。
  2. Thread Group中设置的Thread的数量,也就是Jmeter模拟的用户数量,所以,Thread Group可以被看成是虚拟用户池。
  3. Thread Group中每一个Thread完全独立的运行线程组下面的所有测试。多个线程模拟并发访问。
  4. 测试所需要的controller和sampler都必须放在Thread Group下。
  5. 其他的,如Listener可以直接放在TestPlan下(listener放在Test Plan下和放在Thread Group下时,作用于不同)
  6. 右键点击TestPlan->Add->Threads(Users)->Thread Group,可以给TestPlan添加一个Thread Group
    添加ThreadGroup
  7. 每一个TestPlan下可以放多个Thread Group,这些Thread Group的执行顺序可以在TestPlan中控制

Thread Group配置

  1. Numbers of Threads(users): 设置线程组需要开启的线程的数量(也就是需要模拟的用户的数量)
    ThreadGroup配置
  2. Ramp-Up Period(in seconds): 增加压力持续时间(可以理解成,多长时间以后所有线程开始全力加压)
    • 这一条是指Numbers of Threads中设定的所有线程,在多长时间内全部开始给服务器加压
    • 如果设定需要启动100个线程,而Ramp-Up Period设定为20,那么意味着,每秒钟有5个线程开始工作
    • Ramp-Up Period需要设置的足够大,以保证能够将大量的线程完全开始工作,同时必须设置的足够小,以确保最后一个线程开始工作时,第一个线程的工作还没有完全做完
    • 在模拟大用户量测试时,如果Ramp-Up Period设置过小,短时间内会给服务器较大的压力,有可能会导致服务器过载。
  3. Loop Count:设置每一个线程的循环次数
    • 勾选Forver时,启动一个死循环测试
    • 注意的是,这个设置的是每一个线程的循环次数,所以总的requests数量因该是:“线程总数 循环次数 每个线程组请求数量”
  4. Delay Thread creation until needed:勾选这个选项以后,只有在线程需要开始工作的时候,才会开启线程
    ThreadGroup配置
    • 如果不勾选这个选项,那么线程组启动的时候,就会将所有需要的线程全部创建(但是否给全部开始工作,需要看Ramp-Up Period的设置)
    • 勾选这个选项,因为延缓了线程创建时间,所以,在开始的时候资源占用明显小于未勾选时
    • 勾选这个选项以后,在后台可以看到,随着时间的推移,jmeter的线程数量呈线性增加的,而不勾选时,测试启动时,后台中已经存在大量jmeter线程。
    • 这个选项默认是未勾选的
  5. Scheduler:Jmeter还提供了一个调度器,可以用来控制线程在测试开始后,延迟多长时间启动,持续多长时间
    ThreadGroup配置
  6. Thread Group中,也可以配置当取样器出错时的动作,默认为继续测试
    ThreadGroup配置
    • Continue:如果出错,继续执行。线程会将后续的动作执行完成(如果后面的动作不依赖当前出错的动作,后面的测试仍然有意义的)
    • Start Next Thread Loop:线程会忽略后续动作,之际开始下一个循环
    • Stop Trhead:直接停止当前线程
    • Stop Test:在执行完后续动作以后,结束测试。意味着线程动作列表中,错误动作后面的动作还会做,直到当前循环完成,再结束测试
    • Stop Test Now:立刻停止测试,错误的动作后面的动作也不要做了

ApacheJmeter各Element介绍(一):TestPlan

TestPlan介绍

TestPlan是Jmeter测试用例的“根”,我们所有的测试过程都在TestPlan中进行,每一个TestPlan可以看作是一个“项目”,其它所有element都必须在TestPlan下。Jmeter启动时,我们所看到的就是TestPlan。
TestPlan

  • TestPlan中有一个选项“Functional Test Mode”,这个选项默认时未被选中的。
    • 如果是作功能测试,或者进行调试的话,可以将这个选项打开。
    • 当这个选项被选中的时候,Jmeter会记录所有request请求值等数据(需要记录哪些数据,可以在listener人Configuration中设置),这些数据会被写入到你的listener中设置的文件中。
    • 这个功能被打开是会严重影响性能,且随着requests数量增加,日志占用空间会骤增,所以,在做性能测试,压力测试以及负载测试的时候,最好不要打开这个选项
    • 如果在listener中没有设置存储的文件的话,即便勾选了这个选项,这个选项也是不起作用的

TestPlan

  • TestPlan中可以添加多个线程组
    • 在TestPlan中如果没有勾选“Run Thread Groups consecutively”(默认未勾选),所有线程组是并行执行的。
    • 如果期望一个Thread Group执行完成后,再执行另一个,确保已经勾选了这个选项。
    • 如果一个TestPlan中只有一个线程组,可以忽略这条
  • TestPlan中间还有一个区域是用户自定义变量区域,在这里可以定义一些需要的变量,这些变量都是全局变量(对当前TestPlan来说)

TestPlan

  • 如果有额外的一些jar库需要导入到测试中,在TestPlan下面有一个“Add directory or jar to classpath”的功能,可以在这里添加所需要的额外的包

ApacheJmeter5.0 录制脚本

Jmeter脚本录制,一般有下面几种方式:

  1. 用badboy录制后导出
  2. 用jmeter自已直接录制
  3. 通过BlazeMeter录制后导出。

本篇只讲通过Jmeter自已录制。本篇录制基于5.0版本,和其他版本可能会有些出入。

录制过程

  1. 以GUI模式启动Jmeter
  2. 点击工具栏上模版按钮,打开模版选择页。
    模版按钮
  3. 选择Recording模版,用录制模版在新工程创建一个录制项目.
    模版页
  4. 测试计划会变成这样子。
    模版计划
  5. 然后在“HTTP Request Defaults”元件中填入网址(或者IP地址),路径等信息。
    填入基本信息
  6. 如果“HTTP(S) Test Script Recorder”元件是被禁用的,需要启用.
    启用元件
  7. 确认“HTTP(S) Test Script Recorder”中配置是不可用,主要看端口号是否有其他程户占用,开启代理服务器。
    开启代理服务器
  8. 点击启动后会生成一个证书(bin/ApacheJMeterTemporaryRootCA.crt),弹出一个对话框告诉我们证书生成了。
    证书
  9. 在浏览器中安装这个证,以Firefox为例
    • 在地址栏中输入“about:preferences#privacy”回车
      证书
    • 在打开的设置页面中,Certificates下面,点击“View Certificates”
    • 点击import安装上刚才的证书
      证书
    • 确认安装
      证书
  10. 在浏览器中设置代理服务器为Jmeter
    • 在地址栏中输入“about:preferences”回车
    • 搜索“proxy”
      代理
    • 在弹出的框中设置IP、端口号,和Jmeter中想同即可。
      代理
  11. 浏览器中打开需要录制的网站,进行操作,jmeter会记录下所有的操作(step7中已开启代理服务器)
    录制
  12. 录制动作完成后,点击“停止”按钮结束录制
  13. 完成后,可以在Recing Controller下面查看录制结果,如果有不需要动作,也可以删除或修改。
    录制

Jmeter分布式测试(二)

开始分布式测试

前面的文章,我们已经完成Jmeter分布式测试相关配置,现在准备开始做分布式测试(注意,不需要将testplan手动复制到每一个slave机器上,master会自动分发)。

  1. 在GUI模式中准备脚本(请参考ApacheJmeter的运行模式)。
  2. 在所有需要用到的slave机器上启动jmeter服务器模式
    服务器模式截图
  3. 在Master中以GUI方式启动jmeter
  4. Master中添加聚合报告的listener
    Master添加聚合报告的listener
    Master添加聚合报告的listener

  5. 在Master中启动远程机器测试(也可以直接点击Remote Start All开启已经配置的所有Slave机器)
    Master开启Remote Test

  6. 在Master中聚合报告中可以看到已经又测试数据过来
    Master聚合报告数据

  7. 在Slave机器中应该可以看到下面这样的信息,表示测试已经启动
    Slave测试开启

Jmeter分布式测试(一)

使用ApacheJmeter进行分布式测试前需要注意:

  1. Jmeter分布式测试消耗资要大于非GUI模式
  2. Jmeter测试计划会分发到每个Slave上,jmeter没有类似负载均衡的机制,每台Slave上提供的用户数是相同的。就是说如果脚本中设定1000个户,用了6个Slave,那么共有6000个用户,且平均分配在每个Slave上
  3. Master收集测试信息压力同样很大,如果开启的Slave数量太多,master有可能承受不了

Jmeter分布式测试

搭建Jmeter完成分布式测试,需要机器满足下面几个条件:

  • 确保客户端(提供负载的机器)可以正常连接服务器(防火墙/网段设置等)
  • 确保所有客户端的java/jemeter版本相同。版本不同,有可能会出错
  • Jmeter RMI默认使用动态端口,所以Slave上如果有防火墙,最好将端口号设置为固定端口
  • Jmeter 4.0以后RMI默认使用SSL传输,需要配置或者禁用SSL
    主从模型

名词解释

  • Master:控制其他测试机器的机器。用户之际操作的机器。在master上以GUI模式运行Jmeter,一般不直提供负载
  • Slave:远程机器,提供负载的机器,运行jmeter-server。可以有多个。和master通过RMI通讯
  • Target:测试目标机器
    名词解释

配置

  • 禁用RMI的SSL,修改Slave机器上“jmeter\bin\user.prorerties”文件

    1
    2
    # user.prorerties
    server.rmi.ssl.disable=true
  • 配置RMI SSL(如果不禁用SSL,需要进行下面的配置)

    • 运行Master上面“jemter\bin\create-rmi-keystore.bat”(linux上create-rmi-keystore.sh)
    • 回复一些问题(可以使用默认值)
    • 脚本会在Master的“jemter\bin\”下面生成一个key文件:rmi_keystore.jks
    • 将这个文件复制到所有Slave机的“jemter\bin\”目录下
  • 让Slave机器RMI使用固定端口,修改修改Slave机器上jmeter\bin\user.prorerties文件

    1
    2
    # user.prorerties
    server.rmi.localport=44242
  • 在Master上配置远程机器,修改“jmeter\bin\user.prorerties”文件,配置远程机器以后,GUI模式启动Jmeter才可以看到相应的远程机器

    1
    2
    # 添加下面这一行,ip地址为Slave机器ip地址,如果有多台,用英文逗号分隔
    remote_hosts=172.16.3.138

    名词解释

  • 多个网卡配置
    有多个网卡时,必须指定网卡,否则会很可能会出现RMI通讯时并不是你期望的网卡,导致测试失败

    • windows下修改jmeter\bin\jmeter.bat

      1
      2
      3
      4
      # 172.16.2.39为需要使绑定的ip地址
      set rmi_host=-Djava.rmi.server.hostname=172.16.2.39
      # 在ARGS后面追加%rmi_host%
      set ARGS=%ARGS% %rmi_host%
    • linux服务器下修改jmeter/bin/jmeter_server

      1
      2
      # 去掉下面这一行前#符号,并修改为自已想要的ip地址
      RMI_HOST_DEF=-Djava.rmi.server.hostname=xxx.xxx.xxx.xxx

ApacheJmeter的运行模式

Jmeter测试时有3种启动方式:GUI模式,非GUI模式,服务器模式。另外还有1种使用现有jtl文件(csv文件)专门用来生成报告的模式

  • GUI模式

    • 多用于编写/修改测试计划,debug测试计划,或者查看报告,不要用于实际测试,因为其会消耗更多的资源
    • windows下运行Jmeter\bin\jmeter.bat命令启动GUI模式
    • linux运行Jmeter/bin/jmeter.sh命令
      GUI模式截图
  • 非GUI模式

    • 测试时推荐使用非GUI模式,资源占用率最小
    • 运行下面命令

      1
      2
      3
      4
      5
      6
      7
      8
      # -n 使用非GUI模式式启动jmeter
      # -t 指定需要运行的测试计划的脚本,后面需紧根脚本名,如testplan.jmx
      # -l 指定生成的日志(产生报告需要用到的日志文件)名称,后面需要接日志文件名,如testresult.jtl
      jmeter -n -t testplan.jmx -l testresult.jtl
      # 如果配置好了远程机器地址,可以通过下面这条命令,以非GUI方式启全部远程机器
      # jmeter -n -t testplan.jmx -r
      # 或者以下面这个面令指定启动服务器
      # jmeter -n -t testplan.jmx -R xx.xx.xx.xx, zz.zz.zz.zz, ...

      非GUI模式截图

  • 服务器模式
    很多时候我们受负载机性能限制,负载机没法提供足够的压力,这时候我们需要使用多台机器同时给目标机提供压力,这时就需要用到服务器模式启动

    • 在Slave机器上启动Jmeter/bin/jmeter-server
    • 在Master机器上启动Jmeter\bin\jmeter.bat
    • 在Master机器,JmeterGUI中,Run->Remote Start菜单中会列出配置的所有远程机器(Slave机器)
    • 配置服务器模式,可以参照商议篇:Apachejmeter分布式测试配置

      服务器模式截图

  • 生成HTML报告

    • 生成HTML报告,使用下面的命令
      1
      2
      3
      # -g 指定使用现有的结果文件(testresult.jtl),参数后面紧跟结果文件名称
      # -o 指定存放报告的目录,目录可以不存在。如果目录存在,目录必须时空目录
      jmeter -g testresult.jtl -o reportPath

Tensorflow基础(1)

名词解释

  • 张量(Tensor): TensorFlow的数据流图由节点和边组成。张量代表的数据流图的边。任意维度的数据统称为张量
  • 残差:实际观察值与训练的估值之间的差
  • 向前传播:张量在数据流图中从前向后流动一遍,为一次向前传播
  • 反向传播:残差从后向前流动一遍就完成一次反向传播

Tensorflow设计理念

TensorFlod的设计理念有两个方面

  1. TensorFlow是符号式编程,如

    1
    2
    t = 8 + 9
    print(t)

    在传统的编程中,这样的代码将会直接执行,但在TensorFlow中:

    1
    2
    3
    import tensorflow as tf
    t = tf.add(8, 9)
    print(t)

    这个输出为Tensor("Add:0", shape=(), dtype=int32),实际上只是定义了一个操作,但没有运行

  2. TensorFlow中涉及的运算都要放在图中,而图的运行只会发生在会话(session)中。

    1
    2
    3
    4
    5
    6
    7
    8
    import tensorflow as tf
    a = tf.constant([1.0, 2.0])
    b = tf.constant([3.0, 4.0])
    c = a * b

    sess = tf.Session()
    print(sess.run(c))
    sess.close()
  3. 编程模型
    3.1 边
    TensorFlow的边有两种连接关系:数据依赖(实线)和控制依赖(虚线)。控制依赖可用于控制操作的运行,这类边上没有数据流过,但源节点必须在目的节点开始前执行完成。常用代码如:

    1
    tf.Graph.control_dependencies(control_inputs)

3.2 张量支持的数据类型
Tensorflow的张量支持下面所有数据类型
|数据类型|Python类型|描术|
|:–|:–|:–|
|DT_FLOAT|tf.float32|32位浮点|
|DT_DOUBLE|tf.float64|64位浮点|
|DT_INT64|tf.int64|64位整型|
|DT_INT32|tf.int32|32位整型|
|DT_INT16|tf.int61|16位整型|
|DT_INT8|tf.int8|8位整型|
|DT_UINT8|tf.uint8|8位无符号整型|
|DT_STRING|tf.string|字符串|
|DT_BOOL|tf.bool|布尔|
|DT_COMPLEX64|tf.complex64|复数,两个32位浮点分别表示实部和虚部|
|DT_QINT32|tf.qint32|用于量化操作的32位整型|
|DT_QINT8|tf.qint8|用于量化操作的位整型|
|DT_QUINT8|tf.quint8|用于量化操作的8位无符号整型|

  1. 编程模型:
    TensorFlow计算过程如下:
    • 从输入开始,经过reshape后往前传播
    • 流经ReLu层(隐层),这里会有两个参数Wh1和bh1,在输出前使用ReLu激活函数作非线性处理
    • 流经Logit(输出层),学习两个参数Wsm和bsm
    • 使用Softmax来计算输出结果中和个类别的概率分布
    • 用交叉熵灭度量源样本的概率分布和输出结果的概率分布间的相似性
    • 计算梯度,这里需要参Wh1/bh1/Wsm/bsm,以及交叉熵后的结果
    • 进入SGD训练,也就是反向传播过程,从上向下的计算每层的参数,并依次更新
      流如下图TensorFlow流

服务器,web框架,wsgi,ngix关系疏理

用python开发服务器,小打小闹的,花些时间将一些东西疏理一下

各组件作用

服务器、web框架和wsgi关系图

web服务器,web框架与 WSGI 的三层关系

web框架/应用服务器

  • 主要用于方便开发人员开发服务器应用程序。HTTP请求的动态数据,由web框架提供
  • Python常见的web框架有:Flask,Dianjo,Tornado等(Tornado同时也实现了WSGI容器功能)

web服务器

  • nginx等,理论上说web服务器只处理http协议,处理静态的网页内容,而动态的内容通过WSGI接口,由web框架、应用服务器处理。处理结果经由WSGI接口再给web服务器
  • 好一点儿的web服务还会有负载均衡等其他功能
  • 常见的web服务器有nginx,apache等

WSGI

  • wsgi不是独立的服务器或者什么中间件,只是一种接口的规范,仅用于python,定义了web服务器和应用服务器之间的接口规范。
  • 因为PEP0333中指出需要一个东西,来处理web服务器和应用服务器之间的关系,于是出现了WSGI !^_^
  • WSIG 应用服务器/web框架端,需要满足:有两个参数的callable对象,这个对象必须是可多次调用的即可
  • app = flask.Flask(),app其实就是实现了__call__接口的一个对象实体

nginx配置负载均衡

nginx配置负载均衡,使用upstream模块以及负载均衡模块

upstream模块

upstream(上游)可以说仍然是nginx的handle模块,但是upstream不自已产生内容,而是转发请求到配置的后端服务器得到内容。

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
http {
# 这个配置文件将www.myserver.com均衡到本地不同端口上,也可以均衡至不同机器上
upstream backserver {
# iphash # 指定为ip哈希算法
server 127.0.0.1:8000 weight=3; # 带权重的轮询
server 127.0.0.1:8001; # 轮询
}

server {
listen 80;
server_name www.myserver.com;
location / {
proxy_pass http://backserver;
}
}
}

配置说明

  1. location /的请求直接转发给后端配置backserver,upsteam模块调用负载均衡模块,按照负载均衡类型,转发至配置的几个服务器中的一个。未指定负载均衡算法时,采用默认的round robin(轮询)算法。
  2. 可以在配置文件的upstream模块中指定算法类型