mina中Half Sync/Half Async模式

 
在解释Half Sync/Half Async模式之前,先介绍一个亲身经历的项目。曾经使用一个通讯支撑模块EMF,该模块完成了底层的socket通讯功能,和外部应用建立长连接,同时为上层应用提供一个回调接口如下:

Java代码
public interface Hook {
void call(Message msg);
}
public interface Hook {
          void call(Message msg);
}上层应用可以根据自己业务逻辑的需要,实现该接口。
Java代码
class MyHook implements Hook{
public void callback(Message msg){
//应用的业务逻辑
}


class MyHook implements Hook{
          public void callback(Message msg){
                    //应用的业务逻辑
          }

}然后应用可以将该实现注册到通讯支撑模块中。
Java代码
//注册应用的回调实现
EmfHook hook = new MyHook();
EmfService.regist(hook);
//注册应用的回调实现
EmfHook hook = new MyHook();
EmfService.regist(hook);在EMF的实现中负责将受到的socket数据拼装成应用需要的消息结构,然后在一定的匹配规则,找到一个应用的Hook,回调之(其实是多个hook,有可能多个应用对同一种消息感兴趣,简化之)。
Java代码
//从socket上收到数据,
Byte[] buf=…..;

//将数据解析成应用的消息结构
Message msg=parse(buf);

//根据消息内容查找相应的EmfHook
EmfHook  hook = findHook(msg);

//调用应用的实现
hook.callback(msg);
//从socket上收到数据,
Byte[] buf=.....;

//将数据解析成应用的消息结构
Message msg=parse(buf);

//根据消息内容查找相应的EmfHook
EmfHook  hook = findHook(msg);

//调用应用的实现
hook.callback(msg);一开始,这个模块运行的很好,性能不错,应用扩展也很方便,大家都很是满意,看来这个月的kpi一定不错,呵呵。然有一天,在做压力测试的时候发现,性能很差了,好久才能处理一条消息,数据吞吐量非常小,cpu却也不忙。这是怎么回事哪?
后来发现问题在于EMF的socket数据读取并解析工作和上层应用的hook操作是在一个线程中的,而某些应用的Hook实现有一些比较耗时操作,而所以导致在执行上层应用操作的时候,EMF并没有去进行I/O操作,读取数据,整个系统停在哪儿了。
最后稍作改动,将hook.callback(data)放在另外一个线程池中,这样EMF的线程和应用逻辑的线程就不再相互干扰,性能大大的提供。
从这个项目中得到的一个教训是:I/O密集操作的线程应该和业务逻辑的线程尽量分开。
再后来看POSA2,发现这不就是一个Half Sync/Half Async模式吗?
POSA2中写道
引用
半同步半异步(Half Sync/Half Async)体系结构模式将并发系统中的异步和同步服务分开,简化了编程,同时又没有降低性能。
通俗一点说,Half Sync/Half Async模式其实就是将异步请求排队到一个同步队列中,然后再从队列中取出请求处理,其实就是一个扩大的生产者/消费者问题。在socket编程的关键 在于,将业务逻辑操作和底层的io操作分散到不同的线程中,避免业务逻辑操作可能导致整个线程的堵塞。
在mina中也存在Half Sync/Half Async模式,在默认情况下,mina的业务逻辑处理接口IoHandler的实现类就是在NioProcessor 的worker现在中执行的(NioProcessor的执行机制请参考)。如果业务逻辑接口堵塞或者耗时都将导致NioProcessor线程无法充分 利用。
ExecutorFilter
Java代码
IoAcceptor acceptor = new NioSocketAcceptor();

acceptor.getFilterChain().addLast( “logger”, new LoggingFilter() );
acceptor.getFilterChain().addLast( “codec”, new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( “UTF-8″ ))));
acceptor.getFilterChain().addLast(“executor”, new ExecutorFilter());

acceptor.setHandler(  new TimeServerHandler() );

acceptor.getSessionConfig().setReadBufferSize( 2048 );
acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );

acceptor.bind( new InetSocketAddress(PORT) );
        IoAcceptor acceptor = new NioSocketAcceptor();

        acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
        acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
        acceptor.getFilterChain().addLast("executor", new ExecutorFilter());

        acceptor.setHandler(  new TimeServerHandler() );

        acceptor.getSessionConfig().setReadBufferSize( 2048 );
        acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );

        acceptor.bind( new InetSocketAddress(PORT) );如上述代码,通过在FilterChain中增加一个ExecutorFilter,mina将NioProcessor的IO操作线程和 TimeServerHandler的业务处理线程分开了。在上述例子中,当NioProcessor收到socket上的数据,filter和 handler的执行顺序为:logger->codec->executor->TimeServerHandler(有关 IoFilter内容具体请参考http://uniseraph.javaeye.com/blog/228194)。
其中logger->codec就在NioProcessor的工作者线程worker中,而executor和TimeServerHandler则是在executorFilter的线程池中。具体参考代码如下:
Java代码
private ExecutorFilter(Executor executor, boolean createdExecutor, IoEventType… eventTypes) {
if (executor == null) {
throw new NullPointerException(“executor”);
}/*这里将evenType初始化为所有事件类型的集合*/
if (eventTypes == null || eventTypes.length == 0) {
eventTypes = new IoEventType[] { IoEventType.EXCEPTION_CAUGHT,
IoEventType.MESSAGE_RECEIVED, IoEventType.MESSAGE_SENT,
IoEventType.SESSION_CLOSED, IoEventType.SESSION_IDLE,
IoEventType.SESSION_OPENED, };
}
//下面省略
}
public final void messageReceived(NextFilter nextFilter, IoSession session,
Object message) {
/*executorFilter对该事件有效*/ if (eventTypes.contains(IoEventType.MESSAGE_RECEIVED)) {
fireEvent(new IoFilterEvent(nextFilter,
IoEventType.MESSAGE_RECEIVED, session, message));
} else {
nextFilter.messageReceived(session, message);
}
}
protected void fireEvent(IoFilterEvent event) {
getExecutor().execute(event);
}
 private ExecutorFilter(Executor executor, boolean createdExecutor, IoEventType... eventTypes) {
        if (executor == null) {
            throw new NullPointerException("executor");
        }/*这里将evenType初始化为所有事件类型的集合*/
        if (eventTypes == null || eventTypes.length == 0) {
            eventTypes = new IoEventType[] { IoEventType.EXCEPTION_CAUGHT,
                    IoEventType.MESSAGE_RECEIVED, IoEventType.MESSAGE_SENT,
                    IoEventType.SESSION_CLOSED, IoEventType.SESSION_IDLE,
                    IoEventType.SESSION_OPENED, };
        }
        //下面省略
    }
    public final void messageReceived(NextFilter nextFilter, IoSession session,
            Object message) {
    /*executorFilter对该事件有效*/   if (eventTypes.contains(IoEventType.MESSAGE_RECEIVED)) {
            fireEvent(new IoFilterEvent(nextFilter,
                    IoEventType.MESSAGE_RECEIVED, session, message));
        } else {
            nextFilter.messageReceived(session, message);
        }
    }
    protected void fireEvent(IoFilterEvent event) {
        getExecutor().execute(event);
    }由上可知,如果executorFilter对某个事件有效,那么将在线程池中执行该事件,如果无效则在原有的NioProcessor.Worker线程中执行下一个IoFilter或者IoHandler。
当然也可以不用ExecutorFilter,而是在IoHandler的实现类中放一个线程池,但是多数情况下是没有这样的必要。
本文来源于 冰山上的播客 http://xinsync.xju.edu.cn , 原文地址:http://xinsync.xju.edu.cn/index.php/archives/2329

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值