一、什么是Netty?
Netty是一个提供了易于使用的api的客户端/服务器框架,Netyy的底层使用的是Nio(非阻塞io)零拷贝,传输快,并发高
二、Netty的线程模型
Netty的线程模型分别为 单线程,多线程,主从线程。 官方推荐的是 让我们使用主从线程
三、Netty之Hello Netty
3.1添加netty依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.50.Final</version>
</dependency>
3.2在启动类中加入以下代码
import com.example.netty.nettydemo.config.HelloServerInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 实现客户端发送一个请求 ,服务器会返回 hello netty
*/
@SpringBootApplication
public class NettydemoApplication {
public static void main(String[] args) throws InterruptedException {
//定义一对线程组
//主线程组,用于接受客户端的连接,但是不做任何处理,跟老板一样,不做事
NioEventLoopGroup boosGroup = new NioEventLoopGroup();
//从线程组,老板线程组会把任务丢给他,让手下线程组去做任务
NioEventLoopGroup workGroup = new NioEventLoopGroup();
try {
//netty服务器的创建,ServerBootStrap是一个启动类
ServerBootstrap bootstrap = new ServerBootstrap();
//设置主从线程组
bootstrap.group(boosGroup,workGroup)
//设置NIO的双向通道
.channel(NioServerSocketChannel.class)
//字处理器,用于处理 workGroup
.childHandler(new HelloServerInitializer());
//启动server,并且设置8088为启动的端口号,同时启动方式为同步
ChannelFuture channelFuture = bootstrap.bind(8088).sync();
//监听关闭的channel设置为同步
channelFuture.channel().closeFuture().sync();
}finally {
//优雅的关闭 netty开启的线程连接
boosGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
SpringApplication.run(NettydemoApplication.class, args);
}
}
3.3创建字处理器
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
/**
* 初始化器,channel注册后,会执行里面的相应的初始化方法
* @author 风行烈
* @version 1.0
* @date 2020/8/26 17:09
*/
public class HelloServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
//通过SocketChannel去获取对应的管道
ChannelPipeline pipeline = channel.pipeline();
//通过管道,添加handler
//HttpServerCodes 是netty提供的助手类,可以理解拦截器
//当请求到服务端 我们需要做编解码 ,响应到客户端做编码,在服务端做解码
pipeline.addLast("HttpServerCodec",new HttpServerCodec());
//添加自定义的助手类,返回hello word
pipeline.addLast("customHandler",new CustomHandler());
}
}
3.4添加自定义handler用来获取客户端的消息
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.Charset;
/**
* 创建自定义助手类
* @author 风行烈
* @version 1.0
* @date 2020/8/27 10:01
*/
/**
* SimpleChannelInboundHandler:对于请求来讲,相当于入站
*/
@Slf4j
public class CustomHandler extends SimpleChannelInboundHandler<HttpObject> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
//从一个上下文对象里面 获取当前的这个管道
Channel channel = ctx.channel();
if(msg instanceof HttpRequest)
{
//获取客户端的远程地址
log.info("客户端的远程地址:{}",channel.remoteAddress());
//通过缓冲区发送数据
ByteBuf word = Unpooled.copiedBuffer("Hello word", CharsetUtil.UTF_8);
//发送到客户端消息
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, word);
//设置发送给客户端的消息的类型 可以设置为 文本类型 也可以设置为图片类型 或者返回的json类型
response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
//设置发送给客户端的消息长度
response.headers().set(HttpHeaderNames.CONTENT_LENGTH,word.readableBytes());
//需要把我们的内容发送给客户端
ctx.writeAndFlush(response);
}
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
log.info("channel-------------------------注册");
super.channelRegistered(ctx);
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
log.info("channel-----------------------移除");
super.channelUnregistered(ctx);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("channel---------------------------活跃");
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.info("channel----------------------------不活跃");
super.channelInactive(ctx);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
log.info("channel------------------读取完毕");
super.channelReadComplete(ctx);
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
log.info("channel用户触发");
super.userEventTriggered(ctx, evt);
}
@Override
public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
log.info("channel可写更改");
super.channelWritabilityChanged(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
}
}
3.5访问监听的8088端口