nginx技术分享之二

Nginx是一个轻量级、高性能、稳定性高、并发性好的HTTP和反向代理服务器。也是由于其的特性,其应用非常广。

主要功能

  • 反向代理
Nginx在反向代理上,提供灵活的功能,可以根据不同的正则采用不同的转发策略,如图设置好后不同的请求就可以走不同的服务器,如下图。

nginx 是否可以从其他linux服务器直接拷贝过去使用 nginx转发到另一个nginx_nginx

  • 负载均衡
其原理就是将数据流量分摊到多个服务器执行,减轻每台服务器的压力,多台服务器(集群)共同完成工作任务,从而提高了数据的吞吐量.如下图。

nginx 是否可以从其他linux服务器直接拷贝过去使用 nginx转发到另一个nginx_后端服务_02

  • 动静分离
Nginx可以根据配置对不同的请求做不同转发,这是动态分离的基础。静态请求对应的静态资源可以直接放在Nginx上做缓冲,更好的做法是放在相应的缓冲服务器上。动态请求由相应的后端服务器处理。

配置文件

ngxin的各种功能的实现基本上是靠配置文件驱动的,配置文件大致划分为以下几个板块,如下图

nginx 是否可以从其他linux服务器直接拷贝过去使用 nginx转发到另一个nginx_后端服务_03

设置防盗链

  • 根据请求文件类型实现防盗链配置实列如下:
server {
  listen 8080;
  server_name xxx.abc.com
  location ~* ^.+\.(gif|jpg|png|swf|flv|rar|zip)$ {
    valid_referers none blocked www.xxx.com www.yyy.com *.baidu.com  *.tabobao.com;
    if ($invalid_referer) {
      rewrite ^/ http://www.xxx.com/images/forbidden.png;
    }
  }
}
  • 根据请求目录实现防盗链的配置实列如下:
server {
  listen 8080;
  server_name xxx.abc.com
  location /file/ {
    root /server/file/;
    valid_referers none blocked www.xxx.com www.yyy.com *.baidu.com  *.tabobao.com;
    if ($invalid_referer) {
      rewrite ^/ http://www.xxx.com/images/forbidden.png;
    }
  }
}

Nginx的负载均衡策略

参数

说明

不写任何参数

默认轮询

weight

权重

ip_hash

依据ip分配

least_conn

最少连接

url_hash(第三方)

依据URL分配

air(第三方)

响应时间

nginx负载均衡策略(轮询、hash_ip、随机、权重、fail、url_hash)

  • 轮询
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端务器down掉,能自动剔除,nginx如下配置

upstream backserver {
    server 192.168.0.14;
    server 192.168.0.15;
}
  • 权重
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况,nginx如下配置

upstream backserver {
    server 192.168.0.14 weight=3;
    server 192.168.0.15 weight=7;
}
  • hash_ip
解决了已经登录某一个服务器的用户再重新定位到另一个服务器,其登录信息会丢失的问题,nginx如下配置

upstream backserver {
    ip_hash;
    server 192.168.0.14:88;
    server 192.168.0.15:80;
}

待会重点讲ip_hash
  • fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。

upstream backserver {
    server server1;
    server server2;
    fair;
}
  • url_hash(第三方)
按访问url的hash结果来分配请求,使每个url定向到同一个(对应的)后端服务器,后端服务器为缓存时比较有效。
upstream backserver {
    server squid1:3128;
    server squid2:3128;
    hash $request_uri;
    hash_method crc32;
}
举个栗子

nginx限流之令牌桶算法和漏桶算法

  • 令牌桶
令牌桶算法(Token Bucket)和 Leaky Bucket 效果一样但方向相反的算法,更加容易理解.随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token(想象和相反,有个水龙头在不断的加水),如果桶已经满了就不再加了.新请求来临时,会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务

nginx 是否可以从其他linux服务器直接拷贝过去使用 nginx转发到另一个nginx_后端服务_04

package main

import (
    "fmt"
    "time"
)

func logs(args ...interface{}) {
    fmt.Println(args...)
}

func tokenBucket(limit int, rate int) chan struct{} {
    tb := make(chan struct{}, limit)
    ticker := time.NewTicker(time.Duration(1) * time.Second)
    for i := 0; i < limit; i++ {
        tb <- struct{}{}
    }

    go func() {
        for {
            for i := 0; i < rate; i++ {
                tb <- struct{}{}
            }
            <-ticker.C
        }
    }()
    return tb
}

func popToken(bucket chan struct{}, n int) {
    for i := 0; i < n; i++ {
        <-bucket
    }
}

func testTokenBucket() {
    rate := 10
    tb := tokenBucket(20, rate)

    dataLen := 100
    for i := 0; i <= dataLen; i += rate {
        popToken(tb, rate)
        logs(i)
    }
}
  • 漏桶
漏桶(Leaky Bucket)算法思路也比较简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率.示意图如下

nginx 是否可以从其他linux服务器直接拷贝过去使用 nginx转发到另一个nginx_nginx_05

package main
 
import (
    "fmt"
    "time"
)
 
// 定义一个漏桶
type LeakBucket struct {
    capacity       int           // 容量,固定时间语序访问次数
    interval       time.Duration // 允许访问的时间间隔
    dropsNum       int           // 固定时间访问了多少次
    lastAccessTime time.Time     // 最近一次的访问时间
}
 
func (b *LeakBucket) accessControl() bool {
    now := time.Now()
    pastTime := now.Sub(b.lastAccessTime)
 
    // 过去这段时间pastTime可以允许多少访问
    leaks := int(float64(pastTime) / float64(b.interval))
    if leaks > 0 { // 说明这段时间可以有leaks可以访问,但没有用户访问
        // 所以放宽访问,下一段访问限制减少一定leaks次限制,通过这种机制达到平滑控制流量
        if b.dropsNum <= leaks {
            b.dropsNum = 0
        } else {
            b.dropsNum -= leaks
        }
        // 更新访问时间
        b.lastAccessTime = now
    }
 
    if b.dropsNum < b.capacity { // 在允许访问之内
        b.dropsNum ++
        return true
    } else {
        return false
    }
}
 
func main() {
    bucket := &LeakBucket{
        capacity: 10,
        interval: time.Second,
    }
 
    for i := 0; i < 12; i++ {
        allowed := bucket.accessControl()
        fmt.Println("i",i)
        fmt.Println("i",allowed)
        time.Sleep(time.Millisecond * 500)
    }
 
    time.Sleep(time.Second*3) // 模拟3秒中时间内没有访问
    fmt.Println("空档期走完")
 
    for j := 0; j < 20; j++ {
        fmt.Println("BEFORE",bucket.dropsNum)
        allowed := bucket.accessControl()
        fmt.Println("AFTER",bucket.dropsNum)
        fmt.Println("j", j)
        fmt.Println("j",allowed)
        time.Sleep(time.Millisecond * 500)
    }
}