Netty学习篇--整合springboot

经过前面的netty学习,大概了解了netty各个组件的概念和作用,开始自己瞎鼓捣netty和我们常用的项目的整合(很简单的整合)
  1. 项目准备
    工具:IDEA2017
    
    jar包导入:maven
    
    项目框架:springboot+netty
  2. 项目操作
    • 右键创建一个maven项目,项目名称: hetangyuese-netty-03(项目已上传github)

      项目完整结构

    ?

    • maven导包

      <!-- netty start -->
      <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.15.Final</version>
      </dependency>
      <!-- netty end -->
      <!-- springboot start -->
      <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter</artifactId>
       </dependency>
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
            <!-- 热部署 -->
      </dependency>
      <!-- springboot end -->
      
      // 之所以没版本,我是在parent项目中配置了maven的全局版本,只能在顶级项目中配置
      <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>1.5.6.RELEASE</version>
      </parent>
      <!-- 日志 slf4j及logback包 start -->
      <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
             <version>1.7.7</version>
      </dependency>
      
      <dependency>
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-core</artifactId>
             <version>1.1.7</version>
      </dependency>
      <dependency>
              <groupId>ch.qos.logback</groupId>
              <artifactId>logback-classic</artifactId>
              <version>1.1.7</version>
      </dependency>
      <!-- 日志 slf4j及logback包 end -->
    • 编码
      • springboot启动类 HetangyueseApplication

        因为需要集成netty启动类不再是继承SpringBootServletInitializer类修改为实现CommandLineRunner(CommandLineRunner项目启动后执行)

        package com.hetangyuese.netty;
        
        import com.hetangyuese.netty.controller.HtServer;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.boot.CommandLineRunner;
        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;
        
        /**
         * @program: netty-root
         * @description: 启动类
         * @author: hetangyuese
         * @create: 2019-10-28 16:47
         **/
        @SpringBootApplication
        public class HetangyueseApplication implements CommandLineRunner {
        
            @Autowired
            private HtServer htServer; // Netty服务端类
        
            public static void main(String[] args) {
                SpringApplication.run(HetangyueseApplication.class, args);
            }
        
            @Override
            public void run(String... strings) throws Exception {
                // 调用netty服务端启动方法
                htServer.start(9000);
            }
        }
      • Netty启动类
        package com.hetangyuese.netty.controller;
        
        import com.hetangyuese.netty.channel.HtServerChannel;
        import io.netty.bootstrap.ServerBootstrap;
        import io.netty.channel.ChannelFuture;
        import io.netty.channel.ChannelHandler;
        import io.netty.channel.ChannelOption;
        import io.netty.channel.EventLoopGroup;
        import io.netty.channel.nio.NioEventLoopGroup;
        import io.netty.channel.socket.nio.NioServerSocketChannel;
        import io.netty.handler.logging.LoggingHandler;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Component;
        
        /**
         * @program: netty-root
         * @description: ht服务类
         * @author: hetangyuese
         * @create: 2019-10-28 17:28
         **/
        @Component
        public class HtServer {
        
            private Logger log = LoggerFactory.getLogger(HtServer.class);
        
            /**
             * Netty服务端启动类
             */
            private ServerBootstrap serverBootstrap;
        
            /**
             *  服务通道
             */
            @Autowired
            private HtServerChannel htServerChannel;
        
            /**
             * Netty日志处理类,可以打印出入站出站的日志,方便排查
             * 需搭配 channelPipeline.addLast(new LoggingHandler(LogLevel.INFO));
             * 使用
             */
            private ChannelHandler logging = new LoggingHandler();
        
            /**
             *
             * @param port 启动端口号
             */
            public void start(int port) {
                log.debug("htServer start port:{}", port);
                // 主线程组 用于处理连接
                EventLoopGroup boss = new NioEventLoopGroup(1);
                // 工作线程组用于处理业务逻辑
                EventLoopGroup work = new NioEventLoopGroup();
                try {
                    serverBootstrap = getServerBootstrap();
                    // 配置服务端启动类
                    serverBootstrap.group(boss, work)
                            .channel(NioServerSocketChannel.class)
                            .option(ChannelOption.SO_BACKLOG, 1024)
                            .childOption(ChannelOption.SO_REUSEADDR, true)
                            .handler(logging)
                            .childHandler(htServerChannel);
        
                    // 服务端绑定端口并持续等待
                    ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
                    // 通道持续阻塞等待直到关闭了服务
                    channelFuture.channel().closeFuture().sync();
                } catch (Exception e) {
                    // 输出错误日志
                    log.error("netty server start happened exception e:{}", e);
                } finally {
                    // 关闭线程组
                    boss.shutdownGracefully();
                    work.shutdownGracefully();
                }
            }
        
            /**
             *  初始化启动类
             * @return
             */
            public ServerBootstrap getServerBootstrap() {
                if (null == serverBootstrap) {
                    serverBootstrap = new ServerBootstrap();
                }
                return serverBootstrap;
            }
        }
      • 管道类(channel、pipeline)
        package com.hetangyuese.netty.channel;
        
        import com.hetangyuese.netty.handler.HtServerHandler;
        import io.netty.channel.Channel;
        import io.netty.channel.ChannelInitializer;
        import io.netty.channel.ChannelPipeline;
        import io.netty.handler.codec.string.StringDecoder;
        import io.netty.handler.logging.LogLevel;
        import io.netty.handler.logging.LoggingHandler;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Service;
        
        /**
         * @program: netty-root
         * @description: 配置管道
         * @author: hetangyuese
         * @create: 2019-10-28 17:35
         **/
        @Service
        public class HtServerChannel extends ChannelInitializer {
        
            @Autowired
            private HtServerHandler htServerHandler;
        
            @Override
            protected void initChannel(Channel ch) throws Exception {
                // 通道流水线 管理channelHandler的有序执行
                ChannelPipeline channelPipeline = ch.pipeline();
                // netty日志
                channelPipeline.addLast(new LoggingHandler(LogLevel.INFO));
                // 字符串解码器 接收到数据直接转为string 这里没有弄自定义和其他的解码器
                channelPipeline.addLast(new StringDecoder());
                // 业务逻辑处理类
                channelPipeline.addLast(htServerHandler);
            }
        }
        
      • 业务逻辑类(handler)

        服务端ChannelPipeline中有许多的ChannelHandler, 如果每个都实例化一个ChannelHandler,在大量的客户端连接的时候将会产生大量的ChannelHandler实例,为了解决这个问题netty中可以通过@ChannelHandler.Sharable注解实现共享实例,由这一个实例去处理客户端连接

        package com.hetangyuese.netty.handler;
        
        import io.netty.buffer.Unpooled;
        import io.netty.channel.ChannelHandler;
        import io.netty.channel.ChannelHandlerContext;
        import io.netty.channel.ChannelInboundHandlerAdapter;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
        import org.springframework.stereotype.Service;
        
        /**
         * @program: netty-root
         * @description: 处理类
         * @author: hetangyuese
         * @create: 2019-10-28 17:39
         **/
        @ChannelHandler.Sharable
        @Service
        public class HtServerHandler extends ChannelInboundHandlerAdapter {
        
            private Logger log = LoggerFactory.getLogger(HtServerHandler.class);
        
            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                log.debug("channel已注册");
                ctx.writeAndFlush(Unpooled.copiedBuffer("xixixix".getBytes()));
            }
        
            /**
             *  服务端接收到的数据
             * @param ctx
             * @param msg
             * @throws Exception
             */
            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                log.debug("htServer receive" + (String)msg);
            }
        
            /**
             *  服务端接收完毕事件
             * @param ctx
             * @throws Exception
             */
            @Override
            public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
                ctx.writeAndFlush(Unpooled.copiedBuffer("Htserver readComplete".getBytes()));
            }
        
            /**
             *  异常捕获事件
             * @param ctx
             * @param cause
             * @throws Exception
             */
            @Override
            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                cause.printStackTrace();
                ctx.close();
            }
        }
        
      • 配置文件(application.yml、logback.xml)
        application.yml文件
        
        spring:
          profiles:
            active: prod
        -----------------------------------------------------------
        application-prod.yml
        
        server:
          port: 8081
        
        <?xml version="1.0" encoding="UTF-8"?>
        <configuration>
         <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 -->
         <property name="LOG_HOME" value="log" />
         <!-- 控制台输出日志 -->
         <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
             <encoder>
                 <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%n
                 </pattern>
             </encoder>
         </appender>
        
         <!-- 文件输出指定项目日志 -->
         <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
             <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                 <fileNamePattern>${LOG_HOME}/netty03.%d{yyyy-MM-dd}.log</fileNamePattern>
                 <maxHistory>30</maxHistory>
             </rollingPolicy>
        
             <encoder>
                 <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
                 </pattern>
             </encoder>
         </appender>
        
         <!-- 异步输出指定项目日志 -->
         <appender name="async" class="ch.qos.logback.classic.AsyncAppender">
             <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
             <discardingThreshold>0</discardingThreshold>
             <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
             <queueSize>512</queueSize>
             <!-- 添加附加的appender,最多只能添加一个 -->
             <appender-ref ref="file" />
         </appender>
        
         <logger name="org.apache" level="info">
             <appender-ref ref="async" />
             <appender-ref ref="stdout"/>
         </logger>
        
         <logger name="org.springframework" level="info">
             <appender-ref ref="async" />
             <appender-ref ref="stdout"/>
         </logger>
        
         <logger name="com.hetangyuese" level="debug">
             <appender-ref ref="async" />
             <appender-ref ref="stdout"/>
         </logger>
        </configuration>
    • 启动(客户端我就不贴代码了)
      // 服务端
      2019-10-29 16:10:20 [restartedMain] DEBUG com.hetangyuese.netty.controller.HtServer -htServer start port:9000
      2019-10-29 16:10:48 [nioEventLoopGroup-3-1] DEBUG com.hetangyuese.netty.handler.HtServerHandler -channel已注册
      2019-10-29 16:10:48 [nioEventLoopGroup-3-1] DEBUG com.hetangyuese.netty.handler.HtServerHandler -htServer receivehello!_My name is hanleilei !_What is your name !_How are you? !_
      
      // 客户端
      服务端返回str: xixixix
      服务端返回str: Htserver readComplete

总结

学习了netty的基础知识后,了解到很多应用框架都运用了netty,看了下dubbo的netty源码部分,也能看明白每一步的用途,学无止境!!!

原文地址:https://www.cnblogs.com/hetangyuese/p/11777090.html

时间: 2024-11-05 12:10:37

Netty学习篇--整合springboot的相关文章

Netty学习篇--02

Channel.ChannelPipeline.ChannelHandlerContent发送数据的不同 // channel往回写数据 Channel channel = ctx.channel(); channel.writeAndFlush(Unpooled.copiedBuffer(new String("123").toCharArray(), CharsetUtil.UTF_8)); // pipeline写回数据 ChannelPipeline pipeline = ct

Netty学习篇④-心跳机制及断线重连

心跳检测 前言 客户端和服务端的连接属于socket连接,也属于长连接,往往会存在客户端在连接了服务端之后就没有任何操作了,但还是占用了一个连接:当越来越多类似的客户端出现就会浪费很多连接,netty中可以通过心跳检测来找出一定程度(自定义规则判断哪些连接是无效链接)的无效链接并断开连接,保存真正活跃的连接. 什么叫心跳检测 我理解的心跳检测应该是客户端/服务端定时发送一个数据包给服务端/客户端,检测对方是否有响应: 如果是存活的连接,在一定的时间内应该会收到响应回来的数据包: 如果在一定时间内

Netty整合SpringBoot并使用Protobuf进行数据传输

前言 本篇文章主要介绍的是SpringBoot整合Netty以及使用Protobuf进行数据传输的相关内容.Protobuf会介绍下用法,至于Netty在netty 之 telnet HelloWorld 详解中已经介绍过了,这里就不再过多细说了. Protobuf 介绍 Protocol Buffer是Google的语言中立的,平台中立的,可扩展机制的,用于序列化结构化数据 - 对比XML,但更小,更快,更简单.您可以定义数据的结构化,然后可以使用特殊生成的源代码轻松地在各种数据流中使用各种语

dubbo入门学习(三)-----dubbo整合springboot

springboot节省了大量的精力去配置各种bean,因此通过一个简单的demo来整合springboot与dubbo 一.创建boot-user-service-provider 本篇博文基于上篇中的dubbo项目,整体工程如下: 1.pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"

Netty学习——Netty和Protobuf的整合(一)

Netty学习——Netty和Protobuf的整合 Protobuf作为序列化的工具,将序列化后的数据,通过Netty来进行在网络上的传输 1.将proto文件里的java包的位置修改一下,然后再执行一下protoc 异常捕获:启动服务器端正常,在启动客户端的时候,发送消息,报错 警告: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the l

从入门到实战,Netty多线程篇案例集锦

从入门到实战,Netty多线程篇案例集锦 原创 2015-09-10 李林峰 InfoQ Netty案例集锦系列文章介绍 1|Netty的特点 Netty入门比较简单,主要原因有如下几点: Netty的API封装比较简单,将复杂的网络通信通过BootStrap等工具类做了二次封装,用户使用起来比较简单: Netty源码自带的Demo比较多,通过Demo可以很快入门: Netty社区资料.相关学习书籍也比较多,学习资料比较丰富. 但是很多入门之后的Netty学习者遇到了很多困惑,例如不知道在实际项

netty 学习资料

最近在做一个网页遥控器的项目,用到了netty,但还未发现比较系统完整的netty博客教程,所以打算自己写一个netty学习教程,会每天更新一篇,欢迎交流. 先给大家提供一些资料: 1. 比较简短易懂的有实例的系列教程,涉及到了netty关键特性:但个人觉得比较速成,不系统,不深入 http://www.coderli.com/netty-course-hello-world http://www.coderli.com/netty-two-concepts http://www.coderli

【Linux】鸟哥的Linux私房菜基础学习篇整理(五)

1. type [-tpa] name:查看name命令是否为bash内置命令.参数:type:不加任何参数,type会显示出那么是外部命令还是bash的内置命令:-t:当加入-t参数时,type会通过如下关键字显示其意义: file:表示为外部命令 alias:表示为命名别名 builtin:表示为bash的内置命令-p:如果后面接的name为外置命令时,才会显示完整文件名:-a:会由PATH变量定义的路径中,将含有name的命令都列出来,包含别名. 2. unset name:取消变量名na

消息中间件——RabbitMQ(十)RabbitMQ整合SpringBoot实战!(全)

前言 1. SpringBoot整合配置详解 publisher-confirms,实现一个监听器用于监听Broker端给我们返回的确认请求:RabbitTemplate.ConfirmCallback publisher-returns,保证消息对Broker端是可达的,如果出现路由键不可达的情况,则使用监听器对不可达的消息进行后续的处理,保证消息的路由成功:RabbitTemplate.ReturnCallback 注意一点,在发送消息的时候对template进行配置mandatory=tr