springboot集成Netty

# springboot集成netty
##1 Netty是什么?
Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

也就是说,Netty 是一个基于NIO的客户端、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。

“快速”和“简单”并不用产生维护性或性能上的问题。Netty 是一个吸收了多种协议(包括FTP、SMTP、HTTP等各种二进制文本协议)的实现经验,并经过相当精心设计的项目。最终,Netty 成功的找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性。
##2 集成步骤
###步骤1:Pom.xml添加依赖
```angular2
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.43.Final</version>
</dependency>
```
###步骤2:NettyServer类
```angular2
@Component
@Slf4j
@Data
public class NettyServer {

    private final ConcurrentHashMap<String,ChannelHandlerContext> channels = new ConcurrentHashMap<>();
    private final NioEventLoopGroup boss = new NioEventLoopGroup();
    private final NioEventLoopGroup worker = new NioEventLoopGroup();

    private Channel channel;

    //启动服务
    public ChannelFuture run(InetSocketAddress address){
        ChannelFuture cf = null;
        try {
            ServerBootstrap server = new ServerBootstrap();
            server.group(boss,worker)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,128)
                    .childOption(ChannelOption.SO_KEEPALIVE,true)
                    .childHandler(new ChannelInitializer<SocketChannel>(){

                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // 解码编码
                            socketChannel.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
                            socketChannel.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));

                            socketChannel.pipeline().addLast(new SimpleChannelInboundHandler<Object>(){
                                @Override
                                public void channelRead0(ChannelHandlerContext ctx, Object msg) {
                                    System.out.println("server receive message :"+ msg);
                                    ctx.channel().writeAndFlush("yes server already accept your message" + msg);
//                                    ctx.close();
                                }

                                @Override
                                public void channelActive(ChannelHandlerContext ctx) {
                                    System.out.println("channelActive>>>>>>>>");
                                    if(channels.get(ctx.channel().id().toString())==null){
                                        channels.put(ctx.channel().id().toString(),ctx);
                                    }else{
                                        System.out.println(ctx.channel().id().toString()+"已经放进通道列表了");
                                    }
                                }

                                @Override
                                public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                                    System.out.println(ctx.channel().localAddress().toString() + " 通道不活跃!已关闭");
                                    removeChannnelMap(ctx);
                                    // 关闭流
                                    ctx.close();
                                }

                                private void removeChannnelMap(ChannelHandlerContext ctx){
                                    Iterator<Map.Entry<String,ChannelHandlerContext>> iterator = channels.entrySet().iterator();
                                    while (iterator.hasNext()){
                                        Map.Entry<String,ChannelHandlerContext> ele = iterator.next();
                                        if(ctx.channel().id().toString().equals(ele.getKey())){
                                            channels.remove(ele);
                                        }
                                    }

                                }

                                @Override
                                public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                                    super.userEventTriggered(ctx, evt);
                                    if (evt instanceof IdleStateEvent) {
                                        IdleStateEvent event = (IdleStateEvent) evt;
                                        if (event.state().equals(IdleState.READER_IDLE))
                                            ctx.close();
                                        //标志该链接已经close 了 
                                    }
                                }

                            });
                        }
                    });
            cf = server.bind(address).syncUninterruptibly();
            channel = cf.channel();

        }catch (Exception e) {
            log.error("Netty start error:", e);
        } finally {
            if (cf != null && cf.isSuccess()) {
                log.info("Netty server listening " + address.getHostName() + " on port " + address.getPort() + " and ready for connections...");
            } else {
                log.error("Netty server start up Error!");
            }
        }

        return cf;
    }


    public void destroy() {
        log.info("Shutdown Netty Server...");
        if(channel != null) { channel.close();}
        worker.shutdownGracefully();
        boss.shutdownGracefully();
        log.info("Shutdown Netty Server Success!");
    }

}
```
###步骤3:启动Netty服务
  在spring启动类里启动Netty服务,实现netty服务端与客户端消息收发。
  其中,如果要实现服务端向客户端主动发消息,需要启动一个异步线程,见步骤4
```angular2
@SpringBootApplication
public class SwaggerApplication implements CommandLineRunner {

    @Autowired
    private NettyServer socketServer;

    @Autowired
    private NettyConfigration config;

    @Autowired
    private AsyncTaskService task;//loop channels实现服务端向客户端消息发送

    public static void main(String[] args) {
        SpringApplication.run(SwaggerApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        InetSocketAddress address = new InetSocketAddress(config.getPort());
        ChannelFuture future = socketServer.run(address);
        task.executeAsyncTask(socketServer);
        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run() {
                socketServer.destroy();
            }
        });
        future.channel().closeFuture().syncUninterruptibly();
    }
}
```
###步骤4:channel轮询异步线程AsyncTaskService
```angular2
@Service
@Slf4j
public class AsyncTaskService {

    @Async
    // 表明是异步方法
    // 无返回值
    public void executeAsyncTask(NettyServer socketServer) {
        System.out.println("开启新线程执行channels loop");
        new Thread(()->{
            ConcurrentHashMap<String, ChannelHandlerContext> channels = socketServer.getChannels();
            try {
                while (true){
                    Thread.sleep(5000);
                    Iterator<Map.Entry<String,ChannelHandlerContext>> iterator = channels.entrySet().iterator();
                    while (iterator.hasNext()){
                        Map.Entry<String,ChannelHandlerContext> ele = iterator.next();
                        ele.getValue().channel().write(new Date()+"::server 主动发了一条消息给你,收到了吗?");
                        ele.getValue().channel().flush();
                    }
                }
            }catch (InterruptedException e){

            }

        }).start();
    }

}
```
###步骤5:客户端测试类NettyClient
```angular2
public class NettyClient {
    public static void main(String[] args) throws InterruptedException {
        Bootstrap bootstrap = new Bootstrap();
        NioEventLoopGroup group = new NioEventLoopGroup();

        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<Channel>() {
                    @Override
                    protected void initChannel(Channel ch) {
                        ch.pipeline().addLast(new StringEncoder());
                        ch.pipeline().addLast(new SimpleChannelInboundHandler() {
                            @Override
                            public void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
                                System.out.println(convertByteBufToString((ByteBuf)o));
                            }
                        });
                    }
                });

        Channel channel = bootstrap.connect("127.0.0.1", 11111).channel();

        while (true) {
            channel.writeAndFlush(new Date() + ": hello world!");
            Thread.sleep(2000);
        }
    }

    public static String convertByteBufToString(ByteBuf buf) {
        String str;
        if(buf.hasArray()) { // 处理堆缓冲区
            str = new String(buf.array(), buf.arrayOffset() + buf.readerIndex(), buf.readableBytes());
        } else { // 处理直接缓冲区以及复合缓冲区
            byte[] bytes = new byte[buf.readableBytes()];
            buf.getBytes(buf.readerIndex(), bytes);
            str = new String(bytes, 0, buf.readableBytes());
        }
        return str;
    }
}
```
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值