热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

WCF实现长连接

由于WCF的机制,连接池会在连接建立一定时间后超时,即使设置了超时时间非常长,也可能被服务端系统主动回收。之前做项目时碰到了这个问题,所以项目上考虑采用长连接,自动管理连接池,当连接超时后,

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

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

 

class AliveConnection where T : System.ServiceModel.ICommunicationObject, new()
    {
        private string _endpointCOnfigName= string.Empty;
        private T _instance = default(T);
        /// 
        /// 正在执行其他过程时,设置为正忙。执行完成后闲置
        /// 连接出错后,正在重新连接创建时设置为正忙,解除正忙状态有俩种情况:
        /// 1.第一次重建连接成功后;
        /// 2.在线程中重试成功后;
        /// 
        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 expression)
        {
            try
            {
                Open();
                expression(_instance);
                return true;
            }
            catch (System.ServiceModel.CommunicationException e)
            {
                return false;
            }
        }

        internal bool Execute(Func 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 = 0;
            Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    //如果抛出异常,表示创建失败,继续重试,如果睡眠时间大于60秒,则睡眠时间不再增加
                    try
                    {
                        retryTimes++;
                        _instance = (T)Activator.CreateInstance(typeof(T), _endpointConfigName);
                        IsBusy = false;
                        break;
                    }
                    catch
                    {
                        int sleepMilliOnSeconds= retryTimes * 5 * 1000;
                        sleepMillionSeconds = sleepMillionSeconds > 60 * 1000 ? 60 * 1000 : sleepMillionSeconds;
                        System.Threading.Thread.Sleep(sleepMillionSeconds);
                        continue;
                    }
                }
            });
        }
    }

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

 /// 
    /// WCF长连接容器
    /// 
    /// 待创建的WCF服务类型
    public class AliveConnectionContainer
        where T : System.ServiceModel.ICommunicationObject, new()
    {
        #region fields
        private List> _cOnnections= null;//所有连接
        private int _currentCOnnectionIndex= 0;//当前使用的连接的索引
        #endregion

        #region Octor
        /// 
        /// 通过终结点配置名称,创建长连接。如果终结点数不等于连接数,则轮询跟节点配置列表,最终创建达到连接数的连接。
        /// 
        /// 需要创建的长连接数
        /// 所有的终结点配置名称,对应配置节点里bind的name
        /// 如果终结点配置为空,则抛出异常,如果创建失败,也会抛出异常
        public AliveConnectionContainer(int maxConnection, params string[] endpointConfigNames)
        {
            _connections = new List>(maxConnection);

            int tmpIndex = 0;
            for (int index = 0; index )
            {
                if (tmpIndex >= endpointConfigNames.Count()) tmpIndex = 0;
                _connections.Add(new AliveConnection(endpointConfigNames[tmpIndex]));
            }
        }
        #endregion

        #region public method
        /// 
        /// 执行服务调用,会一直轮询执行直到执行成功
        /// 
        /// 需要调用的处理方法
        public void Execute(Action expression)
        {
            Func<bool> executeExpression = () => Instance.Execute(expression);
            Execute(executeExpression);
        }    
        /// 
        /// 执行服务调用,会一直轮询执行直到执行成功
        /// 
        /// 需要调用的处理方法
        public TResult Execute(Func 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 = 0;
            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 Instance
        {
            get
            {
                if (_cOnnections== null || _connections.Count == 0) throw new Exception("没有可用的连接,请先设置连接。");
                AliveConnection result;
                while (!(result = GetInstance()).IsBusy) break;//轮询直到找到空闲的连接
                return result;
            }
        }

        private AliveConnection GetInstance()
        {
            if (_currentConnectionIndex >= _connections.Count) _currentCOnnectionIndex= 0;
            return _connections[_currentConnectionIndex++];
        }
        #endregion
    }

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

/// 
    /// 长连接服务的管理类
    /// 
    /// 
    /// AliveConnectionManager.Register(endpoints,10);
    /// var client = AliveConnectionManager.Resolve();
    /// List movies;
    /// client.Execute(service => movies = service.GetMovies());
    /// 
    public static class AliveConnectionManager
    {
        private static Dictionaryobject> _cOntainer= new Dictionaryobject>();
        /// 
        /// 根据输入的终结点列表,在app.config文件中查找对应的终结点,并建立连接
        /// 
        /// 要注册的WCF的服务类型
        /// 连接数
        /// 配置的终结点列表
        public static void Register(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(maxConnection, endpointConfigNames));
        }

        /// 
        /// 获取类型为T的长连接服务
        /// 
        /// 待获取的WCF的服务类型
        /// 对应类型为T的服务
        public static AliveConnectionContainer Resolve() 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;
        }
    }

服务注册代码

 AliveConnectionManager.Register(endpoints,10);

调用服务

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

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


推荐阅读
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 摘要: 在测试数据中,生成中文姓名是一个常见的需求。本文介绍了使用C#编写的随机生成中文姓名的方法,并分享了相关代码。作者欢迎读者提出意见和建议。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • 集合的遍历方式及其局限性
    本文介绍了Java中集合的遍历方式,重点介绍了for-each语句的用法和优势。同时指出了for-each语句无法引用数组或集合的索引的局限性。通过示例代码展示了for-each语句的使用方法,并提供了改写为for语句版本的方法。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • Spring学习(4):Spring管理对象之间的关联关系
    本文是关于Spring学习的第四篇文章,讲述了Spring框架中管理对象之间的关联关系。文章介绍了MessageService类和MessagePrinter类的实现,并解释了它们之间的关联关系。通过学习本文,读者可以了解Spring框架中对象之间的关联关系的概念和实现方式。 ... [详细]
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
author-avatar
房东的猫
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有