Netty learning articles - Integration springboot

After the previous netty learn about and understand the concept of the role of the various components of netty, began his blind tinker integration netty and our common project (simple integration)
  1. Project Preparation
    工具:IDEA2017
    
    jar包导入:maven
    
    项目框架:springboot+netty
  2. Project Operation
    • Right-create a maven project, Project Name: hetangyuese-netty-03 (project has been uploaded github)

      Project complete structure

    avatar

    • Package guide 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 -->
    • coding

      • springboot startup class HetangyueseApplication

        Because the need to integrate netty start classes no longer inherit SpringBootServletInitializer class modified to achieve CommandLineRunner (after the implementation of the project started 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 startup class

        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;
            }
        }
      • Pipeline (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);
            }
        }
        
      • Business logic classes (Handler)

        There are many server ChannelPipeline of ChannelHandler, if each instance of a ChannelHandler, will generate a lot of ChannelHandler instance when a large number of client connections, in order to solve this problem netty instances can be shared by @ ChannelHandler.Sharable comment , an example of which is connected to the client processing

        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();
            }
        }
        
      • Profiles (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>
    • Start (I will not put the client code)

      // 服务端
      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

to sum up

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

Guess you like

Origin www.cnblogs.com/hetangyuese/p/11777090.html