协议扩展-用例

协议扩展可用于以下用例。

  • 消息负载均衡(MBLB)
  • 流动
  • 基于令牌的负载平衡
  • 负载平衡的持久性
  • 基于TCP连接的负载均衡
  • 基于内容的负载均衡
  • SSL
  • 修改交通
  • 向客户端或服务器发起流量
  • 处理连接建立时的数据

基于消息的负载均衡

协议扩展支持基于消息的负载平衡(MBLB),它可以解析Citrix ADC设备上的任何协议,并对到达一个客户端连接的协议消息进行负载平衡,即,通过多个服务器连接分发消息。MBLB由解析客户端TCP数据流的用户代码实现。

TCP数据流被传递给on_数据回调,以用于客户端和服务器行为。TCP数据流可通过类似Lua字符串的接口供扩展函数使用。您可以使用类似于Lua字符串API的API来解析TCP数据流。

有用的api包括:

数据:len ()

数据:发现()

数据:字节()

数据:子()

数据:分裂()

一旦TCP数据流被解析为协议消息,用户代码通过将协议消息发送到下一个上下文(从传递给客户端on_data回调的上下文)可用的上下文中来实现负载平衡。

ns.send()API用于向其他处理模块发送消息。除了目标上下文之外,sendapi还将事件名称和可选负载作为参数。行为的事件名和回调函数名之间存在一对一的对应关系。事件的回调在上调用。回调名称仅使用小写字母。

例如,TCP客户端和服务器的on_data回调是名为“DATA”的事件的用户定义处理程序。为了在一个send调用中发送整个协议消息,使用了EOM事件。EOM代表end of message,表示向下游的LB上下文发送的协议结束消息,因此对该消息之后的数据进行新的负载均衡决策。

扩展代码有时可能无法在on_数据事件中接收整个协议消息。在这种情况下,可以使用ctxt:hold()API保存数据。hold API可用于TCP客户端和服务器回调上下文。调用“hold with data”时,数据存储在上下文中。当在同一上下文中接收到更多数据时,新接收的数据将附加到先前存储的数据中,并且使用组合数据再次调用on_数据回调函数。

注意:使用的负载均衡方式取决于负载均衡上下文对应的负载均衡虚拟服务器的配置。

下面的代码片段显示了如何使用发送API来发送解析的协议消息。

例子:

客户端功能。On_data (ctxt,有效负载)-- --代码来解析有效负载。数据进入协议消息来到这里-- --发送消息lb ns.send(ctxt.)。end——client. output, "EOM", {data = message})on_data功能服务器。On_data (ctxt,有效负载)-- --代码来解析有效负载。数据进入协议消息来到这里-- --发送消息给客户端ns.send(ctxt。输出,"EOM", {data = message})on_data < !——NeedCopy >

流动

在某些场景中,可能没有必要一直保存TCP数据流直到收集到整个协议消息。事实上,除非必要,否则不建议这样做。持有这些数据会增加Citrix ADC设备上的内存使用量,并且在许多连接上使用不完整的协议消息会耗尽Citrix ADC设备上的内存,从而使设备容易受到DDoS攻击。

用户可以使用sendapi在扩展回调处理程序中实现TCP数据流。数据可以分块发送,而不是在收集整个消息之前一直保存数据。使用数据事件将数据发送到ctxt.output会发送部分协议消息。它后面可以有更多的数据事件。必须发送EOM事件以标记协议消息的结束。下游负载平衡上下文对接收到的第一个数据做出负载平衡决策。在收到EOM消息后,将做出新的负载平衡决策。

要流化协议消息数据,发送多个data事件,然后发送EOM事件。连续的DATA事件和下面的EOM事件被发送到由负载平衡决策为序列中的第一个DATA事件选择的相同服务器连接。

对于发送到客户机上下文,EOM和DATA事件实际上是相同的,因为客户机上下文下游没有对EOM事件进行特殊处理。

基于令牌的负载平衡

对于本地支持的协议,Citrix ADC设备支持基于令牌的负载平衡方法,该方法使用PI表达式创建令牌。对于扩展,协议未知,不能使用PI表达式。对于基于令牌的负载平衡,您必须设置默认的负载平衡虚拟服务器来使用USER_TOKEN负载平衡方法,并通过使用USER_TOKEN字段调用send API来从扩展代码中提供令牌值。如果从send API发送token值,并且在默认的负载均衡虚拟服务器上配置了USER_TOKEN负载均衡方法,则根据token值计算散列来决定负载均衡。令牌值的最大长度为64字节。

添加lb vserver v\\ u mqttlb USER\\ u TCP–lbMethod USER\\令牌

下面示例中的代码片段使用发送API来发送LB令牌值。

例子:

--将消息发送到lb——用户令牌被设置为基于clientID ns.send执行lb(ctxt.output,“EOM”{data=message,user\u token=token\u info})<--需要复制-->

负载平衡的持久性

负载均衡持久性与基于令牌的负载均衡密切相关。用户必须能够以编程方式计算持久性会话值,并将其用于负载平衡持久性。send API用于发送持久性参数。要使用负载平衡持久化,您必须在默认负载平衡虚拟服务器上设置USERSESSION持久化类型,并通过使用user_session字段调用send API从扩展代码中提供一个持久化参数。持久化参数值的最大长度为64字节。

如果您需要一个自定义协议的多种持久性类型,那么您必须定义用户持久性类型并配置它们。用于配置虚拟服务器的参数名称由协议实现者决定。参数的配置值也可用于扩展码。

下面的CLI和代码片段展示了如何使用发送API来支持负载平衡持久性。本节中的代码清单mqtt.lua的代码清单还说明了用户会话字段的使用。

对于持久化,您必须在负载均衡虚拟服务器上指定USERSESSION持久化类型,并从ns传递user_session值。发送API。

添加lb vserver v\_mqttlb USER\_TCP -persistencetype USERSESSION

将MQTT消息发送到负载平衡器,在有效负载中将user_session字段设置为clientID。

例子:

——send数据到lb——user_session也设置为clientID(它将用于保持会话){DATA = DATA, user_session = clientID}) 

基于TCP连接的负载均衡

对于某些协议,可能不需要MBLB。相反,您可能需要基于TCP连接的负载平衡。例如,MQTT协议必须解析TCP流的初始部分,以确定用于负载平衡的令牌。而且,同一TCP连接上的所有MQTT消息必须发送到同一服务器连接。

基于TCP连接的负载平衡可以通过使用仅包含数据事件的send API而不发送任何EOM来实现。这样,下游负载平衡上下文将负载平衡决策基于首先接收的数据,并将所有后续数据发送到由负载平衡决策选择的同一服务器连接。

另外,一些用例可能需要在作出负载平衡决定后能够绕过扩展处理。绕过扩展调用可以获得更好的性能,因为通信量完全由本地代码处理。通过使用ns.pipe() API可以实现旁路。调用pipe() API扩展代码可以将输入上下文连接到输出上下文。调用pipe()之后,所有来自输入上下文的事件都直接转到输出上下文。实际上,发出pipe()调用的模块将从管道中删除。

下面的代码片段展示了流式和使用pipe() API绕过模块。本节中的代码清单mqtt.lua的代码清单还说明了如何进行流式处理,以及如何使用pipe()API绕过模块,以获得连接上的其余流量。

例子:

——将当前的数据发送到lb ns.send(ctxt. send)。{DATA = DATA, user_token = clientID})——将后续流量管道到lb -以绕过客户端on_data处理程序ns.pipe(ctxt. pipe)。输入、ctxt.output) < !——NeedCopy >

基于内容的负载均衡

对于原生协议,支持类似协议扩展的内容切换功能。使用这个特性,您可以将数据发送到选定的负载均衡器,而不是发送到默认的负载均衡器。

协议扩展的内容切换特性是通过使用ctxt:lb_connect(< lbname >) API。此API可用于TCP客户机上下文。使用此API,扩展代码可以获得与已经配置的负载平衡虚拟服务器对应的负载平衡上下文。然后,您可以使用这样获得的负载平衡上下文的发送API。

lb上下文有时可以为空:

  • 虚拟服务器不存在
  • 虚拟服务器不属于用户协议类型
  • 虚拟服务器的状态不是UP
  • 虚拟服务器是用户虚拟服务器,不是负载均衡虚拟服务器

如果在使用目标负载平衡虚拟服务器时将其删除,则与该负载平衡虚拟服务器关联的所有连接都将重置。

下面的代码片段展示了lb_connect() API的使用。该代码使用Lua表lb_map将客户端ID映射到负载均衡虚拟服务器名称(lbname),然后使用lb_connect()获取lbname的LB上下文。最后使用send API发送到LB上下文。

local lb_map = {["client1*"] = "lb_1", ["client2*"] = "lb_2", ["client3*"] = "lb_3", ["client4*"] = "lb_4"}——将clientID映射到相应的LB vserver,并连接到它以获取client_pattern, lbname in pairs(lb_map) do local match_idx = string。查找(clientID, client_pattern) if (match_idx == 1) then lb_ctxt = ctxt:lb_connect(lbname) if (lb_ctxt == nil) then error("Failed to connect to LB vserver: " ..lbname) end break end end if (lb_ctxt == nil) then——如果lb context是NULL,用户可以抛出一个错误或发送数据到默认的lb错误("Failed to map lb vserver for client: " ..端——发送数据到lb ns。发送(lb_ctxt, "DATA", {DATA = DATA} 

SSL

对使用扩展的协议的SSL的支持方式与对本机协议的SSL的支持方式类似。使用相同的解析代码创建自定义协议,您可以通过TCP或SSL创建协议实例,然后使用该实例配置虚拟服务器。类似地,您可以通过TCP或SSL添加用户服务。

有关更多信息,请参见为MQTT配置SSL卸载使用端到端加密为MQTT配置SSL卸载

服务器连接多路复用

有时,客户端一次发送一个请求,只有在从服务器接收到第一个请求的响应后才发送下一个请求。在这种情况下,服务器连接可以被其他客户端连接重用,也可以被同一连接上的下一条消息重用(在响应被发送到客户端之后)。要允许其他客户端连接重用服务器连接,必须在服务器端上下文中使用ctxt: reuse_server_connection() API。

请注意这个API在Citrix ADC 12.1 build 49中可用。xx,后来。

修改交通

要修改请求或响应中的数据,必须使用使用高级策略PI表达式的本机重写特性。因为不能在扩展中使用PI表达式,所以可以使用以下api来修改TCP流数据。

Data:replace(offset, length, new_string) Data:insert(offset, new_string) Data:delete(offset, length) Data:gsub(pattern, replace [,n]))

下面的代码片段展示了replace() API的使用。

——得到的补偿模式,我们想要取代当地old_pattern =“repalace模式”old_pattern_length = old_pattern: len()当地pat_off pat_end =数据:找到(old_pattern)模式不存在如果(不是pat_off)那么goto send_data结束——如果我们想要修改的数据是不完全,然后——wait for more data if (not pat_end) Then ctxt:hold(data) data = nil goto done end data:replace(pat_off, old_pattern_length, " new pattern ")::send_data:: ns.send(ctxt. txt, " new pattern ")::{data = data})::完成::

下面的代码片段展示了insert() API的使用。

数据:insert(5, " pattern to insert ")

下面的代码片段展示了insert() API的用法,当我们想在某个模式之后或之前插入时:

——获取模式的偏移量,在我们想要插入local pattern的前后= " pattern after/before which we need to insert " local pattern_length = pattern:len() local pat_off,pat_end =数据:找到(模式)模式不存在如果(不是pat_off)那么goto send_data结束——如果我们想要的模式之后,插入——完全不是礼物,然后等待更多数据如果(不是pat_end)然后ctxt:保存(数据)数据= nil goto完成结束后,插入模式数据:插入(pat_end + 1," pattern to insert ")——在模式数据之前插入:insert(pat_off, " pattern to insert ")::send_data:: ns.send(ctxt. txt. txt. txt. txt. txt. txt。{data = data})::完成::

下面的代码片段展示了delete() API的使用。

——得到的补偿模式,我们要删除当地delete_pattern =“模式删除”delete_pattern_length = delete_pattern: len()当地pat_off pat_end =数据:找到(old_pattern)模式不存在如果(不是pat_off)那么goto send_data结束——如果我们想删除的数据是不完全,——then wait for more data if (not pat_end) then ctxt:hold(data) data = nil goto done end data:delete(pat_off, delete_pattern_length)::send_data:: ns.send(ctxt. text . txt);{data = data})::完成::

下面的代码片段展示了gsub() API的使用。

gsub(" old pattern ", " new string ")——只替换2个" old pattern " data实例:gsub(" old pattern ", " new string ", 2)——在所有" http " data实例之前插入new_string:gsub(" input data ", " (http) ",gsub(" input data ", " (http) ", " %1new_string ")——在" http " data的所有实例后插入new_string:gsub(" input data ", " (http) ", " new_string%1 ", 2)

请注意:此API在Citrix ADC 12.1 build 50.xx及更高版本中提供。

向客户端或服务器发起流量

您可以使用ns.send()API将源于扩展代码的数据发送到客户端和后端服务器。要从客户端上下文直接使用客户端发送或接收响应,必须使用ctxt.client作为目标。要使用后端服务器从服务器上下文直接发送或接收响应,必须使用ctxt.server作为目标。有效负载中的数据可以是TCP流数据或Lua字符串。

要停止连接上的流量处理,可以在客户端或服务器上下文中使用ctxt:close() API。这个API关闭客户端连接或任何连接到它的服务器连接。

当调用ctxt:close() API时,扩展代码向客户端和服务器连接发送TCP FIN包,如果在此连接上从客户端或服务器接收到更多数据,则设备将重置连接。

下面的代码片段展示了ctxt的用法。client和ctxt:close() api。

——如果输入数据包不是MQTT CONNECT类型,那么——向客户端发送一些错误响应。客户端功能。On_data (ctxt,有效负载)本地数据=有效负载。data local offset = 1 local msg_type = 0 local error_response = " Missing MQTT Connect packet. "" msg_type = bit32. "rshift(byte, 4) if (msg_type ~= 1) then——发送错误响应ns.send(ctxt. txt. txt)client, " DATA ", {DATA = error_response})——由于错误响应已经发送,所以现在关闭连接ctxt:close()结束

下面的代码片段展示了用户可以将数据注入到正常流量流中的示例。

——发送请求后,向服务器发送一些日志消息。客户端功能。On_data (ctxt,有效负载)本地数据=有效负载。Data local log_message = " client id: " ..数据:子(3、7). .data:sub(9,15)——将我们从客户端得到的请求发送到后端服务器ns.send(ctxt。发送请求后,也发送日志消息ns.send(ctxt. send)。输出," DATA ", {DATA = log_message "})结束

下面的代码片段展示了ctxt的用法。to_server API。

——如果HTTP响应状态消息为“Not Found”,——则向服务器发送另一个请求。服务器功能。On_data (ctxt,有效负载)本地数据=有效负载。data local request " GET /default.html HTTP/1.1\r\n\r\n " ss local start, end = data:find(" Not Found ") if (start) then——发送另一个请求到服务器。server, " DATA ", {DATA = request})结束

请注意:此API在Citrix ADC 12.1 build 50.xx及更高版本中提供。

建立连接时的数据处理

可能有这样一个用例,您希望在连接建立时(接收到最终的ACK时)发送一些数据。例如,在代理协议中,您可能希望在连接建立时将客户机的源和目的IP地址和端口发送到后端服务器。在这种情况下,可以使用client.init()回调处理程序在建立连接时发送数据。

以下代码段显示了client.init()回调的用法:

——在连接建立时向下一个处理上下文发送请求。function client.init(ctxt) local request " PROXY TCP4 " + ctxt.client.ip.src。To_s + " " + ctxt.client.ip.dst。to_s + " " + ctxt.client.tcp.srcport + " " + ctxt.client.tcp.dstport——向服务器ns.send(ctxt. client.tcp.dstport)发送另一个请求。输出," DATA ", {DATA = request})结束

请注意:此API在Citrix ADC 13.0版本xx.xx及更高版本中提供。