前言
由于今天是要练车的,所以的话只能写一点简单的东西了,做一个整合吧,刚好先前随便说了一下Netty是吧,那么我们就直接实战吧,我们来整合一下Netty。我的设想是使用Netty来实现客户端消息的实时推送,就是这个破玩意:
当然还有咱们的聊天,用户聊天,反正都做推送了,再加一个用户聊天有何不可。都TM是一个玩意。 那么既然咱们是使用SpringBoot或者是Spring的话,那么我们就刚好直接使用IOC来实例化咱们的Netty。那么这样的话,我们可以使用Netty来实现实时的消息推送,以及在线用户聊天功能,同时的话,对于咱们后台的管理系统也刚好有这个需求,有新的博文审核消息啥的这个要推送给后台的。那么同时为了提高整个实时在线的工作效率,数据的存储直接进行异步处理,这个方案很多,那么最省事的就是搞个线程池+异步任务就完了,或者直接MQ过去,然后存储,反正这种数据丢了也没事。像博文,问答之类的数据,这些注意一点就完了。
那么本文的话,两个目标,第一个是怎么整合,服务端和客户端怎么整合。第二个就是我们实际上就是说,创建一个服务端,可以完成websock协议。
项目创建
首先我们创建一个新的项目。 在我这里的话是已经创建好了:
可以看到,我这里的话,做戏做全套,那么这里有两个端,一个是Server,还有是Client,什么意思,就是说,你使用Netty做服务端可以,做客户端链接其他的服务器也可以,rpc嘛。 例如:
整合
导入依赖
首先是导入依赖,这里的话是SpringBoot,那么直接导入这个就完了:
<!-- 导入Netty的依赖-->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
复制代码
编写相关配置
既然是用到了SpringBoot,那么我们直接把对应的配置给提取出来:
#Netty的一些配置
netty:
boss: 4
worker: 2
timeout: 6000
port: 9000
#多端口绑定
portSalve: 9001
host: 127.0.0.1
复制代码
这个看你自己,我这里演示就是多端口的。值得一提的是这个演示的,演示完就删了哈,所以这篇博文会尽可能详细。毕竟,我以后搞不好还会回来copy。
服务端创建
首先是我们的服务端,那么在这里的话,我们想要创建一个Netty服务,基本上就三个东西,一个是我们选择哪一个线程模型,这里我们显然选择主从模型。所以的话,那么我们就需要准备两个线程组,之后的话是我们的初始化器,用来初始化Handler,之后是我们自定义的Handler。这里为了更好地演示,我们这里以创建Http处理的为例子。之后的话我们以创建websocket为例子。
ok,既然如此我们知道了需要那些东西,那么我们直接去搞就好了。
读取配置
首先要做的就是读取配置,我这里准备了专门的配置类。
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@ConfigurationProperties(prefix = "netty")
@Data
@Configuration
public class HoleNettyProperties {
// boss线程数量 默认为cpu线程数*4
private Integer boss;
// worker线程数量 默认为cpu线程数*2
private Integer worker;
// 连接超时时间 默认为30s
private Integer timeout = 30000;
// 服务器主端口 默认9000
private Integer port = 9000;
// 服务器备用端口
private Integer portSalve = 9001;
// 服务器地址 默认为本地
private String host = "127.0.0.1";
}
复制代码
配置类
之后的话,是我们的一个配置: 这个配置主要就是声明一个Bootstrap开启服务,之后绑定我们设定的配置和处理器。
@Configuration
@EnableConfigurationProperties
public class NettyConfig {
@Autowired
HoleNettyProperties holeNettyProperties;
/**
* boss 线程池
* 负责客户端连接
* @return
*/
@Bean
public NioEventLoopGroup boosGroup(){
return new NioEventLoopGroup(holeNettyProperties.getBoss());
}
/**
* worker线程池
* 负责业务处理
* @return
*/
@Bean
public NioEventLoopGroup workerGroup(){
return new NioEventLoopGroup(holeNettyProperties.getWorker());
}
/**
* 服务器启动器
* @return
*/
@Bean
public ServerBootstrap serverBootstrap(){
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap
.group(boosGroup(),workerGroup()) // 指定使用的线程组
.channel(NioServerSocketChannel.class) // 指定使用的通道
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,holeNettyProperties.getTimeout()) // 指定连接超时时间
.childHandler(new ServerHandler()); // 指定worker处理器
return serverBootstrap;
}
/**
* 客户端启动器
* @return
*/
@Bean
public Bootstrap bootstrap(){
// 新建一组线程池
NioEventLoopGroup eventExecutors = new NioEventLoopGroup(holeNettyProperties.getBoss());
Bootstrap bootstrap = new Bootstrap();
bootstrap
.group(eventExecutors) // 指定线程组
.option(ChannelOption.SO_KEEPALIVE, true)
.channel(NioSocketChannel.class) // 指定通道
.handler(new ClientHandler()); // 指定处理器
return bootstrap;
}
}
复制代码
可以看到的话,我们这里是有两个的,一个是用来创建服务端的,还有一个是用来创建客户端的。在这里的话,我们主要还是做声明。
区别是 在于指定线程组。以及在我们后面真正实例化的时候,前者是监听,后者是连接。
服务处理器 ServerHandler
这个玩意其实就是用来做初始化的。
public class ServerHandler extends ChannelInitializer<SocketChannel> {
/**
* 初始化通道以及配置对应管道的处理器
* @param socketChannel
* @throws Exception
*/
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new MessageDecodeHandler());
pipeline.addLast(new MessageEncodeHandler());
pipeline.addLast(new ServerListenerHandler());
}
}
复制代码
同时在这里指定了两个消息的编解码器。
public class MessageDecodeHandler extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
int len = byteBuf.readInt();
byte[] conten