分布式架构下的mvc 异步controller ajax comet 长连接

这两天光干这事了。遇到各种恶心问题,总结一下

mvc异步controller中的异步action成对出现以

public void xxxAsync()

public ActionResult xxxCompleted(object result)

形式,其中void是发起异步,ActionResult的是执行完毕

可以在void上面加[AsyncTimeout(30000)]来控制超时时间

 

在void中,使用

AsyncManager.OutstandingOperations.Increment();

表示有一个需要等待完成的任务。此时这个链接不会返回,而是等待完成

也就是有地方调用了

AsyncManager.OutstandingOperations.Decrement();

 

ajax长连接的情况为

用户a发起一个长连接,服务器端increment,然后在内存中保存下这个AsyncManager。可以用各种方式保存,比如session id相关,用户id相关等

要确保通过用户id(或其他方式)能够找回这个AsyncManager。

用户a发起之后就等,超时就重发,重发的时候,要把重发后新的AsyncManager更新到之前的那个。

 

用户b给用户a发信息,服务器通过用户a的id等信息,找到他连接到服务器AsyncManager,通过AsyncManager.Parameters.Add()设置返回值,然后decrement,此时用户a的长连接就返回了。

在用户a的页面,可以通过js来获取返回的结果,并在页面上作相应的处理。

其中涉及到IAsyncResult这里不详细说明

 

在单一服务器结构下,流程还是相对比较简单的。当多个web服务器,加上负载均衡,恶心事就来了。

我们用大写的 A B表示两台服务器,小写的a b表示两个用户

b给a发消息,a通过ajax 长连接获取到消息

首先,a发起的长连接,发起到具体A B那台服务器了,不确定,通过负载均衡之后,可能在A,也可能在B

其次,b给a发送消息产生的连接在那台服务器也不确定

如果b在发送时,负载到了A服务器,而用户a的长连接也恰好连到了a服务器,则与一台服务器情况一样。

但是如果两个不是同一台服务器,比如a长链到A,而b的发送请求负载到了B,则在B服务器的内存中找不到a的AsyncManager,返回不了。消息没收到

长连接,连到那台服务器上,必须由那台服务器返回,不能由其他的服务器返回。

所以在长连接建立的时候,需要记录是建立在了那台服务器上。而这个记录需要放在一个相对公共的地方,A,B都能操作,比如放在memcache里

额外说,AsyncManager本身是不能放到memcache里的,因为不可序列化。

当b给a发信息时,b先去memcache里找到a连接到的服务器,然后调用此服务器的发送方法给a返回信息。如果两个操作不是一台机器,所操作的信息也要放在公共的地方,由b的发送操作放入,而由a的长链服务器取出并返回。

 

以上的思路可以保证无论是加一个服务器,减一个服务器,都是均衡并且都能即时返回(假设此方法为最优方法)。

另外在思考的过程中,还曾经考虑过一些其他的方法

1、建立主服务器,把所有的ajax长链全分配到此服务器,其他服务器统一调用此服务器的返回

2、对用户做hash,把固定的用户长链分配到固定的服务器。

这两种,在均衡上不是很好

3、记录下未发送出去的信息,等待用户长链超时,重新连接到有信息的服务器时一次性发送

4、根据时间记录信息,重新连接后,从公共部分取出所有未发送的,一次性发送

此两种方式即时性差

 

使用最优方法,依然存在一个问题。对用一个用户来说,他打开了两个网页,建立了两个长连接,而后一个会覆盖掉前一个。

也就是,当b给a发信息时,a打开的两个页面只有一个能收到消息,而且还不一定是那个页面。

 

我个人对所有的页面都收到消息提示的用户友好性保留看法。

下面说多页面的情况

 

 

a开多个页面,建立了多个长连接,这些长连接通过负载连接到了不知道几台服务器上,

b给a发送信息,负载到某一台,然后a的所有长连接均返回

在每台服务器上,记录a的全部长连接,每一个用户id,对应一个长连接数组

b发送时,遍历所有的服务器,每个服务器找自己保存的a的长连接,依次返回。

需要谨慎处理的是超时和返回的连接要即时清除。

(下称此方式为 最优方法改)

 

比如聊天,a给b发一条,b又回a一条,在页面上

a显示

我:xxxxxx

b:yyyyy

b显示

a:xxxxxx

我:yyyyy

在最优方法时,我说的话,可以用js直接写到页面上,接收的才是通过ajax长链返回处理写到页面上的

而在最优方法改时,我说的话,也得通过ajax长链来处理

否则b开3个页面,在其中一个页面上对a说话,a可以收到,b的其他两个页面不显示这句话

所以在b给a发信息时

除了遍历服务器找a的长链全部返回外,还得找b自己的全部长链,也全部返回。

 

以上是最近几天研究的结果,如果哪位牛人有更好的思路,欢迎给我留言。

 

ps:

1、谁知道nginx怎么配置负载成根据各服务器的总请求数均衡,类似ms web farm

2、谁知道ms web farm做farm controller的机器能不能自己也挂web服务?我试了很多方法做controller的机器单纯做controller,web服务在其他机器上没问题,但是如果controller自身也加web服务,怎么也负载不来。 配置方法

 

很抱歉没什么代码,如果有空,可能会整理一个例子代码出来。

posted @ 2012-03-01 17:26  czcz1024  阅读(1140)  评论(4编辑  收藏  举报