跳转到主要内容

小型、可插拔的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 (10.0 kB 查看散列)

上传时间 源代码

由以下提供支持