WCF实现长连接
由于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实现长连接的更多相关文章
- wcf长连接
项目有用到wcf 大体是jquery + webservice + wcf(网页是客户端,wcf是服务端),现在需要服务端往客户端推送信息,本来是用客户端ajax访问 2秒一次访问服务端,后来觉得这 ...
- 保持WCF服务端与客户端的长连接
背景 客户端与服务端使用WCF建立连接后:1.可能长时间不对话(调用服务操作):2.客户端的网络不稳定. 为服务端与客户端两边都写“心跳检测”代码?不愿意. 解决 设置inactivityTimeou ...
- TCP同步与异步,长连接与短连接【转载】
原文地址:TCP同步与异步,长连接与短连接作者:1984346023 [转载说明:http://zjj1211.blog.51cto.com/1812544/373896 这是今天看到的一篇讲到T ...
- HTTP的长连接和短连接
本文总结&分享网络编程中涉及的长连接.短连接概念. 关键字:Keep-Alive,并发连接数限制,TCP,HTTP 一.什么是长连接 HTTP1.1规定了默认保持长连接(HTT ...
- C#中HttpClient使用注意:预热与长连接
最近在测试一个第三方API,准备集成在我们的网站应用中.API的调用使用的是.NET中的HttpClient,由于这个API会在关键业务中用到,对调用API的整体响应速度有严格要求,所以对HttpCl ...
- Erlang C1500K长连接推送服务-内存
上篇 Erlang C1500K长连接推送服务-性能 提到:150w连接,使用了23GB内存,每个连接占用15KB,约一半是内核使用. 大概分析一下: 1. Erlang 节点 12GB,内部因为有内 ...
- Erlang C1500K长连接推送服务-性能
Whatsapp已经使用Erlang在生产环境跑到96GB内存单机 3M长连接,参加:WhatsApp的Erlang世界.毕竟业务级别能达到Whatsapp那样极少,现在只有千万级,单机太多挂一台影响 ...
- HTTP的长连接和短连接——Node上的测试
本文主要从实践角度介绍长.短连接在TCP层面的表现,借助Node.JS搭建后台服务,使用WinHTTP.Ajax做客户端请求测试,最后简单涉及WebSocket. 关键字:长连接.短连 ...
- 分享一个基于长连接+长轮询+原生的JS及AJAX实现的多人在线即时交流聊天室
实现网页版的在线聊天室的方法有很多,在没有来到HTML5之前,常见的有:定时轮询.长连接+长轮询.基于第三方插件(如FLASH的Socket),而如果是HTML5,则比较简单,可以直接使用WebSoc ...
随机推荐
- 在ASP.NET开发中一些单词的标准缩写
有些词可能共用一些缩写.带星号的缩写或词来源于PeopleSoft标准. The following standard word abbreviations should be used in nam ...
- 通过hibernate封装数据库持久化过程回顾泛型/继承/实现等概念
前言 在开发过程中,我们不难发现,客户的需求以及产品的定位对开发内容的走向有很大的决策作用,而这些往往需要在一开始就尽可能考虑周全和设计完善.为什么说是尽可能,因为我们都知道,需求这种东西,一言难尽. ...
- Maven依赖解析
本文将记录Maven工程中依赖解析机制,内容包括: Maven依赖基本结构 从仓库解析依赖的机制 依赖传递性解析实例 1. Maven依赖基本结构 上篇文章记录了Maven依赖的聚合与继承,POM中依 ...
- struts2 内容记录
多xml文件配置 在开发过程中我们经常会将每一张表(如:user表)的struts.xml文件分开,便于管理,故需要建立struts_user.xml文件管理请求等.那么需要用到inculde标签. ...
- 设计模式的征途—7.适配器(Adapter)模式
在现实生活中,我们的笔记本电脑的工作电压大多数都是20V,而我国的家庭用电是220V,如何让20V的笔记本电脑能够工作在220V的电压下工作?答案:引入一个电源适配器,俗称变压器,有了这个电源适配器, ...
- 数据库中File权限的危害
The FILE privilege gives you permission to read and write files on the server host using the LOAD DA ...
- Upgrading an ASP.NET MVC 2 Project to ASP.NET MVC 3 Tools Update
ASP.NET MVC 3 can be installed side by side with ASP.NET MVC 2 on the same computer, which gives you ...
- 【JAVA零基础入门系列】Day15 对象的比较
最近一直有事,博客也停笔了一段时间,十分抱歉. 这一篇主要讲讲对象的比较,什么是对象的比较,我们知道两个数值类型只需要用"=="符号即可进行相等判断,但如果是两个Goods对象呢? ...
- eclipse项目中丢失的R包找回方法
当我们项目中的R文件丢失的时候会令我们痛苦不已,怎样找回呢?总不能删了吧,那样心血会毁于一旦的,我们肯定不会那样做,那要怎么办呢?我这里提供三种方法: 一,一般情况下这样: 方法一:选中 ...
- (五)solr7.1.0之solrJ的使用
(五)solr7.1.0之solrJ的使用 下面是solr7的官网API介绍: 网页翻译的不是很准确,只能了解个大概,基本能获取如下信息: 一.构建和运行SolrJ应用程序 对于用Maven构建的项目 ...