Netty实现简单HTTP代理服务器
自上次使用Openresty+Lua+Nginx的来加速自己的网站,用上了比较时髦的技术,感觉算是让自己的网站响应速度达到极限了,直到看到了Netty,公司就是打算用Netty来替代Openresty这一套,所以,自己也学了好久,琢磨了好一趟才知道怎么用,现在用来写一套HTTP代理服务器吧,之后再测试一下性能。
之前相关的文章如下:
【网页加速】lua redis的二次升级
使用Openresty加快网页速度
一、Netty中的HTTP
参考自《Netty实战》
一个完整的HttpRequest请求
FullHttpRequest:
- HTTP Request 第一部分是包含的头信息
- HttpContent 里面包含的是数据,可以后续有多个 HttpContent 部分
- LastHttpContent 标记是 HTTP request 的结束,同时可能包含头的尾部信息
- 完整的 HTTP request
一个完整的HttpResponse请求
FullHttpResponse:
- HTTP response 第一部分是包含的头信息
- HttpContent 里面包含的是数据,可以后续有多个 HttpContent 部分
- LastHttpContent 标记是 HTTP response 的结束,同时可能包含头的尾部信息
- 完整的 HTTP response
二、Netty实现HTTP代理服务器的流程
在实现Http代理服务器之前,我们先来查看一下Netty实现代理服务器的完整流程:
Netty的Http服务的流程是:
1、Client向Server发送http请求,在通常的情况中,client一般指的是浏览器,也可以由自己用netty实现一个客户端。此时,客户端需要用到HttpRequestEncoder将http请求进行编码。
2、Server端对http请求进行解析,服务端中,需要用到HttpRequestDecoder来对请求进行解码,然后实现自己的业务需求。
3、Server端向client发送http响应,处理完业务需求后,将相应的内容,用HttpResponseEncoder进行编码,返回数据。
4、Client对http响应进行解析,用HttpResponseDecoder进行解码。
而Netty实现Http代理服务器的过程跟上面的所说无意,只不过是在自己的业务层增加了回源到tomcat服务器这一过程。结合上自己之前实现过的用OpenResty+Nginx来做代理服务器这一套,此处的Netty实现的过程也与此类似。此处粘贴一下OpenResty+Nginx实现的流程图:
而使用了Netty之后,便是将中间的OpenResty+Nginx换成了Netty,下面我们来看一下具体的实现过程。
三、主要代码如下:
HttpServer
public class HttpServer {
public void start(int port) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.handler(new LoggingHandler(LogLevel.DEBUG))
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch)
throws Exception {
// server端发送的是httpResponse,所以要使用HttpResponseEncoder进行编码
ch.pipeline().addLast(
new HttpResponseEncoder());
// server端接收到的是httpRequest,所以要使用HttpRequestDecoder进行解码
ch.pipeline().addLast(
new HttpRequestDecoder());
ch.pipeline().addLast(
new HttpServerHandler());
//增加自定义实现的Handler
ch.pipeline().addLast(new HttpServerCodec());
}
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
HttpServer server = new HttpServer();
server.start(8080);
}
}
HttpServerHandler
@Slf4j
public class HttpServerHandler extends ChannelInboundHandlerAdapter {
private RedisUtil redisUtil = new RedisUtil();
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
if (msg instanceof HttpRequest) {
DefaultHttpRequest request = (DefaultHttpRequest) msg;
String uri = request.uri();
if ("/favicon.ico".equals(uri)) {
return;
}
log.info(new Date().toString());
Jedis jedis = redisUtil.getJedis();
String s = jedis.get(uri);
if (s == null || s.length() == 0) {
//这里我们的处理是回源到tomcat服务器进行抓取,然后
//将抓取的内容放回到redis里面
try {
URL url = new URL("http://119.29.188.224:8080" + uri);
log.info(url.toString());
URLConnection urlConnection = url.openConnection();
HttpURLConnection connection = (HttpURLConnection) urlConnection;
connection.setRequestMethod("GET");
//连接
connection.connect();
//得到响应码
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader
(connection.getInputStream(), StandardCharsets.UTF_8));
StringBuilder bs = new StringBuilder();
String l;
while ((l = bufferedReader.readLine()) != null) {
bs.append(l).append("\n");
}
s = bs.toString();
}
jedis.set(uri, s);
connection.disconnect();
} catch (Exception e) {
log.error("", e);
return;
}
}
jedis.close();
FullHttpResponse response = new DefaultFullHttpResponse(
HTTP_1_1, OK, Unpooled.wrappedBuffer(s != null ? s
.getBytes() : new byte[0]));
response.headers().set(CONTENT_TYPE, "text/html");
response.headers().set(CONTENT_LENGTH,
response.content().readableBytes());
response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE);
ctx.write(response);
ctx.flush();
} else {
//这里必须加抛出异常,要不然ab测试的时候一直卡住不动,暂未解决
throw new Exception();
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
ctx.close();
}
}
四、性能测试
下面的是ab测试,在1GHz、2G内存的centos7机器(阿里云服务器)下进行测试,测试命令ab -c 100 -n 10000 localhost:8000/,并发数为100,总数为10000。
性能:
整体响应时间的分布比(单位:ms):
看完之后,我自己也震惊了,Netty实现的不仅稳定、吞吐率还比OpenResty的高出一倍,OpenResty的居然还有那么多的失败次数,不知是不是我的代码的问题还是测试例子不规范,至今,我还是OpenResty的脑残粉。总体的来说,Netty实现的服务器性能还是比较强的,不仅能够快速地开发高性能的面向协议的服务器和客户端,还可以在Netty上轻松实现各种自定义的协议。
五、源码地址
https://github.com/Zephery/myway
参考:
- 《Netty实战》
- 基于Netty4构建HTTP服务----浏览器访问和Netty客户端访问
Netty实现简单HTTP代理服务器的更多相关文章
- netty系列之:小白福利!手把手教你做一个简单的代理服务器
目录 简介 代理和反向代理 netty实现代理的原理 实战 总结 简介 爱因斯坦说过:所有的伟大,都产生于简单的细节中.netty为我们提供了如此强大的eventloop.channel通过对这些简单 ...
- Netty学习——基于netty实现简单的客户端聊天小程序
Netty学习——基于netty实现简单的客户端聊天小程序 效果图,聊天程序展示 (TCP编程实现) 后端代码: package com.dawa.netty.chatexample; import ...
- .Net Core 3.0后台使用httpclient请求网络网页和图片_使用Core3.0做一个简单的代理服务器
目标:使用.net core最新的3.0版本,借助httpclient和本机的host域名代理,实现网络请求转发和内容获取,最终显示到目标客户端! 背景:本人在core领域是个新手,对core的使用不 ...
- netty 实现简单的rpc调用
yls 2020/5/23 netty 实现简单rpc准备 使用netty传输java bean对象,可以使用protobuf,也可以通过json转化 客户端要将调用的接口名称,方法名称,参数列表的类 ...
- netty的简单的应用例子
一.简单的聊天室程序 public class ChatClient { public static void main(String[] args) throws InterruptedExcept ...
- Netty心跳简单Demo
前面简单地了解了一下IdleStateHandler,我们现在写一个简单的心跳demo: 1)服务器端每隔5秒检测服务器端的读超时,如果5秒没有接受到客户端的写请求,也就说服务器端5秒没有收到读事件, ...
- Netty构建游戏服务器(三)--netty spring简单整合
一,基本方法 上节实现了netty的基本连接,这节加入spring来管理netty,由spring来开启netty服务. 在netty服务器中,我们建立了三个类:HelloServer(程序主入口) ...
- Netty+WebSocket简单实现网页聊天
基于Netty+WebSocket的网页聊天简单实现 一.pom依赖 <dependency> <groupId>io.netty</groupId> ...
- Java使用Netty实现简单的RPC
造一个轮子,实现RPC调用 在写了一个Netty实现通信的简单例子后,萌发了自己实现RPC调用的想法,于是就开始进行了Netty-Rpc的工作,实现了一个简单的RPC调用工程. 如果也有兴趣动手造轮子 ...
随机推荐
- tensorflow学习之(七)使用tensorboard 展示神经网络的graph/histogram/scalar
# 创建神经网络, 使用tensorboard 展示graph/histogram/scalar import tensorflow as tf import numpy as np import m ...
- #实验三 敏捷开发与XP实践---实验报告
一.实验三 敏捷开发与XP实践-1 1.实验要求 -实验三 敏捷开发与XP实践 http://www.cnblogs.com/rocedu/p/4795776.html, Eclipse的内容替换成I ...
- MySQL事物管理
sql语句对数据库操作构成事物. 事物的特性:ACID •Atomicity(原子性):一个事物要么全都被执行,要么全都不执行,不会存在执行结束在中间环节.如果事物在执行过程中发生异常,则会回滚到事物 ...
- 欧拉函数-gcd-快速幂(牛客寒假算法基础集训营1-D-小a与黄金街道)
题目描述: 链接:https://ac.nowcoder.com/acm/contest/317/D来源:牛客网小a和小b来到了一条布满了黄金的街道上.它们想要带几块黄金回去,然而这里的城管担心他们拿 ...
- Maths | 层次分析法(Analytic Hierarchy Process)
目录 1. 概述 2. AHP算法 2.1. 建立层级 2.2. 构造 成对 比较 矩阵 2.3. 成对比较矩阵的 一致性检验 与 层次单排序 2.4. 层次总排序 参考: (中文)https://z ...
- 利用websocket实现微信二维码码扫码支付
由于业务需要引入微信扫码支付,故利用websocket来实现消息推送技术. 实现大致流程:首先客户端点击微信支付按钮,触发微信支付接口,同时微信支付响应成功参数后,连接websocket客户端,此刻利 ...
- python之路(五)-文件操作
文件操作无非两个,即:读.写 python 2.x: 文件句柄 = file('文件路径', '模式') python3.x: 文件句柄 = open('文件路径', '模式') 打开文件的模式有: ...
- PIL库图像处理
PIL有如下几个模块 Image模块.ImageChops模块.ImageCrackCode模块 ImageDraw模块.ImageEnhance模块.ImageFile模块 ImageFileIO模 ...
- 让DIV随滚动条滚动
$(window).scroll(function(event){ //JQ监听滚动条 $('DIV').css("top", $(window).scrollTop ...
- day18_雷神_django第一天
# django_day01 1.http 协议 超文本传输协议,HTTP有很多应用,但最著名的是用于web浏览器和web服务器之间的双工通信. 协议概述 HTTP是一个客户端终端(用户)和服务器端( ...