netty
高性能,异步,事件驱动库
Unix io模型
- 阻塞io
- 非阻塞io,netty主要就是基于epoll的多路复用技术。
- io复用模型
- 信号驱动io模型
- 异步io
linux io多路复用的调用:select,pselect,poll,epoll
epoll改进
- 进程打开的socket 描述符(fd)不受限制
- io效率不会随着fd增加而下降
- 使用mmap加速内核与用户空间的消息传递
- api简单
各种io对比
io.png
为什么使用netty
- java原生nio库难用,netty开发门槛低
- 功能强大,内置多种编解码功能
- 稳定,性能强
- bug少
Example
echo服务器
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
<version>3.10.6.Final</version>
</dependency>
EchoServer.java
package com.netty;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder;
import org.jboss.netty.handler.codec.frame.Delimiters;
import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
public class EchoServer {
public static void main(String[] args) {
ServerBootstrap bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(
8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("echo", new EchoServerHandler());
return pipeline;
}
});
bootstrap.bind(new InetSocketAddress(8090));
}
}
EchoServerHandler.java
package com.netty;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
public class EchoServerHandler extends SimpleChannelUpstreamHandler {
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
e.getChannel().write("connect success.");
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
e.getChannel().write(e.getMessage());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
e.getChannel().close();
}
}
TCP粘包拆包
发生原因:
- 应用程序写入的字节大小大于套接字发送缓冲区大小
- 进行MSS大小的tcp分段
- 以太网帧的payload大于MTU进行ip分片
解决办法
- 消息定长
- 增加特殊字符比如回车换行进行分割,示例中就使用的这种方法
- 消息头记录长度
编解码技术
- protobuf
- Marshalling
- json,xml 跨语言