意外的HAPROXY acl行为tcp有效负载路由

时间:2015-05-18 13:09:27

标签: tcp routing acl backend haproxy

我正在使用HAProxy acl设置简单的tcp连接路由。我们的想法是根据具有两种风格的请求内容来路由连接:读取和写入请求。

为了测试,我使用perl进行了简单的tcp客户端/服务器设置。奇怪的是,大约10-40%的ACL失败并被发送到默认后端。

ACL应该找到子字符串' read'或者'写'并相应地路线,但这并非总是如此。

使用nc(netcat)发送读/写请求具有相同的效果。

我用mode = http测试了这个配置,一切都按预期工作。

我也用reg,sub和bin测试过,但没有用。

示例服务器设置如下:

  • HAProxy实例,侦听端口8000

  • 客户端(创建到代理的tcp连接,并通过端口8000将用户输入(读/写字符串)发送到服务器,然后关闭连接)

  • Server1(写入服务器),侦听端口8001

  • Server2(读取服务器),侦听端口8002

  • Server3(默认服务器),侦听端口8003

我的HAProxy配置文件看起来是:

global
    log /dev/log    local0 debug
    #daemon
    maxconn 32

defaults
    log global
    balance roundrobin
    mode tcp
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

frontend tcp-in
    bind *:8000
    tcp-request inspect-delay 3s

    acl read req.payload(0,4) -m sub read
    acl write req.payload(0,5) -m sub write

    use_backend read_servers if read
    use_backend write_server if write

    default_backend testFault

backend write_server
    server server1 127.0.0.1:8001 maxconn 32
backend read_servers
    server server2 127.0.0.1:8002 maxconn 32
backend testFault
    server server3 127.0.0.1:8003 maxconn 32

客户端代码(在perl中):

use IO::Socket::INET;
# auto-flush on socket
#$| = 1;

print "connecting to the server\n";

while(<STDIN>){

# create a connecting socket
my $socket = new IO::Socket::INET (
PeerHost => 'localhost',
PeerPort => '8000',
Proto => 'tcp',
);

die "cannot connect to the server $!\n" unless $socket;

    # data to send to a server
    $req = $_;
    chomp $req;

    $size = $socket->send($req);
    print "sent data of length $size\n";

    # notify server that request has been sent
    shutdown($socket, 1);

    # receive a response of up to 1024 characters from server
    $response = "";
    $socket->recv($response, 1024);
    print "received response: $response\n";
    $socket->close();
    }

服务器(perl代码):

use IO::Socket::INET;
if(!$ARGV[0]){
    die("Usage; specify a port..");
}
# auto-flush on socket
$| = 1;

# creating a listening socket

my $socket = new IO::Socket::INET (
LocalHost => '0.0.0.0',
LocalPort => $ARGV[0],
Proto => 'tcp',
Listen => 5,
Reuse => 0
);

die "cannot create socket $!\n" unless $socket;
print "server waiting for client connection on port $ARGV[0]\n";

while(1){

# waiting for a new client connection
my $client_socket = $socket->accept();

# get information about a newly connected client
my $client_address = $client_socket->peerhost();
my $client_port = $client_socket->peerport();
print "connection from $client_address:$client_port\n";

# read up to 1024 characters from the connected client
my $data = "";
$client_socket->recv($data, 1024);
print "received data: $data\n";
# write response data to the connected client
$data = "ok";
$client_socket->send($data);

# notify client that response has been sent
shutdown($client_socket, 1);
$client_socket->close();
print "Connection closed..\n\n";
}

$socket->close();

2 个答案:

答案 0 :(得分:0)

我不觉得傻。重新阅读HAProxy文档我找到了解决问题的以下指令(获取方法):

tcp-request content accept if WAIT_END

这解决了意想不到的行为。

答案 1 :(得分:0)

haproxy中的二进制数据很棘手。可能是一些错误,但以下对我在haproxy 1.7.9上工作。

我正在尝试构建一个thrift代理服务器,它可以根据有效负载中的user_id路由到适当的后端。

frontend thriftrouter
  bind *:10090
  mode tcp
  option tcplog
  log global
  log-format "%ci:%cp [%t] %ft %b/%s %Tw/%Tc/%Tt %B %ts %ac/%fc/%bc/%sc/%rc %sq/%bq captured_user:%[capture.req.hdr(0)] req.len:%[capture.req.hdr(1)]"
  tcp-request inspect-delay 100ms

  tcp-request content capture req.payload(52,10) len 10
  tcp-request content capture req.len len 10
  tcp-request content accept if WAIT_END

  acl acl_thrift_call    req.payload(2,2)  -m bin 0001          # Thrift CALL method
  acl acl_magic_field_id req.payload(30,2) -m bin 270f          # Magic field number 9999

  # Define access control list for each user
  acl acl_user_u1 req.payload(52,10) -m sub |user1|
  acl acl_user_u2 req.payload(52,10) -m sub |user2|

  # Route based on the user. No default backend so that one always has to set it
  use_backend backend_1 if acl_user_u1 acl_magic_field_id acl_thrift_call
  use_backend backend_2 if acl_user_u2 acl_magic_field_id acl_thrift_call

在acl中匹配二进制数据时,请确保查看正确的字节数,以使子字符串正常工作。或者使用十六进制转换方法并匹配十六进制字节。