协议扩展 - 用例

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

  • 基于消息的负载平衡(mblb)
  • 流媒体
  • 基于令牌的负载均衡
  • 负载平衡持久性
  • 基于TCP连接的负载平衡
  • 基于内容的负载均衡
  • SSL
  • 修改流量
  • 将流量发送到客户端或服务器
  • 连接建立的处理数据

基于消息的负载平衡

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

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

有用的API包括:

数据:len ()

数据:发现()

数据:字节()

数据:子()

数据:分裂()

将TCP数据流解析为协议消息后,用户代码只需将协议消息发送到下一个可用的上下文,即可实现负载平衡,从传递给客户端on_data回调的上下文。

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

例如,TCP客户端和服务器on_data回调是名为" DATA "的事件的用户定义处理程序。对于在一次发送调用中发送整个协议消息,使用eom事件。加工代表消息结束,表示协议消息到磅上下文流的结束,因此对于此消息后面的数据,将做出新的负载平衡决策。

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

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

以下代码段显示了使用发送API发送已解析的协议消息。

示例:

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

流媒体

在某些情况下,可能不需要保留TCP数据流直到收集整个协议消息。事实上,除非需要,否则不建议。保存数据会增加Citrix ADC设备上的内存使用量,并会使设备容易受到DDoS攻击,因为许多连接上具有不完整的协议消息的Citrix ADC设备上的内存。

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

要流式传输协议消息数据,请发送多个数据事件,后跟一个eom事件。连续的数据事件和以下加工事件发送到顺序中第一个数据事件的负载平衡决策所选的相同服务器连接。

对于发送到客户端上下文,加工和数据事件实际上是相同的,因为有由客户端上下游加工事件没有特殊处理。

基于令牌的负载均衡

对于本地支持的协议,Citrix ADC设备支持使用π表达式创建令牌的基于令牌的负载平衡方法。对于扩展,协议事先未知,因此不能使用π表达式。对于基于令牌的负载平衡,您必须将默认负载平衡虚拟服务器设置为使用USER_TOKEN负载平衡方法,并通过使用USER_TOKEN字段调用发送API来从扩展代码中提供令牌值。如果令牌值是从发送API发送的,并且在默认负载平衡虚拟服务器上配置了USER_TOKEN负载平衡方法,则通过基于令牌值计算哈希来做出负载平衡决策。令牌值的最大长度为 64 字节。

添加lb vserver v\_mqttlb USER\_TCP -lbMethod USER\_TOKEN

以下示例中的代码段使用发送API发送lb令牌值。

示例:

——发送消息到lb——user_token设置为基于clientID ns.send(ctxt. send)进行lb。output, "EOM", {data = message, user_token = token_info}) 

负载平衡持久性

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

如果自定义协议需要多种类型的持久性,则必须定义用户持久性类型并对其进行配置。用于配置虚拟服务器的参数名称由协议实现者决定。参数的配置值也可用于扩展代码。

以下cli和代码段显示了使用发送API来支持负载平衡持久性。mqtt。Lua的代码清单部分中的代码清单还说明了user_session字段的使用情况。

对于持久性,您必须在负载平衡虚拟服务器上指定USERSISE持久性类型,并从ns。send API传递user_session值。

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

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

示例:

user_session也设置为clientID(它将被用来持久会话)。output, " DATA ", {DATA = DATA, user_session = clientID}) 

基于TCP连接的负载平衡

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

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

此外,某些用例可能需要在做出负载平衡决策后绕过扩展处理的能力。绕过扩展调用会提高性能,因为流量纯粹由本机代码处理。绕过可以通过使用ns.pipe()API来完成。调用pipe () API扩展代码可以将输入上下文连接到输出上下文。调用pipe()后,来自输入上下文的所有事件直接转到输出上下文。有效地,从管道中移除了进行pipe()调用的模块。

以下代码段显示了流式处理和使用pipe () API绕过模块的情况。mqtt。Lua的代码清单部分中的代码清单还说明了如何进行流媒体以及如何使用管()API绕过该模块以获取连接中的其余流量。

示例:

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

基于内容的负载均衡

对于本机协议,支持像协议扩展功能一样的内容切换。使用此功能,您可以将数据发送到所选负载平衡器,而不是将数据发送到默认负载平衡器。

通过使用ctxt: lb_connect (< lbname >) API来实现协议扩展的内容切换功能。此API可用于TCP客户端上下文。使用此api,扩展代码可以获取与已配置的负载平衡虚拟服务器相对应的负载平衡上下文。然后,您可以将发送API与由此获得的负载平衡上下文一起使用。

lb上下文有时可以为NULL:

  • 虚拟服务器不存在
  • 虚拟服务器不是用户协议类型
  • 虚拟服务器的状态不为up
  • 虚拟服务器是用户虚拟服务器,而不是负载平衡虚拟服务器

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

以下代码段显示了lb_connect () API的使用。代码将客户端ID映射到使用Lua表lb_map对虚拟服务器名称(lbname)进行负载平衡,然后使用lb_connect()获取lbname的磅上下文。最后使用发送API发送到lb上下文。

local lb_map = {["client1*"] = "lb_1", ["client2*"] = "lb_2", ["client3*"] = "lb_3", ["client4*"] = "lb_4"}——将clientID映射到相应的LB vserver并连接到它for client_pattern, lbname in pairs(lb_map) do local match_idx = string。find(clientID, client_pattern) if (match_idx == 1) then lb_ctxx = ctxx:lb_connect(lbname) if (lb_ctxx == 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: " ..clientID) end——发送到目前为止的数据到lb ns。send(lb_ctxt, "DATA", {DATA = DATA} 

SSL

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

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

服务器连接复用

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

注意:此API在Citrix ADC 12.1版本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结束——如果我们想要修改的数据是不完全,然后,等待更多数据如果(不是pat_end)然后ctxt:保存(数据)数据= nil goto端数据:替换(pat_off old_pattern_length,“新模式”)::send_data: ns.send (ctxt。output, " EOM ", {data = data})::done::

以下代码段显示了插入()API的使用。

数据:插入(5、“模式插入”)

下面的代码片段显示了插入()api的使用,当我们想在某个模式之后或之前插入:

——获取模式的偏移量,之后或之前,我们想插入本地模式= "模式后/之前,我们需要插入"本地pattern_length = pattern:len()本地pat_off, pat_end = data:find(pattern)——模式不存在如果(不是pat_off)然后去send_data结束——如果我们想插入的模式之后,没有——完全存在,然后等待更多的数据如果(不是pat_end)那么ctxt:hold(数据)数据= nil goto done结束——插入模式数据之后:Insert (pat_end + 1, " pattern to Insert ")——插入模式数据之前:Insert (pat_off, " pattern to Insert ")::send_data:: ns.send(ctxt。output, " EOM ", {data = data})::done::

以下代码段显示了删除()API的使用。

——得到的补偿模式,我们要删除当地delete_pattern =“模式删除”delete_pattern_length = delete_pattern: len()当地pat_off pat_end =数据:找到(old_pattern)模式不存在如果(不是pat_off)那么goto send_data结束——如果我们想删除的数据是不完全,,然后等待更多数据如果(不是pat_end)然后ctxt:保存(数据)数据= nil goto端数据:删除(pat_off delete_pattern_length):: send_data: ns.send (ctxt。output, " EOM ", {data = data})::done::

以下代码段显示了gsub () API的使用。

——替换所有的实例模式与新的字符串数据:gsub(“老模式”,“新的字符串”)——取代“旧模式”的只有2实例数据:gsub(“老模式”,“新字符串”,2)——插入new_string之前“http”的所有实例数据:gsub(“输入数据”,“(http)”,“new_string % 1”),插入后new_string“http”的所有实例数据:gsub(“输入数据”,“(http)”,“% 1 new_string”)——插入new_string之前只有2“http”数据的实例:gsub(“输入数据”,“(http)”,“new_string % 1”,2)

注意:此API在Citrix ADC 12.1版本50。Xx及更高版本中可用。

将流量发送到客户端或服务器

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

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

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

以下代码段显示了ctxt。客户端和ctxt: close () API的使用。

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

以下代码段显示了用户可以在正常流量流中注入数据的示例。

发送请求后,向服务器发送一些日志消息。客户端功能。On_data (ctxt, payload)本地数据= payload。数据本地log_message = "客户端id: " ..数据:子(3、7). .“用户名:”data:sub(9,15)——将我们从客户端获得的请求发送到后端服务器ns.send(ctxt。output, " DATA ", {DATA = DATA})发送完请求后,同时发送日志消息ns.send(ctxt. txt)。output, " DATA ", {DATA = log_message "})结束

以下代码段显示了ctxt。to_server API的使用。

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

注意:此API在Citrix ADC 12.1版本50。Xx及更高版本中可用。

连接建立的数据处理

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

下面的代码片段显示了对客户端。Init()回调的使用:

——在连接建立时向下一个处理上下文发送请求。函数client.init(ctxt)本地请求“PROXY TCP4”+ ctx .client.ip.src。To_s + " " + ctx .client.ip.dst。to_s + " " + ctext .client.tcp.srcport + " " + ctext .client.tcp.dstport——将另一个请求发送到服务器ns.send(ctext .tcp.srcport)output, " DATA ", {DATA = request})结束

注意:此API在Citrix ADC 13.0版本xx。Xx及更高版本中可用。