第10章 配置内置代理
- 功能内置代理配置内置代理配置内置代理本页面介绍如何配置 Memcached 内置代理。有关代理的总体概述,请参阅内置代理。有关通过简单配置编译并运行代理的实操教程,请参阅内置代理快速入门。
已安装 Memcached,且启用了代理功能。
已将代理路由库文件下载到文件系统中的某个路径。
更多信息请参阅安装内置代理。
创建代理配置文件 运行代理之前,需要创建一个配置文件,其中列出要用作代理后端的 Memcached 服务器池,并描述代理要使用的路由树。该配置扩展了标准的代理路由库。它使用 Lua 语法,但你无需掌握 Lua 编程就能创建和修改配置文件。
高级用户可以修改路由库本身,但本页面介绍的技术重点在于按原样使用路由库,这已覆盖大多数 Memcached 代理的使用场景。
配置文件概览 配置文件通常包含四个部分:(可选)调用全局函数来开启日志或调试功能。
(可选)settings{} 块,将各项代理设置设为默认值以外的值。
一个或多个 pools{} 块,定义代理转发查询的后端 Memcached 服务器组。
一个或多个 routes{} 块,规定代理将请求路由到 pools{} 块中定义的池所依据的规则。
通常只需一个 routes{} 块。如果希望路由器根据连接上下文以不同方式处理请求,可以定义多个 routes{} 块。
以下各节将结合示例详细介绍上述每个部分的内容。
启用日志和调试 要启用路由库的日志和调试功能,在配置文件顶部包含以下任意函数调用:verbose(true) 让 Memcached 在加载代理配置时向标准输出打印额外信息。此设置还允许你在加载自定义配置时使用 say(TEXT) 函数将 TEXT 打印到标准输出。
debug(true) 开启调试模式,主要用于修改路由库时。此设置还允许你在加载代理配置时使用 dsay(TEXT) 函数将 TEXT 打印到标准输出。
调整代理设置 要微调各项代理行为,可在配置文件中加入 settings{} 块。该块可以为代理设置参考中列出的任何设置指定新值。例如,下面的 settings{} 块将后端连接超时设为三秒,并将活跃请求上限设为五个:
settings { backend_connect_timeout = 3, active_req_limit = 5, } 定义后端池 使用必需的 pools{} 块来定义路由器转发查询的目标后端 Memcached 服务器的地址及其他属性。你可以选择定义多个 pools{} 块,效果等同于将所有内容合并到一个 pools{} 块中。为简洁起见,本文档的示例配置文件只用一个 pools{} 块。
pools{} 块包含一个或多个按以下格式编写的池定义:
POOL_NAME = { options = { POOL_OPTIONS }, backend_options = { BACKEND_OPTIONS }, backends = { "HOST:PORT", [ ... ] } } 替换以下内容:POOL_NAME:池的名称,例如 northeast_1。
⚠️ 不要以 set_ 作为池名的开头。该命名方式保留给池的集合使用。更多信息请参阅将相关池组成集合。
POOL_OPTIONS:(可选)以 key=value 格式、逗号分隔的设置列表,代理会对该池应用这些设置。可用设置列表见 Pool API。
BACKEND_OPTIONS:(可选)以 key=value 格式、逗号分隔的设置列表,代理会对池中所有后端应用这些设置。可用设置列表见 Backend API。
HOST:作为后端的 Memcached 服务器的 IP 地址或主机名。
PORT:(可选)作为后端的 Memcached 服务器的 TCP 端口,默认值为 11211。
要为单个后端微调代理设置,可将它在 backends 列表中的条目替换为用花括号括起来的后端选项列表。该列表必须包含 host。
下面的示例定义了两个后端,其中一个运行在 11212 端口,连接超时为十秒:
backends = { "127.0.0.1:11211", { host = "127.0.0.1", port = 11212, connecttimeout = 10 }, }再看一个更完整的例子,下面的 pools{} 块定义了两个池,每个池各有三个后端:
pools { main_pool = { backend_options = { connecttimeout = 5 }, backends = { "192.0.2.1", "192.0.2.2", "192.0.2.3:11212", } }, customer_pool = { backends = { "203.0.113.1", "203.0.113.10", { host = "203.0.113.20", port = 11212, connecttimeout = 10 }, }, }, }除了单独命名的池之外,你还可以选择在 pools{} 块中定义命名的池集合。更多信息请参阅将相关池组成集合。
定义代理路由 使用必需的 routes{} 块来定义代理应如何处理数据存储或检索查询。一般来说,就是把涉及特定键或命令的请求转发到 pools{} 部分定义的各个 Memcached 服务器池。你可以使用路由库提供的众多路由处理器,以多种方式转发这些请求。ℹ️ route{} 块的可选项非常丰富,因此本文档的这一部分内容较多。想看一些 route{} 块实际用法的示例,请参阅示例配置文件。
routes{} 块至少包含以下内容之一:
- map 部分:将键前缀映射到路由处理器。可搭配一个可选的 conf 部分,用于调整代理从键名中分离前缀字符串的方式。
- cmap 部分:将 Memcached 命令映射到路由处理器。
- default 部分:为不匹配 map 或 cmap 部分任何条件的请求指定路由处理器。
有关路由处理器的更多信息,请参阅关键概念。
以下各节将更详细地解释这些配置部分。
map 部分 routes{} 的 map 部分定义一个键前缀列表,并将每个前缀映射到特定的路由处理器。映射内容包括路由处理器作为参数接收的池或集合的名称:map = { KEY_PREFIX = HANDLER_TYPE{ HANDLER_ARGS }, [ ... ] },
替换以下内容:KEY_PREFIX:要映射到此路由处理器的键前缀。
HANDLER_TYPE:路由库处理器的名称。所有可用处理器的列表见路由处理器参考。
HANDLER_ARGS:传给路由处理器的参数,通常在这里指定一个或多个池。参数列表及其语法取决于处理器类型。更多信息请参阅路由处理器参考。
下面的示例 route{} 块将代理配置为:所有带 cust 前缀的键(例如 cust/key-12345 和 cust/another-key-321)的请求都定向到名为 customer_pool 的池。由于依赖默认的前缀样式,它没有 conf 部分。
route { map = { cust = route_direct { child = "customer_pool", }, }, default = { [...] }, }ℹ️ 池名和键前缀不必像 customer_pool 和 cust/key-12345 那样相似。不过,为了清晰起见,让池与其对应的键前缀使用相近的名称是一种好做法。
你还可以进一步细化 map 指令,只在请求同时匹配特定前缀和特定 Memcached 命令时才处理。更多信息请参阅将路由处理器限定到特定命令。
conf 部分 如果定义了 map 部分,还可以定义一个可选的 conf 部分,告诉代理如何解析键前缀。默认情况下,代理将键名中第一个 / 之前的内容作为其前缀。如果键中没有 / 字符,则代理在无前缀的情况下处理请求。例如,cust/12345 的默认前缀是 cust。你可以使用 route{} 块中的 conf 部分来改变这一行为:
conf = { mode = "MODE", start = "START_STRING", stop = "STOP_STRING", }, 替换以下内容:MODE:(可选)代理确定每个键前缀的方式,取值必须是以下之一:
- prefix:前缀是从键开头直到 stop 参数所定义字符之间的所有字符。这是默认值。
- anchor:前缀是 start 和 stop 参数所定义的两个子串之间的所有字符。
START_STRING:如果 mode 为 anchor,则是每个键名开头、位于前缀之前的不超过五个字符的子串。
STOP_STRING:分隔前缀与键名其余部分的子串,默认为 /。
cmap 部分 要让代理根据收到的 Memcached 命令以不同方式处理请求,可在 routes{} 块中加入 cmap 部分。例如,你可以配置代理将所有 get 请求(无论前缀如何)定向到 direct 处理器,而将所有 set 请求定向到 ttl-modifier 处理器。cmap = { KEY_PREFIX = HANDLER_TYPE{ HANDLER_ARGS }, [ ... ] },
下面的示例 cmap 部分将代理配置为:把所有 get 请求转发到名为 main-1 和 main-2 的池,并返回最先响应的那个池的数据:
route { cmap = { get = route_allfastest{ children = { "main-1", "main-2" }, }, }, default = { [...] }, }cmap 配置部分中的条目匹配命令时不考虑任何键前缀。如果你希望路由处理器只在代理收到特定键前缀和特定命令的请求时才生效,可以使用 map 条目实现。更多信息请参阅将路由处理器限定到特定命令。
default 部分 要处理不匹配 map 或 cmap 部分中任何前缀或命令的请求,可添加一个 default 部分。该部分指定一个路由处理器:default = HANDLER_TYPE{ HANDLER_ARGS },
⚠️ 如果 route{} 块中没有定义 default 部分,代理会对不匹配该 route{} 块中任何 map 或 cmap 部分的请求返回错误。示例配置文件 下面的示例文件展示了本页面前面介绍的配置文件全部四个部分:
-- 开启路由库的详细模式。 verbose(true)
-- 覆盖若干代理行为的默认设置。 settings { backend_connect_timeout = 3, pool_options = { filter = "tags", filter_conf = "{}" } }
-- 定义两个后端 Memcached 服务器池。每个池有三个节点。 pools { main_pool = { backend_options = { connecttimeout = 5 }, backends = { "192.0.2.1", "192.0.2.2", "192.0.2.3:11212", } }, customer_pool = { backends = { "203.0.113.1", "203.0.113.10", { host = "203.0.113.20", port = 11212, connecttimeout = 10 }, }, }, }
-- 定义两条路由,分别对应前面定义的池。 -- 然后定义一个默认的兜底路由。 routes { map = { -- 处理以 "main/" 开头的键 main = route_direct { child = "main_pool", }, cust = cmdmap{ -- 只处理前缀为 "cust" 的键的 SET 命令 set = route_direct { child = "customer_pool", }, }, },
-- 处理没有映射前缀的键。
default = route_direct {
child = "main_pool"
},
}
此示例还演示了如何使用 Lua 注释语法在代理配置文件中添加注释。
更多展示如何配置代理以应对各种情况和拓扑的示例,请参阅路由库源码中的 examples 目录。
将后端标记为下线 你可以修改代理配置文件,将某个后端服务器标记为下线。这会告诉代理该后端仍然存在,但暂时不可用。将后端标记为下线有两个效果:代理不会将任何查询路由到被标记为下线的后端。
代理不会重新映射已存储在该后端上的键。
要将后端标记为下线,在其定义(配置文件 pools{} 块中)加上 _down_ 关键字。下面的池定义中有一个服务器被标记为下线:
main_pool = {
backends = {
"192.0.2.1",
"192.0.2.2 _down_",
"192.0.2.3:11212",
}
},
如果该后端使用的是基于列表的另一种语法定义,则改为添加 down=true:
customer_pool = {
backends = {
"203.0.113.1",
"203.0.113.10",
{ host="203.0.113.20", port=11212, down=true, flaptime=10 },
},
},
要将后端恢复为在线状态,从代理配置文件中该后端的定义里移除 _down_ 或 down=true 属性即可。
将相关池组成集合 任何参数中需要接收池列表的路由处理器,都可以改为接收一个池集合。你可以在配置文件的 pools{} 部分中使用以下语法定义集合:SET_NAME = { { { POOL_DEFINITION }, [ ... ] }, },
替换以下内容:SET_NAME:池集合的名称,例如 set_customer_pools。名称必须以 set_ 开头。
⚠️ 如果集合名称不以 set_ 开头,Memcached 会把它当作普通的池定义来读取,很可能导致语法错误。
POOL_DEFINITION:一个完整的“匿名”池定义,即不带池名。
最简单的情况下,该定义就是 backends = { [ ... ] },列出该池的后端。
下面的示例定义了一个名为 set_main_pools 的集合,其中包含三个后端,然后用该集合创建一条故障转移路由:
pools { set_main_pools = { { backends = { "192.0.2.1", "192.0.2.2", "192.0.2.3:11212", }, }, { backends = { "203.0.113.1", "203.0.113.10", { host = "203.0.113.20", port = 11212, connecttimeout = 10 }, }, }, }, } routes { route_failover { children = "set_main_pools", shuffle = true, }, }在向路由处理器传参时,以下两种做法在功能上是等价的:
- 向路由处理器传入池名称列表,例如: route_allsync{ children = { "main_pool_1", "main_pool_2" } }
- 向路由处理器传入单个集合名称,例如: route_allsync{ children = "set_main_pools" }
从配置文件的可读性和可维护性出发,选择最适合你的使用场景的方式即可。
使用本地 zone 为路由设置优先级 如果你已将池组织成命名的集合,还可以进一步配置代理,使其优先将请求路由到集合中的某些池,即使其他池可用。例如,你可能希望优先路由到地理位置上离代理更近的池。具体步骤如下:
- 在代理配置文件中声明一个本地 zone。
- 在集合定义中为各个池附加 zone 标签。
- 在路由定义中,调用支持利用 zone 信息的路由处理器。支持此功能的处理器会在其文档中注明(如 route_failover)。
要声明本地 zone 并为定义的池附加 zone 标签,可按如下方式修改配置文件的 pools{} 块:
local_zone("LOCAL_ZONE_NAME") pools { SET_NAME = { { POOL_ZONE_NAME = { backends = [ ... ] }, [ ... ] }, [ ... ] } 替换以下内容:LOCAL_ZONE_NAME:代理优先将请求路由到的 zone 的名称。
SET_NAME:池集合的名称,例如 set_customer_pools。名称必须以 set_ 开头。
POOL_ZONE_NAME:该池所在的 zone 的名称。
下面的代理配置文件节选声明了一个本地 zone(此处为 "west"),然后定义了两组带 zone 标签的池。
local_zone("west") -- 通过环境变量配置 zone: -- local_zone_from_env("ZONE") -- 通过读取磁盘上文件中的一行来配置 zone: -- local_zone_from_file("./zonefile.txt") pools { set_main_pools = { west = { backends = { "192.0.2.1", "192.0.2.2", "192.0.2.3:11212", }, south = { backends = { [ ... ] }, east = { backends = { [ ... ] } }, }, set_customer_pools = { west = { backends = { "203.0.113.1", "203.0.113.10", { host="203.0.113.20", port=11212, flaptime=10 }, }, south = { backends = { [ ... ] }, east = { backends = { [ ... ] } }, }, }下面的代理配置节选在上例基础上增加了一个使用 route_failover 的 routes{} 块。
routes { map = { main = route_failover { children = "set_main_pools", }, cust = cmdmap{ set = route_failover { children = "set_customer_pools", }, }, }, default = route_allfastest { children = "set_main_pools" }, } 定义多条路由树 你可以配置代理拥有不止一棵路由处理器树,每棵树在配置文件中都有各自独立的 routes{} 块。代理根据请求到达的 TCP 端口来决定对给定请求应用哪个 routes{} 块。定义多条路由树的步骤如下:
- 为每棵路由树定义一个 routes{} 块。在每个块中定义一个该块独有的 tag。你可以选择让其中一个 routes{} 块不带 tag,它将被视为默认树。下面的示例定义了两棵分别标记为 main 和 alt 的路由树,以及一棵默认路由树: routes { tag = "main", map { [ ... ] }, } routes { tag = "alt", map { [ ... ] }, } routes { map { [ ... ] }, }
- 运行代理,使其为你定义的每棵树各监听一个 TCP 端口,并为各端口分配与配置文件中指定的 tag 相匹配的标签。沿用上例,这意味着代理至少要运行在两个端口上,一个标记为 main,另一个标记为 alt。
更多信息请参阅以多个带标签的 TCP 端口运行代理。
重新配置运行中的代理 你可以在不中断代理服务的情况下更新代理配置文件。具体步骤如下:- 根据需要更新配置文件。
- 向 Memcached 代理发送 SIGHUP 信号: kill -HUP PROXY_PROCESS 将 PROXY_PROCESS 替换为代理的进程 ID。
如果更新后的配置文件有效,Memcached 会重新加载配置并继续运行;否则不会重新加载,而是继续使用之前的配置。错误会发送到事件日志,可通过对代理运行 watch proxyevents 查看。
安装内置代理配置示例