由于WCF的机制,连接池会在连接建立一定时间后超时,即使设置了超时时间非常长,也可能被服务端系统主动回收。之前做项目时碰到了这个问题,所以项目上考虑采用长连接,自动管理连接池,当连接超时后,自动重建,保持会话,这样在业务层就不需要再去处理连接超时的问题。具体的思路是,在程序启动时,先将需要使用长连接的连接放到长连接容器中,并设置连接的最大数量,在使用时,轮询使用连接,当使用时捕获到异常时,自动切换到下一个连接,并重建上一个连接。代码如下:

AliveConnection类功能是保持连接,具体执行WCF调用,在检测到连接不可用时进行重建。

class AliveConnection<T> where T : System.ServiceModel.ICommunicationObject, new()
{
private string _endpointConfigName = string.Empty;
private T _instance = default(T);
/// <summary>
/// 正在执行其他过程时,设置为正忙。执行完成后闲置
/// 连接出错后,正在重新连接创建时设置为正忙,解除正忙状态有俩种情况:
/// 1.第一次重建连接成功后;
/// 2.在线程中重试成功后;
/// </summary>
public bool IsBusy { get; set; } internal AliveConnection(string endpointConfigName)
{
if (string.IsNullOrEmpty(endpointConfigName.Trim())) throw new ArgumentException("终结点不能配置为空。");
_endpointConfigName = endpointConfigName;
//_instance = CreateConnection();
} internal bool Execute(Action<T> expression)
{
try
{
Open();
expression(_instance);
return true;
}
catch (System.ServiceModel.CommunicationException e)
{
return false;
}
} internal bool Execute<TResult>(Func<T, TResult> expression,out TResult result)
{
result = default(TResult);
try
{
Open();
result = expression(_instance);
return true;
}
catch (System.ServiceModel.CommunicationException e)
{
return false;
}
} private void Open()
{
if (_instance == null)//使用时才创建
{
_instance = CreateConnection();
_instance.Faulted += Faulted;
}
if (_instance.State != System.ServiceModel.CommunicationState.Opened)
_instance.Open();
} private void Faulted(object sender, EventArgs e)
{
lock (_instance)
{
IsBusy = true;
//失败后锁住并重新建立连接
_instance = CreateConnection();
}
} private T CreateConnection()
{
try
{
var instance = (T)Activator.CreateInstance(typeof(T), _endpointConfigName);
IsBusy = false;
return instance;
}
catch (Exception e)
{
IsBusy = true;
RetryWhenFailed();
throw new Exception("创建连接失败,请检测终结点配置和服务。", e);
}
}
//创建一个线程来不间断重试创建连接
private void RetryWhenFailed()
{
int retryTimes = ;
Task.Factory.StartNew(() =>
{
while (true)
{
//如果抛出异常,表示创建失败,继续重试,如果睡眠时间大于60秒,则睡眠时间不再增加
try
{
retryTimes++;
_instance = (T)Activator.CreateInstance(typeof(T), _endpointConfigName);
IsBusy = false;
break;
}
catch
{
int sleepMillionSeconds = retryTimes * * ;
sleepMillionSeconds = sleepMillionSeconds > * ? * : sleepMillionSeconds;
System.Threading.Thread.Sleep(sleepMillionSeconds);
continue;
}
}
});
}
}

另外我们需要一个类,来做连接的初始化,并做轮询,并且暴露执行的函数。

 /// <summary>
/// WCF长连接容器
/// </summary>
/// <typeparam name="T">待创建的WCF服务类型</typeparam>
public class AliveConnectionContainer<T>
where T : System.ServiceModel.ICommunicationObject, new()
{
#region fields
private List<AliveConnection<T>> _connections = null;//所有连接
private int _currentConnectionIndex = ;//当前使用的连接的索引
#endregion #region Octor
/// <summary>
/// 通过终结点配置名称,创建长连接。如果终结点数不等于连接数,则轮询跟节点配置列表,最终创建达到连接数的连接。
/// </summary>
/// <param name="maxConnection">需要创建的长连接数</param>
/// <param name="endpointConfigNames">所有的终结点配置名称,对应配置节点里bind的name</param>
/// <exception>如果终结点配置为空,则抛出异常,如果创建失败,也会抛出异常</exception>
public AliveConnectionContainer(int maxConnection, params string[] endpointConfigNames)
{
_connections = new List<AliveConnection<T>>(maxConnection); int tmpIndex = ;
for (int index = ; index < maxConnection; index++)
{
if (tmpIndex >= endpointConfigNames.Count()) tmpIndex = ;
_connections.Add(new AliveConnection<T>(endpointConfigNames[tmpIndex]));
}
}
#endregion #region public method
/// <summary>
/// 执行服务调用,会一直轮询执行直到执行成功
/// </summary>
/// <param name="expression">需要调用的处理方法</param>
public void Execute(Action<T> expression)
{
Func<bool> executeExpression = () => Instance.Execute(expression);
Execute(executeExpression);
}
/// <summary>
/// 执行服务调用,会一直轮询执行直到执行成功
/// </summary>
/// <param name="expression">需要调用的处理方法</param>
public TResult Execute<TResult>(Func<T, TResult> expression)
{
TResult result = default(TResult);
Func<bool> executeExpression = () => Instance.Execute(expression,out result);
Execute(executeExpression);
return result;
} private void Execute(Func<bool> expression)
{
bool success = false;
int failedCount = ;
try
{
while (true)
{
success = expression();
if (!success) failedCount++;
else break; if (failedCount >= _connections.Count) throw new Exception("没有可用的服务,请检测服务运行状态。");
}
}
catch (Exception e)
{
throw new Exception("执行WCF服务调用失败。", e);
}
} #endregion #region private method
private AliveConnection<T> Instance
{
get
{
if (_connections == null || _connections.Count == ) throw new Exception("没有可用的连接,请先设置连接。");
AliveConnection<T> result;
while (!(result = GetInstance()).IsBusy) break;//轮询直到找到空闲的连接
return result;
}
} private AliveConnection<T> GetInstance()
{
if (_currentConnectionIndex >= _connections.Count) _currentConnectionIndex = ;
return _connections[_currentConnectionIndex++];
}
#endregion
}

使用静态类,做全局的WCF连接注册和检索使用

/// <summary>
/// 长连接服务的管理类
/// </summary>
/// <example>
/// AliveConnectionManager.Register<Service>(endpoints,10);
/// var client = AliveConnectionManager.Resolve<Service>();
/// List<Movie> movies;
/// client.Execute(service => movies = service.GetMovies());
/// </example>
public static class AliveConnectionManager
{
private static Dictionary<Type, object> _container = new Dictionary<Type, object>();
/// <summary>
/// 根据输入的终结点列表,在app.config文件中查找对应的终结点,并建立连接
/// </summary>
/// <typeparam name="T">要注册的WCF的服务类型</typeparam>
/// <param name="maxConnection">连接数</param>
/// <param name="endpointConfigNames">配置的终结点列表</param>
public static void Register<T>(int maxConnection, params string[] endpointConfigNames)
where T : System.ServiceModel.ICommunicationObject, new()
{
if (_container.ContainsKey(typeof(T))) throw new ArgumentException(string.Format("容器中已经添加过{0}的长连接服务。", typeof(T)));
_container.Add(typeof(T), new AliveConnectionContainer<T>(maxConnection, endpointConfigNames));
} /// <summary>
/// 获取类型为T的长连接服务
/// </summary>
/// <typeparam name="T">待获取的WCF的服务类型</typeparam>
/// <returns>对应类型为T的服务</returns>
public static AliveConnectionContainer<T> Resolve<T>() where T : System.ServiceModel.ICommunicationObject, new()
{
Type type = typeof(T);
if (!_container.ContainsKey(type)) throw new ArgumentException(string.Format("没有找到类型为{0}的长连接服务。", type));
return _container[type] as AliveConnectionContainer<T>;
}
}

服务注册代码

 AliveConnectionManager.Register<Service>(endpoints,);

调用服务

var client = AliveConnectionManager.Resolve<Service>();
List<Movie> movies;
client.Execute(service => movies = service.GetMovies());

Service即我们在客户端引入的WCF服务

WCF实现长连接的更多相关文章

  1. wcf长连接

    项目有用到wcf  大体是jquery + webservice + wcf(网页是客户端,wcf是服务端),现在需要服务端往客户端推送信息,本来是用客户端ajax访问 2秒一次访问服务端,后来觉得这 ...

  2. 保持WCF服务端与客户端的长连接

    背景 客户端与服务端使用WCF建立连接后:1.可能长时间不对话(调用服务操作):2.客户端的网络不稳定. 为服务端与客户端两边都写“心跳检测”代码?不愿意. 解决 设置inactivityTimeou ...

  3. TCP同步与异步,长连接与短连接【转载】

    原文地址:TCP同步与异步,长连接与短连接作者:1984346023 [转载说明:http://zjj1211.blog.51cto.com/1812544/373896   这是今天看到的一篇讲到T ...

  4. HTTP的长连接和短连接

        本文总结&分享网络编程中涉及的长连接.短连接概念.     关键字:Keep-Alive,并发连接数限制,TCP,HTTP 一.什么是长连接 HTTP1.1规定了默认保持长连接(HTT ...

  5. C#中HttpClient使用注意:预热与长连接

    最近在测试一个第三方API,准备集成在我们的网站应用中.API的调用使用的是.NET中的HttpClient,由于这个API会在关键业务中用到,对调用API的整体响应速度有严格要求,所以对HttpCl ...

  6. Erlang C1500K长连接推送服务-内存

    上篇 Erlang C1500K长连接推送服务-性能 提到:150w连接,使用了23GB内存,每个连接占用15KB,约一半是内核使用. 大概分析一下: 1. Erlang 节点 12GB,内部因为有内 ...

  7. Erlang C1500K长连接推送服务-性能

    Whatsapp已经使用Erlang在生产环境跑到96GB内存单机 3M长连接,参加:WhatsApp的Erlang世界.毕竟业务级别能达到Whatsapp那样极少,现在只有千万级,单机太多挂一台影响 ...

  8. HTTP的长连接和短连接——Node上的测试

        本文主要从实践角度介绍长.短连接在TCP层面的表现,借助Node.JS搭建后台服务,使用WinHTTP.Ajax做客户端请求测试,最后简单涉及WebSocket.     关键字:长连接.短连 ...

  9. 分享一个基于长连接+长轮询+原生的JS及AJAX实现的多人在线即时交流聊天室

    实现网页版的在线聊天室的方法有很多,在没有来到HTML5之前,常见的有:定时轮询.长连接+长轮询.基于第三方插件(如FLASH的Socket),而如果是HTML5,则比较简单,可以直接使用WebSoc ...

随机推荐

  1. 使用工厂方法模式实现多数据库WinForm手机号码查询器(附源码)

    先讲一下简单工厂模式.工厂方法模式.抽象工厂模式的东西: 简单工厂模式(Simple Factory Pattern):工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关类,也就是说产品 ...

  2. 在moba游戏里面模拟实现绝地求生毒雾圈功能

    ---恢复内容开始--- 已经有很长一段时间没做项目了上半年大多数时间都荒废在王者荣耀,哈哈,S8赛季也上了王者,美滋滋 转回正题,公司需要开个新项目,需要把原有的moba游戏,改成类似绝地求生那玩法 ...

  3. [转载] 一致性hash算法释义

    转载自http://www.cnblogs.com/haippy/archive/2011/12/10/2282943.html 一致性Hash算法背景 一致性哈希算法在1997年由麻省理工学院的Ka ...

  4. MySQL Flush Data

    http://dimitrik.free.fr/db_STRESS_MySQL_540_Purge_Lag_and_Ahead_Flushing_Jun2009.html http://dimitri ...

  5. 在网页标题栏title加入图标?

    方法一:制作一个ico格式的图片,将图片命名为favicon.ico,像素大小为16*16,所使用的颜色不得超过16色,然后再把favicon.ico放到网站的根目录下就行了.这样的话浏览器会不停的搜 ...

  6. Python 面向对象(一) 基础

    Python 中一切皆对象 什么是面向对象? 面向对象就是将一些事物的共有特征抽象成类,从类来创建实例. 类class 可以理解为模版 比如人类,都具有身高.体重.年龄.性别.籍贯...等属性,但属性 ...

  7. 开源项目 easydownload

    一个用于下载的 android library库,   源码 支持多线程断点下载, 支持消息通知. 支持优先级下载. 支持暂停,继续,删除下载列表 支持多服务器下载. 使用方式 compile 'co ...

  8. MeshRenderer组件及相关API

    MeshRenderer:网格过滤器,用于"渲染"显示模型. Cast Shodows:是否投射阴影.(on:开.off:关) Receive Shodows:是否接收阴影. Ma ...

  9. head first python菜鸟学习笔记(第四章)

    1,p124,错误:NameError: name 'print_lol' is not defined 要想文件内如图显示,需要把调用BIF print()改为调用第二章的nester模块中的pri ...

  10. 基于iTextSharp的PDF文档操作

    公司是跨境电商,需要和各种物流打交道,需要把东西交给物流,让他们发到世界各地.其中需要物流公司提供一个运单号,来追踪货物到达哪里?! 最近在和DHL物流公司(应该是个大公司)对接,取运单号的方式是调用 ...