Nginx学习(9)—— 负载均衡模块

文章目录

  • Nginx负载均衡模块
    • 负载均衡配置
    • 指令
    • 钩子
    • 初始化配置
    • 初始化请求
    • peer.get和peer.free回调函数
  • 小结

Nginx负载均衡模块

负载均衡模块用于从”upstream”指令定义的后端主机列表中选取一台主机。nginx先使用负载均衡模块找到一台主机,再使用upstream模块实现与这台主机的交互。

负载均衡配置

要了解负载均衡模块的开发方法,首先需要了解负载均衡模块的使用方法。因为负载均衡模块与之前提到的模块差别比较大,所以我们从配置入手比较容易理解。

  • 在nginx.conf配置文件中我们如果需要使用ip hash的负载均衡算法。我们需要写一个类似下面的配置:(负载均衡的简单配置可参考https://blog.csdn.net/Stars____/article/details/129381510?spm=1001.2014.3001.5501)
    upstream test {
    	ip_hash;
    	server 192.168.0.1;
    	server 192.168.0.2;
    }
    
    从配置我们可以看出负载均衡模块的使用场景:
    1. 核心指令”ip_hash”只能在upstream {}中使用。这条指令用于通知nginx使用ip hash负载均衡算法。如果没加这条指令,nginx会使用默认的round robin负载均衡模块。
    2. upstream {}中的指令可能出现在”server”指令前,可能出现在”server”指令后,也可能出现在两条”server”指令之间。
      • 两者有什么区别呢:看一下下面这条配置
        upstream test {
        	server 192.168.0.1 weight=5;
        	ip_hash;
        	server 192.168.0.2 weight=7;
        }
        
        此时报错:nginx: [emerg] invalid parameter "weight=7"
        可见ip_hash指令的确能影响到配置的解析的。

指令

看一下ip_hash指令的定义:

static ngx_command_t  ngx_http_upstream_ip_hash_commands[] = {

    { ngx_string("ip_hash"),
      NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,	// NGX_HTTP_UPS_CONF。这个属性表示该指令的适用范围是upstream{}
      ngx_http_upstream_ip_hash,
      0,
      0,
      NULL },

      ngx_null_command
};

钩子

  • ngx_http_upstream_ip_hash函数代码如下:

    static char *
    ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
    {
        ngx_http_upstream_srv_conf_t  *uscf;
    
        uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
    
        if (uscf->peer.init_upstream) {
            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                               "load balancing method redefined");
        }
    
        uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash;
    
        uscf->flags = NGX_HTTP_UPSTREAM_CREATE
                      |NGX_HTTP_UPSTREAM_WEIGHT
                      |NGX_HTTP_UPSTREAM_MAX_CONNS
                      |NGX_HTTP_UPSTREAM_MAX_FAILS
                      |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
                      |NGX_HTTP_UPSTREAM_DOWN;
    
        return NGX_CONF_OK;
    }
    

    这段代码中有两点值得我们注意。一个是uscf->flags的设置,另一个是设置init_upstream回调。

    • 设置uscf->flags
      1. NGX_HTTP_UPSTREAM_CREATE:创建标志,如果含有创建标志的话,nginx会检查重复创建,以及必要参数是否填写。
      2. NGX_HTTP_UPSTREAM_MAX_FAILS:可以在server中使用max_fails属性。
      3. NGX_HTTP_UPSTREAM_FAIL_TIMEOUT:可以在server中使用fail_timeout属性。
      4. NGX_HTTP_UPSTREAM_DOWN:可以在server中使用down属性。
      5. NGX_HTTP_UPSTREAM_WEIGHT:可以在server中使用weight属性。
      6. NGX_HTTP_UPSTREAM_BACKUP:可以在server中使用backup属性。

    联想到刚刚遇到的那个神奇的配置错误,可以得出一个结论:在负载均衡模块的指令处理函数中可以设置并修改upstream{}中”server”指令支持的属性。

    这是一个很重要的性质,因为不同的负载均衡模块对各种属性的支持情况都是不一样的,那么就需要在解析配置文件的时候检测出是否使用了不支持的负载均衡属性并给出错误提示,这对于提升系统维护性是很有意义的。(也就是说,当解析到upstream中的ip_hash指令时检测是否使用了不支持的属性配置)

    但是,这种机制也存在缺陷,正如前面的例子所示,没有机制能够追加检查在更新支持属性之前已经配置了不支持属性的”server”指令。(好比ip_hash之前已经配置的weight=5的那一条server指令,是不能被ip_hash的钩子函数检查的)

    • 设置init_upstream回调
      nginx初始化upstream时,会在ngx_http_upstream_init_main_conf函数中调用设置好的初始化负载均衡模块的回调函数。
      umcf->upstreams数组中的每个元素对应upstream{}中的信息。
      uscfp = umcf->upstreams.elts;
      
      for (i = 0; i < umcf->upstreams.nelts; i++) {
      
          init = uscfp[i]->peer.init_upstream ? uscfp[i]->peer.init_upstream:
                                              ngx_http_upstream_init_round_robin;
      
          if (init(cf, uscfp[i]) != NGX_OK) {
              return NGX_CONF_ERROR;
          }
      }
      

初始化配置

init_upstream回调函数执行时需要初始化负载均衡模块的配置,还要设置一个新钩子,这个钩子函数会在nginx处理每个请求
时作为初始化函数调用。

先看ip_hash模块初始化配置的代码:

static ngx_int_t
ngx_http_upstream_init_ip_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
{
    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
        return NGX_ERROR;
    }

    us->peer.init = ngx_http_upstream_init_ip_hash_peer;

    return NGX_OK;
}

这段代码中 ip_hash 模块首先调用另一个负载均衡模块Round Robin的初始化函数,然后再设置自己的处理请求阶段初始化钩子。

实际上几个负载均衡模块可以组成一条链表,每次都是从链首的模块开始进行处理。如果模块决定不处理,可以将处理权交给链表中的下一个模块。

这里,ip_hash模块指定Round Robin模块作为自己的后继负载均衡模块,所以在自己的初始化配置函数中也对Round Robin模块进行初始化。

初始化请求

nginx收到一个请求以后,如果发现需要访问upstream,就会执行对应的peer.init函数。这是在初始化配置时设置的回调函数。这个函数最重要的作用是构造一张表,当前请求可以使用的upstream服务器被依次添加到这张表中。之所以需要这张表,最重要的原因是如果upstream服务器出现异常,不能提供服务时,可以从这张表中取得其他服务器进行重试操作。此外,这张表也可以用于负载均衡的计算。之所以构造这张表的行为放在这里而不是在前面初始化配置的阶段,是因为upstream需要为每一个请求提供独立隔离的环境。

ngx_http_upstream_init_ip_hash_peer 函数中部分代码如下:

// 设置数据指针,这个指针就是指向前面提到的那张表。
r->upstream->peer.data = &iphp->rrp;	

// 是调用Round Robin模块的回调函数对该模块进行请求初始化。
// 前面已经提到,一个负载均衡模块可以调用其他负载均衡模块以提供功能的补充。
if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
    return NGX_ERROR;
}

/*设置一个新的回调函数get。该函数负责从表中取出某个服务器。
除了get回调函数,还有另一个r->upstream->peer.free的回调函数。
该函数在upstream请求完成后调用,负责做一些善后工作。
比如我们需要维护一个upstream服务器访问计数器,那么可以在get函数中对其加1,在free中对其减1
如果是SSL的话,nginx还提供两个回调函数peer.set_session和peer.save_session*/
r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer;

peer.get和peer.free回调函数

这两个函数是负载均衡模块最底层的函数,负责实际获取一个连接和回收一个连接的预备操作。之所以说是预备操作,是因为在这两个函数中,并不实际进行建立连接或者释放连接的动作,而只是执行获取连接的地址或维护连接状态的操作。需要理解的清楚一点,在peer.get函数中获取连接的地址信息,并不代表这时连接一定没有被建立,相反的,通过get函数的返回值,nginx可以了解是否存在可用连接,连接是否已经建立。这些返回值总结如下:

返回值说明nginx后续动作
NGX_DONE得到了连接地址信息,并且连接已经建立直接使用连接,发送数据
NGX_OK得到了连接地址信息,但连接并未建立建立连接,如连接不能立即建立,设置事件, 暂停执行本请求,执行别的请求
NGX_BUSY所有连接均不可用返回502错误至客户端

可能的疑问

  1. 什么时候连接是已经建立的?
    使用后端keepalive连接的时候,连接在使用完以后并不关闭,而是存放在一个队列中,新的请求只需要从队列中取出连接,这些连接都是已经准备好的。
  2. 什么叫所有连接均不可用?
    初始化请求的过程中,建立了一张表,get函数负责每次从这张表中不重复的取出一个连接,当无法从表中取得一个新的连接时,即所有连接均不可用
  3. 对于一个请求,peer.get函数可能被调用多次么?
    当某次peer.get函数得到的连接地址连接不上,或者请求对应的服务器得到异常响应,nginx会执行ngx_http_upstream_next,然后可能再次调用peer.get函数尝试别的连接
  4. upstream整体流程如下
    在这里插入图片描述

小结

以上内容是负载均衡模块的基本组成。

负载均衡模块的配置区集中在upstream{}块中。

负载均衡模块的回调函数体系是以init_upstream为起点,经历init_peer,最终到达peer.get和peer.free。

其中init_peer负责建立每个请求使用的server列表,peer.get负责从server列表中选择某个server(一般是不重复选择),而peer.free负责server释放前的资源释放工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/4367.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HTTP代理端口是什么意思?

HTTP代理端口是指代理服务器所使用的端口。代理服务器是一种介于客户端和服务器之间的计算机系统&#xff0c;它可以拦截客户端发送给服务器的请求&#xff0c;并将其转发到服务器。而HTTP代理端口则是代理服务器上专门用于处理HTTP请求和响应的端口号。默认情况下&#xff0c;…

【Java】JavaSE概要

整理&#xff1a;【狂神说Java】JavaSE阶段回顾总结_哔哩哔哩_bilibili JavaSE概要 简介 JDK&#xff1a;开发者工具包 JRE&#xff1a;运行环境 //Hello.java public class Hello{public static void main(String[] args){System.out.println("Hello,World!");} }…

Redis数据库

一、关系数据库与非关系型数据库概述 1、关系型数据库 关系型数据库是一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上&#xff0c;一般面向于记录。 SQL 语句&#xff08;标准数据查询语言&#xff09;就是一种基于关系型数据库的语…

Spring Boot基础学习之(六):前后端交互实现用户登录界面

本篇博客写的内容&#xff0c;是一个系列&#xff0c;内容都是关于spring boot架构的学习&#xff0c;实现前后端交互&#xff0c;极大的解放双手spring boot学习系列这是关于spring boot的专栏&#xff0c;后期也会不定期进行更新。内容都是有序号的&#xff0c;一步接着一步。…

有人物联口红DTU DR154配置与RS 485传感器数据处理

一、硬件设备 &#xff08;1&#xff09;有人物联口红DTU DR154&#xff08;RS 485版本&#xff09; 这个DTU非常给力&#xff0c;不用插卡自带esim卡&#xff0c;送8年流量&#xff0c;配置的话通过小程序【联博士】蓝牙配置&#xff08;手机扫描DTU背后的二维码即可&#x…

界面开发框架Qt新手入门教程 - 项目视图示例介绍

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。Qt提供了许多功能&…

java基础问答

57、synchronized 各种加锁场景的作用范围 1.作用于非静态方法&#xff0c;锁住的是对象实例&#xff08;this&#xff09;&#xff0c;每一个对象实例有一个锁。 public synchronized void method() {} 2.作用于静态方法&#xff0c;锁住的是类的Class对象&#xff0c;因为Cl…

chatgpt+安全机器人控制器+底盘一体化方案设计构想

“你有没有想过&#xff0c;你只需告诉你的家庭助理机器人&#xff1a;‘请加热我的午餐’&#xff0c;它就会自己找到微波炉。这是不是很神奇&#xff1f;” 近日&#xff0c;微软在其官网发表了一篇名为《机器人 ChatGPT&#xff1a;设计原则和模型能力&#xff08;ChatGPT …

MongoDB 6.0 入门(一)

为什么研究MongDB 6.0 今天和老大聊天 聊到了一个场景的设计&#xff0c;我刚开始推荐了 clickhouse &#xff0c;然后老大指出 前两天 测试的结果&#xff0c;因为clickhouse 因为 是列式存储&#xff0c;导致我们要查询一行数据&#xff0c;需要200ms&#xff08;库中有2000…

MyBatis源码分析(二、续)SqlSource创建流程,SQL如何解析?如何将#{id}变成?的

文章目录实例一、SqlSource处理入口二、SqlSource处理逻辑1、XMLScriptBuilder 构造方法2、解析动态sql3、DynamicSqlSource4、RawSqlSource解析sql&#xff08;1&#xff09;parse方法解析sql写在后面实例 此处我们分析的sql&#xff1a; <select id"selectBlog&quo…

redis 十. 线程基础

目录一. redis 基础复习与了解redis6二. redis 线程问题总结一. redis 基础复习与了解redis6 redis官网, redis中文网站, redis命令参考网站此处以redis6.0.8或以上版本为例(查看自己redis版本命令"redis- server -v")按照redis6以上版本测试使用时,redis.conf下需要…

Baklib:企业知识管理帮助文档制作平台

在当今的商业环境中&#xff0c;企业面临着越来越多的挑战。其中之一是如何管理并传递企业内部的知识。企业知识管理的重要性不言而喻&#xff0c;它可以帮助企业更好地组织和利用内部的知识资源&#xff0c;提高生产力和竞争力。而Baklib作为一款企业知识管理&帮助文档制作…

新四级强化辅导

词汇题&#xff08;55道&#xff09; 1. You should carefully think over_____ the manager said at the meeting. A. that B. which C. what D. whose 1.选C,考察宾语从句连接词&#xff0c;主句谓语动词think over后面缺宾语&#xff0c;后面的宾语从句谓语动…

聚焦“专精特新” 共话高质量发展

3月23日&#xff0c;第七届杭州全球企业家论坛暨第三届中国专精特新企业&#xff08;新三板&#xff09;高峰论坛在杭州国博中心成功举办。超过500位专精特新企业代表、专家学者、政府部门代表、科研院所代表共聚一堂&#xff0c;现场座无虚席&#xff0c;气氛热烈。本届峰会以…

SQL Server 2016安装教程

✅作者简介&#xff1a;CSDN内容合伙人、阿里云专家博主、51CTO专家博主、新星计划第三季python赛道Top1&#x1f3c6; &#x1f4c3;个人主页&#xff1a;hacker707的csdn博客 &#x1f525;欢迎订阅系列专栏&#xff1a;SQL Server 2016从入门到精通&#x1f947; &#x1f4…

借东风拉马力,龙头券商东方财富发展持续向好

3月17日&#xff0c;互联网券商东方财富公布2022年年报。具体来看&#xff0c;东方财富2022年实现总营收124.9亿元&#xff0c;同比下滑4.6%&#xff1b;归母净利润85.1亿元&#xff0c;同比下滑0.5%&#xff0c;略低于预期。其中&#xff0c;2022年第四季度总营收同比下滑15.4…

【Python课堂】使用Akshare高效获得A股可转债的重要信息

文章目录前言一、准备二、获取基本信息1.引入库2.读入数据3.获取行情数据4.可转债比价表总结前言 Akshare是一个非常好用的开源A股数据获取模块&#xff0c;它是基于 Python 的财经数据接口库&#xff0c;目的是实现对A股、美股、期货等金融产品的基本面数据、实时和历史行情数…

【干货】交换机管理

实验目的 熟悉掌握交换机的管理&#xff0c;包括&#xff1a;密码恢复&#xff0c;IOS升级和备份&#xff0c;IOS恢复 掌握实际工程中控制线的操作 交换机管理&#xff08;一&#xff09; 交换机密码恢复&#xff08;以下实验都需要在真机上操作&#xff09; 控制线和电脑的连…

oracle语句查询时间范围

oracle语句查询时间范围 参考网址 &#xff1a; https://blog.csdn.net/weixin_36436373/article/details/116541220?ops_request_misc%257B%2522request%255Fid%2522%253A%2522167930069316800188562500%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%25…

Web自动化测试入门

1.Web自动化测试的价值&#xff08;为什么要做web自动化测试&#xff09; 我们可以使用脚本语言代替人来进行测试 2.Web自动化测试相关技术&#xff1a; Selenium:支持多语言&#xff0c;行业内最火最主流Pytest/JUnit5:最好用最全面的单元测试框架Allure:测试报告3.Web自动化…