创建maven项目,导入netty
io.netty
netty-all
4.1.6.Final
项目结构为
首先编写tomcat启动类 Tomcat.java 代码如下,注释清晰
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
public class Tomcat {
public void start(int port) throws Exception {
// Boss线程
EventLoopGroup bossGroup = new NioEventLoopGroup();
// Worker线程
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// Netty服务
ServerBootstrap server = new ServerBootstrap();
// 链路式编程
server.group(bossGroup, workerGroup)
// 主线程处理类
.channel(NioServerSocketChannel.class)
// 子线程处理类 , Handler
.childHandler(new ChannelInitializer() {
// 客户端初始化处理
protected void initChannel(SocketChannel client) throws Exception {
// 无锁化串行编程
// HttpResponseEncoder 编码器
client.pipeline().addLast(new HttpResponseEncoder());
// HttpRequestDecoder 解码器
client.pipeline().addLast(new HttpRequestDecoder());
// 业务逻辑处理
client.pipeline().addLast(new TomcatHandler());
}
})
// 针对主线程的配置 分配线程最大数量 128
.option(ChannelOption.SO_BACKLOG, 128)
// 针对子线程的配置 保持长连接
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 启动服务器 sync 同步阻塞
ChannelFuture f = server.bind(port).sync();
System.out.println("Tomcat Startd Port: " + port);
f.channel().closeFuture().sync();
} finally {
// 关闭线程池
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
try {
new Tomcat().start(8080);
} catch (Exception e) {
e.printStackTrace();
}
}
}
接下来 我们来实现我们自己的业务处理类
继承 ChannelInboundHandlerAdapter 重写 channelRead 方法 指定我们自己实现的Servlet
import com.pro.catalina.http.Request;
import com.pro.catalina.http.Response;
import com.pro.catalina.servlets.MyServlet;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.HttpRequest;
public class TomcatHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest){
HttpRequest r = (HttpRequest) msg;
// 转交给我们自己的request实现
Request request = new Request(ctx,r);
// 转交给我们自己的response实现
Response response = new Response(ctx,r);
// 实际业务处理
MyServlet.class.newInstance().doGet(request,response);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
}
}
然后 我们来编写 Request Response 实现
Request 很简单 不细说
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import java.util.List;
import java.util.Map;
public class Request {
private ChannelHandlerContext ctx;
private HttpRequest r;
public Request(ChannelHandlerContext ctx, HttpRequest r) {
this.ctx = ctx;
this.r = r;
}
public String getUri() {
return r.uri();
}
public String getMethod() {
return r.method().name();
}
public Map> getParameters() {
QueryStringDecoder decoder = new QueryStringDecoder(r.uri());
return decoder.parameters();
}
public String getParameter(String name) {
Map> params = getParameters();
List param = params.get(name);
if (null == param) {
return null;
} else {
return param.get(0);
}
}
}
Response 需要设置http协议及请求头信息 , 代码如下,注释详细
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import static io.netty.handler.codec.http.HttpHeaderNames.*;
public class Response {
private ChannelHandlerContext ctx;
private HttpRequest r;
public Response(ChannelHandlerContext ctx, HttpRequest r) {
this.ctx = ctx;
this.r = r;
}
public void write(String out) throws Exception {
try {
if (out == null || out.length() == 0) {
return;
}
// 设置 http协议及请求头信息
FullHttpResponse response = new DefaultFullHttpResponse(
// 设置http版本为1.1
HttpVersion.HTTP_1_1,
// 设置响应状态码
HttpResponseStatus.OK,
// 将输出值写出 编码为UTF-8
Unpooled.wrappedBuffer(out.getBytes("UTF-8")));
// 设置连接类型 为 JSON
response.headers().set(CONTENT_TYPE, "text/json");
// 设置请求头长度
response.headers().set(CONTENT_LANGUAGE, response.content().readableBytes());
// 设置超时时间为5000ms
response.headers().set(EXPIRES, 5000);
// 当前是否支持长连接
// if (HttpUtil.isKeepAlive(r)) {
// // 设置连接内容为长连接
// response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE);
// }
ctx.write(response);
} finally {
ctx.flush();
ctx.close();
}
}
接下来进行Servlet编写
首先编写一个抽象的Servlet类
public abstract class Servlet {
public abstract void doGet(Request request,Response response);
public abstract void doPost(Request request,Response response);
}
然后实现我们自己的MyServlet类,进行业务处理
这里我们从 request 中获取 参数 name 再通过 response 写出
import com.pro.catalina.http.Request;
import com.pro.catalina.http.Response;
import com.pro.catalina.http.Servlet;
public class MyServlet extends Servlet {
public void doGet(Request request, Response response) {
try {
// 获取 name 参数 并返回
response.write(request.getParameter("name"));
} catch (Exception e) {
e.printStackTrace();
}
}
public void doPost(Request request, Response response) {
doGet(request,response);
}
}
接下来 启动Tomcat进行测试
访问本地localhost端口为8080在URL上增加参数name=tomcat
name的值tomcat 被成功返回 测试成功
项目地址: netty-tomcat-demo