浅谈高可用架构中NIO的重要性

一个功能引发的思考

今天同事开发了一个文件读写的模块,发现读写性能异常的低,他的做法是单线程纯IO操作,频繁的打开关闭IO流,读写。
于是乎他问我这个应该怎么做,我给他讲解到这种做法的低效,建议他批量的一次性写入,频繁直接操作IO性能当然是无法接受的。

再谈IO操作的演变

BIO:传统的cs端架构,都是一个请求提交,后台一个专门的线程负责接受这个请求,分配给新的线程去处理。这种做法的缺点很明显,当并发量过大的时候,不断的创建新的线程,对于服务器来说,肯定是无法接受的,这样会导致大量的请求阻塞未响应。而且创建线程这个操作java是直接操作 操作系统内核的,光创建线程这个,就会导致系统假死,cpu100等情况。
NIO:直到jdk1.4以后,才推出了非阻塞的IO操作,基于I/O多路复用技术,减小系统开销。即,把多个I/O的阻塞复用到同一个select上阻塞,从而使得单线程情况下,可以同时处理多个请求。系统不需要创建新的额外进程或者线程,也不需要维护这些进程和线程的运行。并且在jdk1.5中,epool替代传统select/poll,极大提升NIO通信新能

jdk nio存在的缺陷

1.开发复杂度非常高,例如:客户端重连,网络闪断,半包读写,失败缓存,网络拥塞,异常码流的处理等。
2.jdk epoll bug会导致selector空轮询,最终导致cpu100

基于Netty实现NIO

Netty算是一个比较成熟的nio框架了,他解决了jdk中epoll的bug,并且开发复杂度也比较低一点。
后面上一个简单的netty nio实现源码

基于Netty开发应用层协议

随着互联网发展,传统垂直架构逐渐被分布式,弹性可伸缩分布式架构替代。
那么问题也随之而出,系统只有分布式,就面临各个节点间通信的问题,尤为强调高可用,扩展性强,高性能。
1.高性能
传统java序列化性能比较低,而且序列化后的字节流太大,现在出的很多新的序列化框架,性能都比传统的jdk要高很多,如:google的protobuf facebook的thrift jboss的marshalling。另外netty对异步非阻塞通信的支持,以及高效reactor线程模型,无锁化串行设计,0拷贝,灵活tcp参数配置等。
2.扩展性
传统java序列化无法跨语言,所以目前的java rpc框架基本也都没用jdk的,比如thirft:可以跨c++,c#,cocoa,erlang,java,perl,php,python,ruby等

3.高可用
传统bio模型,对并发访问支持很差,而新的nio就不同,具体的上面也谈到了

4.可靠性
1>网络通信类
连接超时接口,强制关闭对端连接
2>链路有效性
通过心跳检查
3>reactor线程保护
主要谨慎处理I/O异常,以及规避NIO bug
4>链路控制
5>优雅停机
5.安全性
安全性可以通过netty提供的SSL认证,也可以通过第三方CA认证来保障
6.成功案例
目前也已经有很多成熟应用netty的框架,如alibaba的rocketMQ dubbo ,Apache的avro等等

副一段Netty基础NIO代码

package com.solace.nio;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * Created by Administrator on 2018/4/10.
 *
 * @author solace
 */
public class TimerServer {
    public void bind(int port) throws Exception{
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try{
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,1024)
                    .childHandler(new ChildChannelHandler());
            //绑定端口,同步等待成功
            ChannelFuture future = bootstrap.bind(port).sync();
            //等待服务端监听端口关闭
            future.channel().closeFuture().sync();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        new TimerServer().bind(8088);
    }

}
package com.solace.nio;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

import java.util.logging.Logger;


/**
 * Created by Administrator on 2018/4/10.
 *
 * @author solace
 */
public class TimeClientHandler extends ChannelHandlerAdapter{
    public static final Logger logger = Logger.getLogger(TimeClientHandler.class.getName());
    private final ByteBuf fisrtMessage;

    public TimeClientHandler() {
        byte[] req = "QUERY TIME ORDER".getBytes();
        fisrtMessage = Unpooled.buffer(req.length);
        fisrtMessage.writeBytes(req);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(fisrtMessage);
    }

    /**
     * 解码读取消息
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req,"UTF-8");
        System.out.println("Now is :"+body);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.warning("Unexpercted exception from downstream :"+cause.getMessage());
        ctx.close();
    }
}
package com.solace.nio;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

import java.util.Date;


/**
 * Created by Administrator on 2018/4/10.
 *
 * @author solace
 */
public class TimeServerHandler extends ChannelHandlerAdapter{

    /**
     * 读到消息后回执
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req,"UTF-8");
        System.out.println("the time server receive order:"+body);
        String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body)?new Date(System.currentTimeMillis()).toString():"bad order";
        ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
        ctx.write(resp);
    }

    /**
     * 消息读完了后flush
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    /**
     * 消息读取异常处理
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}
package com.solace.nio;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * Created by Administrator on 2018/4/10.
 *
 * @author solace
 */
public class TimeClient {
    public void connect(int port,String host) throws InterruptedException {
        //配置NIO线程租
        EventLoopGroup group = new NioEventLoopGroup();
        try{
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY,true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new TimeClientHandler());
                        }
                    });
            //异步发起连接请求
            ChannelFuture future = bootstrap.connect(host,port).sync();
            //等待客户端链路关闭
            future.channel().closeFuture().sync();
        }finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new TimeClient().connect(8088,"127.0.0.1");
    }
}
package com.solace.nio;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;


import java.util.logging.SocketHandler;

/**
 * Created by Administrator on 2018/4/10.
 *
 * @author solace
 */
public class ChildChannelHandler extends ChannelInitializer<SocketChannel>{

    /**
     * 添加自定义处理类
     * @param channel
     * @throws Exception
     */
    @Override
    protected void initChannel(SocketChannel channel) throws Exception {
        channel.pipeline().addLast(new TimeServerHandler());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值