为了提高吞吐量,可以对nginx进行扩容处理。
其中,nginx两种扩容方式:(1)单机垂直扩容;(2)水平扩展
接下来对这两种方式进行讲解学习。
增加硬件资源,提高性能,物理手段
主要方式有:
云服务资源增加
整机:IBM、浪潮、DELL、HP等
CPU/主板:更新到主流
网卡:10G/40G网卡
磁盘:SAS(SCSI) HDD(机械)、HHD(混合)、SATA SSD、PCI-e SSD、 MVMe SSD
SSD
多副本机制
系统盘/热点数据/数据库存储
HDD
冷数据存储
接下来水平化扩展方式,才是我们真正要学习的
本次学习主要讲述:ip_hash、hash $request_uri; 、使用sticky模块
ip_hash技术能够将某个ip 的请求定向到同一台后端web机器中,这样一来这个ip 下的客户端和某个后端 web机器就能建立起稳固的session.(保持会话)
ip_hash机制能够让某一客户机在相当长的一段时间内只访问固定的后端的某台真实的web服务器,这样会话就会得以保持,在网站页面进行login的时候就不会在后面的web服务器之间跳来跳去了,也不会出现登录一次的网站又提醒重新登录的情况.
了解了ip_hash,我们知道nginx是靠c语言来实现的,那么ip_hash是怎么实现的呢?只有从根上出发才能更好的了解ip_hash 。
在ngx_http_upstream_ip_hash_module.c的源文件中,我们可以看到nginx的ip_hash算法的实现。
实现代码如下:
for ( ;; ) {
for (i = 0; i < 3; i++) {
hash = (hash * 113 + iphp->addr[i]) % 6271; //iphp->addr[i]为ip的点分十进制法的第i段
}
p = hash % iphp->rrp.peers->number;
n = p / (8 * sizeof(uintptr_t));
m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
if (!(iphp->rrp.tried[n] & m)) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get ip hash peer, hash: %ui %04XA", p, m);
peer = &iphp->rrp.peers->peer[p];
/* ngx_lock_mutex(iphp->rrp.peers->mutex); */
if (!peer->down) {
if (peer->max_fails == 0 || peer->fails < peer->max_fails) {
break;
}
if (now - peer->accessed > peer->fail_timeout) {
peer->fails = 0;
break;
}
}
iphp->rrp.tried[n] |= m;
/* ngx_unlock_mutex(iphp->rrp.peers->mutex); */
pc->tries--;
}
if (++iphp->tries >= 20) {
return iphp->get_rr_peer(pc, &iphp->rrp);
}
}
其中,重点就是哈希值的计算,代码如下:
for(i = 0; i < 3; i++) {
hash = (hash * 113+ iphp->addr[i]) % 6271;
}
可以查询addrlen的值是3,从代码中可以看出,hash值是根据ip段的前三个来计算的,例如192.168.1.12,192.168.1.13,192.168.1.14 经过nginx的hash值计算是根据ip段的192 168 1来计算的。所以如果在本地的虚拟机中来使用这种负载均衡的方式,那么192.168.1.12,192.168.1.13,192.168.1.14 这三个ip访问的其实是一台tomcat。
了解了ip_hash实现,那么在实际开发中ip_hash是怎么配置的呢啊
配置也比较简单,只是在nginx的反向代理中,添加ip_hash关键字即可。
如下所示:
upstream httpds {
# ip_hash
ip_hash;
server 192.168.44.102 ;
server 192.168.44.103 ;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://httpds;
# root html;
}
location ~*/(css|img|js) {
root /usr/local/nginx/html;
}
了解了ip_hash的实现原理,以及配置。那么不仅疑惑,ip_hash怎么场景才使用比较好呢,它有什么优缺点啊
适用业务场景:适用于需要账号登录的系统,会话连接保持的业务。
优点:
(1)Nginx中的ip_hash技术能够将某个ip 的请求定向到同一台后端web机器中,在这个ip 下的客户端和某个后端 web机器就能建立起稳固的session.
(2)p_hash机制能够让某一客户机在相当长的一段时间内只访问固定的后端的某台真实的web服务器,会话就会得以保持,在网站页面进行login的时候就不会在后面的web服务器之间跳来跳去了,也不会出现登录一次的网站又提醒重新登录的情况.
缺点:
(1)nginx不是最前端的服务器
ip_hash要求nginx一定是最前端的服务器,否则nginx得不到正确ip, 就不能根据ip作hash.
如: 使用的是squid(代理服务器)为最前端.那么nginx取ip时只能得到squid的服务器ip地址,用这个地址来作分流肯定是错乱的
(2)nginx的后端还有其它负载均衡
假如nginx后端还有其它负载均衡,将请求又通过另外的方式分流了,那么某个客户端的请求肯定不能定位到同一台session应用服务器上,这么算起来,nginx后端只能直接指向应用服务器,或者再搭一人squid,然后指向应用服务器. 最好 的办法是用location作一次分流,将需要session的部分请求通过ip_hash分流,剩下的走其它后端去.
需要注意的是, 如果在集群中的某台服务器出现故障,我们想要从nginx的集群配置中移除掉,我们不可以直接的将那一行删掉,比如server 192.168.121.167:8080 ;删掉,如果直接删掉会导致nginx的hash算法重新计算,那么用户的回话或者说缓存都会失效掉,所以这里如果不用这台服务器,直接比较为down即可,也就是 server 192.168.121.167:8080 down;这么做就可以了。
针对宕机这种情况,我们可以使用一致性hash算法。
这样的hash算法在某台服务器发生故障的情况下可以保障大部分的用户体验不出现问题,之前的ip_hash是因为少了节点,note_count数量减少所以导致每次请求都需要重新计算分配到哪个节点,但是一致性hash算法不会,它仅会影响到少部分靠近更新节点的位置,比如节点3由于故障移除掉了,那么它上游的两个用户请求会到节点4,而其他的用户都不会改变
hash request_uri是针对客户端访问的uri来做的绑定。客户端访问同一个uri的时候,会被分配到同一个服务器上去。这样提高了缓存的命中率。并且这个request_uri就是完整url中刨去最前面host剩下的部分,比如http://www.baidu.com/pan/beta/test1?fid=3这个url,去掉www.baidu.com剩下的就是了,日志里会看到打印出来的request_uri其实是/pan/beta/test1?fid=3。如果只访问www.baidu.com,$request_uri里也会有个/的。
都是通过hash算法来实现的,基本实现都是一样的,每个uri进行hash计算得到一个数值,这个数值除以整个节点数量取余数。(取模算法)
如果一个节点挂了,那么整个全局都会乱掉。因为整个的节点数变了,因为除数变了。
upstream httpds {
hash $request_uri;
server 192.168.44.102 ;
server 192.168.44.103 ;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://httpds;
# root html;
}
location ~*/(css|img|js) {
root /usr/local/nginx/html;
}
适用于后端服务器为缓存服务器时比较有效。
(1)在不支持cookie的情况下
(2)资源不平均分区
Sticky基于cookie的一种nginx的负载均衡解决方案,通过分发和识别cookie,来使同一个客户端的请求落在同一台服务器上,默认标识名为route
1.客户端首次发起访问请求,nginx接收后,发现请求头没有cookie,则以轮询方式将请求分发给后端服务器。
2.后端服务器处理完请求,将响应数据返回给nginx。
3.此时nginx生成带route的cookie,返回给客户端。route的值与后端服务器对应,可能是明文,也可能是md5、sha1等Hash值
4.客户端接收请求,并保存带route的cookie。
5.当客户端下一次发送请求时,会带上route,nginx根据接收到的cookie中的route值,转发给对应的后端服务器。
upstream {
sticky;
server 127.0.0.1:9000;
server 127.0.0.1:9001;
server 127.0.0.1:9002;
}
总之需要注意:Sticky基于cookie的一种nginx的负载均衡解决方案
1.同一客户端的请求,有可能落在不同的后端服务器上
如果客户端启动时同时发起多个请求。由于这些请求都没带cookie,所以服务器会随机选择后端服务器,返回不同的cookie。当这些请求中的最后一个请求返回时,客户端的cookie才会稳定下来,值以最后返回的cookie为准。
2.cookie不一定生效
由于cookie最初由服务器端下发,如果客户端禁用cookie,则cookie不会生效。
3.客户端可能不带cookie
Android客户端发送请求时,一般不会带上所有的cookie,需要明确指定哪些cookie会带上。如果希望用sticky做负载均衡,请对Android开发说加上cookie。
4.cookie名称不要和业务使用的cookie重名。Sticky默认的cookie名称是route,可以改成任何值。
5.客户端发的第一个请求是不带cookie的。服务器下发的cookie,在客户端下一次请求时才能生效。
6.Nginx sticky模块不能与ip_hash同时使用
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,
分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:服务器课程