21CTO 社区导读:在管理大型系统时,负载均衡的问题一直是个热点话题。负载均衡的目的是最优化资源使用,最大化吞吐量,最小化响应时间,避免任何单点资源的过载,因而解决这个问题对性能表现非常关键。在本文中我们将看一看针对这个问题的一些解决方法。
为了各位更好的理解WS负载均衡,让我们深入了解一下TCP套接字的背景知识。默认地,一个单独的服务器可以处理65536个套接字连接,因为65536是可用的最大的TCP端口数。因此,既然WS连接拥有TCP的特性,每个WS客户端占用一个端口,可以肯定地说WebSocket的连接数目也是有限的。
但是事实上,这只说对了一半儿。对每个单独的IP地址,服务器都可以处理65536个套接字。因而通过向服务器上添加额外的网络接口,就很容易扩展那个数量。与此同时,去追踪服务器上现有多少链接是极其重要的。一旦超出了限制范围,你会遇到很多关于TCP连接的问题(例如,这个时候不能通过SSH去连接到一个服务器)。因而,在你的应用程序代码中限制每个节点的WS连接数是很好的主意。当我们处理WebSockets问题时也会在我们的apps中使用相同的思路。
一旦我们理解了主要的限制条件,以及解决方式之后,让我们开始考虑负载均衡。下面我将描述在我们自己的项目中试过的三种方式。请注意所有的系统部分都是部署在AWS上的,其中的一些技巧和提示只适用于Amazon的主机配置。
ELB方法
实现负载均衡最简单的办法是直接使用AWS提供的Elastic Load Balancer方法。从ELB切换到TCP模式是很容易的,这就使得任何类型的TCP连接的负载均衡成为可能, 包括WebSockets。这个方法具备如下特性:
1)LB的自动故障恢复
2)负载均衡节点可以自动伸缩
3)设置非常简单
基本上,对大多数情况而言它是一个很好的解决方案,在你遇到负载飞溅的增长情况之前。在这种情况下,ELB运行的很慢以至于无法建立新的连接。可以去联系Amazon,告诉他们去“预热(pre-warm)”ELB,但是它对我们不是一个好的选项,基于负载测试的目的,当我们需要快速建立成千上万的WS连接时;基于对系统的可用性,对客户都不是好选项。
软件负载均衡器
我们曾经试过使用HAProxy作为一个负载均衡器。但是为了让HAProxy正确工作我们需要考虑上文讨论的端口限制问题。为了使HAProxy能够处理超过65536个链接,我们需要执行以下步骤:
1.创建一堆私有的IP地址。为了实现这个目的,选择Amazon Instance -> Actions -> Networking -> Manage Private IP Addresses,我们添加三个IP地址:192.168.1.1,192.168.1.2,192.168.1.3。切记,IP要处于与你的真实应用程序服务器同样的子网中;
2.使用SSH连接到你的HAProxy实例,执行下面的命令:[size=15]$> ifconfig eth0:1 192.168.1.1
$> ifconfig eth0:2 192.168.1.2
$> ifconfig eth0:3 192.168.1.3[/size]
这些命令会往实例中添加3个虚拟网络接口;
3.配置HAProxy。这里是haproxy.cfg文件中的一段,用于3个Erlang节点接收WS连接。[size=15]listen erlang_front :8888 mode http
balance roundrobin
timeout connect 1s
timeout queue 5s
timeout server 3600s
option httpclose
option forwardfor
server erlang-1 192.168.0.1:8888 source 192.168.1.1 server erlang-2 192.168.0.2:8888 source 192.168.1.2 server erlang-3 192.168.0.3:8888 source 192.168.1.3[/size]
现在,HAProxy 已经能够处理超过65536个WebSockets链接,通过增加虚拟网络接口,连接限制可以简单地增长。同时,它也能非常快地建立新连接。这个方法看上去可行,尽管它尚有如下缺点:
1)故障恢复的HAProxy实例需要使用类似Keepalived工具去手动建立;
2)每次你增加一个新的Erlang节点,都要做些操作去重新配置HAProxy;
3)随着连接数目增长,没有能水平伸缩HAProxy的选项。我们只有一个垂直选项可用,因此当我们有了越来越多的在线用户后,我们需要越来越多的昂贵的HAProxy实例(以及HAProxy镜像节点)。
对于这些缺点我们也还算觉得满意,但是一个更简单的方案已经被实现了。那是可能的,因为我们已经有了一些实现好的代码,而系统设计也允许我们使用一个定制方法。
定制方法
在继续前进之前,我们先回顾下面的图,它展示了我们系统的架构。
我们有一个JavaScript客户端应用程序,一个auth应用负责用户授权,一个Erlang应用实现主要的功能。数据流程如下:
1.客户端生成一个含有凭证的HTTP请求,发送给Auth Application;
2.Auth Application检查凭证,生成一个令牌,以一种HTTP请求的形式将它发送给Erlang Cluster;
3.Erlang应用确认,令牌已经收到,给Auth应用返回一个包含配置信息的HTTP响应;
4.Auth App给客户端应用程序发送一个HTTP响应。响应中包含生成的令牌;
5.客户端使用令牌通过我们的HAProxy负载均衡器建立与Erlang应用之间的WebSocket连接。
这是我们基本的数据流,它被稍微修改了一点。我们添加一个简单的模块到Erlang应用程序中去追踪每个Erlang节点上的WebSocket连接数。基于Erlang“分布式的”特性,每个节点可以知道其它节点的连接数。因而,我们可以选择一个拥有更少连接的节点,我们使用这个节点的公共IP地址,把它发送回那个auth应用程序。然后那个auth应用程序将这个IP地址随令牌返回到客户端。客户端使用接收到的IP地址和令牌去建立连接。因而,最终的图显示如下:
现在我们能够:
1)去掉WS的负载均衡器,这就使得我们的系统变得不是那么复杂;
2)添加Erlang节点,而不用对系统其它部分进行重新配置。
除此之外:
1)现在WS连接是平均分布于Erlang节点之间的;
2)系统很容易水平伸缩;
3)对Erlang节点我们不需要使用Elastic IPs。
作者:Konstantin Shamko
编译:乔乔
原文:https://dzone.com/articles/loa ... tions