经过前面的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的基础知识后,了解到很多rpc框架都运用了netty,看了下dubbo的netty源码部分,也能明白每一步的用途,接下来自己琢磨写个rpc框架试试,学无止境!!!

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

  1. Netty学习篇⑤--编、解码

    前言 学习Netty也有一段时间了,Netty作为一个高性能的异步框架,很多RPC框架也运用到了Netty中的知识,在rpc框架中丰富的数据协议及编解码可以让使用者更加青睐: Netty支持丰富的编解 ...

  2. 小书MybatisPlus第1篇-整合SpringBoot快速开始增删改查

    Mybatis Plus官方文档已经很完善了,为什么还要写一个这样的文档? 官方文档注重知识结构的整理,没有注重学习者的学习顺序 官方文档中的案例注重API描述,比较适合学会mybatis plus之 ...

  3. Java-Dubbo学习及整合SpringBoot

    Dubbo架构 Dubbo是Java的RPC框架,具有三大核心功能:面向接口的远程方法调用,智能容错和负载均衡,以及服务的自动注册和发现 Dubbo架构图: 节点角色说明: 节点 说明 Provide ...

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

    心跳检测 前言 客户端和服务端的连接属于socket连接,也属于长连接,往往会存在客户端在连接了服务端之后就没有任何操作了,但还是占用了一个连接:当越来越多类似的客户端出现就会浪费很多连接,netty ...

  5. Netty学习篇⑥--ByteBuf源码分析

    什么是ByteBuf? ByteBuf在Netty中充当着非常重要的角色:它是在数据传输中负责装载字节数据的一个容器;其内部结构和数组类似,初始化默认长度为256,默认最大长度为Integer.MAX ...

  6. Netty学习篇②

    Channel.ChannelPipeline.ChannelHandlerContent发送数据的不同 // channel往回写数据 Channel channel = ctx.channel() ...

  7. Netty学习篇①

    什么是netty Netty封装了JDK自带的NIO,运用起来更加简单快速,Netty是一个异步事件驱动的网络应用框架,让开发更加简便 Netty相比JDK自带的NIO的优点 Netty的api调用简 ...

  8. jackson学习之十(终篇):springboot整合(配置类)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  9. Netty学习——Netty和Protobuf的整合(二)

    Netty学习——Netty和Protobuf的整合(二) 这程序是有瑕疵的,解码器那里不通用,耦合性太强,有两个很明显的问题,但是要怎么解决呢?如:再加一个内部类型 Person2,之前的代码就不能 ...

随机推荐

  1. python执行ad_hoc

    import shutil from collections import namedtuple from ansible.parsing.dataloader import DataLoader f ...

  2. jQuery.proxy(function,context)

    jQuery.proxy(function,context) 概述 jQuery 1.4 新增.返回一个新函数,并且这个函数始终保持了特定的作用域.大理石平台检定规程 当有事件处理函数要附加到元素上, ...

  3. xShell终端中文乱码-解决方法

    中文乱码的原因有三种:(1)Linux系统的编码问题(2)xShell终端的编码问题(3)两端的语言编码不一致 linux系统编码 (1) locale:查看系统语言 (2)echo $LANG:查看 ...

  4. 一个list<Map>里map其中的一个字段的值相同,如何判断这个字段相同,就把这个map的其他字段存入另一个map中

    //不建议使用Map保存这些,使用实体bean更好 package com.rxlamo.zhidao;   import java.util.*;   public class Main {     ...

  5. mysql8.0.17gtid方式实现主从同步

    数据库的安装: [root@node1 8.0.17]# rpm -ivh mysql-community-common-8.0.17-1.el7.x86_64.rpm 警告:mysql-commun ...

  6. k8s之yaml详解

    k8s之yaml详解 apiVersion: v1 #指定api版本,此值必须在kubectl apiversion中 kind: Pod #指定创建资源的角色/类型 metadata: #资源的元数 ...

  7. Matlab下imwrite,Uint16的深度图像

    Matlab下imwrite,Uint16的深度图像 1. 在Matlab命令窗口输入命令: help imwrite 会有如下解释: If the input array is of class u ...

  8. oracle查找表索引信息

    select owner,index_name,index_type from all_indexes where owner='xxxx' and table_name='xxx' select * ...

  9. 转载---WCF、WPF、Silverlight和区别

    转自--http://hi.baidu.com/wl5026442/item/6ce62b4d19ff64e61381da9c SilverLight可以看作是WPF的一个简化版本,或者一个轻量版本. ...

  10. CLR 虚方法调用和接口方法调用

    不知接口方法和虚方法分发有什么区别?似乎在CIL中都是callvirt指令. 对,MSIL里都是callvirt,但JIT的时候得到了不同的处理:对虚方法的分发是编译成这样: mov  ecx, es ...