这两天光干这事了。遇到各种恶心问题,总结一下
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服务,怎么也负载不来。 配置方法
很抱歉没什么代码,如果有空,可能会整理一个例子代码出来。