- 浏览: 192842 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
dabing69221:
感谢
linux下安装和配置memcached,以及java操作的示例代码 -
dabing69221:
楼主,写的不错,学习了
linux下安装和配置memcached,以及java操作的示例代码 -
jpsb:
...
Eclipse 保存文件时自动格式化代码 -
lwj1113:
非常感谢
通过WSC的工具来生成SalesForce的WSDL文件描述对象和方法 -
alajl:
写的好,分析过程也能详尽,对于我碰到的问题,很有建设性的帮助
Java NIO类库Selector机制解析(Too many open files 和 No buffer space available)
项目背景介绍
系统发展遇到的瓶颈问题
目前主流网站都是由开源软件构建的。使用 Nginx 做为 Web 服务器,Tomcat/Resin 做 App 容器,Memcached 做通用 Cache,MySQL 做数据库,使用 Linux 操作系统。网站系统刚上线初期,用户数并不多,所有的模块都整合一个系统中,所有业务由一个应用提供,此时采取将全部的逻辑都放在一个应用的方式利于系统的维护和管理。但是,随着网站用户的不断增加,系统的访问压力越来越大,为了满足越来越多用户的需求,原有的系统需要增加新的功能进来,随着系统功能模块的增多,系统就会变得越来越难以维护和扩展,同时系统伸缩性和可用性也会受到影响。例如一个网站初期只有用户服务功能,随着网站发展,可能会需要用户信息中心、充值支付中心、商户服务中心等越来越多的子系统,如果把这些子系统都整合在原有的系统中,整个网站将会变得非常复杂,并且难以维护。另外,由于所有子系统都整合在一起,只要有一个模块出问题,那么所有的功能都会受影响,造成非常严重的后果。所以系统发展遇到的瓶颈就是随着系统的发展,如果所有模块都整合在一起,系统的可伸缩性和扩展性将受到影响。
如何解决系统发展遇到的瓶颈问题
遇到以上瓶颈该如何解决呢?明智的办法就是系统拆分,将系统根据一定的标准,比如业务相关性等拆分为不同的子系统, 不同的子系统负责不同的业务功。拆分完成后,每个子系统单独进行扩展和维护,不会影响其他子系统,从而大大提高整个网站系统的扩展性和可维护性,同时系统的水平伸缩性也大大提升了。对于压力比较大的子系统可以再进行扩展而不影响其他子系统,如果某个子系统出现问题也不会影响其他服务。从而增强了整个网站系统的健壮性,更有利于保障核心业务。因此一个大型的互联网应用,肯定是要经过系统拆分的,因为只有进行拆分,系统的扩展性、维护性、伸缩性、可用性才会变得更好。但是拆分也会给系统带来问题,就是子系统之间如何通信。本文介绍 MINA2 就是用来充当消息中间件解决各子系统之间的通信问题
MINA2 的原理及主要功能
MINA2 简介
MINA2 是一个网络通信应用框架,它主要用于基于 TCP/IP、UDP/IP 协议栈的通信框架,也可以提供 Java 对象的序列化服务、虚拟机管道通信服务等。MINA2 可以帮助我们快速开发高性能、高扩展性的网络通信应用。MINA2 提供了事件驱动、异步(MINA2 的异步 IO 默认使用的是 Java NIO 作为底层支持)操作的编程模型。
MINA2 同时提供了网络通信的 Server 端、Client 端的封装,无论是哪端,MINA2 在整个网络通信结构中都处于如下的位置:
图 1.MINA2 在网络通信中的作用图
可见 MINA2 的 API 将真正的网络通信与我们的应用程序隔离开来,你只需要关心你要发送、接收的数据以及你的业务逻辑即可。同样的,无论是哪端,MINA2 的执行流程如下所示:
图 2.MINA2 执行流程图
MINA2 通信原理
异步 IO 模型
异步 I/O 模型大体上可以分为两种,反应式 (Reactive) 模型和前摄式 (Proactive) 模型:
1. 传统的 select / epoll / kqueue 模型,以及 Java NIO 模型,都是典型的反应式模型,即应用代码对 I/O 描述符进行注册,然后等待 I/O 事件。当某个或某些 I/O 描述符所对应的 I/O 设备上产生 I/O 事件(可读、可写、异常等)时,系统将发出通知,于是应用便有机会进行 I/O 操作并避免阻塞。由于在反应式模型中应用代码需要根据相应的事件类型采取不同的动作,最常见的结构便是嵌套的 if {...} else {...} 或 switch ,并常常需要结合状态机来完成复杂的逻辑。
2. 前摄式模型则不同。在前摄式模型中,应用代码主动地投递异步操作而不管 I/O 设备当前是否可读或可写。投递的异步 I/O 操作被系统接管,应用代码也并不阻塞在该操作上,而是指定一个回调函数并继续自己的应用逻辑。当该异步操作完成时,系统将发起通知并调用应用代码指定的回调函数。在前摄式模型中,程序逻辑由各个回调函数串联起来:异步操作 A 的回调发起异步操作 B ,B 的回调再发起异步操作 C ,以此往复。 MINA2 便是一个前摄式的异步 I/O 框架。
MINA2 基本概念
I/O服务:I/O 服务用来执行实际的 I/O 操作。MINA2 已经提供了一系列支持不同协议的 I/O 服务,如 TCP/IP、UDP/IP、串口和虚拟机内部的管道等。开发人员也可以实现自己的 I/O 服务。由于 I/O 服务执行的是输入和输出两种操作,实际上有两种具体的子类型。一种称为“I/O 接受器(I/O acceptor)”,用来接受连接,一般用在服务器的实现中;另外一种称为“I/O 连接器(I/O connector)”,用来发起连接,一般用在客户端的实现中。对应在 MINA2 中的实现,org.apache.mina.core.service.IoService 是 I/O 服务的接口,而继承自它的接口 org.apache.mina.core.service.IoAcceptor 和 org.apache.mina.core.service.IoConnector 则分别表示 I/O 接受器和 I/O 连接器。
I/O 接受器:I/O 接受器用来接受连接,与对等体(客户端)进行通讯,并发出相应的 I/O 事件交给 I/O 处理器来处理。使用 I/O 接受器的时候,只需要调用 bind 方法并指定要监听的套接字地址。当不再接受连接的时候,调用 unbind 停止监听即可。
I/O 连接器:I/O 连接器用来发起连接,与对等体(服务器)进行通讯,并发出相应的 I/O 事件交给 I/O 处理器来处理。使用 I/O 连接器的时候,只需要调用 connect 方法连接指定的套接字地址。另外可以通过 setConnectTimeoutMillis 设置连接超时时间(毫秒数)。
I/O 会话:I/O 会话表示一个活动的网络连接,与所使用的传输方式无关。I/O 会话可以用来存储用户自定义的与应用相关的属性。这些属性通常用来保存应用的状态信息,还可以用来在 I/O 过滤器和 I/O 处理器之间交换数据。I/O 会话在作用上类似于 Servlet 规范中的 HTTP 会话。
I/O过滤器:I/O 服务能够传输的是字节流,而上层应用需要的是特定的对象与数据结构。I/O 过滤器用来完成这两者之间的转换。I/O 过滤器的另外一个重要作用是对输入输出的数据进行处理,满足横切的需求。多个 I/O 过滤器串联起来,形成 I/O 过滤器链。每个过滤器都可对通过的数据进行任意的操作,包括增加、删除、更新、类型转换等。先装上的过滤器更靠近远程端点 ( 客户端),后装上的更靠近本地端点 ( 服务器)。
I/O处理器:I/O 事件通过过滤器链之后会到达 I/O 处理器。I/O 处理器中与 I/O 事件对应的方法会被调用。MINA2 中 org.apache.mina.core.service.IoHandler 是 I/O 处理器要实现的接口,一般情况下,只需要继承自 org.apache.mina.core.service.IoHandlerAdapter 并覆写所需方法即可。
MINA2 就是用来充当消息中间件解决各子系统之间通信的问题。在每个子系统增加 MINA2 的客户端和服务端,负责接收和发送 Mina 消息,调用其他子系统的业务功能和数据。
MINA2 实现消息中间件
MINA2 在系统功能拆分中的作用
基于 MINA2 消息中间件的系统架构如下所示
图 3. 系统架构
以某业务运营平台拆分后的系统架构为例,整个平台包含三个子系统:业务运营子系统,负责提供用户服务;用户社区子系统是类似于 SNS 用户交互平台;用户子系统是用户账户、个人信息的子系统,是整个平台的公共基础系统。整个平台的最顶层是 web 服务器层,包含数台 Nginx 服务器(根据业务流量确定)。web 服务器层负责把不同的请求分发到对应的子系统,平台服务相关请求分发到业务运营子系统,用户社区动态信息相关请求分发到用户社区子系统,用户个人账户相关相关请求分发到用户子系统。三个子系统的 APP 服务器都是由一定数量的 Tomcat 服务器组成,一般情况下,运行 Spring+Struts2+Hibernate 程序的 tomcat 服务器能够支持 130-150 个并发请求。APP 服务器把大量数据缓存在 Memcache 服务器。另外通过 DB-Proxy 实现主从数据库分离和集群。每个子系统都有一个消息中间件层,即 MINA 服务器,通过 DB-Proxy 与本子系统的数据库进行交互,消息中间件层都包括 MINA 客户端和 MINA 服务端用于接收和发送 MINA 消息。
图 4. 某业务运营平台拆分后的系统架构图
MINA2 实现服务器端程序开发
建立 IOListener 类继承 IOHandlerAdapter。IoHandlerAdapter 类实现了 IoHandler 接口要求的方法,但是都没有任何业务逻辑处理。如果要编写 Handler 时,可以扩展 IoHandlerAdapter,重写需要的事件方法即可。一般情况,我们比较关心接收到请求消息这个事件,那么我们就可以覆盖 messageReceived 方法,不用管其他方法。 程序清单如下:
清单 1. 创建 IOListener 类
重写 messageReceived 方法。messageReceived 由 IoHandler 接口声明。IoHandler 封装了来自客户端不同事件的处理,如果对某个事件感兴趣,可以实现相应的方法,当该事件发生时,IoHandler 中的方法就会被触发执行。
当接收到新的消息的时候,该方法就会被调用。此处的逻辑是如果传入 invoke_class 是 aService、,则通过反射机制调用 aService 的 invoke_method 方法,并把结果通过 session.write 发送回去。如果消息参数中包含"return_method"值 , 则直接把 service 返回结果回写给 session,由 session 通知客户端调用"return_method"对应的方法。程序清单如下:
清单 2 消息处理方法
MINA2 与 Spring 集成的配置文件,其中 mainHandler 是处理器。
声明 IO 过滤器包括:
executorFilter:MINA 可以通过 ExecutorFilter 将 IO 线程与业务处理线程分开。
textCodecFilter:ProtocolCodecFilter 用来在字节流和消息对象之间互相转换。
loggingFilter:记录所有 MINA 的协议事件。由于该过滤器只是实现了 MINA 事件的简单记录,实际作用不大,可配合 log4j 等日志框架一起使用。
filterChainBuilder:用来构建过滤器链。
清单 3 Spring 配置 MINA2
MINA2 实现客户端程序
建立 ProcessHandler 类继承 IOHandlerAdapter。无论客户端还是服务端,都需要创建继承自 IOHandlerAdapter 的 hanlder 类。清单如下:
清单 4 建立 ProcessHandler 类
由于消息中间件需要处理较高并发的请求,所以一般使用 MINA2 服务器集群。在程序清 1 中,定义了一个 String 数组 HOSTS 保存所有集群的 MINA2 服务器 IP,在初始化 ProcessHandler 对象实例的时候通过 selectServer()方法为本次连接选择一个 MINA2 服务器 IP。
与 Server 端 Handler 类不同的是,需要开发者重写相应的连接方法,已建立与 Server 端的连接。为满足需要实现了三种连接方式:
Client 不等待返回结果的连接方式,适合传递数据进行更新插入的请求,并返回连接建立后创建的会话,方便在同一个 Action 方法里重用。代码中的 IoSession connect() 就是这种连接方式。
清单 5.connect 方法
创建连接并等待返回结果后关闭连接和会话。适合从 Server 端请求对象的操作。connectWithClose(boolean waitingFlag) 方法连接根据 waitinFlag 是否为真判断是否等待返回结果,并关闭连接。
清单 6 connectWithClosure 方法
续用已创建的会话,并等待返回结果后关闭连接和会话。适合从 Server 端请求对象的操作。connectWithClose(IoSession session,Map<String, String> map, boolean waitingFlag) 方法复用 IoSession,根据 waitingFlag 是否为真判断是否等待返回结果,并关闭连接。
清单 7 connectWithClosure 重写方法
重写请求结果回调方法,在请求返回结果后,通过反射调用对应方法。
清单 8.messageReceived 方法
MINA2 调用方法如下:
清单 9 MINA2 调用方法
以上代码的含义是:创建 ProcessHandler 实例,调 MINA2 服务器的 userFeedService 的 findUserFeed 方法, 并在接收到处理结果后直接调用本对象的 setFeedList 把结果赋给 feedList。
总结与展望
本文首先提出了大型 Web 系统在发展过程中遇到的瓶颈—系统扩展性和维护性越来越困难,只有系统拆分才能突破这个瓶颈,而 MINA2 正是作为消息中间件解决系统拆分后的同步通信问题。本文接着介绍了 MINA2 的通信原理和核心组件。本文提供了基于 MINA2 实现同步通信的客户端、服务端程序,方便读者掌握 MINA2 开发消息中间件。
摘抄于网络
系统发展遇到的瓶颈问题
目前主流网站都是由开源软件构建的。使用 Nginx 做为 Web 服务器,Tomcat/Resin 做 App 容器,Memcached 做通用 Cache,MySQL 做数据库,使用 Linux 操作系统。网站系统刚上线初期,用户数并不多,所有的模块都整合一个系统中,所有业务由一个应用提供,此时采取将全部的逻辑都放在一个应用的方式利于系统的维护和管理。但是,随着网站用户的不断增加,系统的访问压力越来越大,为了满足越来越多用户的需求,原有的系统需要增加新的功能进来,随着系统功能模块的增多,系统就会变得越来越难以维护和扩展,同时系统伸缩性和可用性也会受到影响。例如一个网站初期只有用户服务功能,随着网站发展,可能会需要用户信息中心、充值支付中心、商户服务中心等越来越多的子系统,如果把这些子系统都整合在原有的系统中,整个网站将会变得非常复杂,并且难以维护。另外,由于所有子系统都整合在一起,只要有一个模块出问题,那么所有的功能都会受影响,造成非常严重的后果。所以系统发展遇到的瓶颈就是随着系统的发展,如果所有模块都整合在一起,系统的可伸缩性和扩展性将受到影响。
如何解决系统发展遇到的瓶颈问题
遇到以上瓶颈该如何解决呢?明智的办法就是系统拆分,将系统根据一定的标准,比如业务相关性等拆分为不同的子系统, 不同的子系统负责不同的业务功。拆分完成后,每个子系统单独进行扩展和维护,不会影响其他子系统,从而大大提高整个网站系统的扩展性和可维护性,同时系统的水平伸缩性也大大提升了。对于压力比较大的子系统可以再进行扩展而不影响其他子系统,如果某个子系统出现问题也不会影响其他服务。从而增强了整个网站系统的健壮性,更有利于保障核心业务。因此一个大型的互联网应用,肯定是要经过系统拆分的,因为只有进行拆分,系统的扩展性、维护性、伸缩性、可用性才会变得更好。但是拆分也会给系统带来问题,就是子系统之间如何通信。本文介绍 MINA2 就是用来充当消息中间件解决各子系统之间的通信问题
MINA2 的原理及主要功能
MINA2 简介
MINA2 是一个网络通信应用框架,它主要用于基于 TCP/IP、UDP/IP 协议栈的通信框架,也可以提供 Java 对象的序列化服务、虚拟机管道通信服务等。MINA2 可以帮助我们快速开发高性能、高扩展性的网络通信应用。MINA2 提供了事件驱动、异步(MINA2 的异步 IO 默认使用的是 Java NIO 作为底层支持)操作的编程模型。
MINA2 同时提供了网络通信的 Server 端、Client 端的封装,无论是哪端,MINA2 在整个网络通信结构中都处于如下的位置:
图 1.MINA2 在网络通信中的作用图
可见 MINA2 的 API 将真正的网络通信与我们的应用程序隔离开来,你只需要关心你要发送、接收的数据以及你的业务逻辑即可。同样的,无论是哪端,MINA2 的执行流程如下所示:
图 2.MINA2 执行流程图
MINA2 通信原理
异步 IO 模型
异步 I/O 模型大体上可以分为两种,反应式 (Reactive) 模型和前摄式 (Proactive) 模型:
1. 传统的 select / epoll / kqueue 模型,以及 Java NIO 模型,都是典型的反应式模型,即应用代码对 I/O 描述符进行注册,然后等待 I/O 事件。当某个或某些 I/O 描述符所对应的 I/O 设备上产生 I/O 事件(可读、可写、异常等)时,系统将发出通知,于是应用便有机会进行 I/O 操作并避免阻塞。由于在反应式模型中应用代码需要根据相应的事件类型采取不同的动作,最常见的结构便是嵌套的 if {...} else {...} 或 switch ,并常常需要结合状态机来完成复杂的逻辑。
2. 前摄式模型则不同。在前摄式模型中,应用代码主动地投递异步操作而不管 I/O 设备当前是否可读或可写。投递的异步 I/O 操作被系统接管,应用代码也并不阻塞在该操作上,而是指定一个回调函数并继续自己的应用逻辑。当该异步操作完成时,系统将发起通知并调用应用代码指定的回调函数。在前摄式模型中,程序逻辑由各个回调函数串联起来:异步操作 A 的回调发起异步操作 B ,B 的回调再发起异步操作 C ,以此往复。 MINA2 便是一个前摄式的异步 I/O 框架。
MINA2 基本概念
I/O服务:I/O 服务用来执行实际的 I/O 操作。MINA2 已经提供了一系列支持不同协议的 I/O 服务,如 TCP/IP、UDP/IP、串口和虚拟机内部的管道等。开发人员也可以实现自己的 I/O 服务。由于 I/O 服务执行的是输入和输出两种操作,实际上有两种具体的子类型。一种称为“I/O 接受器(I/O acceptor)”,用来接受连接,一般用在服务器的实现中;另外一种称为“I/O 连接器(I/O connector)”,用来发起连接,一般用在客户端的实现中。对应在 MINA2 中的实现,org.apache.mina.core.service.IoService 是 I/O 服务的接口,而继承自它的接口 org.apache.mina.core.service.IoAcceptor 和 org.apache.mina.core.service.IoConnector 则分别表示 I/O 接受器和 I/O 连接器。
I/O 接受器:I/O 接受器用来接受连接,与对等体(客户端)进行通讯,并发出相应的 I/O 事件交给 I/O 处理器来处理。使用 I/O 接受器的时候,只需要调用 bind 方法并指定要监听的套接字地址。当不再接受连接的时候,调用 unbind 停止监听即可。
I/O 连接器:I/O 连接器用来发起连接,与对等体(服务器)进行通讯,并发出相应的 I/O 事件交给 I/O 处理器来处理。使用 I/O 连接器的时候,只需要调用 connect 方法连接指定的套接字地址。另外可以通过 setConnectTimeoutMillis 设置连接超时时间(毫秒数)。
I/O 会话:I/O 会话表示一个活动的网络连接,与所使用的传输方式无关。I/O 会话可以用来存储用户自定义的与应用相关的属性。这些属性通常用来保存应用的状态信息,还可以用来在 I/O 过滤器和 I/O 处理器之间交换数据。I/O 会话在作用上类似于 Servlet 规范中的 HTTP 会话。
I/O过滤器:I/O 服务能够传输的是字节流,而上层应用需要的是特定的对象与数据结构。I/O 过滤器用来完成这两者之间的转换。I/O 过滤器的另外一个重要作用是对输入输出的数据进行处理,满足横切的需求。多个 I/O 过滤器串联起来,形成 I/O 过滤器链。每个过滤器都可对通过的数据进行任意的操作,包括增加、删除、更新、类型转换等。先装上的过滤器更靠近远程端点 ( 客户端),后装上的更靠近本地端点 ( 服务器)。
I/O处理器:I/O 事件通过过滤器链之后会到达 I/O 处理器。I/O 处理器中与 I/O 事件对应的方法会被调用。MINA2 中 org.apache.mina.core.service.IoHandler 是 I/O 处理器要实现的接口,一般情况下,只需要继承自 org.apache.mina.core.service.IoHandlerAdapter 并覆写所需方法即可。
MINA2 就是用来充当消息中间件解决各子系统之间通信的问题。在每个子系统增加 MINA2 的客户端和服务端,负责接收和发送 Mina 消息,调用其他子系统的业务功能和数据。
MINA2 实现消息中间件
MINA2 在系统功能拆分中的作用
基于 MINA2 消息中间件的系统架构如下所示
图 3. 系统架构
以某业务运营平台拆分后的系统架构为例,整个平台包含三个子系统:业务运营子系统,负责提供用户服务;用户社区子系统是类似于 SNS 用户交互平台;用户子系统是用户账户、个人信息的子系统,是整个平台的公共基础系统。整个平台的最顶层是 web 服务器层,包含数台 Nginx 服务器(根据业务流量确定)。web 服务器层负责把不同的请求分发到对应的子系统,平台服务相关请求分发到业务运营子系统,用户社区动态信息相关请求分发到用户社区子系统,用户个人账户相关相关请求分发到用户子系统。三个子系统的 APP 服务器都是由一定数量的 Tomcat 服务器组成,一般情况下,运行 Spring+Struts2+Hibernate 程序的 tomcat 服务器能够支持 130-150 个并发请求。APP 服务器把大量数据缓存在 Memcache 服务器。另外通过 DB-Proxy 实现主从数据库分离和集群。每个子系统都有一个消息中间件层,即 MINA 服务器,通过 DB-Proxy 与本子系统的数据库进行交互,消息中间件层都包括 MINA 客户端和 MINA 服务端用于接收和发送 MINA 消息。
图 4. 某业务运营平台拆分后的系统架构图
MINA2 实现服务器端程序开发
建立 IOListener 类继承 IOHandlerAdapter。IoHandlerAdapter 类实现了 IoHandler 接口要求的方法,但是都没有任何业务逻辑处理。如果要编写 Handler 时,可以扩展 IoHandlerAdapter,重写需要的事件方法即可。一般情况,我们比较关心接收到请求消息这个事件,那么我们就可以覆盖 messageReceived 方法,不用管其他方法。 程序清单如下:
清单 1. 创建 IOListener 类
import java.lang.reflect.Method; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; import org.springframework.beans.factory.annotation.Autowired; public class IOListener extends IoHandlerAdapter { private final static Log logger = LogFactory.getLog(MainProtocolHandler.class); @Autowired private AService aService; @Autowired private BService bService; …… }
重写 messageReceived 方法。messageReceived 由 IoHandler 接口声明。IoHandler 封装了来自客户端不同事件的处理,如果对某个事件感兴趣,可以实现相应的方法,当该事件发生时,IoHandler 中的方法就会被触发执行。
当接收到新的消息的时候,该方法就会被调用。此处的逻辑是如果传入 invoke_class 是 aService、,则通过反射机制调用 aService 的 invoke_method 方法,并把结果通过 session.write 发送回去。如果消息参数中包含"return_method"值 , 则直接把 service 返回结果回写给 session,由 session 通知客户端调用"return_method"对应的方法。程序清单如下:
清单 2 消息处理方法
public void messageReceived(IoSession session, Object message) { try { /** * invote_class 决定调用实例对象, * invoke_method 是要调用实例对象的方法 * return_method 是执行完成后的回调方法 */ Map<String, String> map = (Map<String, String>) message; if (!map.isEmpty() && map.containsKey("invoke_class") && map.containsKey("invoke_method")) { // 根据 message 中的 invoke_class 值调用对应 service if (map.get("invoke_class").equals("aService")) { if (!map.containsKey("return_method")) { // 如果 message 中包含 return_method 键值,则调用 aService 中 return_method 键对应的方法 Method method = aService.getClass().getMethod( map.get("invoke_method"), new Class[] { Map.class }); method.invoke(aService, new Object[] { map }); session.write("done"); } else { // 通过 Java 的反射机制调用阿 Service 的方法,并把结果回写给 session Method method = aService.getClass().getMethod( map.get("invoke_method"), new Class[] { Map.class }); session.write(method.invoke(aService, new Object[] { map })); } } …… } else { session.write("parameter error"); } } catch (Exception ex) { logger.error(ex.getMessage()); } return; }
MINA2 与 Spring 集成的配置文件,其中 mainHandler 是处理器。
声明 IO 过滤器包括:
executorFilter:MINA 可以通过 ExecutorFilter 将 IO 线程与业务处理线程分开。
textCodecFilter:ProtocolCodecFilter 用来在字节流和消息对象之间互相转换。
loggingFilter:记录所有 MINA 的协议事件。由于该过滤器只是实现了 MINA 事件的简单记录,实际作用不大,可配合 log4j 等日志框架一起使用。
filterChainBuilder:用来构建过滤器链。
清单 3 Spring 配置 MINA2
<bean id="mainHandler" class="com.xxx.ProcessHandler"></bean> <!-- the IoFilters --> <! — - 配置 executorFilter --> <bean id="executorFilter" class="org.apache.mina.filter.executor.ExecutorFilter"> <constructor-arg index="0"> <value>1000</value> </constructor-arg> <constructor-arg index="1"> <value>1800</value> </constructor-arg> </bean> <! — - 配置 textCodecFilter --> <bean id="textCodecFilter" class="org.apache.mina.filter.codec.ProtocolCodecFilter"> <constructor-arg> <bean class="org.apache.mina.filter.codec.textline.TextLineCodecFactory" /> </constructor-arg> </bean> <! — - 配置 codecFilter --> <bean id="codecFilter" class="org.apache.mina.filter.codec.ProtocolCodecFilter"> <constructor-arg> <bean class=" org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory"/> </constructor-arg> </bean> <bean id="loggingFilter" class="org.apache.mina.filter.logging.LoggingFilter" /> <! —声明过滤器链 --> <bean id="filterChainBuilder" class="org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder"> <property name="filters"> <map> <entry key="codecFilter" value-ref="codecFilter" /> <entry key="executor" value-ref="executorFilter" /> <entry key="loggingFilter" value-ref="loggingFilter" /> </map> </property> </bean> <!-- 设置 I/O 接受器,并指定接收到请求后交给 mainHandler 进行处理 --> <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer" > <property name="customEditors" > <map> <entry key="Java.net.SocketAddress" > <bean class="org.apache.mina.integration.beans.InetSocketAddressEditor" /> </entry> </map> </property> </bean> <bean id="ioAcceptor" class="org.apache.mina.transport.socket.nio.NioSocketAcceptor" init-method="bind" destroy-method="unbind" > <property name="defaultLocalAddress" value=":1234" /> <property name="handler" ref="mainHandler" /> <property name="reuseAddress" value="true" /> <property name="filterChainBuilder" ref="filterChainBuilder" /> </bean>
MINA2 实现客户端程序
建立 ProcessHandler 类继承 IOHandlerAdapter。无论客户端还是服务端,都需要创建继承自 IOHandlerAdapter 的 hanlder 类。清单如下:
清单 4 建立 ProcessHandler 类
import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.util.Map; import java.util.Random; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory; import org.apache.mina.filter.logging.LoggingFilter; import org.apache.mina.transport.socket.nio.NioSocketConnector; public class ProcessHandler extends IoHandlerAdapter { private String hostName ; //MINA2 服务器 IP 数组 private static final String[] HOSTS = {"xx.xx.xx.xx","xx.xx.xx.xx"}; private static final int CONNECT_TIMEOUT = 1000; private NioSocketConnector connector; private static final int PORT = 1234; private IoSession session; // 构造方法 public ProcessHandler (Map<String, String> map) { this.map = map; this.hostName = this.selectServer(); } // 随机选择 MINA2 服务器 IP,以实现 MINA2 集群 private String selectServer() { try { int cc = HOSTS.length; if (cc <= 0) return null; Random rd = new Random(); int idx = (Math.abs(rd.nextInt()) % cc); return HOSTS[idx]; } catch (Exception e) { e.printStackTrace(); return null; } } …… }
由于消息中间件需要处理较高并发的请求,所以一般使用 MINA2 服务器集群。在程序清 1 中,定义了一个 String 数组 HOSTS 保存所有集群的 MINA2 服务器 IP,在初始化 ProcessHandler 对象实例的时候通过 selectServer()方法为本次连接选择一个 MINA2 服务器 IP。
与 Server 端 Handler 类不同的是,需要开发者重写相应的连接方法,已建立与 Server 端的连接。为满足需要实现了三种连接方式:
Client 不等待返回结果的连接方式,适合传递数据进行更新插入的请求,并返回连接建立后创建的会话,方便在同一个 Action 方法里重用。代码中的 IoSession connect() 就是这种连接方式。
清单 5.connect 方法
public IoSession connect() { if (session != null && session.isConnected()) { throw new IllegalStateException( "Already connected. Disconnect first."); } try { connector = new NioSocketConnector(); connector.setConnectTimeoutMillis(CONNECT_TIMEOUT ); connector.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new ObjectSerializationCodecFactory())); connector.getFilterChain().addLast("logger", new LoggingFilter()); connector.setHandler(this); future = connector.connect(new InetSocketAddress(hostName, PORT )); future.awaitUninterruptibly(); if (!future.isConnected()) { return null; } session = future.getSession(); session.write(map); } catch (Exception ex) { throw new IllegalStateException("session is already closed"); } return session; }
创建连接并等待返回结果后关闭连接和会话。适合从 Server 端请求对象的操作。connectWithClose(boolean waitingFlag) 方法连接根据 waitinFlag 是否为真判断是否等待返回结果,并关闭连接。
清单 6 connectWithClosure 方法
public boolean connectWithClosure(IoSession session, Map<String, String> map, boolean waitingFlag) { if (session != null && session.isConnected()) { try { session.write(map); if (waitingFlag) { session.getCloseFuture().awaitUninterruptibly(); } if (connector != null) { connector.dispose(); } return true; } catch (Exception ex) { throw new IllegalStateException("session is already closed"); } } return false; }
续用已创建的会话,并等待返回结果后关闭连接和会话。适合从 Server 端请求对象的操作。connectWithClose(IoSession session,Map<String, String> map, boolean waitingFlag) 方法复用 IoSession,根据 waitingFlag 是否为真判断是否等待返回结果,并关闭连接。
清单 7 connectWithClosure 重写方法
public boolean connectWithClosure(IoSession session, Map<String, String> map, boolean waitingFlag) { if (session != null && session.isConnected()) { try { session.write(map); if (waitingFlag) { session.getCloseFuture().awaitUninterruptibly(); } if (connector != null) { connector.dispose(); } return true; } catch (Exception ex) { throw new IllegalStateException("session is already closed"); } } return false; }
重写请求结果回调方法,在请求返回结果后,通过反射调用对应方法。
清单 8.messageReceived 方法
@Override public void messageReceived(IoSession session, Object message) { try { if (!map.isEmpty() if (aService!=null){ Method method = aService.getClass().getMethod( map.get("return_method"), new Class[] { Object.class }); method.invoke(aService, new Object[] { message }); } session.close(true); } } catch (Exception ex) { logger.error("exception: " + ex.getMessage()); return; } }
MINA2 调用方法如下:
清单 9 MINA2 调用方法
Map<String,String> map = new HashMap<String,String>(); map.put("invoke_class", "userFeedService"); map.put("invoke_class","findUserFeed"); map.put("invoke_class","setFeedList"); map.put("userName", userName); ProcessHandler handler = new ProcessHandler (map,this); handler.connectWithClose(true);
以上代码的含义是:创建 ProcessHandler 实例,调 MINA2 服务器的 userFeedService 的 findUserFeed 方法, 并在接收到处理结果后直接调用本对象的 setFeedList 把结果赋给 feedList。
总结与展望
本文首先提出了大型 Web 系统在发展过程中遇到的瓶颈—系统扩展性和维护性越来越困难,只有系统拆分才能突破这个瓶颈,而 MINA2 正是作为消息中间件解决系统拆分后的同步通信问题。本文接着介绍了 MINA2 的通信原理和核心组件。本文提供了基于 MINA2 实现同步通信的客户端、服务端程序,方便读者掌握 MINA2 开发消息中间件。
摘抄于网络
发表评论
-
Step by step tutorial to create Keystore and Truststore file
2012-07-17 17:46 4084Step by step tutorial to create ... -
MINA2在Web系统中的使用(Struts2+Spring2+Mybatis)
2012-04-17 16:25 0项目,需要web项目需要与硬件设备进行数据交互,已经web客户 ... -
MINA2的优化
2012-04-17 13:59 6018mina2优化指南(摘抄自网络) MINA默认配置的性能并不 ... -
Apache NIO 框架 Mina 使用中出现 too many open files 问题的解决办法(转)
2012-01-04 12:26 6911最近一段时间在用 Apache NIO 框架 Mina, 用起 ... -
Java NIO类库Selector机制解析(Too many open files 和 No buffer space available)
2012-01-04 12:08 3451一、 前言 自从 J2SE 1.4 版本以来, JDK 发 ...
相关推荐
5. **安全性**:ActiveMQ提供了用户认证和授权机制,可以通过JAAS(Java Authentication and Authorization Service)进行配置,确保只有授权的用户和应用可以访问消息系统。 6. **网络连接器**:允许ActiveMQ与...
2. **Apache MINA** 或 **Netty**:这两个都是高性能的网络应用框架,能够简化网络编程,提供异步非阻塞I/O模型,适用于大规模并发连接的即时通信系统。 3. **Apache ActiveMQ**:这是一个强大的消息中间件,可以...
1. **URI驱动**:Apache Camel通过使用URI来定义路由路径,使得开发者能够轻松地配置和管理不同的传输协议和消息处理模型。 2. **轻量级**:该框架具有很小的内存占用,并且对外部依赖的要求很低,这使其非常适合...
还包括了先进的分布式服务框架(如SOFAStack、Apache Dubbo)、大数据处理框架(如Apache Spark、Blink)、数据库系统(如Apache HBase、Apache Drill)以及消息队列中间件(如Apache RocketMQ)等。这对于理解当前...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
5. **activemq**:ActiveMQ是Apache出品的一款开源的消息中间件,遵循JMS(Java消息服务)标准,提供高可用、高性能的消息传递服务。 6. **mina**:Apache MINA是一个网络通信应用框架,用于简化开发高性能且可靠的...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
6. **消息队列**:`Apache RocketMQ`是阿里巴巴开源的分布式消息中间件,常用于构建高可用、高吞吐的消息系统。 7. **NoSQL数据库**:`Apache HBase`是一个分布式的、版本化的、基于列的大型表数据库,运行在Hadoop...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
13. **joram-jms.jar**:可能包含的是Joram(一个JMS实现)的兼容库,用于与其他消息中间件的互操作。 14. **zookeeper-*.jar**:如果是高可用性和集群设置,可能需要ZooKeeper的JAR文件,它用于管理ActiveMQ集群的...