今天对mina的线程做了些实验,先上代码
MyCodecFactory:
public class MyCodecFactory implements ProtocolCodecFactory
{
ProtocolEncoderAdapter encoder = new MyEncoder();
ProtocolDecoder decoder = new MyDecoder();
public ProtocolDecoder getDecoder(IoSession session) throws Exception
{
return decoder;
}
public ProtocolEncoder getEncoder(IoSession session) throws Exception
{
return encoder;
}
}
MyDecoder:
public class MyDecoder extends ProtocolDecoderAdapter
{
@Override
public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception
{
int id = in.getInt();
int len = in.getInt();
byte [] dst = new byte [len];
in.get(dst);
String name = new String(dst,"GBK");
Item item = new Item();
item.setId(id);
item.setName(name);
out.write(item);
}
}
MyEncoder:
public class MyEncoder extends ProtocolEncoderAdapter
{
public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception
{
Item item = (Item) message;
int byteLen = 8 + item.getName().getBytes("GBK").length;
IoBuffer buf = IoBuffer.allocate(byteLen);
buf.putInt(item.getId());
buf.putInt(item.getName().getBytes("GBK").length);
buf.put(item.getName().getBytes("GBK"));
buf.flip();
out.write(buf);
}
}
ClientSessionHandle:
public class ClientSessionHandle implements IoHandler
{
@Override
public void sessionCreated(IoSession session) throws Exception
{
System.out.println("client sessionCreated" + System.currentTimeMillis());
}
@Override
public void sessionOpened(IoSession session) throws Exception
{
System.out.println("client sessionOpened" + System.currentTimeMillis());
}
@Override
public void sessionClosed(IoSession session) throws Exception
{
System.out.println("client sessionClosed" + System.currentTimeMillis());
}
@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception
{
System.out.println("client sessionIdle" + System.currentTimeMillis());
}
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception
{
cause.printStackTrace();
System.out.println("client exceptionCaught" + System.currentTimeMillis());
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception
{
System.out.println("recieveMsg:" + System.currentTimeMillis());
}
@Override
public void messageSent(IoSession session, Object message) throws Exception
{
// Thread.sleep(3000);
System.out.println("client messageSent" + System.currentTimeMillis());
}
}
ServerSessionHandle:
public class ServerSessionHandle implements IoHandler
{
@Override
public void sessionCreated(IoSession session) throws Exception
{
System.out.println("server sessionCreated" + System.currentTimeMillis());
}
@Override
public void sessionOpened(IoSession session) throws Exception
{
System.out.println("server sessionOpened" + System.currentTimeMillis());
}
@Override
public void sessionClosed(IoSession session) throws Exception
{
System.out.println("server sessionClosed" + System.currentTimeMillis());
}
@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception
{
System.out.println("server sessionIdle" + System.currentTimeMillis());
}
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception
{
cause.printStackTrace();
System.out.println("server exceptionCaught");
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception
{
// Thread.sleep(3000);
System.out.println("server recieveMsg:" + System.currentTimeMillis());
}
@Override
public void messageSent(IoSession session, Object message) throws Exception
{
System.out.println("server messageSent" + System.currentTimeMillis());
}
}
Client:
public class Client
{
public static void main(String[] args) throws IOException
{
IoConnector connector = new NioSocketConnector();
connector.getFilterChain().addLast("protocol", new ProtocolCodecFilter(new MyCodecFactory()));
connector.setHandler(new ClientSessionHandle());
// connector.getFilterChain().addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool()));
ConnectFuture future = connector.connect(new InetSocketAddress(8888));
future.awaitUninterruptibly();
IoSession session = future.getSession();
Item item = new Item();
item.setId(1);
item.setName("aaa");
session.write(item);
session.write(item);
session.write(item);
session.write(item);
session.write(item);
}
}
Server:
public class Server
{
public static void main(String[] args) throws IOException
{
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast("protocol", new ProtocolCodecFilter(new MyCodecFactory()));
// acceptor.getFilterChain().addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool()));
acceptor.setHandler(new ServerSessionHandle());
acceptor.bind(new InetSocketAddress(8888));
}
}
mina简介
mina的工作模式如下图所示
acceptor用于处理客户端的连接,当有客户端连接时,将连接交给Processor处理,Process是多线程的,在内部由一个线程池来处理,线程的个数由创建Acceptor时参数指定,默认是cpu核数加1,filter主要用来对消息进行过虑,经常用来做编码解码,消息经过filter过虑之后由handler来处理,我们做开发时业务也都是在handler层做。
一个客户端连接到服务器端之后,由一个且只会有一个processor来处理。服务器启动后,processor的数量是0,每一个客户端连接上来,就会创建一个Processor,直到达到最高数量。当所有的processor都在处理客户端的信息时,后续的客户端都会产生阻塞,所以当同时处理消息量过多,io不多,但是handler处理时间较长时,我们就要采用另一种处理方式 ,即把handler放在另外的线程来处理,代码如下:
acceptor.getFilterChain().addLast("threadPool", new ExecutorFilter(Executors.newCachedThreadPool()));
实验过程
这篇文章的重点内容是来实验mina的线程问题,主要是processor和handler使用不同线程处理客户端消息的情况,实验内容是客户端连接成功之后连续发送5条消息
(1)客户端与服务器端都不采用handler单独线程,客户端ClientSessionHandle的messageSent方法延迟3s发送,结果如下,消息是排着队发送
客户端结果
client sessionCreated1384482297824
client sessionOpened1384482297825
client messageSent1384482300829
client messageSent1384482303829
client messageSent1384482306830
client messageSent1384482309830
client messageSent1384482312830
服务器结果
server sessionCreated1384482297813
server sessionOpened1384482297813
server recieveMsg:1384482297828
server recieveMsg:1384482300829
server recieveMsg:1384482303830
server recieveMsg:1384482306830
server recieveMsg:1384482309830
(2)客户端采用handler单独线程,客户端ClientSessionHandle的messageSent方法延迟3s发送,结果如下,消息是连接成功之后延迟3s并行发送出去
客户端结果
client sessionCreated1384482710537
client sessionOpened1384482710538
client messageSent1384482713542
client messageSent1384482713542
client messageSent1384482713542
client messageSent1384482713542
client messageSent1384482713542
服务器结果
server sessionCreated1384482710525
server sessionOpened1384482710526
server recieveMsg:1384482710541
server recieveMsg:1384482710541
server recieveMsg:1384482710542
server recieveMsg:1384482710542
server recieveMsg:1384482710542
(3)客户端采用handler单独线程,客户端连接成功之后不延迟3s直接发送5条消息,服务器端ServerSessionHandle延迟3s接收,结果如下,5条消息是一起发出,但服务器端每3s处理一条消息,jconcle监控发现只有一个processor线程,由此验证了:一个客户端只会有一个processor进行处理,handler与processor同一线程的情况下,消息是排队的。
客户端结果
client sessionCreated1384482908375
client sessionOpened1384482908377
client messageSent1384482908380
client messageSent1384482908380
client messageSent1384482908381
client messageSent1384482908381
client messageSent1384482908381
服务器结果
server sessionCreated1384482908372
server sessionOpened1384482908372
server recieveMsg:1384482911381
server recieveMsg:1384482914382
server recieveMsg:1384482917382
server recieveMsg:1384482920382
server recieveMsg:1384482923382
(4)客户端与服务器端都采用handler单独线程,客户端连接成功之后不延迟3s直接发送5条消息,服务器端ServerSessionHandle延迟3s接收,结果如下,5条消息是一起发出,服务端延迟3s后一起处理了5条消息,jconcle监控发现还是只有一个processor线程,但还会创建另外5个线程,这五个线程各自处理了一条消息,由此验证了:一个客户端只会有一个processor进行处理,processor接收到客户端的消息之后,放到了handler处理线程中处理。
客户端结果
client sessionCreated1384483318084
client sessionOpened1384483318086
client messageSent1384483318088
client messageSent1384483318089
client messageSent1384483318089
client messageSent1384483318089
client messageSent1384483318089
服务器结果
server sessionCreated1384483318081
server sessionOpened1384483318082
server recieveMsg:1384483321090
server recieveMsg:1384483321091
server recieveMsg:1384483321091
server recieveMsg:1384483321091
server recieveMsg:1384483321091