小型、可插拔的ICP(互联网缓存协议)服务器
项目描述
在多机(或多进程)的Web服务器安装中,某些Web服务器可能比其他服务器更快地处理HTTP请求。像Squid这样的HTTP加速器(反向代理)可以使用ICP查询来找到最适合处理特定请求的服务器。此包提供了一个小型的UDP服务器,可以根据可插拔的策略响应ICP查询。
变更历史
1.0.0 (2008-02-07)
初始发布。
ICP何时有用
当动态生成内容时,如果所有数据都可在本地满足请求,则可以显著影响服务时间。使数据可用的方法之一是使用一个或多个缓存。在有些情况下,这些缓存可能不足以包含为有效处理传入请求所需的整个工作集。添加额外的请求处理器(服务器或进程)并没有帮助,因为从一个或多个存储服务器(例如,数据库)加载数据的时间是请求时间的主导因素。在这些情况下,请求空间可以被分割,使得特定处理器(服务器或进程)负责的工作集部分可以适合其缓存。
静态配置请求空间分区可能很困难,容易出错,甚至不可能。在这些情况下,最好让源服务器提供有关是否应处理特定请求的反馈。这就是ICP发挥作用的地方。
命中与未命中
当接收到ICP查询请求时,服务器可以返回以下之一:ICP_OP_HIT、ICP_OP_MISS、ICP_OP_ERR、ICP_OP_MISS_NOFETCH或ICP_OP_DENIED。这些结果代码的含义在ICP RFC中定义如下。
- ICP_OP_HIT
ICP_OP_HIT响应表示请求的URL存在于缓存中,并且请求者允许检索它。
- ICP_OP_MISS
ICP_OP_MISS响应表示请求的URL不存在于此缓存中。查询缓存可能仍然选择从回复缓存中获取URL。
- ICP_OP_ERR
ICP_OP_ERR响应表示在解析或处理查询消息时出现某种错误(例如无效的URL)。
- ICP_OP_MISS_NOFETCH
ICP_OP_MISS_NOFETCH响应表示缓存正在运行,但处于不希望处理缓存缺失的状态。这种状态的一个例子是在启动阶段,缓存可能会重建其对象存储。处于这种模式的缓存可能希望对于缓存命中返回ICP_OP_HIT,但对于缺失不返回ICP_OP_MISS。ICP_OP_MISS_NOFETCH本质上意味着“我在运行,但现在请不要从我这儿获取这个URL。”
注意,ICP_OP_MISS_NOFETCH与ICP_OP_MISS的含义不同。ICP_OP_MISS回复是邀请从回复缓存中获取URL(如果它们的关系允许的话),但ICP_OP_MISS_NOFETCH是请求不要从回复缓存中获取URL。
- ICP_OP_DENIED
ICP_OP_DENIED响应表示查询网站不允许从该缓存检索指定的对象。缓存和代理可以实施复杂的访问控制。此回复必须被解释为“您现在不允许从我这里请求这个特定的URL。”
因为我们想使用ICP来沟通原始服务器(而不是缓存服务器)是否希望处理特定的请求,所以我们将对一些结果代码的定义稍作不同。
- ICP_OP_HIT
ICP_OP_HIT响应表示查询的服务器更愿意处理HTTP请求。原始服务器返回命中的原因可能是它最近处理了“类似的”请求,或者它已被配置为处理给定URL占用的URL空间的部分。
- ICP_OP_MISS
ICP_OP_MISS响应表示查询的服务器没有偏好处理请求,但仍然能够处理请求。这通常是默认响应。
- ICP_OP_MISS_NOFETCH
ICP_OP_MISS_NOFETCH响应表示请求服务器可能无法从该服务器请求指定的对象。这可能是因为原始服务器在此时负载过重,或者某些其他策略表示此刻不应转发请求。
特定ICP查询的响应(命中、缺失等)基于一个或多个配置策略。定义和注册这些策略的机制将在下一节中解释。
本包不实现已弃用的ICP_OP_HIT_OBJ。
定义策略
要使用此包,必须定义并注册一个或多个策略。使用Zope组件架构将策略作为“实用程序”进行管理。
策略必须实现IICPPolicy接口。
>>> from zc.icp.interfaces import IICPPolicy >>> IICPPolicy <InterfaceClass zc.icp.interfaces.IICPPolicy>
目前没有注册任何策略,因此任何URL都会生成缺失。
>>> import zc.icp >>> zc.icp.check_url('http://example.com/foo') 'ICP_OP_MISS'
假设我们想要返回包含“foo”的所有URL的ICP_OP_HIT,我们可以这样定义该策略
>>> def foo_hit_policy(url): ... if 'foo' in url: ... return 'ICP_OP_HIT'
在注册此策略时,我们必须提供一个相关的名称。使用相同名称的任何后续注册都将覆盖以前的注册。默认名称为空字符串。
>>> import zope.component >>> zope.component.provideUtility(foo_hit_policy, IICPPolicy, 'foo')
已注册的策略立即可用。
>>> zc.icp.check_url('http://example.com/foo') 'ICP_OP_HIT'
非foo URL仍然是缺失。
>>> zc.icp.check_url('http://example.com/bar') 'ICP_OP_MISS'
现在我们可以添加另一个策略,表示我们不希望有包含“baz”的请求。
>>> def baz_hit_policy(url): ... if 'baz' in url: ... return 'ICP_OP_MISS_NOFETCH' >>> zope.component.provideUtility(baz_hit_policy, IICPPolicy, 'baz')>>> zc.icp.check_url('http://example.com/foo') 'ICP_OP_HIT' >>> zc.icp.check_url('http://example.com/bar') 'ICP_OP_MISS' >>> zc.icp.check_url('http://example.com/baz') 'ICP_OP_MISS_NOFETCH'
策略按名称优先级排序。首先返回非None结果的策略会被遵循。因此,如果我们检查包含“foo”和“baz”的URL,则会遵循“baz”的策略。
>>> zc.icp.check_url('http://example.com/foo/baz') 'ICP_OP_MISS_NOFETCH'
运行服务器
启动服务器开始监听指定的端口和IP。
>>> zc.icp.start_server('localhost', 3130) info: ICP server started Address: localhost Port: 3130
现在我们可以开始发送ICP请求并获取响应。要这样做,我们首先必须构建一个ICP请求。
>>> import struct >>> query = zc.icp.HEADER_LAYOUT + zc.icp.QUERY_LAYOUT >>> url = 'http://example.com/\0' >>> format = query % len(url) >>> icp_request = struct.pack( ... format, 1, 2, struct.calcsize(format), 0xDEADBEEF, 0, 0, 0, 0, url) >>> print zc.icp.format_datagram(icp_request) +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ICP_OP_QUERY | Version: 2 | Message Length: 44 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Request Number: DEADBEEF | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options: 0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Option Data: 0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sender Host Address: 0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | Payload: \x00\x00\x00\x00http://example.com/\x00 | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
发送请求后,我们得到一个响应。
>>> import socket >>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) >>> s.connect(('localhost', 3130))>>> s.send(icp_request) 44 >>> icp_response = s.recv(16384) >>> icp_response '\x03\x02\x00(\xde\xad\xbe\xef\x00\x00\x00\x00\...http://example.com/\x00' >>> print zc.icp.format_datagram(icp_response) +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ICP_OP_MISS | Version: 2 | Message Length: 40 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Request Number: DEADBEEF | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options: 0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Option Data: 0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sender Host Address: 0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | Payload: http://example.com/\x00 | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
这是一个未命中。我们还可以通过满足我们的一个策略来引发一个命中。
>>> url = 'http://example.com/foo\0' >>> format = query % len(url) >>> icp_request = struct.pack( ... format, 1, 2, struct.calcsize(format), 0xDEADBEEF, 0, 0, 0, 0, url) >>> print zc.icp.format_datagram(icp_request) +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ICP_OP_QUERY | Version: 2 | Message Length: 47 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Request Number: DEADBEEF | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options: 0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Option Data: 0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sender Host Address: 0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | Payload: \x00\x00\x00\x00http://example.com/foo\x00 | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+>>> s.send(icp_request) 47 >>> print zc.icp.format_datagram(s.recv(16384)) +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ICP_OP_HIT | Version: 2 | Message Length: 43 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Request Number: DEADBEEF | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options: 0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Option Data: 0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sender Host Address: 0 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | Payload: http://example.com/foo\x00 | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
项目详情
zc.icp-1.0.0.tar.gz的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | f379638ffd391cb3cef2b912f55587469c873ab5ecf8192b8ef43581671af2ab |
|
MD5 | 6cc5ace77b4e6463706dacf3c3d84ced |
|
BLAKE2b-256 | e816637211a0e041158e8177695421e6fcfb1b5cdf3d75b3c77b2bf6c73d03fb |