1. demo1: netty开发http服务器代码demo
TestServer
public class TestServer {
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new TestServerInitializer());
ChannelFuture channelFuture = serverBootstrap.bind(8787).sync();
System.out.println("start server");
channelFuture.channel().closeFuture().sync();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
TestServerInitializer
public class TestServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//加入一个netty 提供的httpServerCodec codec =>[coder - decoder]
//HttpServerCodec 说明
//1. HttpServerCodec 是netty 提供的处理http的 编-解码器,可以加入名称
pipeline.addLast("MyHttpServerCodec",new HttpServerCodec());
//2. 增加一个自定义的handler
pipeline.addLast("MyTestHttpServerHandler", new TestHttpServerHandler());
System.out.println("ok~~~~");
}
}
TestHttpServerHandler
/**
* 说明
* 1. SimpleChannelInboundHandler 是 ChannelInboundHandlerAdapter的子类,提供的方法更多一些
* 2. HttpObject 客户端和服务器端相互通讯的数据被封装成 HttpObject
*/
public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
System.out.println("对应的channel=" + ctx.channel() + " pipeline=" + ctx
.pipeline() + " 通过pipeline获取channel" + ctx.pipeline().channel());
System.out.println("当前ctx的handler=" + ctx.handler());
//判断 msg 是不是 httprequest请求
if(msg instanceof HttpRequest) {
System.out.println("ctx 类型="+ctx.getClass());
System.out.println("pipeline hashcode" + ctx.pipeline().hashCode() + " TestHttpServerHandler hash=" + this.hashCode());
System.out.println("msg 类型=" + msg.getClass());
System.out.println("客户端地址" + ctx.channel().remoteAddress());
//获取到
HttpRequest httpRequest = (HttpRequest) msg;
//获取uri, 过滤指定的资源
String uri1 = httpRequest.uri();
URI uri = new URI(uri1);
if("/favicon.ico".equals(uri.getPath())) {
System.out.println("请求了 favicon.ico, 不做响应");
return;
}
//回复信息给浏览器 [http协议]
ByteBuf content = Unpooled.copiedBuffer("hello, 我是服务器", CharsetUtil.UTF_8);
//构造一个http的响应,即 httpresponse
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
//将构建好response返回
ctx.writeAndFlush(response);
}
}
}
netty服务器和TCP服务器的区别
1. http服务器需要添加编解码器pipeline.addLast("MyHttpServerCodec",new HttpServerCodec());
2. handler继承的对象不一样,TCP继承ChannelInboundHandlerAdapter,http继承的是ChannelInboundHandlerAdapter的子类SimpleChannelInboundHandler
2. demo2 网关的入口的写法
2.1 启动测试类
我们在线程池中启动netty,其实和主函数是一样的道理
@Test
public void test() throws ExecutionException, InterruptedException {
SessionServer server = new SessionServer();
Future<Channel> future = Executors.newFixedThreadPool(2).submit(server);
Channel channel = future.get();
if (null == channel) {
throw new RuntimeException("netty server start error channel is null");
}
while (!channel.isActive()) {
logger.info("NettyServer启动服务 ...");
Thread.sleep(500);
}
logger.info("NettyServer启动服务完成 {}", channel.localAddress());
Thread.sleep(Long.MAX_VALUE);
}
/**
* 实现callable接口,返回Channel
*/
public class SessionServer implements Callable<Channel> {
private final Logger logger = LoggerFactory.getLogger(SessionServer.class);
private final EventLoopGroup boss = new NioEventLoopGroup(1);
private final EventLoopGroup work = new NioEventLoopGroup();
private Channel channel;
@Override
public Channel call() throws Exception {
ChannelFuture channelFuture = null;
try {
ServerBootstrap b = new ServerBootstrap();
b.group(boss, work)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childHandler(new SessionChannelInitializer());
//不响应中断的 sync()
//阻塞等待任务结束,和 sync() 功能是一样的,不过如果任务失败,它不会抛出执行过程中的异常
channelFuture = b.bind(new InetSocketAddress(7397)).syncUninterruptibly();
this.channel = channelFuture.channel();
} catch (Exception e) {
logger.error("socket server start error.", e);
} finally {
if (null != channelFuture && channelFuture.isSuccess()) {
logger.info("socket server start done.");
} else {
logger.error("socket server start error.");
}
}
return channel;
}
}
public class SessionChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline line = channel.pipeline();
line.addLast(new HttpRequestDecoder());
line.addLast(new HttpResponseEncoder());
line.addLast(new HttpObjectAggregator(1024 * 1024));
line.addLast(new SessionServerHandler());
}
}
/**
* 这个地方还是写的非常精妙的,巧妙的把一些共同点抽出来了,值得学习这样的方式
*/
public class SessionServerHandler extends BaseHandler<FullHttpRequest> {
private final Logger logger = LoggerFactory.getLogger(SessionServerHandler.class);
@Override
protected void session(ChannelHandlerContext ctx, final Channel channel, FullHttpRequest request) {
String uri = request.uri();
HttpMethod method = request.method();
if (Objects.equals("/favicon.ico", uri)){
return;
}
logger.info("网关接收请求 uri:{} method:{}",uri,method);
// 返回信息处理
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
// 返回信息控制
response.content().writeBytes(JSON.toJSONBytes("你访问路径被小傅哥的网关管理了 URI:" + request.uri(), SerializerFeature.PrettyFormat));
// 头部信息设置
HttpHeaders heads = response.headers();
// 返回内容类型
heads.add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON + "; charset=UTF-8");
// 响应体的长度
heads.add(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
// 配置持久连接
heads.add(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
// 配置跨域访问
heads.add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
heads.add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS, "*");
heads.add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, PUT, DELETE");
heads.add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
channel.writeAndFlush(response);
}
}
public abstract class BaseHandler<T> extends SimpleChannelInboundHandler<T> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, T msg) throws Exception {
session(ctx, ctx.channel(), msg);
}
protected abstract void session(ChannelHandlerContext ctx, final Channel channel, T request);
}