Netty学习篇--整合springboot
经过前面的netty学习,大概了解了netty各个组件的概念和作用,开始自己瞎鼓捣netty和我们常用的项目的整合(很简单的整合)
项目准备
工具:IDEA2017 jar包导入:maven 项目框架:springboot+netty
项目操作
右键创建一个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源码部分,也能看明白每一步的用途,学无止境!!!
相关推荐
fengshantao 2020-10-29
arctan0 2020-10-14
爱传文档 2020-07-28
gzx0 2020-07-05
fengshantao 2020-07-04
fengshantao 2020-07-02
jannal 2020-06-21
arctan0 2020-06-19
arctan0 2020-06-16
gzx0 2020-06-14
fengshantao 2020-06-13
gzx0 2020-06-12
arctan0 2020-06-11
fengshantao 2020-06-11
mbcsdn 2020-05-19
arctan0 2020-05-16
爱传文档 2020-05-08
爱传文档 2020-05-04