简简单单利用Memcached实行缓存层设计

正在考虑web应用缓存层的布署,参考了成都百货上千材料,预计照旧必要用到对峙成熟应用广泛的分布式缓存Memcached。在.net平台上早已有相对成熟的Memcached客户端产品,如BeITMemcachedEnyimMemcached,业余时间看了一下源码,本身分析并调用一下并不困难。那里大致介绍一下运用Memcached的3个粗略的缓存层设计,示例代码基于EnyimMemcached,下面以贴代码为主。

1、引言

京东的京麦商行后台二零一五年营造网关,从HTTP网关发展到TCP网关。在二〇一四年重构完结基于Netty4.x+Protobuf3.x完毕对接PC和App上下行通讯的高可用、高质量、高稳定性的TCP长连接网关。

最初京麦搭建HTTP和TCP长连接效用首要用于消息文告的推送,并未接纳于API网关。随着慢慢对NIO的中肯学习和对Netty框架的打听,以及对系统通讯稳定能力的愈加高需要,采纳NIO技术使用网关达成API请求调用的想法,最后在二零一六年达成,并完全支持业务化运转。由于许多的一字不苟,包涵TCP长连接容器、Protobuf的连串化、服务泛化调用框架等等,质量比HTTP网关升高10倍以上,稳定性也远远超乎HTTP网关。

本文重点介绍京麦TCP网关的技艺架构及Netty的施用实践。

简易介绍一下京麦是哪些:

京麦工作台是京东商城为京东的商号准备的一款后台管理工科具,它能够使你不登陆商户后台就能开始展览订单生产,连忙完成订单下载发货流程。类似于天猫商城的旺旺卖家版(今后叫天猫商城千牛)那样的东西。

学学沟通:


即时报纸发表支出交换群:320837163 [推荐]


移动端IM开发入门小说:《新手入门一篇就够:从零付出移动端IM

(本文同步公布于:http://www.52im.net/thread-1243-1-1.html

壹 、公共缓存接口

剖析asp.net web caching的缓存类,大家大致能够抽象出如下多少个接口方法:

图片 1图片 2

Contractnamespace DotNet.Common.EnyimCache
{
    /// <summary>
    /// memcached公共缓存调用方法接口(读)
    /// </summary>
    public interface ICacheReaderService
    {

        /// <summary>
        /// 返回指定key的对象
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        object Get(string key);

        /// <summary>
        /// 返回指定key的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        T Get<T>(string key);

        /// <summary>
        /// 是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        bool isExists(string key);
    }

    /// <summary>
    /// memcached公共缓存调用方法接口(写)
    /// </summary>
    public interface ICacheWriterService
    {
        /// <summary>
        /// 缓存有效间隔时间 (以分钟为单位)
        /// </summary>
        int TimeOut { set; get; }

        /// <summary>
        /// 添加指定key的对象
        /// </summary>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        void Add(string key, object obj);

        /// <summary>
        /// 添加指定key的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        void Add<T>(string key, T obj);

        /// <summary>
        /// 移除指定key的对象
        /// </summary>
        /// <param name="key"></param>
        bool Remove(string key);

        /// <summary>
        /// 修改指定key的对象
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        bool Modify(string key, object destObj);

        /// <summary>
        /// 清空缓存
        /// </summary>
        /// <returns></returns>
        bool Release();
    }
}

 

看命名就驾驭,增加和删除改查是也。依据个体选拔缓存的经历,修改操作经常是不须要的,如若确实供给修改缓存数据,直接删除然后添加便是改了。

再有,你可能会问,那里为啥要定义五个接口?原因根本是考虑到读操作(查询)是平时利用的,而写操作(增加和删除改)相对较少,所以也把它们设计成读写分离的方法。

二 、本文小编

张松然

– 京东供销合作社研究开发部架构师;

– 充足的构建高品质高可用大规模分布式系统的研究开发、架构经验;

– 二〇一三年加盟京东,如今负责京麦服务网关和京麦服务市镇的系统研究开发工作。

二 、缓存服务落成

那边就须求调用Memcached客户端封装好的调用方法,达成增删改查等艺术。

图片 3图片 4

Implementusing System;

namespace DotNet.Common.EnyimCache
{
    using Enyim.Caching.Memcached;

    public class CacheReaderService : BaseService, ICacheReaderService
    {

        public int TimeOut
        {
            get;
            set;
        }

        public CacheReaderService()
        {

        }

        public object Get(string key)
        {
            object obj = null;
            Client.TryGet(key, out obj);
            return obj;
        }

        public T Get<T>(string key)
        {
            object obj = Get(key);
            T result = default(T);
            if (obj != null)
            {
                result = (T)obj;
            }
            return result;
        }

        public bool isExists(string key)
        {
            object obj = Get(key);
            return (obj == null) ? false : true;
        }
    }

    public class CacheWriterService : BaseService, ICacheWriterService
    {
        public int TimeOut
        {
            get;
            set;
        }

        public CacheWriterService()
        {

        }

        public CacheWriterService(int timeOut)
        {
            this.TimeOut = timeOut;
        }

        public void Add(string key, object obj)
        {
            if (TimeOut > 0)
            {
                Client.Store(StoreMode.Add, key, obj, DateTime.Now.AddMinutes(TimeOut));
            }
            else
            {
                Client.Store(StoreMode.Add, key, obj);
            }
        }

        public void Add<T>(string key, T obj)
        {
            if (TimeOut > 0)
            {
                Client.Store(StoreMode.Add, key, obj, DateTime.Now.AddMinutes(TimeOut));
            }
            else
            {
                Client.Store(StoreMode.Add, key, obj);
            }
        }

        public bool Remove(string key)
        {
            return Client.Remove(key);
        }

        public bool Modify(string key, object destObj)
        {
            return Client.Store(StoreMode.Set, key, destObj);
        }

        /// <summary>
        /// 清空缓存 TO DO
        /// </summary>
        /// <returns></returns>
        public bool Release()
        {
            throw new NotImplementedException();
        }
    }
}

 

基类里伊始化二个MemcachedClient示例Client,这几个Client的点子里封装了较多的函数。查看源码可以通晓,它们本质上皆以向Memcached服务端发送有关指令(run
command),然后解析重回的二进制数据,假设您熟习memcached所利用的协商,通晓起来应当会一定简单。本文示例只行使了客户端提供的几个点子。

再者要注意,在促成具体缓存服务的时候,CacheWriterService有八个构造函数,当中带参数的是为缓存显式钦赐过期时间。那几个参数在实质上接纳中常见须求配备,显著是比较灵活一些的。

备注:在接口中有1个函数Release,本来的指标是清空全部的缓存数据,不过客户端从未一向提供对应的函数,假如你有好的艺术,请不吝赐教。

三 、TCP网关的网络布局

依照Netty营造京麦TCP网关的长连接容器,作为网关接入层提供服务API请求调用。

客户端通过域名+端口访问TCP网关,域名分裂的运维商对应分歧的VIP,VIP公布在LVS上,LVS将请求转发给后端的HAProxy,再由HAProxy把请求转载给后端的Netty的IP+Port。

LVS转载给后端的HAProxy,请求经过LVS,但是响应是HAProxy直接举报给客户端的,那也正是LVS的DENCORE情势。

③ 、简单的读写测试

贴一下字符串、时间、单个类和集聚的增加和删除改查示例代码:

图片 5图片 6

CRUD            ICacheWriterService writer = CacheBuilder.GetWriterService();//writer 使用memcached默认过期时间
            ICacheReaderService reader = CacheBuilder.GetReaderService();//reader

            #region 字符串

            string strKey = "hello";

            bool isOK = writer.Remove(strKey); //移除
            Console.WriteLine("Removed key {0}:{1}", strKey, isOK);

            writer.Add(strKey, "hello world"); //添加
            Console.WriteLine("Add key {0}, value:hello world", strKey);

            bool isExists = reader.isExists(strKey);//是否存在
            Console.WriteLine("Key {0} exists:{1}", strKey, isExists);

            string result = reader.Get(strKey) as string;//查询
            Console.WriteLine("Get key {0}:{1}", strKey, result);

            bool isModify = writer.Modify(strKey, "Hello Memcached!");//修改
            Console.WriteLine("Modify key {0}, value:Hello Memcached. The result is:{1}", strKey, isModify);

            result = reader.Get<string>(strKey);
            Console.WriteLine("Generic get key {0}:{1}", strKey, result);

            isOK = writer.Remove(strKey);
            Console.WriteLine("Removed key {0}:{1}", strKey, isOK);

            isExists = reader.isExists(strKey);
            Console.WriteLine("Key {0} exists:{1}", strKey, isExists);

            result = reader.Get(strKey) as string;
            Console.WriteLine("Get key {0}:{1}", strKey, result);

            result = reader.Get<string>(strKey);
            Console.WriteLine("Generic get key {0}:{1}", strKey, result);
            Console.WriteLine();
            Console.WriteLine("===========================================");
            Console.Read();

            #endregion

            #region 时间

            DateTime dtNow = DateTime.Now;
            strKey = "datetime";
            isOK = writer.Remove(strKey); //移除
            Console.WriteLine("Removed key {0}:{1}", strKey, isOK);

            writer.Add(strKey, dtNow); //添加
            Console.WriteLine("Add key {0}, value:{1}", strKey, dtNow);

            isExists = reader.isExists(strKey);//是否存在
            Console.WriteLine("Key {0} exists:{1}", strKey, isExists);

            DateTime dt = (DateTime)reader.Get(strKey);//查询
            Console.WriteLine("Get key {0}:{1}", strKey, dt);

            dt = reader.Get<DateTime>(strKey);
            Console.WriteLine("Generic get key {0}:{1}", strKey, dt);

            isOK = writer.Remove(strKey);
            Console.WriteLine("Removed key {0}:{1}", strKey, isOK);

            isExists = reader.isExists(strKey);
            Console.WriteLine("Key {0} exists:{1}", strKey, isExists);

            Console.WriteLine("Get key {0}:{1}", strKey, reader.Get(strKey));

            Console.WriteLine("Generic get key {0}:{1}", strKey, reader.Get<DateTime>(strKey));//default(datetime)
            Console.WriteLine();
            Console.WriteLine("===========================================");

            Console.Read();

            #endregion

            #region 类

            dtNow = DateTime.Now;
            Province province = new Province(13579, "江苏", dtNow, dtNow);

            strKey = string.Format("{0}_{1}", province.GetType().Name, province.Id);//省
            isOK = writer.Remove(strKey); //移除
            Console.WriteLine("Removed key {0}:{1}", strKey, isOK);

            writer.Add(strKey, province); //添加
            Console.WriteLine("Add key {0}, value:{1}", strKey, dtNow);

            isExists = reader.isExists(strKey);//是否存在
            Console.WriteLine("Key {0} exists:{1}", strKey, isExists);

            Province queryProvince = (Province)reader.Get(strKey);//查询
            Console.WriteLine("Get key {0}:{1}", strKey, queryProvince.ProvinceName);

            queryProvince = reader.Get<Province>(strKey);
            Console.WriteLine("Generic get key {0}:{1}", strKey, queryProvince.ProvinceName);

            isOK = writer.Remove(strKey);
            Console.WriteLine("Removed key {0}:{1}", strKey, isOK);

            isExists = reader.isExists(strKey);
            Console.WriteLine("Key {0} exists:{1}", strKey, isExists);

            Console.WriteLine("Get key {0}:{1}", strKey, reader.Get(strKey));

            Console.WriteLine("Generic get key {0}:{1}", strKey, reader.Get<Province>(strKey));
            Console.WriteLine();
            Console.WriteLine("===========================================");

            Console.Read();

            #endregion

            #region 集合(列表)

            dtNow = DateTime.Now;
            IList<City> listCities = new List<City>();
            City city = new City(135, province.Id, "南京", "210000", dtNow, dtNow);
            listCities.Add(city);
            city = new City(246, province.Id, "苏州", "215000", dtNow, dtNow);
            listCities.Add(city);

            strKey = string.Format("List_{0}_{1}_{2}", province.GetType().Name, province.Id, city.GetType().Name);//省份对应城市
            isOK = writer.Remove(strKey); //移除
            Console.WriteLine("Removed key {0}:{1}", strKey, isOK);

            writer.Add(strKey, listCities); //添加
            Console.WriteLine("Add key {0}, value:", strKey);
            foreach (var item in listCities)
            {
                Console.WriteLine("CityId:{0} CityName:{1}", item.Id, item.CityName);
            }

            isExists = reader.isExists(strKey);//是否存在
            Console.WriteLine("Key {0} exists:{1}", strKey, isExists);

            IList<City> queryCities = reader.Get(strKey) as IList<City>;//查询
            Console.WriteLine("Get key {0}:", strKey);
            foreach (var item in queryCities)
            {
                Console.WriteLine("CityId:{0} CityName:{1}", item.Id, item.CityName);
            }

            queryCities = reader.Get<IList<City>>(strKey);
            Console.WriteLine("Generic get key {0}:", strKey);
            foreach (var item in queryCities)
            {
                Console.WriteLine("CityId:{0} CityName:{1}", item.Id, item.CityName);
            }

            isOK = writer.Remove(strKey);
            Console.WriteLine("Removed key {0}:{1}", strKey, isOK);

            isExists = reader.isExists(strKey);
            Console.WriteLine("Key {0} exists:{1}", strKey, isExists);

            Console.WriteLine("Get key {0}:{1}", strKey, reader.Get(strKey));

            Console.WriteLine("Generic get key {0}:{1}", strKey, reader.Get<IList<City>>(strKey));
            Console.WriteLine();
            Console.WriteLine("===========================================");

            Console.Read();

            #endregion

            #region 集合(字典)

            dtNow = DateTime.Now;
            IDictionary<int, City> dictCities = new Dictionary<int, City>();
            city = new City(123, province.Id, "镇江", "212000", dtNow, dtNow);
            dictCities.Add(city.Id, city);
            city = new City(321, province.Id, "扬州", "225000", dtNow, dtNow);
            dictCities.Add(city.Id, city);

            strKey = string.Format("Dictionary_{0}_{1}_{2}", province.GetType().Name, province.Id, city.GetType().Name);//省份对应城市
            isOK = writer.Remove(strKey); //移除
            Console.WriteLine("Removed key {0}:{1}", strKey, isOK);

            writer.Add(strKey, dictCities); //添加
            Console.WriteLine("Add key {0}, value:", strKey);
            foreach (var item in dictCities)
            {
                Console.WriteLine("CityId:{0} CityName:{1}", item.Key, item.Value.CityName);
            }

            isExists = reader.isExists(strKey);//是否存在
            Console.WriteLine("Key {0} exists:{1}", strKey, isExists);

            IDictionary<int, City> queryDictCities = reader.Get(strKey) as IDictionary<int, City>;//查询
            Console.WriteLine("Get key {0}:", strKey);
            foreach (var item in queryDictCities)
            {
                Console.WriteLine("CityId:{0} CityName:{1}", item.Key, item.Value.CityName);
            }

            queryDictCities = reader.Get<IDictionary<int, City>>(strKey);
            Console.WriteLine("Generic get key {0}:", strKey);
            foreach (var item in queryDictCities)
            {
                Console.WriteLine("CityId:{0} CityName:{1}", item.Key, item.Value.CityName);
            }

            isOK = writer.Remove(strKey);
            Console.WriteLine("Removed key {0}:{1}", strKey, isOK);

            isExists = reader.isExists(strKey);
            Console.WriteLine("Key {0} exists:{1}", strKey, isExists);

            Console.WriteLine("Get key {0}:{1}", strKey, reader.Get(strKey));

            Console.WriteLine("Generic get key {0}:{1}", strKey, reader.Get<IDictionary<int, City>>(strKey));
            Console.WriteLine();
            Console.WriteLine("===========================================");

            Console.Read();

            #endregion

 

那边就不贴全体代码了,小说最终有示范能够下载。

在本身的回顾测试中,对科学普及基础数据类型如(字符串、数组、数字和岁月)、集合(列表和字典)都有优良的显现,对datatable和dataset同样显示不俗,然则不太提出直接缓存那二种重粒度的种类。

在显式钦赐过期时间的言传身教中,钦命过期时间是一分钟,然而memcached实际过期时间有时候好像会多于一秒钟,推断是系统里面包车型地铁延迟。

在地面电脑上拓展10万次循环添加缓存的进程中,发现系统内部存储器果然扩张的相当了得。然后查询品质并没有明白下落,大概和本人的单机测试环境有关,所以本人认为测试结果并从未说服力,要精晓,memcached的优势是它的分布式缓存完成。

有人发现怎么保管缓存系统的键唯一也相当令人脑瓜疼。同样的缓存框架,分化品类不一样开发者怎么样确保本人程序添加的缓存键唯一呢?有一种不难方法正是透过拼接字符串成为有含义的主键,比如依照项目名、命名空间、类名、数据库中的主键组合构成主键等等。当然了,在询问的时候也要协调封装特定格式的字符串主键。个人感觉确实是二个管用的法子。

demo下载:SimpleCacheApp

四 、TCP网关长连接容器架构

TCP网关的宗旨器件是Netty,而Netty的NIO模型是Reactor反应堆模型(Reactor约等于有分发功用的多路复用器Selector)。每3个老是对应三个Channel(多路指多少个Channel,复用指两个三番五次复用了二个线程或少量线程,在Netty指伊芙ntLoop),3个Channel对应唯一的ChannelPipeline,八个Handler串行的进入到Pipeline中,各样Handler关联唯一的ChannelHandlerContext。

TCP网关长连接容器的Handler正是放在Pipeline的中。我们明白TCP属于OSI的传输层,所以建立Session管理机制创设会话层来提供应用层服务,能够小幅度的降低系统复杂度。所以,每一个Channel对应贰个Connection,一个Connection又呼应二个Session,Session由Session
Manager管理,Session与Connection是逐一对应,Connection保存着ChannelHandlerContext(ChannelHanderContext能够找到Channel),Session通过心跳机制来维系Channel的Active状态。

每2遍Session的对话请求(ChannelRead)都以因此Proxy代理体制调用瑟维斯层,数据请求完结后经过写入ChannelHandlerConext再传递到Channel中。数据下行主动推送也是那样,通过Session
Manager找到Active的Session,轮询写入Session中的ChannelHandlerContext,就可以完毕广播或点对点的数目推送逻辑。如下图所示。

京麦TCP网关使用Netty
Channel实行数据通讯,使用Protobuf进行连串化和反类别化,各种请求都将被封装成Byte二进制字节流,在全部生命周期中,Channel保持长连接,而不是每一回调用都重复成立Channel,达到链接的复用。

咱俩吸收来来看看基于Netty的具体技术实施。

5、TCP网关Netty Server的IO模型

实际的贯彻进度如下:

1)创建ServerBootstrap,设定BossGroup与WorkerGroup线程池;

2)bind钦赐的port,早先侦听和收受客户端链接(尽管系统唯有3个服务端port必要监听,则BossGroup线程组线程数设置为1);

3)在ChannelPipeline注册childHandler,用来拍卖客户端链接中的请求帧。

六 、TCP网关的线程模型

TCP网关使用Netty的线程池,共三组线程池,分别为BossGroup、WorkerGroup和ExecutorGroup。个中,BossGroup用于吸收接纳客户端的TCP连接,WorkerGroup用于拍卖I/O、执行系统Task和定时职分,ExecutorGroup用于拍卖网关业务加解密、限流、路由,及将呼吁转载给后端的抓取服务等工作操作。

NioEventLoop是Netty的Reactor线程,其角色:

1)Boss
Group:作为服务端Acceptor线程,用于accept客户端链接,并转发给WorkerGroup中的线程;

2)Worker
Group:作为IO线程,负责IO的读写,从SocketChannel中读取报文或向SocketChannel写入报文;

3)Task Queue/Delay Task
Queu:作为定时职分线程,执行定时义务,例如链路空闲检查和测试和出殡和埋葬心跳音讯等。

七 、TCP网关执行时序图

如上海体育场所所示,在那之中步骤一至步骤九是Netty服务端的创始时序,步骤十至步骤十三是TCP网关容器创设的时序。

步骤一:开创ServerBootstrap实例,ServerBootstrap是Netty服务端的启航扶助类。

步骤二:设置并绑定Reactor线程池,伊夫ntLoopGroup是Netty的Reactor线程池,伊芙ntLoop负责全数注册到本线程的Channel。

步骤三:设置并绑定服务器Channel,Netty
Server供给创设NioServerSocketChannel对象。

步骤四:TCP链接建立即创设ChannelPipeline,ChannelPipeline本质上是1个负责和实施ChannelHandler的任务链。

步骤五:累加并安装ChannelHandler,ChannelHandler串行的加盟ChannelPipeline中。

步骤六:绑定监听端口并运转服务端,将NioServerSocketChannel注册到Selector上。

步骤七:Selector轮流培训,由伊芙ntLoop负责调度和推行Selector轮询操作。

步骤八:执行网络请求事件通报,轮询准备妥善的Channel,由伊芙ntLoop执行ChannelPipeline。

步骤九:进行Netty系统和作业ChannelHandler,依次调度并施行ChannelPipeline的ChannelHandler。

步骤十:通过Proxy代理调用后端服务,ChannelRead事件后,通过发出调度后端Service。

手续十一:开创Session,Session与Connection是相互正视关系。

手续十二:创建Connection,Connection保存ChannelHandlerContext。

步骤十三:添加SessionListener,SessionListener监听SessionCreate和SessionDestory等事件。

⑧ 、TCP网关源码分析

8.1 Session管理

Session是客户端与服务端建立的3次会话链接,会话信息中保留着SessionId、连接创制时间、上次访问事件,以及Connection和SessionListener,在Connection中保留了Netty的ChannelHandlerContext上下文新闻。Session会话音信会保留在SessionManager内部存款和储蓄器管理器中。

创建Session的源码:

透过源码分析,若是Session已经存在销毁Session,可是这么些需求尤其注意,创设Session一定毫无成立那3个断线重连的Channel,不然会并发Channel被误销毁的难点。因为假使在早就确立Connection(1)的Channel上,再建立Connection(2),进入session.close方法会将cxt关闭,Connection(1)和Connection(2)的Channel都将会被关闭。在断线之后再建立连接Connection(3),由于Session是有一定延迟,Connection(3)和Connection(1/2)不是同一个,但Channel大概是同3个。

于是,怎么样处理是或不是是断线重练的Channel,具体的艺术是在Channel中存入SessionId,每一次事件请求判断Channel中是或不是存在SessionId,借使Channel中存在SessionId则判断为断线重连的Channel,代码如下图所示。

8.2 心跳

心跳是用来检查和测试保持接二连三的客户端是或不是还存世着,客户端每间隔一段时间就会发送贰次心跳包上传到服务端,服务端收到心跳之后更新Session的结尾访问时间。在劳动端长连接会电话机质量检查和测试测通过轮询Session集合判断最终访问时间是否过期,假诺过期则关闭Session和Connection,蕴含将其从内部存款和储蓄器中删除,同时撤消Channel等。如下图代码所示。

经过源码分析,在各种Session创制成功之后,都会在Session中添加TcpHeartbeatListener那些心跳检查和测试的监听,TcpHeartbeatListener是一个贯彻了SessionListener接口的守护线程,通过定时休眠轮询塞申斯检查是否存在逾期的Session,假如轮训出过期的Session,则关闭Session。如下图代码所示。

与此同时,注意到session.connect方法,在connect方法中会对Session添加的Listeners实行添加时间,它会循环调用全体Listner的sessionCreated事件,在那之中TcpHeartbeatListener也是在那几个历程中被唤起。如下图代码所示。

8.3 数据上行

数据上行特指从客户端发送数据到服务端,数据从ChannelHander的channelRead方法获取数据。数据包罗创设会话、发送心跳、数据请求等。那里注意的是,channelRead的多少包罗客户端主动请求服务端的多寡,以及服务端下行公告客户端的回到数据,所以在处理object数据时,通过数量标识区分是呼吁-应答,依旧通告-回复。如下图代码所示。

8.4 数据下行

数据下行通过MQ广播机制到具有服务器,全体服务器收到新闻后,获取当前服务器所怀有的装有Session会话,举办多少广播下行通知。假设是点对点的数据推送下行,数据也是先广播到拥有服务器,每日服务器判断推送的端是或不是是当前服务器持有的对话,如若判断新闻数据中的音信是在当下服务,则开始展览推送,不然吐弃。如下图代码所示。

透过源码分析,数据下行则透过NotifyProxy的法子发送数据,须要注意的是Netty是NIO,假设下行文告要求获得重临值,则要将异步转同步,所以NotifyFuture是促成java.util.concurrent.Future的主意,通过安装超时时间,在channelRead获取到上行数据以往,通过seq来涉及NotifyFuture的章程。如下图代码所示。

下水的数目经过TcpConnector的send方法发送,send格局则是通过ChannelHandlerContext的writeAndFlush方法写入Channel,并实现多少下行,这里要求留意的是,此前有另一种写法正是cf.await,通过阻塞的章程来判定写入是不是成功,那种写法偶发出现BlockingOperationException的十分。如下图代码所示。

使用阻塞获取再次来到值的写法:

关于BlockingOperationException的题材本身在StackOverflow进行提问,极度幸运的得到了Norman
Maurer(Netty的为主进献者之一)的解答:

最后敲定大概分析出,在执行write方法时,Netty会判断current
thread是不是正是分给该Channe的伊夫ntLoop,假设是则行线程执行IO操作,不然提交executor等待分配。当执行await方法时,会从executor里fetch出执行线程,那里就必要checkDeadLock,判断执行线程和current
threads是或不是时同八个线程,借使是就检查和测试为死锁抛出十三分BlockingOperationException。

九 、本文小结

本篇小说粗浅的向我们介绍了京麦TCP网关中使用的Netty达成长连接容器的框架结构,涉及TCP长连接容器搭建的主要点一一实行掌握说,以及对源码进行简要的解析。在京麦发展进度里Netty还有不少的推行应用,例如Netty4.11+HTTP2达成APNs的音信推送等等。

(本文同步公布于:http://www.52im.net/thread-1243-1-1.html

附录:越来越多精编资料汇聚

[1] 网络编制程序基础材质:

TCP/IP详解第②1章·UDP:用户数据报业协会议

TCP/IP详解第一7章·TCP:传输控制协议

TCP/IP详解第③8章·TCP连接的确立与终止

TCP/IP详解第③1章·TCP的逾期与重传

技术往事:改变世界的TCP/IP协议(保养多图、手机慎点)

深刻浅出易懂-深切明白TCP协议(上):理论功底

深远浅出易懂-深刻精晓TCP协议(下):奥迪Q3TT、滑动窗口、拥挤堵塞处理

力排众议经典:TCP协议的三次握手与五次挥手进程详解

反驳联系实际:Wireshark抓包分析TCP
3遍握手、伍遍挥手进度

总结机网络通信协议提到图(中文珍藏版)

UDP中一个包的高低最大能多大?

P2P技术详解(一):NAT详解——详细原理、P2P简介

P2P技术详解(二):P2P中的NAT穿越(打洞)方案详解

P2P技术详解(三):P2P技术之STUN、TUSportageN、ICE详解

通俗易懂:火速通晓P2P技术中的NAT穿透原理

高性能网络编程(一):单台服务器并发TCP连接数到底能够有稍许

高质量互联网编制程序(二):上三个10年,盛名的C10K并发连接难点

高品质网络编制程序(三):下2个10年,是时候考虑C10M油然则生难题了

高品质网络编制程序(四):从C10K到C10M高品质网络利用的论争探索

不解的互连网编制程序(一):浅析TCP协议中的疑难杂症(上篇)

不解的互联网编制程序(二):浅析TCP协议中的疑难杂症(下篇)

不解的互联网编制程序(三):关闭TCP连接时怎么会TIME_WAIT、CLOSE_WAIT

不解的互连网编制程序(四):浓厚商量分析TCP的可怜关闭

不解的互连网编制程序(五):UDP的连接性和负载均衡

不解的网络编制程序(六):深入地领会UDP协议并用好它

互联网编制程序懒人入门(一):飞速明白网络通信协议(上篇)

互联网编制程序懒人入门(二):连忙明白互连网通讯协议(下篇)

互连网编程懒人入门(三):飞快领悟TCP协议一篇就够

网络编制程序懒人入门(四):火速精晓TCP和UDP的出入

Netty干货分享:京东京麦的生产级TCP网关技术实施总括

>>更加多同类小说……

[2] NIO异步网络编制程序资料:

Java新一代互连网编制程序模型AIO原理及Linux系统AIO介绍

关于“为啥选取Netty”的13个问号及解答

开源NIO框架八卦——到底是先有MINA还是先有Netty?

选Netty照旧米纳:长远钻研与相比(一)

选Netty照旧米纳:深远商量与对待(二)

NIO框架入门(一):服务端基于Netty4的UDP双向通讯德姆o演示

NIO框架入门(二):服务端基于MINA2的UDP双向通讯德姆o演示

NIO框架入门(三):iOS与MINA② 、Netty4的跨平台UDP双向通讯实战

NIO框架入门(四):Android与MINA二 、Netty4的跨平台UDP双向通讯实战

Netty
4.x学习(一):ByteBuf详解

Netty
4.x学习(二):Channel和Pipeline详解

Netty
4.x学习(三):线程模型详解

Apache
Mina框架高级篇(一):IoFilter详解

Apache
米纳框架高级篇(二):IoHandler详解

MINA2
线程原理计算(含简单测试实例)

Apache MINA2.0
开发指南(普通话版)[附属类小部件下载]

MINA、Netty的源代码(在线阅读版)已整理公布

消除MINA数据传输中TCP的粘包、缺包难题(有源码)

缓解Mina中多少个同种类Filter实例共存的难题

履行总括:Netty3.x升级Netty4.x境遇的那么些坑(线程篇)

推行总计:Netty3.x VS
Netty4.x的线程模型

详解Netty的安全性:原理介绍、代码演示(上篇)

详解Netty的安全性:原理介绍、代码演示(下篇)

详解Netty的优雅退出机制和规律

NIO框架详解:Netty的高品质之道

照片墙:怎样行使Netty
4来压缩JVM的GC费用(译文)

纯属干货:基于Netty完毕海量接入的推送服务技巧中央

Netty干货分享:京东京麦的生产级TCP网关技术实施总计

>>越多同类小说……

[3] 有关IM/推送的通讯格式、协议的选拔:

简述传输层协议TCP和UDP的不同

何以QQ用的是UDP磋商而不是TCP协议?

移步端即时通信协议选取:UDP依旧TCP?

怎么样抉择即时通信应用的数目传输格式

强列建议将Protobuf作为你的即时通信应用数据传输格式

成套评测:Protobuf质量到底有没有比JSON快5倍?

一举手一投足端IM开发须要直面包车型地铁技能难题(含通讯协议选用)

简述移动端IM开发的那2个坑:架构划设想计、通讯协议和客户端

辩解联系实际:一套典型的IM通讯协议设计详解

58到家实时新闻系统的协议布署等技术实施分享

详解怎么样在NodeJS中采用谷歌(Google)的Protobuf

>>越多同类文章……

[4] 有关IM/推送的心跳保活处理:

动用保活终极总计(一):Android6.0以下的双历程守护保活实践

利用保活终极总括(二):Android6.0及以上的保活实践(进度防杀篇)

动用保活终极总计(三):Android6.0及以上的保活实践(被杀复活篇)

Android进度保活详解:一篇小说消除你的全部疑点

Android端音信推送总计:完成原理、心跳保活、际遇的题材等

浓厚的聊聊Android音信推送那件麻烦事

缘何基于TCP协议的活动端IM依然必要心跳保活机制?

微信团队原创分享:Android版微信后台保活实战分享(进度保活篇)

微信团队原创分享:Android版微信后台保活实战分享(互联网保活篇)

挪动端IM实践:完成Android版微信的智能心跳机制

运动端IM实践:WhatsApp、Line、微信的心跳策略分析

>>愈来愈多同类文章……

[5] 有关WEB端即时通信开发:

新手入门贴:史上最全Web端即时通信技术原理详解

Web端即时通信技术盘点:短轮询、Comet、Websocket、SSE

SSE技术详解:一种全新的HTML5服务器推送事件技术

Comet技术详解:基于HTTP长连接的Web端实时通讯技术

新手急忙入门:WebSocket简明教程

WebSocket详解(一):起初认识WebSocket技术

WebSocket详解(二):技术原理、代码演示和利用案例

WebSocket详解(三):长远WebSocket通讯协议细节

socket.io完结新闻推送的有些履行及思路

LinkedIn的Web端即时通讯实践:实现单机几80000条长连接

Web端即时通信技术的升华与WebSocket、Socket.io的技能实施

Web端即时通信安全:跨站点WebSocket恐吓漏洞详解(含示例代码)

开源框架Pomelo实践:搭建Web端高品质分布式IM聊天服务器

应用WebSocket和SSE技术达成Web端音讯推送

详解Web端通讯方式的变异:从Ajax、JSONP 到
SSE、Websocket

>>越来越多同类文章……

[6] 有关IM架构划设想计:

浅谈IM系统的架构划设想计

简述移动端IM开发的那多少个坑:框架结构划设想计、通讯协议和客户端

一套海量在线用户的位移端IM架构划设想计实践分享(含详细图像和文字)

一套原创分布式即时通信(IM)系统理论架构方案

从零到优异:京东客服即时报导系统的技艺框架结构演进历程

蘑菇街即时通信/IM服务器开发之架构选择

腾讯QQ1.4亿在线用户的技术挑衅和架构演进之路PPT

微信后台基于时间序的雅量数据冷热分级架构划设想计实践

微信技术CEO谈架构:微信之道——大道至简(演讲全文)

何以解读《微信技术主管谈架构:微信之道——大道至简》

高效裂变:见证微信强大后台架构从0到1的演进历程(一)

17年的执行:腾讯海量产品的技能方法论

移步端IM中常见群消息的推送怎么样保管成效、实时性?

现代IM系统中聊天音信的一路和仓库储存方案研商

>>更加多同类文章……

[7] 有关IM安全的小说:

即时通信安全篇(一):正确地精晓和行使Android端加密算法

即时通信安全篇(二):研商组合加密算法在IM中的应用

即时通信安全篇(三):常用加解密算法与报导安全讲解

即时通讯安全篇(四):实例分析Android中密钥硬编码的高风险

即时通信安全篇(五):对称加密技术在Android平台上的接纳实践

即时通信安全篇(六):非对称加密技术的规律与应用实践

传输层安全磋商SSL/TLS的Java平台实现简介和德姆o演示

辩论联系实际:一套典型的IM通讯协议设计详解(含安全层设计)

微信新一代通讯安全解决方案:基于TLS1.3的MMTLS详解

起点AliOpenIM:构建安全可信赖即时通信服务的技艺实施分享

简述实时音录制聊天中端到端加密(E2EE)的劳作规律

移动端安全通讯的利器——端到端加密(E2EE)技术详解

Web端即时通信安全:跨站点WebSocket胁迫漏洞详解(含示例代码)

通俗易懂:一篇通晓即时通信的音信传输安全原理

>>越多同类文章……

[8] 有关实时音录像开发:

专访微信录像技术官员:微信实时录制聊天技术的变异

即时通信音录制开发(一):录制编解码之理论概述

即时通讯音录像开发(二):录像编解码之数字录制介绍

即时通讯音摄像开发(三):摄像编解码之编码基础

即时通信音录像开发(四):摄像编解码之预测技术介绍

即时通信音录像开发(五):认识主流录制编码技术H.264

即时通信音摄像开发(六):怎样开首音频编解码技术的学习

即时通讯音录制开发(七):音频基础及编码原理入门

即时通信音录制开发(八):常见的实时语音通信编码标准

即时通信音录制开发(九):实时语音通讯的回信及回音消除�概述

即时通信音录制开发(十):实时语音通信的复信解决�技术详解

即时通信音摄像开发(十一):实时语音通信丢包补偿技术详解

即时通信音摄像开发(十二):三个人实时音录制聊天架构探究

即时通信音录制开发(十三):实时录制编码H.264的特征与优势

即时通讯音摄像开发(十四):实时音摄像数据传输协议介绍

即时通信音录制开发(十五):聊聊P2P与实时音摄像的运用境况

即时通信音录制开发(十六):移动端实时音摄像开发的多少个提出

即时通讯音摄像开发(十七):录制编码H.26④ 、VP8的前生今生

实时语音聊天中的音频处理与编码压缩技术简述

乐乎摄像云技术分享:音频处理与减弱技术连忙入门

学习本田CR-VFC3550:奥迪Q5TP/GL450TCP实时传输协议基础知识

简述开源实时音录像技术WebKoleosTC的优缺点

良心分享:Web昂CoraTC
零基础开发者教程(汉语)

开源实时音摄像技术WebWranglerTC中CRUISERTP/TiggoTCP数据传输协议的运用

据说OdysseyTMP数据传输协议的实时代时尚媒体技术钻探(杂谈全文)

声网架构师谈实时音摄像云的落到实处困难(录像采访)

浅谈开发实时录制直播平台的技艺中央

还在靠“喂喂喂”测试实时语音通话质量?本文化教育您正确的估测方法!

贯彻延迟低于500微秒的1080P实时音视频直播的进行分享

一举手一投足端实时摄像直播技术实施:如何做到实时秒开、流畅不卡

何以用最简便易行的措施测试你的实时音摄像方案

技术揭秘:协助百万级客官互动的推特实时录像直播

简述实时音录像聊天中端到端加密(E2EE)的劳作规律

移动端实时音摄像直播技术详解(一):开篇

挪动端实时音录像直播技术详解(二):采集

移动端实时音录像直播技术详解(三):处理

活动端实时音录像直播技术详解(四):编码和包裹

移步端实时音录制直播技术详解(五):推流和传导

活动端实时音录制直播技术详解(六):延迟优化

力排众议联系实际:达成一个简练地基于HTML5的实时录像直播

IM实时音录制聊天时的回声解决技术详解

浅谈实时音摄像直播中央直机关接影响用户体验的几项关键技术目的

什么优化传输体制来贯彻实时音录像的超低延迟?

第②回表露:快手是哪些做到百万观者同场看直播还可以秒开且不卡顿的?

实时通讯大切诺基TC技术栈之:录像编解码

开源实时音摄像技术Web本田CR-VTC在Windows下的斐然编写翻译教程

Android直播入门实践:入手搭建一套简单的直播系统

>>更加多同类小说……

[9] IM开发综合小说:

移步端IM湖北中国广播集团泛群音信的推送怎么着保管效用、实时性?

挪动端IM开发要求直面包车型大巴技术难题

付出IM是投机设计协议用字节流好照旧字符流好?

请问有人明白语音留言聊天的主流完毕格局啊?

IM音讯送达保证编写制定完毕(一):保障在线实时音信的保险投递

IM音讯送达有限支撑编写制定落实(二):保障离线音讯的笃定投递

什么保管IM实时音讯的“时序性”与“一致性”?

二个低本钱确定保证IM新闻时序的艺术切磋

IM单聊和群聊中的在线状态同步应该用“推”依然“拉”?

IM群聊音讯如此复杂,怎么着确定保证不丢不重?

探究移动端 IM
开发中登录请求的优化

运动端IM登录时拉取数据怎么样作到省流量?

浅谈移动端IM的多点登陆和音信漫游原理

全然自已支出的IM该怎么样规划“战败重试”机制?

通俗易懂:基于集群的移动端IM接入层负载均衡方案分享

微信对互连网影响的技艺试验及分析(故事集全文)

即时通信系统的法则、技术和采取(技术散文)

开源IM工程“蘑菇街TeamTalk”的现状:一场半途而废的开源秀

QQ音乐公司分享:Android中的图片压缩技术详解(上篇)

QQ音乐团队分享:Android中的图片压缩技术详解(下篇)

腾讯原创分享(一):怎样大幅度提高活动网络出手机QQ的图形传输速度和成功率

腾讯原创分享(二):如何大幅度减小移动网络下APP的流量消耗(上篇)

腾讯原创分享(二):怎样小幅削减移动互联网下APP的流量消耗(下篇)

根据而至:微信自用的位移端IM网络层跨平台组件库马尔斯已正式开源

依照社交互联网的Yelp是什么贯彻海量用户图片的无损压缩的?

>>越多同类作品……

[10] 开源移动端IM技术框架质感:

开源移动端IM技术框架MobileIMSDK:急忙入门

开源移动端IM技术框架MobileIMSDK:常见难点解答

开源移动端IM技术框架MobileIMSDK:压力测试报告

>>越多同类作品……

[11] 有关推送技术的作品:

iOS的推送服务APNs详解:设计思路、技术原理及缺陷等

信鸽团队原创:一起度过 iOS10
上音讯推送(APNS)的坑

Android端音信推送计算:实现原理、心跳保活、遭逢的标题等

扫盲贴:认识MQTT通讯协议

1个依照MQTT通讯协议的一体化Android推送德姆o

IBM技术老董访谈:MQTT协议的制订进程、发呈现状等

求教android音信推送:GCM、XMPP、MQTT三种方案的优劣

移步端实时音讯推送技术分析

扫除文盲贴:浅谈iOS和Android后台实时新闻推送的规律和区分

纯属干货:基于Netty完毕海量接入的推送服务技能主旨

运动端IM实践:谷歌(Google)消息推送服务(GCM)钻探(来自微信)

缘何微信、QQ这样的IM工具不使用GCM服务推送音信?

极光推送系统广大高并发架构的技能实施分享

从HTTP到MQTT:一个基于地点服务的APP数据通讯实践概述

One plus2500万长连接的实时新闻推送架构的技能实施分享

专访Samsung架构师:海量长连接的实时音信推送系统的心体面会

深深的聊聊Android消息推送那件麻烦事

基于WebSocket完毕Hybrid移动选用的音讯推送实践(含代码示例)

贰个依据长连接的安全可扩张的订阅/推送服务完成思路

推行分享:怎样创设一套高可用的位移端新闻推送系统?

Go语言创设千万级在线的高并发消息推送系统执行(来自360商行)

腾讯信鸽技术分享:百亿级实时音讯推送的实战经验

百万在线的美拍直播弹幕系统的实时推送技术实施之路

>>越多同类小说……

[12] 更加多即时通信技术好文分类:

http://www.52im.net/forum.php?mod=collection&op=all

(本文同步发表于:http://www.52im.net/thread-1243-1-1.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注