前端常用代码示例-1

  1. Anagrams of string(变更字母顺序构成新字符串-字谜)
    使用递归。对于给定字符串中的每个字母,为字母创建字谜。使用map()将字母与每部分字谜组合,然后使用reduce()将所有字谜组合到一个数组中,最基本情况是字符串长度等于2或1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    anagrams = str => {
    if (str.length < 2) {
    return [str];
    }
    if (str.length === 2) {
    return [str, str[1] + str[0]]
    }
    return [...str].reduce((pre, letter, i) =>
    pre.concat(anagrams(str.slice(0, i) + str.slice(i + 1)).map(value => letter + value)), []);
    }

    anagrams('abcd');
  2. 数组平均数
    使用reduce()将每个值添加到累加器,初始值为0,总和除以数组长度

    1
    2
    3
    average = arr => arr.reduce((pre, val) => pre + val, 0) / arr.length;

    average([1, 2, 3, 4, 5, 6, 7, 8]);
  3. 大写每个单词的首字母
    使使用replace()匹配每个单词的第一个字符,并使用toUpperCase()来将其大写

    1
    2
    3
    capitalizeEveryWord = str => str.replace(/\b[a-z]/g, char => char.toUpperCase());

    capitalizeEveryWord('hello world !');
  4. 首字母大写
    使用slice(0,1)和toUpperCase()大写第一个字母,slice(1)获取字符串的其余部分。省略lowerRest参数以保持字符串的其余部分不变,或将其设置为true以转换为小写。(注意:这和上一个示例不是同一件事情)

    1
    2
    3
    4
    capitalize = (str, lowerRest = false) =>
    str.slice(0, 1).toUpperCase() + (lowerRest ? str.slice(1).toLowerCase() : str.slice(1));

    capitalize('jarryLiu',true);
  5. 字符串反转

    1
    2
    3
    reverseString = str => [...str].reverse().join('');

    reverseString('hello');
  6. 检查回文
    将字符串toLowerCase,并使用replace从中删除非字母的字符。然后拆分为单独字符,reverse,join(’’),与原始的非反转字符串进行比较

    1
    2
    3
    4
    5
    6
    palindrome = str => {
    let s = str.toLowerCase().replace(/[^a-z0-9]/g, '');
    return s === [...s].reverse().join('');
    }

    palindrome('eye');
  7. 计算数组中值的出现次数
    使用reduce迭代增加计数器

    1
    2
    3
    4
    countOccurrences = (arr, target) => 
    arr.reduce((count, val) => val === target ? count + 1 : count, 0);

    countOccurrences(['a','c','c','b','d'],'c');
  8. Curry(柯里化)

    1
    2
    3
    4
    5
    curry = (fn, arity = fn.length, ...args) =>
    arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args);

    curry(Math.max, 3)(3)(7)(90);
    curry(Math.pow)(2)(10);
  9. Deep flatten array(拍平数组)
    递归,使用reduce来获取所有不是数组的元素,思考一下为什么不用 push

    1
    2
    3
    4
    5
    6
    7
    8
    //拍平二维数组
    flatten = arr => arr.reduce((a, v) => a.concat(v), []);
    //递归拍平多维嵌套数组
    deepFlatten = arr =>
    arr.reduce((pre, val) =>
    pre.concat(Array.isArray(val) ? deepFlatten(val) : val), []);

    deepFlatten([8,5,[4,[6,[7]]],4]);
  10. 数组比较差异
    从b创建一个Set,然后在a上使用Array.filter,思考使用Set的好处

    1
    2
    3
    4
    5
    6
    7
    different = (arr1, arr2, include = false) => {
    let _arr2 = new Set(arr2);
    return arr1.filter(ele => include ? _arr2.has(ele) : !_arr2.has(ele));
    }
    similarity = (arr1, arr2) => arr1.filter(ele => arr2.includes(ele));

    different([1, 2, 3, 4, 5], [1, 3, 5], true);
  11. 计算两点之间欧几里得距离
    Math.hypot

    1
    2
    3
    distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0);

    distance(1,1,2,3);
  12. 转义正则表达式
    转义正则表达式

    1
    2
    3
    escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

    escapeRegExp('(test)');
  13. 阶乘
    使用递归。如果n小于或等于1,则返回1。否则返回n和n – 1的阶乘的乘积

    1
    2
    3
    factorial = n => n <= 1 ? 1 : n * factorial(n - 1);

    factorial(10);
  14. 斐波那契数组生成
    创建一个特定长度的空数组,初始化前两个值(m,n)。使用Array.reduce向数组中添加值,后面的一个数等于前面两个数相加之和(前两个除外)

    1
    2
    3
    4
    fibonacci = (l, m, n) => Array(l).fill(0).reduce((pre, val, i) =>
    pre.concat(i > 1 ? pre[i - 1] + pre[i - 2] : ((i === 0) ? m : n)), []);

    fibonacci(5,2,3);
  15. 过滤数组中的非唯一值
    将Array.filter用于仅包含唯一值的数组

    1
    2
    3
    filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i));

    filterNonUnique([1,22,3,444,3,22,6]);
  16. 获取最大公约数
    使用递归。基本情况是当y等于0时。在这种情况下,返回m。否则,返回n的GCD和 m/n的其余部分

    1
    2
    3
    gcd = (m, n) => !n ? m : gcd(n, m % n);

    gcd(8,36);
  17. 函数执行计时
    console.time

    1
    2
    3
    4
    5
    6
    7
    8
    timeTaken = callback => {
    console.time('timetask');
    let r = callback();
    console.timeEnd('timetask');
    return r;
    }

    timeTaken(()=> anagrams('abcdefghij'));
  18. 键值对转对象
    [[‘a’,1],[‘b’,2]] -> {a: 1, b: 2}

    1
    2
    3
    objFromPairs = arr => arr.reduce((pre, val) => (pre[val[0]] = val[1], pre), {});

    objFromPairs([['a',1],['b',2]]);
  19. 管道函数
    pipe(func1,func2…)(args)

    1
    2
    3
    pipe = (...funcs) => arg => funcs.reduce((funcArgs, func) => func(funcArgs), arg);

    pipe(x => x + 1, x => console.log(x))(2);
  20. 生成数组幂集
    [1,2] -> [[], [1], [2], [2,1]]

    1
    2
    3
    powerset = arr => arr.reduce((pre, val) => pre.concat(pre.map(ele => [val].concat(ele))), [[]]);

    powerset([1,2]);
  21. 范围内随机数(默认0-100)

    1
    2
    3
    randomInRange = (min = 0, max = 100) => Math.random() * (max - min) + min;

    Math.floor(randomInRange(2,10));
  22. 随机化数组顺序

    1
    2
    3
    shuffle = arr => arr.sort(() => Math.random() - 0.5);

    shuffle([1,2,3,4,5,6,7]);
  23. RGB转十六进制
    (255, 165, 1) -> ‘ffa501’。 *padStart(ES2017)

    1
    2
    3
    4
    //rgbToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16).padStart(6,'0');
    rgbToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16);

    rgbToHex(255,165,1);
  24. 字符串中字母排序
    使用split(’‘)分割字符串,sort()使用localeCompare(),使用join(‘’)重新组合

    1
    2
    3
    sortCharsInString = str => [...str].sort((a, b) => a.localeCompare(b)).join('');

    sortCharsInString('ibsuidibjbacb');
  25. 数组元素求和
    reduce累加

    1
    2
    3
    sumArray = arr => arr.reduce((sum, val) => sum + val, 0);

    sumArray([1,2,3,4,5]);
  26. 交换变量值
    数组解构

    1
    2
    3
    4
    5
    6
    let a = 1;
    let b = 'bbb';
    let c = { key: 'value' };
    [a, b, c] = [c, a, b];

    console.log(JSON.stringify(a) + '-' + b + '-' + c)
  27. 数组去重
    …new Set(arr)

    1
    2
    3
    unique = arr => [...new Set(arr)];

    unique([11,22,3,444,444,55,55]);
  28. 获取URL参数
    match + reduce + map

    1
    2
    3
    4
    5
    getUrlParameters = url =>
    url.match(/([^?=&]+)(=([^&]*))/g).reduce((obj, val) =>
    (obj[val.slice(0, val.indexOf('='))] = val.slice(val.indexOf('=') + 1), obj), {});

    getUrlParameters('http://url.com/page?name=Adam&surname=Smith');
  29. 验证数字
    使用!isNaN和parseFloat()来检查参数是否是一个数字,使用isFinite()来检查数字是否是有限

    1
    2
    3
    validateNumber = n => !isNaN(parseFloat(n)) && isFinite(n) && Number(n) == n;

    validateNumber(10.1);
  30. 过滤空值
    空值指的是没有具体意义的一些值,比如0,undefined,null,false,空字符串等

    1
    let res = [1, 2, 0, '0', undefined, null, false, ''].filter(Boolean);

为什么评审代码

最近有位朋友问我为什么做代码评审很有价值。至少大多数硅谷科技公司都会对每一个变更进行代码评审,以确保至少有两个人看过该变更。在我之前的工作中,我们选择性地(很少地)进行代码评审,后来团队来了一位来自谷歌的新员工,他鼓励我们评审所有代码 - 而我们照做了。事实证明这是个很好的决定。

如果你按正确的方式进行代码评审,它不会让你觉得麻烦。你和审阅你代码的人不是对手关系,你们是一起努力构建最好的软件。(重点是不要把反馈太个人化的看待 - 即使你得改动代码,也不代表你有问题。获得反馈很正常,因为它帮助你成长!)

有些公司严格规定了每一段代码必须有多少人评审,以及每一段代码必须有严格的归属者。我觉得这么做完全没有必要,我更喜欢简单点的系统,唯一的规则是每段代码都至少有一个人评审过。事实上,你仍然得向维护你所改代码的人提交评审意见,但不做硬性要求会更好一些。

以下是我想到的为什么代码评审很有价值的几大原因。这有很多了!

  1. 代码本身。代码评审最明显的价值是“发现错误”。或者如果你再深入一些,发现了一些作者不知道的最佳实践或潜在规则的情况,你可以反馈给他以改进那些具体的代码。
  2. 宏观层面的知识分享。当你评审别人的代码时,你其实是在学习对你有益的新技术 - 反之亦然,可能别人在评审你代码时也给你提出了更好的建议。如果你能够学以致用,你必将成长为一个工程师。
  3. 微观层面的知识分享。通过增加熟悉所有代码的人数,来缓和“公车因子(bus factor)”。(译注:公车因子越大,代表关键人物流失导致项目受到影响的概率越小)
  4. 趋势分享。相应地,代码评审迫使你与队友交流你们正在做的事情,这也确保了你们方向的正确性,而不至于数天或者数周之后才发现走在错误的方向上。
  5. 沟通实践。无论是在团队内部还是外部,清晰的沟通都是成功工作的最重要技能!代码评审给了你机会去练习怎么写作更清楚,不论是描述变更目的还是提交反馈的时候。而且幸运的话,下次你要写一些“非常重要”的东西时,你会发现自己已经准备好了。
  6. 历史记录。根据我的经验,如果人们知道他们写的东西会有人看,他们会写出更好的提交描述消息。这在回顾旧变更时非常有用!
  7. 可以讨论的东西。有时候你想同意某一变更,你会发现很难口头描述以及表达对比如特定算法细节等的赞同。通过一段代码进行交流会更精确些,因为代码往往比较明确。
  8. 团队凝聚力。当代码评审变成常规活动时,你会感觉更像是一个团队一起工作,而不是每个人“都在自己的轨道上”。
  9. 阅读练习。练习阅读别人的代码,有助于你把自己的代码写得更具可读性(因此,更具可维护性)。 这会让你之后写出更好的代码!
    如果非得做个选择,那么原因 2、5、6 对我来说可能是最有价值的。

原文以及参考译文

Why review code?

译文:Why review code?

ssh-keygen

ssh-keygen 参数说明

  • ssh-keygen - 生成、管理和转换认证密钥
  • ssh-keygen [-q] [-b bits] -t type [-N new_passphrase] [-C comment] [-foutput_keyfile]
  • ssh-keygen -p [-P old_passphrase] [-N new_passphrase] [-f keyfile]
  • ssh-keygen -i [-f input_keyfile]
  • ssh-keygen -e [-f input_keyfile]
  • ssh-keygen -y [-f input_keyfile]
  • ssh-keygen -c [-P passphrase] [-C comment] [-f keyfile]
  • ssh-keygen -l [-f input_keyfile]
  • ssh-keygen -B [-f input_keyfile]
  • ssh-keygen -D reader
  • ssh-keygen -F hostname [-f known_hosts_file]
  • ssh-keygen -H [-f known_hosts_file]
  • ssh-keygen -R hostname [-f known_hosts_file]
  • ssh-keygen -U reader [-f input_keyfile]
  • ssh-keygen -r hostname [-f input_keyfile] [-g]
  • ssh-keygen -G output_file [-v] [-b bits] [-M memory] [-S start_point]
  • ssh-keygen -T output_file -f input_file [-v] [-a num_trials] [-W generator]

ssh-keygen 用于为生成、管理和转换认证密钥,包括 RSA 和 DSA 两种密钥。

密钥类型可以用 -t 选项指定。如果没有指定则默认生成用于SSH-2的RSA密钥。
ssh-keygen 还可以用来产生 Diffie-Hellman group exchange (DH-GEX) 中使用的素数模数。
一般说来,如果用户希望使用 RSADSA 认证,那么至少应该运行一次这个程序,
~/.ssh/identity, ~/.ssh/id_dsa~/.ssh/id_rsa 文件中创建认证所需的密钥。
另外,系统管理员还可以用它产生主机密钥。
通常,这个程序产生一个密钥对,并要求指定一个文件存放私钥,同时将公钥存放在附加了”.pub”后缀的同名文件中。
程序同时要求输入一个密语字符串(passphrase),空表示没有密语(主机密钥的密语必须为空)。
密语和口令(password)非常相似,但是密语可以是一句话,里面有单词、标点符号、数字、空格或任何你想要的字符。
好的密语要30个以上的字符,难以猜出,由大小写字母、数字、非字母混合组成。密语可以用 -p 选项修改。
丢失的密语不可恢复。如果丢失或忘记了密语,用户必须产生新的密钥,然后把相应的公钥分发到其他机器上去。
RSA1 的密钥文件中有一个”注释”字段,可以方便用户标识这个密钥,指出密钥的用途或其他有用的信息。
创建密钥的时候,注释域初始化为”user@host”,以后可以用 -c 选项修改。
密钥产生后,下面的命令描述了怎样处置和激活密钥。可用的选项有:
-a trials
在使用 -TDH-GEX 候选素数进行安全筛选时需要执行的基本测试数量。
-B 显示指定的公钥/私钥文件的 bubblebabble 摘要。
-b bits
指定密钥长度。对于RSA密钥,最小要求768位,默认是2048位。DSA密钥必须恰好是1024位(FIPS 186-2 标准的要求)。
-C comment
提供一个新注释
-c 要求修改私钥和公钥文件中的注释。本选项只支持 RSA1 密钥。
程序将提示输入私钥文件名、密语(如果存在)、新注释。
-D reader
下载存储在智能卡 reader 里的 RSA 公钥。
-e 读取OpenSSH的私钥或公钥文件,并以 RFC 4716 SSH 公钥文件格式在 stdout 上显示出来。
该选项能够为多种商业版本的 SSH 输出密钥。
-F hostname
known_hosts 文件中搜索指定的 hostname ,并列出所有的匹配项。
这个选项主要用于查找散列过的主机名/ip地址,还可以和 -H 选项联用打印找到的公钥的散列值。
-f filename
指定密钥文件名。
-G output_file
DH-GEX 产生候选素数。这些素数必须在使用之前使用 -T 选项进行安全筛选。
-g 在使用 -r 打印指纹资源记录的时候使用通用的 DNS 格式。
-H 对 known_hosts 文件进行散列计算。这将把文件中的所有主机名/ip地址替换为相应的散列值。
原来文件的内容将会添加一个”.old”后缀后保存。这些散列值只能被 sshsshd 使用。
这个选项不会修改已经经过散列的主机名/ip地址,因此可以在部分公钥已经散列过的文件上安全使用。
-i 读取未加密的 SSH-2 兼容的私钥/公钥文件,然后在 stdout 显示 OpenSSH 兼容的私钥/公钥。
该选项主要用于从多种商业版本的 SSH 中导入密钥。
-l 显示公钥文件的指纹数据。它也支持 RSA1 的私钥。
对于 RSADSA 密钥,将会寻找对应的公钥文件,然后显示其指纹数据。
-M memory
指定在生成 DH-GEXS 候选素数的时候最大内存用量(MB)。
-N new_passphrase
提供一个新的密语。
-P passphrase
提供(旧)密语。
-p 要求改变某私钥文件的密语而不重建私钥。程序将提示输入私钥文件名、原来的密语、以及两次输入新密语。
-q 安静模式。用于在 /etc/rc 中创建新密钥的时候。
-R hostname
known_hosts 文件中删除所有属于 hostname 的密钥。
这个选项主要用于删除经过散列的主机(参见 -H 选项)的密钥。
-r hostname
打印名为 hostname 的公钥文件的 SSHFP 指纹资源记录。
-S start
指定在生成 DH-GEX 候选模数时的起始点(16进制)。
-T output_file
测试 Diffie-Hellman group exchange 候选素数(由 -G 选项生成)的安全性。
-t type
指定要创建的密钥类型。可以使用:”rsa1”(SSH-1) “rsa”(SSH-2) “dsa”(SSH-2)
-U reader
把现存的 RSA 私钥上传到智能卡 reader
-v 详细模式。ssh-keygen 将会输出处理过程的详细调试信息。常用于调试模数的产生过程。
重复使用多个 -v 选项将会增加信息的详细程度(最大3次)。
-W generator
指定在为 DH-GEX 测试候选模数时想要使用的 generator
-y 读取 OpenSSH 专有格式的公钥文件,并将 OpenSSH 公钥显示在 stdout 上。
ssh-keygen 可以生成用于 Diffie-Hellman Group Exchange (DH-GEX) 协议的 groups
生成过程分为两步:
首先,使用一个快速且消耗内存较多的方法生成一些候选素数。然后,对这些素数进行适应性测试(消耗CPU较多)。
可以使用 -G 选项生成候选素数,同时使用 -b 选项制定其位数。例如:

ssh-keygen -G moduli-2048.candidates -b 2048

默认将从指定位数范围内的一个随机点开始搜索素数,不过可以使用 -S 选项来指定这个随机点(16进制)。
生成一组候选数之后,接下来就需要使用 -T 选项进行适应性测试。
此时 ssh-keygen 将会从 stdin 读取候选素数(或者通过 -f 选项读取一个文件),例如:

ssh-keygen -T moduli-2048 -f moduli-2048.candidates

每个候选素数默认都要通过 100 个基本测试(可以通过 -a 选项修改)。
DH generator 的值会自动选择,但是你也可以通过 -W 选项强制指定。有效的值可以是: 2, 3, 5
经过筛选之后的 DH groups 就可以存放到 /etc/ssh/moduli 里面了。
很重要的一点是这个文件必须包括不同长度范围的模数,而且通信双方双方共享相同的模数。

~/.ssh/identity

该用户默认的 RSA1 身份认证私钥(SSH-1)。此文件的权限应当至少限制为”600”。
生成密钥的时候可以指定采用密语来加密该私钥(3DES)。
将在登录的时候读取这个文件。

~/.ssh/identity.pub

该用户默认的 RSA1 身份认证公钥(SSH-1)。此文件无需保密。
此文件的内容应该添加到所有 RSA1 目标主机的 ~/.ssh/authorized_keys 文件中。

~/.ssh/id_dsa

该用户默认的 DSA 身份认证私钥(SSH-2)。此文件的权限应当至少限制为”600”。
生成密钥的时候可以指定采用密语来加密该私钥(3DES)。
将在登录的时候读取这个文件。

~/.ssh/id_dsa.pub

该用户默认的 DSA 身份认证公钥(SSH-2)。此文件无需保密。
此文件的内容应该添加到所有 DSA 目标主机的 ~/.ssh/authorized_keys 文件中。

~/.ssh/id_rsa

该用户默认的 RSA 身份认证私钥(SSH-2)。此文件的权限应当至少限制为”600”。
生成密钥的时候可以指定采用密语来加密该私钥(3DES)。
将在登录的时候读取这个文件。

~/.ssh/id_rsa.pub

该用户默认的 RSA 身份认证公钥(SSH-2)。此文件无需保密。
此文件的内容应该添加到所有 RSA 目标主机的 ~/.ssh/authorized_keys 文件中。

/etc/ssh/moduli

包含用于 DH-GEXDiffie-Hellman groups

Nginx-默认mimetype集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;

text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;

image/png png;
image/svg+xml svg svgz;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/webp webp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;

font/woff woff;
font/woff2 woff2;

application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.apple.mpegurl m3u8;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/vnd.ms-excel xls;
application/vnd.ms-fontobject eot;
application/vnd.ms-powerpoint ppt;
application/vnd.oasis.opendocument.graphics odg;
application/vnd.oasis.opendocument.presentation odp;
application/vnd.oasis.opendocument.spreadsheet ods;
application/vnd.oasis.opendocument.text odt;
application/vnd.openxmlformats-officedocument.presentationml.presentation
pptx;
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xlsx;
application/vnd.openxmlformats-officedocument.wordprocessingml.document
docx;
application/vnd.wap.wmlc wmlc;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;

application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream iso img;
application/octet-stream msi msp msm;

audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;

video/3gpp 3gpp 3gp;
video/mp2t ts;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
}

Nginx-基础使用

Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。

常用场景

默认配置文件

全局块:nginx 全局配置。比如用户以及用户组 usernginx 进程 pid 文件存放路径,日志存放路径 error_log,工作进程worker process 数等,也可以引入配置文件。

events 块:服务器连接相关配置。比如进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。

http 块:可嵌套多个虚拟主机 server,配置代理,设置缓存,配置日志定义等。支持很多第三方模块的配置。比如文件引入,mime-type 定义,图片压缩,日志自定义,传输文件配置,设置连接超时时间,单个连接请求数等等。

server 块:虚拟主机的相关配置。

location 块:请求的路由相关配置,特殊页面的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# {全局块}

# 设置用户组
#user nobody;
# 配置用户或者组,默认为nobody nobody。
#user admin admin;

# 设置工作进程的数量
worker_processes 1;

# 指定日志路径,日志级别包括 debug | info | notice | warn | error | alert 等
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

# 指定进程文件
#pid logs/nginx.pid;

events { # {event块}
# 设置一个进程是否同时接受多个网络连接,默认为off
#multi_accept on;
# 事件驱动模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport
#use epoll;
# 进程的最大连接数
worker_connections 1024;
}

http { {#http块}
# 文件拓展名查找集合
include mime.types;
# 默认文件类型为文件流,匹配.*
default_type application/octet-stream;

# 日志格式定义,main为日志别名
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';

# 取消服务日志
#access_log off;
# 指定日志输入文件
#access_log logs/access.log main;

# 调用 sendfile 系统传输文件,sendfile on 表示通过文件描述符直接拷贝,而不是用read()/write()
sendfile on;
# tcp_nopush = on 会设置调用tcp_cork方法,这个也是默认的,结果就是数据包不会马上传送出去,等到数据包最大时,一次性的传输出去,这样有助于解决网络堵塞。
#tcp_nopush on;

# 设置连接超时时间
#keepalive_timeout 0;
keepalive_timeout 65;

# 是否启用 gzip 压缩,需要浏览器支持,需要解压缩
#gzip on;
# 设置gzip所需的http协议最低版本 (HTTP/1.1, HTTP/1.0)
#gzip_http_version 1.1;
# 设置压缩级别,压缩级别越高压缩时间越长 (1-9)
#gzip_comp_level 4;
# 设置压缩的最小字节数, 页面Content-Length获取
#gzip_min_length 1000;
# 设置压缩文件的类型
#gzip_types text/plain application/javascript text/css;

server { #{server块}
# 监听端口
listen 80;
# 虚拟主机名称
server_name localhost;

# 设置编码字符集
#charset koi8-r;

# 指定日志输入文件
#access_log logs/host.access.log main;

# 监听请求路由
location / { #{location块}
# 查找根目录
root html;
# 默认匹配
index index.html index.htm;
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
# 重定位服务器错误到静态页面 /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}


# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;

# location / {
# root html;
# index index.html index.htm;
# }
#}


# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;

# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;

# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;

# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;

# location / {
# root html;
# index index.html index.htm;
# }
#}

}

nginx 内置变量

  • $args : #这个变量等于请求行中的参数,同 $query_string
  • $content_length : 请求头中的 Content-length 字段。
  • $content_type : 请求头中的 Content-Type 字段。
  • $document_root : 当前请求在 root 指令中指定的值。
  • $host : 请求主机头字段,否则为服务器名称。
  • $http_user_agent : 客户端 agent 信息
  • $http_cookie : 客户端 cookie 信息
  • $limit_rate : 这个变量可以限制连接速率。
  • $request_method : 客户端请求的动作,通常为 GETPOST
  • $remote_addr : 客户端的 IP 地址。
  • $remote_port : 客户端的端口。
  • $remote_user : 已经经过 Auth Basic Module 验证的用户名。
  • $request_filename : 当前请求的文件路径,由 rootalias 指令与 URI 请求生成。
  • $scheme : HTTP 方法(如 httphttps)。
  • $server_protocol : 请求使用的协议,通常是 HTTP/1.0HTTP/1.1
  • $server_addr : 服务器地址,在完成一次系统调用后可以确定这个值。
  • $server_name : 服务器名称。
  • $server_port : 请求到达服务器的端口号。
  • $request_uri : 包含请求参数的原始 URI,不包含主机名,如:”/foo/bar.php?arg=baz”。
  • $uri : 不带请求参数的当前 URI$uri 不包含主机名,如”/foo/bar.html”。
  • $document_uri : 与 $uri 相同

nginx 基本命令

  • nginx -t 检查配置文件是否有语法错误
  • nginx -s reload 向主进程发送信号,重新加载配置文件
  • nginx -s stop 快速关闭
  • nginx -s quit 等待工作进程处理完成后关闭

location 匹配规则(优先级递减)

匹配符 含义
= 进行普通字符精确匹配。也就是完全匹配
^~ 前缀匹配。如果匹配成功,则不再匹配其他location

v~|表示执行一个正则匹配,区分大小写|
|~*|表示执行一个正则匹配,不区分大小写|
|/xxx/|常规字符串路径匹配|
|/|通用匹配,任何请求都会匹配到|

静态资源服务器

常见的静态资源包括图片,文档,视频等文件,所以 nginx 常用来作为静态网站服务器,或者作为 cdn 资源服务器。
例如以下是一个前后端分离的工程的前端工程部署配置:

1
2
3
4
5
6
7
8
9
10
11
12
...
server {
...
listen 8080;
server_name localhost;
root html/project;
location / {
// 按照优先级 '$uri' > '$uri/' > '/index.html' > '=404'依次匹配
try_files $uri $uri/ /index.html =404;
index index.html index.htm;
}
}

代理服务

nginx 用作代理服务器可以作为正向代理或者反向代理,而反向代理正是跨站资源访问的解决方案之一。
如下是正向代理配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
server {
...
listen 8080;
server_name localhost;
# DNS 域名解析服务器
resolver 8.8.8.8;
# 解析超时时间
resolver_timeout 5s;

location / {
proxy_pass $scheme://$http_host/$request_uri;
proxy_set_header Host $http_host;

proxy_buffers 256 4k;
proxy_max_temp_file_size 0;

proxy_connect_timeout 30;

proxy_cache_valid 200 302 10m;
proxy_cache_valid 301 1h;
proxy_cache_valid any 1m;
}
}

一次代理,直接在shell执行:

1
export http_proxy=http://x.x.x.x:8080

永久使用:

1
2
3
vim ~/.bashrc
export http_proxy=http://x.x.x.x:8080
source .bashrc # 刷新以加载变量

跨站资源访问

反向代理配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
...
server {
...
listen 80;
server_name localhost;

location /api {
# 代理转发
proxy_pass http://localhost:8081;
# 路径重写(如果需要)
# rewrite /api/(.*) /$1 break;

# 请求host传给后端
proxy_set_header Host $http_host;
# 设置用户ip地址
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;

proxy_buffers 256 4k;
proxy_max_temp_file_size 0;

# 代理超时时间
proxy_connect_timeout 30;

# 当请求服务器出错去寻找其他服务器
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
}

}

适配PC与移动端环境

nginx 可以通过内置变量 $http_user_agent,获取到请求客户端的 userAgent,从而知道用户处于移动端还是PC,进而控制重定向到H5站还是PC站。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
server {
...
listen 80;
server_name localhost;
location / {
# 移动、pc设备适配
if ($http_user_agent ~* '(Android|webOS|iPhone|iPod|BlackBerry)') {
set $mobile_request '1';
}
if ($mobile_request = '1') {
rewrite ^.+ http://mysite-base-H5.com;
}
}
}

负载均衡调度

RR(round robin:轮询)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
server {
...
upstream web_servers {
server localhost:8081;
server localhost:8082;
}
server {
...
listen 80;
server_name localhost;
#access_log logs/host.access.log main;


location / {
proxy_pass http://web_servers;
# 必须指定Header Host
proxy_set_header Host $host:$server_port;
}
}
}

按权重分配

1
2
3
4
5
upstream test {
server localhost:8081 weight=1;
server localhost:8082 weight=3;
server localhost:8083 weight=4 backup;
}

按地址分配:ip_hash

1
2
3
4
5
upstream test {
ip_hash;
server localhost:8080;
server localhost:8081;
}

后端服务器的响应时间来分配:fair(第三方)

1
2
3
4
5
upstream backend {
fair;
server localhost:8080;
server localhost:8081;
}

按访问url的hash结果来分配:url_hash(第三方)

1
2
3
4
5
6
upstream backend {
hash $request_uri;
hash_method crc32;
server localhost:8080;
server localhost:8081;
}

缓存服务

对扩展名为 gifjpgjpegpngbmpswfjscss 的图片,flashjavascriptcss 文件开启 Web 缓存,其他文件不缓存。配置文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
user  www;
worker_processes 8;

events {
worker_connections 65535;
}

http {
include mime.types;
default_type application/octet-stream;
charset utf-8;

log_format main '$http_x_forwarded_for $remote_addr $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_cookie" $host $request_time';
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;

#要想开启nginx的缓存功能,需要添加此处的两行内容!
#设置Web缓存区名称为cache_one,内存缓存空间大小为500M,缓存的数据超过1天没有被访问就自动清除;访问的缓存数据,硬盘缓存空间大小为30G
proxy_cache_path /usr/local/nginx/proxy_cache_path levels=1:2 keys_zone=cache_one:500m inactive=1d max_size=30g;

#创建缓存的时候可能生成一些临时文件存放的位置
proxy_temp_path /usr/local/nginx/proxy_temp_path;

fastcgi_connect_timeout 3000;
fastcgi_send_timeout 3000;
fastcgi_read_timeout 3000;
fastcgi_buffer_size 256k;
fastcgi_buffers 8 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;


client_header_timeout 600s;
client_body_timeout 600s;

client_max_body_size 100m;
client_body_buffer_size 256k;

gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 9;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php;
gzip_vary on;


upstream LB-WWW {
ip_hash;
server 192.168.1.101:80 max_fails=3 fail_timeout=30s; #max_fails = 3 为允许失败的次数,默认值为1
server 192.168.1.102:80 max_fails=3 fail_timeout=30s; #fail_timeout = 30s 当max_fails次失败后,暂停将请求分发到该后端服务器的时间
server 192.168.1.118:80 max_fails=3 fail_timeout=30s;
}

server {
listen 80;
server_name www.wangshibo.com;
index index.html index.php index.htm;
root /var/www/html;

access_log /usr/local/nginx/logs/www-access.log main;
error_log /usr/local/nginx/logs/www-error.log;

location / {
proxy_pass http://LB-WWW;
proxy_redirect off ;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#跟后端服务器连接超时时间,发起握手等候响应时间
proxy_connect_timeout 300;
#后端服务器回传时间,就是在规定时间内后端服务器必须传完所有数据
proxy_send_timeout 300;
#连接成功后等待后端服务器的响应时间,已经进入后端的排队之中等候处理
proxy_read_timeout 600;
#代理请求缓冲区,会保存用户的头信息以供nginx进行处理
proxy_buffer_size 256k;
#同上,告诉nginx保存单个用几个buffer最大用多少空间
proxy_buffers 4 256k;
#如果系统很忙时候可以申请最大的proxy_buffers
proxy_busy_buffers_size 256k;
#proxy缓存临时文件的大小
proxy_temp_file_write_size 256k;
proxy_next_upstream error timeout invalid_header http_500 http_503 http_404;
proxy_max_temp_file_size 128m;
}

location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ {
#使用Web缓存区cache_one,已在nginx.conf的缓存配置中命名的。
proxy_cache cache_one ;
#对不同HTTP状态码缓存设置不同的缓存时间
proxy_cache_valid 200 304 12h ;
proxy_cache_valid 301 302 1m ;
proxy_cache_valid any 1m ;
#设置Web缓存的Key值,Nginx根据Key值md5哈希存储缓存,这里根据"域名,URI,
#参数"组合成Key
proxy_cache_key $host$uri$is_args$args;
}

#用于清除缓存的url设置
#假设一个URL为http://www.wangshibo.com/test.gif,那么就可以通过访问http://www.wangshibo.com/purge/test.gif清除该URL的缓存。
location ~ /purge(/.*) {
#设置只允许指定的IP或IP段才可以清除URL缓存
allow 127.0.0.1 ;
allow 192.168.0.0/16 ;
deny all ;
proxy_cache_purge cache_one $host$1$is_args$args ;
}
}
}

简单的访问控制

denyallowngx_http_access_module 模块(已内置)中的语法。采用的是从上到下匹配方式,匹配到就跳出不再继续匹配。上述配置的意思就是,首先禁止 192.168.1.100 访问,然后允许 192.168.1.10-200 ip段内的访问(排除 192.168.1.100),同时允许 10.110.50.16 这个单独ip的访问,剩下未匹配到的全部禁止访问。实际生产中,经常和 ngx_http_geo_module 模块(可以更好地管理ip地址表,已内置)配合使用。

1
2
3
4
5
6
7
8
9
10
...
server {
...
location / {
deny 192.168.1.100;
allow 192.168.1.10/200;
allow 10.110.50.16;
deny all;
}
}

图片处理

满足日常对图片的裁剪/缩放/旋转/图片品质等处理需求。要用到 ngx_http_image_filter_module 模块。这个模块是非基本模块,需要安装。 下面是图片缩放功能部分的 nginx 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
server {
...
# 图片缩放处理
# 这里约定的图片处理url格式:以 mysite-base.com/img/路径访问
location ~* /img/(.+)$ {
#图片服务端储存地址
alias /Users/cc/Desktop/server/static/image/$1;
#图片宽度默认值
set $width -;
#图片高度默认值
set $height -;
if ($arg_width != "") {
set $width $arg_width;
}
if ($arg_height != "") {
set $height $arg_height;
}
#设置图片宽高
image_filter resize $width $height;
#设置Nginx读取图片的最大buffer
image_filter_buffer 10M;
#是否开启图片图像隔行扫描
image_filter_interlace on;
#图片处理错误提示图,例如缩放参数不是数字
error_page 415 = 415.png;
}
}

参考网站以及文章

nginx官网

Nginx与前端开发

前端想要了解的Nginx

Nginx常见使用场景-WEB服务(四)

nginx应用场景

Nginx 之六: Nginx服务器的正向及反向代理功能

nginx之正向代理

nginx的web缓存服务环境部署记录