美文网首页
nginx负载均衡

nginx负载均衡

作者: 橙小汁 | 来源:发表于2016-08-31 21:23 被阅读86次

    sina

    0_01291925244503.jpg

    负载均衡:将相同的引用部署到多台机器上。在集群前增加负载均衡设备,实现流量分发。意思是将负载(工作任务、访问请求)进行平衡、分摊到多个操作单元(服务器、组件)上进行执行。是解决高性能、高可用、扩展性的终极解决方案。

    中国大陆使用nginx网站用户有:新浪、网易、 腾讯、小米官网等。以其稳定性和高性能等众多优点迅速扩大市场。

    结构图

    上图为Nginx的基本框架,可以看到Nginx采用多进程的方式,它的好处是:
    (1)对于每个worker进程来说,独立的进程不需要加锁。
    (2)采用独立的进程,使得之间互不影响,一个进程退出后,服务不会中断,master进程则会很快启动新的worker进程。
    (3)它采用异步非阻塞模式来处理请求,可以同时处理成千上万个请求。

    Nginx的upstream目前支持5中分配方式:
    (1)轮询(默认):每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除
    (2)Weight:指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的时候。
    (3)Ip_hash:每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session(服务器利用一种散列表的结构来保存信息,给客户端返回一个session id保存于cookie)的问题。
    (4)Fair:按后端服务器的响应时间来分配,响应时间短的优先分配。
    (5)Url_hash:按访问url的hash结果来分配请求,使每一个url定向到同一个后端服务器。

    Nginx的内存池管理分析
    (1)Nginx的内存管理,主要是用来实现防止内存泄漏、和内存碎片。
    (2)内存池就是一个单链表,拥有独立的多个内存池,内存池本身分配小内存,防止碎片,大内存直接使用malloc,多个大内存构成单链表。
    (3)小内存一旦分配使用,直到整个池释放,才被回收。
    (4)大内存,通过调用释放函数,在摧毁池之前,回收。

    Nginx源代码分析
    Nginx由以下几个元素组成: 1. worker(进程) 2. thread(线程) 3. connection(连接) 4. event(事件) 5. module(模块) 6. pool(内存池) 7. cycle(全局设置) 8. log(日志)
    接下来比较重要的函数是 ngx_init_cycle(),这个函数初始化系统的配置以及网络连接等。如果是多进程方式加载的会继续调用ngx_master_process_cycle(),这是main函数中调用的最关键的两个函数。
    ngx_init_cycle()实际上是个复杂的初始化函数,首先是加载各子模块的配置信息、并初始化各组成模块。 任何模块都有两个重要接口组成,一个是create_conf,一个是init_conf。分别是创建配置和初始化配置信息。 首先是内核模块、错误日志、配置模块、事件模块、时间内核模块、EPOLL模块、http模块、http内核模块、http日志模块,剩下的模块都算不上关键。 epoll是比较关键的核心模块之一,Nginx兼容多种IO控制模型,memecached用的是libevent不如Nginx彻头彻尾是自己实现的。
    在ngx_init_cycle()中对模块初始化完毕后,调用ngx_open_listening_sockets()函数对socket进行了初始化。 在listen上80端口以后,调用模块的另外一个重要接口init_module对各模块进行初始化。
    ngx_init_cycle()返回后,主要的工作都是在ngx_master_process_cycle()函数中继续进行的。ngx_master_process_cycle()函数中的重要过程有调用ngx_start_worker_processes()生成多个子进程,一般Nginx是多进程的。 ngx_start_worker_processes()函数内部调用ngx_worker_process_cycle()函数建立每个进程的实际工作内容,在这个函数中首先调用ngx_create_thread()初始化各线程。我们知道每个线程都有一个启动处理函数,Nginx的线程处理函数为 ngx_worker_thread_cycle(),内部过程中最重要的是对ngx_event_thread_process_posted()函数的调用,用于实际处理每一次请求。 在初始化线程结束后,首先调用ngx_process_events_and_timers()函数,该函数继续调用 ngx_process_events接口监听事件,一般情况下对应的函数是ngx_epoll_process_events(),如果使用的是其它种 类的IO模型,则应该实现相应的实际函数。这个接口负责把事件投递到ngx_posted_events事件队列里,并在 ngx_event_thread_process_posted()函数中进行处理。
    Nginx是一个支持负载均衡的反向代理服务器,Apache的弱点就在于它的并发模型是普通的进程/线程池,连接数和进程/线程数是1:1的,因此无论是prefork还是worker模式,都将每一个连接对应到一个独立的进程/线程。 这样的并发模型在连接数不太多(1000以内)时还算可以,但在大规模并发时,其进程/线程总数会非常多。由于Apache本身也比较吃内存,所以到了1000以上的并发时,服务器的内存基本上也就被吃的差不多了,操作系统也在频繁地做进程/线程的切换,非常吃力。相比之下,更高级的大型网络服务系统(如电信的智能网系统)一般采用进程/线程池+状态机的模型——也即连接数和进程/线程数是m:n的,这样进程/线程总数就不会由于连接的增多而增多,避免了内存和调度切换的开销,但这种做法对程序逻辑的要求较高,需要一个连接拆分为多个逻辑状态(创建,读,写,关闭等,根据实际业务还可以更加细化)每个进程/线程处理完某一种状态后,需要改变该连接的状态值,后续状态由下一个空闲的进程/线程处理。
    Nginx就采用了这样的并发模型,对于连接状态的存储,Nginx主要采用了这样一个复杂结构。
    struct ngx_connection_s
    {
    void *data;
    ngx_event_t *read;
    ngx_event_t *write;
    ...
    }; 结构ngx_event_t存储了连接IO状态的详细信息,同时所有的ngx_event_t组成了两个全局的链表,以便进行存取操作。

    Nginx使用了下面这两个函数来完成每个进程/线程的循环
    1.ngx_locked_post_event 这个函数负责更新某一个连接的状态,在检查到连接IO状态改变(比如通过select)后被调用。
    2.ngx_event_thread_process_posted 这个函数检查event表,并调用event对应的handler函数,每次处理1个event。

    Nginx实践
    Nginx有一个memcached_module,可以直接从后端的memached服务器中读取内容, 直接输出。通过这个模块,可以极大的提升动态页面的访问速度。memcached可以通过upstream来从多台memcached服务器中读取,也可以支持热备份。很多事情都不是那么完美, 对于Nginx的这个module,其最大的问题就是不支持压缩,直接缓存页面, 非常浪费内存。

    相关文章

      网友评论

          本文标题:nginx负载均衡

          本文链接:https://www.haomeiwen.com/subject/nfkcettx.html