当前位置:

首页 > 编程开发 > Nginx中的upstream模块如何使用

Nginx中的upstream模块如何使用

upstream模块简介nginx模块一般被分成三大类:handler、filter和upstream。前面的章节中,读者已经了解了handler、filter。利用这两类模块,可以使nginx轻松完成任何单机工作。而upstream模块,将使nginx跨越单机的限制,完成网络数据的接收、处理和转发。数据转发功能,为nginx提供了跨越单机的横向处理能力,使nginx摆脱只能为终端节点提供单一功能的限制,使它具备了网络应用级别的拆分、封装和整合的功能。数据转发是nginx有能力构建一个网络应用的关键组件。

upstream模块简介

  • nginx模块一般被分成三大类:handler、filter和upstream。前面的章节中,读者已经了解了handler、filter。利用这两类模块,可以使nginx轻松完成任何单机工作。

  • 而upstream模块,将使nginx跨越单机的限制,完成网络数据的接收、处理和转发。

  • 数据转发功能,为nginx提供了跨越单机的横向处理能力,使nginx摆脱只能为终端节点提供单一功能的限制,使它具备了网络应用级别的拆分、封装和整合的功能。

  • 数据转发是nginx有能力构建一个网络应用的关键组件。当然,鉴于开发成本的问题,一个网络应用的关键组件一开始往往会采用高级编程语言开发。但是当系统到达一定规模,并且需要更重视性能的时候,为了达到所要求的性能目标,高级语言开发出的组件必须进行结构化修改。

此时,对于修改代价而言,nginx的upstream模块体现出了它的优势,因为它天生就快。作为附带,nginx的配置系统提供的层次化和松耦合使得系统的扩展性也达到比较高的程度。

upstream模块接口

从本质上说,upstream属于handler,只是他不产生自己的内容,而是通过请求后端服务器得到内容,所以才称为upstream(上游)。请求并取得响应内容的整个过程已经被封装到nginx内部,所以upstream模块只需要开发若干回调函数,完成构造请求和解析响应等具体的工作。

upstream模块回调函数列举如下:

函数名称描述
create_request生成发送到后端服务器的请求缓冲(缓冲链),在初始化upstream 时使用
reinit_request在某台后端服务器出错的情况,nginx会尝试另一台后端服务器。 nginx选定新的服务器以后,会先调用此函数,以重新初始化 upstream模块的工作状态,然后再次进行upstream连接
process_header处理后端服务器返回的信息头部。所谓头部是与upstream server 通信的协议规定的,比如HTTP协议的header部分,或者memcached 协议的响应状态部分
abort_request在客户端放弃请求时被调用。不需要在函数中实现关闭后端服务 器连接的功能,系统会自动完成关闭连接的步骤,所以一般此函 数不会进行任何具体工作
finalize_request正常完成与后端服务器的请求后调用该函数,与abort_request 相同,一般也不会进行任何具体工作
input_filter处理后端服务器返回的响应正文。nginx默认的input_filter会 将收到的内容封装成为缓冲区链ngx_chain。该链由upstream的 out_bufs指针域定位,所以开发人员可以在模块以外通过该指针得到后端服务器返回的正文数据。memcached模块实现了自己的 input_filter,在后面会具体分析这个模块。
input_filter_init初始化input filter的上下文。nginx默认的input_filter_init 直接返回

memcached模块分析

  • memcache是一款高性能的分布式cache系统,得到了非常广泛的应用。memcache定义了一套私有通信协议,使得不能通过HTTP请求来访问memcache。但协议本身简单高效,而且memcache使用广泛,所以大部分现代开发语言和平台都提供了memcache支持,方便开发者使用memcache。

  • nginx提供了ngx_http_memcached模块,提供从memcache读取数据的功能,而不提供向memcache写数据的功能。

upstream模块使用的就是handler模块的接入方式。

同时,upstream模块的指令系统的设计也是遵循handler模块的基本规则:配置该模块才会执行该模块。

那么,upstream模块的特别之处究竟在哪里呢?那就是upstream模块的处理函数,upstream模块的处理函数进行的操作都包含一个固定的流程:(以memcached模块举例,在memcached的处理函数ngx_http_memcached_handler中)

创建upstream数据结构:

ngx_http_upstream_t            *u;
if (ngx_http_upstream_create(r) != NGX_OK) {
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
u = r->upstream;

设置模块的tag和schema。schema现在只会用于日志,tag会用于buf_chain管理:

ngx_str_set(&u->schema, "memcached://");
u->output.tag = (ngx_buf_tag_t) &ngx_http_memcached_module;

设置upstream的后端服务器列表数据结构:

mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
u->conf = &mlcf->upstream;

设置upstream回调函数:

u->create_request = ngx_http_memcached_create_request;
u->reinit_request = ngx_http_memcached_reinit_request;
u->process_header = ngx_http_memcached_process_header;
u->abort_request = ngx_http_memcached_abort_request;
u->finalize_request = ngx_http_memcached_finalize_request;
   
u->input_filter_init = ngx_http_memcached_filter_init;
u->input_filter = ngx_http_memcached_filter;

创建并设置upstream环境数据结构:

ctx = ngx_palloc(r->pool, sizeof(ngx_http_memcached_ctx_t));
if (ctx == NULL) {
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ctx->request = r;

ngx_http_set_ctx(r, ctx, ngx_http_memcached_module);

u->input_filter_ctx = ctx;

完成upstream初始化并进行收尾工作:

r->main->count++;
ngx_http_upstream_init(r);
return NGX_DONE;

任何upstream模块,简单如memcached,复杂如proxy、fastcgi都是如此。
不同的upstream模块在这6步中的最大差别会出现在第2、3、4、5上。

其中第2、4两步很容易理解,不同的模块设置的标志和使用的回调函数肯定不同。第5步也不难理解。

只有第3步是有点费解的,不同的模块在取得后端服务器列表时,策略的差异非常大,有如memcached这样简单明了的,也有如proxy那样逻辑复杂的。

第6步不同模块之间通常是一致的。将count加1,然后返回NGX_DONE。
nginx遇到这种情况,虽然会认为当前请求的处理已经结束,但是不会释放请求使用的内存资源,也不会关闭与客户端的连接。
之所以需要这样,是因为nginx建立了upstream请求和客户端请求之间一对一的关系,在后续使用ngx_event_pipe将upstream响应发送回客户端时,还要使用到这些保存着客户端信息的数据结构。
将upstream请求和客户端请求进行一对一绑定,这个设计有优势也有缺陷。优势就是简化模块开发,可以将精力集中在模块逻辑上,而缺陷同样明显,一对一的设计很多时候都不能满足复杂逻辑的需要。

回调函数:(依然是以memcached模块的处理函数为例)

  • ngx_http_memcached_create_request:很简单的按照设置的内容生成一个key,接着生成一个“get $key”的请求,放在r->upstream->request_bufs里面。

  • ngx_http_memcached_reinit_request:无需初始化。

  • ngx_http_memcached_abort_request:无需额外操作。

  • ngx_http_memcached_finalize_request:无需额外操作。

  • ngx_http_memcached_process_header:模块的业务重点函数。memcache协议的头部信息被定义为第一行文本,代码如下:

#define LF     (u_char) '\n'
for (p = u->buffer.pos; p < u->buffer.last; p++) {
    if (*p == LF) {
        goto found;
    }
}

如果在已读入缓冲的数据中没有发现LF(‘\n’)字符,函数返回NGX_AGAIN,表示头部未完全读入,需要继续读取数据。nginx在收到新的数据以后会再次调用该函数。

nginx处理后端服务器的响应头时只会使用一块缓存,所有数据都在这块缓存中,所以解析头部信息时不需要考虑头部信息跨越多块缓存的情况。而如果头部过大,不能保存在这块缓存中,nginx会返回错误信息给客户端,并记录error log,提示缓存不够大。

ngx_http_memcached_process_header的重要职责是将后端服务器返回的状态翻译成返回给客户端的状态。例如:

u->headers_in.content_length_n = ngx_atoof(start, p - start);
···
u->headers_in.status_n = 200;
u->state->status = 200;
···
u->headers_in.status_n = 404;
u->state->status = 404;

u->state用于计算upstream相关的变量。比如u->state->status将被用于计算变量“upstream_status”的值。u->headers_in将被作为返回给客户端的响应返回状态码。而u->headers_in.content_length_n则是设置返回给客户端的响应的长度。

在这个函数中一定要在处理完头部信息以后需要将读指针pos后移,否则这段数据也将被复制到返回给客户端的响应的正文中,进而导致正文内容不正确。

ngx_http_memcached_process_header函数完成响应头的正确处理,应该返回NGX_OK。如果返回NGX_AGAIN,表示未读取完整数据,需要从后端服务器继续读取数据。返回NGX_DECLINED无意义,其他任何返回值都被认为是出错状态,nginx将结束upstream请求并返回错误信息。

ngx_http_memcached_filter_init:修正从后端服务器收到的内容长度。因为在处理header时没有加上这部分长度。

ngx_http_memcached_filter:
memcached模块是少有的带有处理正文的回调函数的模块。
因为memcached模块需要过滤正文末尾CRLF “END” CRLF,所以实现了自己的filter回调函数。

处理正文的实际意义是将从后端服务器收到的正文有效内容封装成ngx_chain_t,并加在u->out_bufs末尾。

nginx并不进行数据拷贝,而是建立ngx_buf_t数据结构指向这些数据内存区,然后由ngx_chain_t组织这些buf。这种实现避免了内存大量搬迁,也是nginx高效的原因之一。

本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发
相关文章 更多
C++动态数组初始化怎么写?常用语句与代码示例
C++动态数组初始化怎么写?常用语句与代码示例

深入解析C++中动态数组的初始化机制,涵盖new操作符的不同用法、基本类型与类对象的初始化差异,以及为何在现代C++开发中应优先使用std::vector。

Nginx Windows版安装、启动与验证完整指南
Nginx Windows版安装、启动与验证完整指南

本教程针对Windows环境,详解Nginx稳定版(如1.24.0)的下载、解压、启动及验证流程。核心步骤包括:下载官方压缩包至英文目录,使用start nginx启动,通过localhost访问默认页面,并利用tasklist和nginx -t命令确认进程状态及配置语法。涵盖端口冲突排查、配置重载及停止服务的标准操作,适用于本地开发环境搭建与基础运维验证。

using namespace 使用中遇到的问题怎么解决
using namespace 使用中遇到的问题怎么解决

命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

c语言函数递归 实操经验总结:这些技巧很实用
c语言函数递归 实操经验总结:这些技巧很实用

理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接

c语言函数递归 怎么选?常见方案对比分析
c语言函数递归 怎么选?常见方案对比分析

递归函数的基本概念与适用场景在C语言编程中,递归是一种函数调用自身的编程技巧。它并非适用于所有问题,但在处理某些具有自相似结构的问题时,能提供极其清晰和优雅的解决方案。递归的核心思想是将一个大规模问题分解为一个或多个同类型但规模更小的子问题,直到子问题简单到可以直接求解。典型的适用场景包括树形结构的

Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解
Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解

理解内存管理的基石在Objective-C的编程世界中,内存管理是开发者必须掌握的核心技能之一。它直接关系到应用的性能、稳定性与资源利用效率。与一些采用自动垃圾回收机制的语言不同,Objective-C在很长一段时间里,依赖一套基于引用计数的、需要开发者部分介入的管理规则。这套规则的核心思想是明确的

如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏
如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏

理解 dealloc 的角色与时机在 iOS 应用开发中,内存管理是保障应用性能与稳定性的基石。dealloc 方法是 Objective-C 中对象生命周期结束时的关键回调,它标志着对象即将被系统回收内存。正确理解其触发时机至关重要:当一个对象的引用计数降为零时,运行时系统会自动调用该对象的 de

深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制
深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制

内存管理的基石在Objective-C的世界里,内存管理是开发者必须掌握的核心技能之一。作为一门在手动引用计数(MRC)时代诞生的语言,Objective-C要求程序员对对象的生命周期有清晰的认识。dealloc方法正是这一生命周期中至关重要的终点站。它是一个实例方法,当对象的引用计数降为零时,系统

理解 native2ascii:Java 国际化开发中的字符编码工具
理解 native2ascii:Java 国际化开发中的字符编码工具

native2ascii 工具的基本定位在Ja va应用程序的国际化与本地化开发过程中,处理非拉丁字符集是一个常见且关键的环节。Ja va内部使用Unicode字符集来统一表示全球各种语言的文字,但其属性文件(.properties)在历史上要求使用ASCII编码,或者更准确地说,要求非ASCII字

如何使用 native2ascii 转换中文字符为 Unicode 转义序列
如何使用 native2ascii 转换中文字符为 Unicode 转义序列

理解 native2ascii 工具的基本用途在软件开发,特别是涉及国际化处理的场景中,开发者常常需要处理不同编码的文本资源。native2ascii 是 Ja va 开发工具包(JDK)中提供的一个命令行实用程序,其主要功能是将包含本地字符编码(非ASCII字符)的文件,转换为包含 Unicode

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师
macOS

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

WINDOWS 更多
Windows 10
Windows 10
Windows

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘
Windows/macOS/iOS/Android

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。