公司有个业务需要优化,场景是手机客户端上传小文件到服务端,需要支持断点上传。现在直接用tomcat直接面对客户端进行网络IO,现在准备把网络IO这块剥离出来,彻底交给Nginx+Lua来处理。理由有如下几点:
1、应用服务器还是乖乖的待在后端做业务就好,底层的网络IO跟具体业务无关,这样使得项目的维护更方便,看起来更清晰。
2、对于底层IO操作,不是java的强项,特别是手机用户网速慢的场景,非WIFI用户网速不快,传输时间长,且数据量是间歇性的,时有时无,这种场景使用Nginx的epoll模型最合适不过了。
3、可以使用Nginx做负载均衡进行扩展。
下面写一个小demo来让Nginx+Lua进行网络IO,流程如下:
客户端POST一行数据给Nginx,地址为:http://192.168.112.64:38080/upload,Lua脚本读取数据,如果读取超时,则把当前已读取到的数据POST到后端tomcat,地址为:http://192.168.112.64/GoSmsMarService/Test;如果读取正常,也把读取到的数据POST到后端tomcat。可以根据具体场景直接把POST数据PUSH到缓存服务器,可以提高命中率,因为大多数用户在收到文件后马上会去查看,tomcat服务器再去异步的从缓存服务器读取数据进行持久化。
这里使用了nginx的扩展版本openresty,已经包含了N多第三方模块,当然包括了要使用到的Lua模块,安装过程略。
nginx配置文件如下,Lua代码注释挺详细了,不赘述:
worker_processes 1; error_log logs/error.log; events { worker_connections 1024; } http { # 后端tomcat upstream tomcat { server 127.0.0.1; } server { listen 38080; # tomcat地址 location ~ ^/GoSmsMarService { proxy_store off; proxy_redirect off; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $http_host; proxy_pass http://tomcat; } # 上传地址 location /upload { default_type text/html; # lua脚本超时设为1秒 lua_socket_read_timeout 1s; content_by_lua ' -- 得到cosocket对象 local sock, err_socket = ngx.req.socket() -- 开始读取 local line, err, partial = sock:receive() -- 如果读一行数据失败 if not line then -- 此次HTTP请求URI设为tomcat后端URI ngx.req.set_uri("/GoSmsMarService/Test") -- 设置post长度 ngx.req.set_header("Content-Length", string.len(partial)); -- 使用读取的数据片段重置post数据 ngx.req.set_body_data(partial) -- 内部请求跳转 return ngx.exec("/GoSmsMarService/Test") end -- 转发完整的post数据,注释参见上面几行 ngx.req.set_uri("/GoSmsMarService/Test") ngx.req.set_header("Content-Length", string.len(line)); ngx.req.set_body_data(line) return ngx.exec("/GoSmsMarService/Test") '; } } }
下面是tomcat接收Nginx的转发请求的servlet代码,把收到的数据写到文件并返回数据长度,代码很简单,不过多解释:
- import java.io.IOException;
- import javax.servlet.ServletException;
- import javax.servlet.ServletInputStream;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import com.g3net.tool.FileUtils;
- import com.g3net.tool.WriteStream;
- public class TestServlet extends HttpServlet {
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- ServletInputStream sis = req.getInputStream();
- WriteStream ws = new WriteStream();
- while (true) {
- int l = sis.read();
- if (l == -1) {
- break;
- }
- ws.write((byte) l);
- }
- try {
- // 将读取到的post数据写到文件系统
- FileUtils.write("/data/" + System.currentTimeMillis(), ws.toByteArray());
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- // 响应返回读到的post数据的长度
- resp.getWriter().print(ws.toByteArray().length);
- }
- }
import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.g3net.tool.FileUtils; import com.g3net.tool.WriteStream; public class TestServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletInputStream sis = req.getInputStream(); WriteStream ws = new WriteStream(); while (true) { int l = sis.read(); if (l == -1) { break; } ws.write((byte) l); } try { // 将读取到的post数据写到文件系统 FileUtils.write("/data/" + System.currentTimeMillis(), ws.toByteArray()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } // 响应返回读到的post数据的长度 resp.getWriter().print(ws.toByteArray().length); } }
下面写一个测试客户端测试一下读超时的情况,客户端发送post请求到Nginx,并把响应打印出来。post数据是不完整的一行,所以脚本1秒过后就会读超时,代码如下:
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.Socket;
- import com.g3net.tool.WriteStream;
- public class TestSocket {
- public static void main(String[] args) throws Exception {
- Socket socket = new Socket("192.168.112.64", 38080);
- InputStream is = socket.getInputStream();
- OutputStream os = socket.getOutputStream();
- WriteStream ws = new WriteStream();
- byte[] data = "post data".getBytes();
- StringBuffer sb = new StringBuffer("POST /upload HTTP/1.1\r\n");
- sb.append("User-Agent: Java/1.6.0_20\r\n");
- sb.append("Host: 192.168.112.64:38080\r\n");
- sb.append("Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2\r\n");
- sb.append("Content-Type: application/octet-stream\r\n");
- sb.append("Content-Length: " + data.length + "\r\n");
- sb.append("Connection: Close\r\n\r\n");
- os.write(sb.toString().getBytes());
- os.write(data);
- os.flush();
- while (true) {
- int l = is.read();
- if (l == -1) {
- break;
- }
- ws.writeByte((byte) l);
- }
- is.close();
- os.close();
- socket.close();
- System.out.println(new String(ws.toByteArray()));
- }
- }
import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import com.g3net.tool.WriteStream; public class TestSocket { public static void main(String[] args) throws Exception { Socket socket = new Socket("192.168.112.64", 38080); InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream(); WriteStream ws = new WriteStream(); byte[] data = "post data".getBytes(); StringBuffer sb = new StringBuffer("POST /upload HTTP/1.1\r\n"); sb.append("User-Agent: Java/1.6.0_20\r\n"); sb.append("Host: 192.168.112.64:38080\r\n"); sb.append("Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2\r\n"); sb.append("Content-Type: application/octet-stream\r\n"); sb.append("Content-Length: " + data.length + "\r\n"); sb.append("Connection: Close\r\n\r\n"); os.write(sb.toString().getBytes()); os.write(data); os.flush(); while (true) { int l = is.read(); if (l == -1) { break; } ws.writeByte((byte) l); } is.close(); os.close(); socket.close(); System.out.println(new String(ws.toByteArray())); } }
返回结果如下图所示,返回的字节数正好是9个。
再看下文件系统的数据,如下图所示:
把byte[] data = "post data".getBytes();改成byte[] data = "post data complete\r\n".getBytes();
加上换行符就不会出现读超时了,再运行一下,返回的字节发生变化,结果如下图所示:
相关推荐
例如,我们可以编写一个`lua`脚本来从请求头中提取`token`,然后使用`redis`进行验证。`lua`连接`redis`的库,如`lua-resty-redis`,可以帮助我们与`redis`进行通信,查询或设置键值。 以下是`lua`连接`redis`的一...
在这个“nginx+lua+redis集群”解决方案中,提供的连接插件可能是指Nginx与Redis之间的通信模块,例如lua-redis-pconnector,这个插件允许Nginx通过Lua脚本直接与Redis进行交互。而脚本部分可能包括了处理业务逻辑、...
本教程将详细解析如何使用Nginx、upload模块以及lua来搭建一个简单的文件上传系统。 首先,我们需要了解Nginx的ngx_http_upload_module模块,这是一个用于处理HTTP POST请求中的文件上传的第三方模块。它允许我们在...
nginx+lua+redis通过匹配客户端ip进行灰度发布 本文将讲述如何使用nginx、lua和redis来实现灰度发布,通过匹配客户端IP来实现灰度发布。灰度发布是一种常见的软件发布方式,它允许开发者在生产环境中发布新的版本,...
《跟我学Nginx+Lua开发》这本书主要探讨了如何利用Nginx与Lua进行高效、灵活的Web服务开发。Nginx以其高性能、反向代理和负载均衡能力而闻名,而Lua则是一种轻量级的脚本语言,两者结合可以实现强大的服务器端功能。...
### Nginx+Lua通过URL传参方式实现动态代理 #### 概述 在现代Web应用开发中,灵活地管理后端服务之间的通信是至关重要的。动态代理技术为这一需求提供了解决方案,使得前端可以方便地根据业务逻辑将请求转发至不同...
nginx+lua+docker+docker-compose实现简单服务分发
Nginx+Lua+GraphicsMagick实现动态生成指定尺寸的图片
"Idea OpenResty + Nginx + Lua" 的组合提供了一种强大的解决方案,它允许开发者利用 IntelliJ IDEA(Idea)作为集成开发环境,OpenResty(一个扩展了 Nginx 的版本)作为服务器平台,以及 Lua 语言进行高性能的脚本...
我们选择使用OpenResty,其是由Nginx核心加很多第三方模块组成,其最大的亮点是默认集成了Lua开发环境,使得Nginx可以作为一个Web Server使用。借助于Nginx的事件驱动模型和非阻塞IO,可以实现高性能的Web应用程 序...
为了在Tengine/Nginx的rewrite规则中插入复杂逻辑,可以使用rewrite_by_lua和set_by_lua方法。前者适合编写完整的rewrite逻辑,但不适用于与现有rewrite功能混合使用,后者则可用于设置变量,然后在location块中进行...
3. **Lua 获取参数与JSON操作**:可以使用`lua`处理请求参数和JSON数据,便于在服务调度逻辑中进行复杂的数据处理。 4. **Lua 读写文件路径问题**:`lua`处理文件路径时需要注意路径的正确性和权限设置。 **七、`...
它不仅包含了标准Nginx的核心功能,还集成了LuaJIT运行时环境,允许开发者直接在Nginx内部使用Lua脚本编写高性能的应用程序逻辑。 - **核心优势**: - **高性能**:OpenResty利用Nginx的事件驱动模型和非阻塞I/O...
这些模块使得开发者能够在Nginx内直接使用Lua脚本来编写网络程序,非常适合于实现高并发、低延迟的网络服务。 具体到知识点,本文件涉及的主要内容包括: 1. Nginx+Lua开发环境安装 安装Nginx+Lua开发环境首先...
提到高并发或者抗压力,有这种高qps经验的同学第一反应大都是Nginx+lua+Redis,网上也满天非那种高并发架构方案大都是这种,但是Nginx+lua来做接入层到底是怎么抗住压力的呢?本篇顺序:1、Nginx如何抗住的高并发,...
《跟我学Nginx+Lua开发》是一本针对Nginx与Lua结合使用的教程书籍,旨在帮助读者理解如何利用这两种技术进行高效且灵活的Web应用开发。Nginx作为一款广泛使用的高性能HTTP服务器和反向代理服务器,在处理高并发连接...
nginx +lua 实现 访问远程地址 下载文件 环境配置: 启动tomcat 把要下载的文件放到跟目录比如:pdf zip 等 启动nginx 访问nginx lua 打开socket 访问tomcat 文件路径,并下载到本地
在本文中,我们将介绍如何使用 NGINX + LUA 实现复杂的控制,包括安装 LUA 模块、使用 LUA 脚本实现访问限制、rewrite 跳转等功能。 一、安装 LUA 模块 要使用 LUA 在 NGINX 中实现复杂的控制,需要安装 LUA 模块...
- 线上压测/功能验证:使用Nginx+Lua进行线上环境的性能测试和功能验证。 六、优化实践案例 在实际应用中,京东通过Nginx+Lua的技术组合实现了多种优化: - 使用Unix Domain Socket套接字,减少了本机TCP连接数,...