-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 32.7 KB
/
content.json
1
{"pages":[{"title":"","text":"About me","link":"/about/me.html"}],"posts":[{"title":"JS异步总结","text":"一、若干概念 事件循环javascript引擎并不是独立运行的,它运行在宿主环境(如:web浏览器)。所有这些环境都有一个共同 点(线程),他们提供了一种机制来处理程序中多个块的执行,且执行块时调用JavaScript引擎。这种机制成为事件循环。Javascript引擎本身并没有时间的概念,只是一个按序执行JavaScript任意代码片段的环境。”事件“调度总是由包含它的环境运行。 并行线程并行计算最常见的工具就是进程和线程。进程和线程独立运行,并可能同时运行,甚至不同的计算机上,但多个线程共享单个进程的内存事件循环把自身的工作分成一个个任务并顺序执行,不允许对共享内存的并行访问和修改。 任务在es6中有一个新的概念建立在事件循环队列之上,叫做任务队列。promise的异步特性是基于任务的。 完整运行(es6前)由于JavaScript的单线程特性,函数中的代码具有原子性 1234567891011// 如果foo的代码开始运行,foo内部的代码都会在bar的任意运行之前完成var a = 20;function foo() { a = a + 1;}function bar() { a = a * 2;}ajax('http://some.url.1', foo);ajax('http://some.url.2', bar); 并发 非交互,这种情况的并发并不会给程序带来bug 12345678910// 非交互var res = {};function foo(results) { res.foo = results;}function bar(results) { res.bar = results;}ajax('http://some.url.1', foo);ajax('http://some.url.2', bar); 交互这种情况的并发可能会给程序带来竞态bug,这时代码就需要添加一些逻辑来避免程序出现问题123456789101112131415// 交互var res = [];function response(results) { res.push(data);}ajax('http://some.url.1', response);ajax('http://some.url.2', response);// 解决function response(data) { if (data.url === url1) { res[0] = data; } else { res[1] = data; }} 协作这里的目标是取到一个长期运行的”进程”,并将其分割成多个步骤或多批任务,使其他并发“进程”有机会将自己的运算插入到时间循环队列中交替运行。下面的例子把数据集合放到最多包含1000条项目的块中。这样就确保了“进程”运行时间很短,即使这意味着需要更多的后续的“进程”,因为事件循环队列的交替运行会提高应用的响应性能。1234567891011121314var res = [];function response(data) { var chunk = data.splice(0, 1000); res = res.concat(chunk.map(function(val) { return val * 2; })); if (data.length > 0) { setTimeout(function() { response(data); }, 0); }}ajax('http://some.url.1', response);ajax('http://some.url.2', response); 二、回调回调问题 嵌套回调众所周知,回调地狱由此产生 信任问题 回调最大的问题就是控制反转,控制权转移到了你依赖的第三方库等,它会导致信任链的完全断裂 回调方式进行异步编程可能出现的信任: 调用回调过早 调用回调过晚(或没有调用) 调用回调的次数太多或太少 没有把所需的环境、参数成功传给你的回调函数 吞掉可能出现的错误和异常 … 挽救回调问题的一些尝试: 更优雅的处理错误,提供回调分离,当然还有熟悉的error-first风格的回调模式 1234567function success() { // ...}function failure() { // ...}ajax('http://some.url.1', success, failure); 解决回调函数完全不被调用的问题 12345678910111213141516171819202122// 自行设计一个超时处理function timoutfiy(fn, delay) { var timer = setTimeout(() => { timer = null; fn(new Error('Timeout!')); }, delay); return function() { // 没有超时 if (timer) { clearTimeout(intv); fn.apply(this, arguments); } }}function foo(err, data) { if (err) { console.error(err); } else { console.log(data); }}ajax('http://some.url', timeoutfiy(foo, 3000)); 解决不确定关注的api会不会永远异步执行的问题(如:过早调用) 1234567891011121314151617181920212223242526function asyncify(fn) { var _fn = fn; var timer = setTimeout(() => { timer = null; if (fn) fn(); }, 0); fn = null; return function() { // 触发太快 if (timer) { fn = _fn.bind.apply( _fn, [this].concat([].slice.call(arguments)), ); } else { // 已经是异步 // 调用原来的函数 _fn.apply(this, arguments); } }}function foo() { // do something}ajax('http://some.url', asyncify(foo)); 三、Promise通过回调表达程序的异步和管理并发的两个主要缺陷:缺乏顺序性和可信任性看看promise是如何解决回调中存在的信任问题的: 调用过早:promise调用then方法的时候,及时这个promise已经决议,提供给then()的回调也总会被异步调用 调用过晚:Promise创建对象调用resolve()或reject()时,这个Promise的then()注册的观察回调就会被自动调度。可以确信,这些被调度的回调在下一个异步事件点一定会被触发。 回调未调用:如果Promise本身永远不被决议,可能导致这个问题,但是promise提供了一种称为竞态的超级抽象机制解决这个问题1234567891011121314151617function timeoutPromise(delay) { return new Promise((resolve, reject) => { setTimeout(() => { reject('timeout!'); }, delay); });}function fetchData() { // ajax()...};Promise.race([ fetchData(), timeoutPromise(3000),]).then( (value) => console.log(value), (err) => console.err(err)); 调用次数过少或过多:调用过少的情况和未被调用类似;Promise只能决议一次,then()注册的回调也就只能被调用一次 为传递参数、环境值:Promise至多只能有一个决议值,如果你没有用任何值显示决议resolve(), reject(),那么这个值就是undefined,但不管这个值是什么,无论当前或将来,他都会被传给所有注册的回调 吞掉错误和异常:如果拒绝一个Promise并给出一个理由 reject(‘err’),这个值就会被传给拒绝回调 如何能够确定返回的promise是可信任的呢?: Promise.resolve() 如果向Promise.resolve()传递一个非promise、非thenable的立即值,就会的到一个用这个值填充的promise123// p1 和 p2的行为完全一样var p1 = new Promise((resolve) => resolve(42));var p2 = Promise.resovle(42); 如果向Promise.resolve()传递一个真正的Promise,就返回同一个Promise 如果向Promise.resolve()传递一个非promise的thenable值,就会试图展开这个值,而且展开过程会持续到提取出一个具体的非类promise的最终值12345678910111213var p = { then: function(cb, errCb) { cb(42); errCb('err'); }}p.then( val => console.log(val), err => console.err(err));// 上面的运行结果并不符合promise的运行机制// 通过Promise.resolve使其成为一个真正的promisePromise.resolve(p) 链式流 promise的then方法会放回一个新的promise,并像Promise.resolve()一样处理完成或拒绝回调的返回值,这是promise序列能够在每一步有异步能力的关键 如果你调用promise的then()并且只传入了一个完成处理函数,then会提供一个默认的拒绝处理函数 如果没给then传递一个适当有效的函数作为完成处理函数,then还是会提供一个默认的处理函数 注意: promise 构造器的一个参数回调 resolve会像Promis.resolve一样展开传递给它的值 reject会原封不动的将传递给他的值设置为拒绝理由 Promise.reject()类似reject 错误处理 promise.catch(): 他的许多机制类似于promise.then() 在promise.then()中传入错误处理函数 Promise模式 Promise.all()1234567891011121314151617// 实现Promise.all = function(ps) { const res = []; let index = 0; return new Promise((resolve, reject) => { ps.forEach((p, index) => { p.then( value => { res[i] = value index++; if (index === ps.length) resolve(res); }, err => reject(err) ) }); });} Promise.race()1234567891011// 实现Promise.race = function(ps) { return new Promise((resolve, reject) => { for (let p of ps) { p.then( value => resolve(value), err => reject(err) ); } })} 考虑之前的超时例子,如果fetchData中保留了一些有用的资源,但是又超时了该如何处理呢?promise.finally()也许可以派上用场也可以polyfill一个方法来处理1234567891011121314151617181920212223242526272829Promise.observe = function(p, cb) { // 观察p的决议 p.then( data => Promise.resolve(data).then(cb), err => Promise.resolve(err).then(cb), ); // 返回原来的promise return p;}// function timeoutPromise(delay) { return new Promise((resolve, reject) => { setTimeout(() => { reject('timeout!'); }, delay); });}function fetchData() { // ajax()...};Promise.race([ Promise.observe(fetchData(), function handle() { // 即使没有在超时之前完成,也添加操作 }), timeoutPromise(3000),]).then( (value) => console.log(value), (err) => console.err(err)); Promise局限性 顺序错误处理,容易导致一些错误被忽略(如在promise链中最后一步中抛出的错误似乎很难避免忽略) 单一值:借助Promise.all()对多个promise操作 单决议: 某些场景会不太适用 我们使用的ajax工具得支持promise(应该都支持)123456789101112131415161718// 实现一个工厂函数,promise化Promise.wrap = function(fn) { return function() { var args = [].sclice.call(arguments); return new Promise((resolve, reject) => { fn.apply( null, args.concat(function(err, data) { if (err) { reject(err); } else { resolve(data); } }); ) }) }} promise无法取消:promise的一个消费者取消这个promise将会影响其他消费者查看这个promise12345678910111213141516171819202122232425262728// 可侵入式的定义自己的决议回调var ok = true;function timeoutPromise(delay) { return new Promise((resolve, reject) => { setTimeout(() => { reject('timeout!'); }, delay); });}function fetchData() { // ajax()...};var p = fetchData();Promise.race([ p, timeoutPromise(3000).catch(err => { ok = false; throw err; }),]).then( (value) => console.log(value), (err) => console.err(err));p.then(() => { if (ok) { // ... }}) 但是很丑陋,取消功能应该建立在promise之上更高级的抽象 四、生成器(Generator)打破完整运行1234567891011121314151617var x = 1;function *foo() { x++; yield; // 暂停 console.log('x', x);}function bar() { x++;}// 执行var it = foo();it.next();x // 2bar(); x // 3it.next(); // x: 3 迭代信息传递,生成器通过yield 和 next(…)实现迭代消息的传递123456789101112131415function *foo(x) { var y = x * (yield 'hello'); return y;}var it = foo(6);var res = it.next();res.value // hellores = it.next(7);res.value // 42// 一种比较好理解生成器代码运行机制的方法是,以上述代码为例:// 1. 第一个next()提出问题:生成器*foo(...)要给我的下一个值是什么// 2. 第一个yield给出上一个问题的答案: hello// 3. 第二个next(7)提出问题:生成器给我的下一个值是什么;并且这个next向迭代器传递了值7作为yield语句的返回值// 4. 由于没有yield回答问题,这个return回答了上一个问题 多个迭代器,通过多个迭代器交替运行,可以模拟多线程竞态条件环境123456789101112131415// 如下面的代码,迭代器的执行顺序影响着最终的输出结果:function *foo(x) { var x = yield 2; z++; var y = yield (x * z); console.log(x, y, z);}var z = 1;var it1 = foo();var it2 = foo();it1.next();it1.next();it2.next();// .... 生成器 + Promise获得Promise和生成器最大效用的最自然的方法就是yield出一个promise,然后通过这个promise来控制生成器的迭代器 12345678910111213141516171819function foo(x, y) { return request(`http://some.url?x=${x}&y=${x}`);}function *main() { try { const text = yield foo(11, 31); console.log(text); } catch(err) { console.error(err); }}var it = main();var p = it.next().value;p.then((text) => { it.next(text);}, (err) => { it.throw(err);}); 实现一个promise驱动的生成器,不管其内部有多少步骤:12345678910111213141516171819202122function run(gen) { var args = [].slice.call(arguments, 1); var it = gen.apply(this, args); return Promise.resolve() .then(function handleNext(val) { var next = it.next(val); return (handleResult(nextIt) { if (nextIt.done) { return nextIt.value; } else { return Promise.resolve(nextIt.value) .then( handleNext, handleError(err) { return Promise.resolve(it.throw(err)) .then(handleResult) } ) } })(next) })} 生成器中的promise并发123456789101112131415161718// 最简单的方法function *foo() { var p1 = request('url1'); var p2 = request('url2'); var r1 = yield p1; var r2 = yield p2; var r3 = yield request(`url3${r1}${r2}`);}// 使用Promise.all()function *foo() { var res = Promise.all([ request('url1'), request('url2') ]);var r3 = yield request(`url3${res[0]}${res[1]}`);} 生成器委托(yield*) yield 委托的主要目的是代码组织,以达到与普通函数调用的对称 yield 委托不必要求转移到另一个生成器,他可以转到一个非生成器的一般iterable 异常也被委托 生成器并发12345678910111213141516171819202122// 考虑下面代码的实现:var res = [];function *fetchData() { var data = yield require(url); // 控制转移 yield; res.push(data);}var it1 = fetchData('url1');var it2 = fetchData('url2');var p1 = it1.next();var p2 = it2.next();p1.then(function(data) { it1.next(data);})p2.then(function(data) { it2.next(data);});Promise.all([p1, p2]).then(function() { it1.next(); it2.next();}); 生成器底层原理, 通过下面的代码简要探索generator底层的运行机制123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172function *foo(url) { // 状态1 try { console.log('requesting:', url); var p = require(url); // 状态2 var val = yield p; console.log(val); } catch(err) { // 状态3 console.log('err:', err); return false; }}// 编译成es5:function foo () { var state; var val; function process(v) { switch(state) { case 1: console.log('requesting:', url); return request(url); case 2: val = v; console.log(val); return; case 3: var err = v; console.log('err', err); return false; } } return { next: function(v) { if (!state) { state = 1; return { done: false, value: process(v), }; } else if (state === 1) { state = 2; return { done: true, value: process(v), }; } else { return { done: true, value: undefined, }; } }, throw: function(e) { if (state === 1) { state = 3; return { done: true, value: process(e), }; } else { throw e; } } }} 五、async/await生成器的语法糖 《你不知道的JavaScript》中卷","link":"/2022/05/04/JS%E5%BC%82%E6%AD%A5%E6%80%BB%E7%BB%93/"},{"title":"Javascript 继承方式总结","text":"0.寄生组合式继承(这种方式是引用类型最理想的继承范式)这种方式的做法是,通过借用构造函数来继承属性,通过原型链的形式来继承方法。与组合式继承不同的是:不必为了指定子类型的原型而调用超类型的构造函数, 只需获得超类型原型的一个副本。基本模式如下: 12345678910111213141516171819202122232425262728293031323334353637383940// 封装子类型继承超类型原型的过程function inheritPrototype(subType, superType) { subType.prototype = Object.create(superType.prototype); subType.prototype.constructor = subType;}function Animal(name) { this.name = name; this.colors = ['black', 'white'];}Animal.prototype.getName = function() { console.log(this.name);}Animal.prototype.getColos = function() { console.log(this.colors);}function Cat(name, age) { Animal.call(this, name); this.age = age;} // 注意:要在给Cat原型添加方法之前调用inheritPrototype(Cat, Animal);Cat.prototype.getAge = function() { console.log(this.age);}var cat1 = new Cat('koko', 2);cat1.colors.push('yellow'); cat1.getColors(); // ['black', 'white', 'yellow']cat1.getName(); // 'koko'cat1.getAge(); // 2var cat2 = new Cat('yuli', 3);cat2.getColors(); // ['black', 'white']cat2.getName(); // 'yuli'cat2.getAge(); // 3 1.原型链基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法: 123456789101112131415161718function Animal() { this.name = 'animal';}Animal.prototype.getName = function () { console.log(this.name);}function Cat() { this.word = 'miao~';}// inherit AnimalCat.prototype = new Animal();Cat.prototype.bark = function () { console.log(this.word);}var cat = new Cat();console.log(cat.getName()); // 'animal'; 上述例子中:通过将Cat的prototype设置为Animal类型的实例实现了继承。因此,cat1作为Cat类型的实例,也具有Animal类型的name属性和getName方法。 缺点:1.当原型中包含引用类型值,所有实例都共享一个属性值。 1234567891011121314151617function Animal() { this.colors = ['black', 'white'];}function Cat() {}Cat.prototype = new Animal();var cat1 = new Cat();var cat2 = new Cat();console.log(cat1.colors); // ['black', 'white']console.log(cat2.colors); // ['black', 'white']cat1.colors.push('yelow');console.log(cat1.colors); // ['black', 'white', 'yellow']console.log(cat2.colors); // ['black', 'white', 'yellow'] 2.在创建子类型的实例时,不能向超类型的构造函数传递参数。 2.借用构造函数(伪造对象或经典继承)基本思想就是在子类型构造函数中调用超类型的构造函数: 12345678910111213function Animal(name) { this.name = name;}function Cat(name) { Animal.call(this, name); this.age = 1;}var cat = new Cat('koko');console.log(cat.name); // 'koko'console.log(cat.age); // 1 上述例子中:Cat类型内部调用了Animal类型的构造函数,并且向构造函数中传递了参数,这种方式解决了原型链方法不能向超类型构造函数传递参数的问题和所有子类型实例共享应用类型值的问题。 缺点1.构造函数模式问题:方法都在构造函数中定义,函数无法复用。2.超类型的原型中定义的方法,对于子类型而言是不可见的。 3.组合继承(伪经典继承)顾名思义,指的是将原型链和借用构造函数的技术组合到一起: 12345678910111213141516171819202122232425262728293031323334function Animal(name) { this.name = name; this.colors = ['black', 'white'];}Animal.prototype.getName = function() { console.log(this.name);}Animal.prototype.getColors = function() { console.log(this.colors);}function Cat(name, age) { Animal.call(this, name); this.age = age;}Cat.prototype = new Animal();Cat.prototype.constructor = Cat;Cat.prototype.getAge = function() { console.log(this.age);}var cat1 = new Cat('koko', 2);cat1.colors.push('yellow');cat1.getColors(); // ['black', 'white', 'yellow'];cat1.getName(); // 'koko'cat1.getAge(); // 2var cat2 = new Cat('yuli', 3);cat2.getColors(); // ['black', 'white'];cat2.getName(); // 'yuli';cat2.getAge(); // 3 可以看到上述例子中,避免了原型链继承和借用构造函数的缺陷,融合了他们的优点。 缺点1.需要调用两次超类型的构造函数 4.原型式继承这种方式没有使用严格意义上的构造函数,是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型: 12345function object(o) { function F() {}; F.prototype = o; return new F();} 从本质上讲,object对传入其的对象进行了一次浅复制,如下: 123456789101112var animal = { name: 'animal', colors: ['black', 'white'];}var cat = object(animal);cat.name = 'cat';cat.colors.push('yellow');var dog = object(animal);dog.name = 'dog';console.log(dog.colors); // ['black', 'white', 'yellow']; 可以看到上述例子中,cat和dog都拥有了name、colors属性,而colors属性作为应用类型值,在三个对象中都是共享的。在ES5中,新增的**Object.create()**方法规范了原型式继承。 缺点1.引用类型值在所有对象中是共享的。 5.寄生式继承寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数内部以某种方式来增强对象,然后返回增强后对象。 1234567function createAnother(orignal) { var clone = Object.create(original); clone.sayHi = function() { console.log('Hi~'); } return clone;} 可以看到上述函数利用Object.create()方法创建了original的副本,然后给副本添加sayHi方法来增强这个副本并返回。再看看使用示例: 12345678var animal = { name: 'animal', colors: ['black', 'white'],};var cat = createAnother(animal);cat.name = 'cat';cat.sayHi(); // 'hi~' 缺点1.方法无法复用","link":"/2019/06/30/Javascript-%E7%BB%A7%E6%89%BF%E6%96%B9%E5%BC%8F%E6%80%BB%E7%BB%93/"},{"title":"Docker指令","text":"一、指令示例1234567891011121314151617181920212223242526272829303132333435# 运行Nginx服务器docker run -p 8080:80 nginx# 后台运行Nginx服务器# -d(--detach)指后台运行# --name 指定容器名称docker run -p 8080:80 --name my-nginx -d nginx# 交互式运行# -it 选项,等于是同时指定 -i(--interactive,交互式模式)和 -t(--tty,分配一个模拟终端) 两个选项docker run -it --name dreamland ubuntu# 输出所有容器的ID docker ps -aq# 删除所有容器docker rm $(docker ps -aq)# 创建自定义网络docker network create dream-net# 容器化服务器# 构建服务器镜像docker build -t dream-server server# 使用官方Mongo镜像创建并运行容器docker run --name dream-db --network dream-net -d mongo# 运行服务器容器docker run -p 4000:4000 --name dream-api --network dream-net -d dream-serve# 容器化前端页面# 构建镜像(.指当前处于项目根目录)docker build -it dream-client .# 运行前端容器docker run -p 8080:80 --name client -d dream-client 二、DockerFile FROM 用于指定基础镜像,这里我们基于 nginx:1.13 镜像作为构建的起点 RUN 命令用于在容器内运行任何命令(当然前提是命令必须存在) COPY 命令用于从 Dockerfile 所在的目录拷贝文件到容器指定的路径 ENV 指令用于向容器中注入环境变量,配置服务器的 HOST 和 PORT等 EXPOSE 指令用于开放端口。(Nginx 基础镜像已经开放了 8080 端口,无需我们设置) CMD 指令用于指定此容器的启动命令(也就是 docker ps 查看时的 COMMAND 一列),对于服务器来说当然就是保持运行状态。 运行node server的dockerfile配置示例123456789101112131415161718192021222324FROM node:10# 指定工作目录为 /usr/src/app,接下来的命令全部在这个目录下操作WORKDIR /usr/src/app# 将 package.json 拷贝到工作目录COPY package.json .# 安装 npm 依赖RUN npm config set registry https://registry.npm.taobao.org && npm install# 拷贝源代码COPY . .# 设置环境变量(服务器的主机 IP 和端口)ENV MONGO_URI=mongodb://dream-db:27017/todosENV HOST=0.0.0.0ENV PORT=4000# 开放 4000 端口EXPOSE 4000# 设置镜像运行命令CMD [ "node", "index.js" ] 构建前端镜像dockerfile示例12345678910FROM nginx:1.13# 删除 Nginx 的默认配置RUN rm /etc/nginx/conf.d/default.conf# 添加自定义 Nginx 配置COPY config/nginx.conf /etc/nginx/conf.d/# 将前端静态文件拷贝到容器的 /www 目录下COPY build /www 三、指令参数 run: -d(后台模式) -i(交互模式)和 -t(虚拟终端) build: -t(–tag) 四、docker指令 主体 操作 简写 含义 docker container ls docker ps 列出所有容器 docker container rm docker rm 删除容器 docker container inspect docker inspect 查看容器详情 docker container prune 删除所有无用容器 docker container create docker create 创建容器 docker container run docker run 创建并允许容器 docker container start/restart docker start/restart 启动容器或重启容器 docker container stop docker stop 停止容器 docker container exec docker exec 进入容器 docker container attach docker attach 连接容器的输入输出流 主体 操作 简写 含义 docker image ls docker images 列出所有镜像 docker image rm docker rmi 删除镜像 docker image inspect docker inspect 查看镜像详情 docker image prune 删除所有无用镜像 docker image build docker build 创建镜像 docker image pull docker pull 从仓库拉去镜像 docker image push docker push 向仓库推送镜像 docker image tag docker tag 给镜像打标签 主体 操作 简写 含义 docker network ls 列出所有网络 docker network rm 删除网络 docker network inspect docker inspect 查看网络详情 docker network prune 删除所有无用网络 docker network create 创建网络 docker network connect 将容器连上网络 docker network disconnect 将容器从指定网络断开","link":"/2022/05/04/docker/"},{"title":"nginx","text":"Nginx工作原理 master和worker worker工作方式 一个master和多个worker的好处 可以使用nginx -s reload 进行热部署操作 某个worker出现问题,其他worker独立不受影响,保证服务不会中断 worker_processes: worker数量和服务器CPU数相等时最合适 worker_connections 发送静态资源请求占用了2个worker连接数 发送动态资源请求占用4个worker连接数 普通静态资源访问支持的最大并发数: worker_connections * worker_processes / 2 动态资源访问支持的最大并发数:worker_connections * worker_processes / 4 一、Nginx基本概念 Nginx简介: Nginx是高性能的HTTP和反向代理服务器,特点是占用内存小、并发能力强Nginx专为性能优化开发,能支持高达50,000并发连接数 反向代理 反向代理服务器位于用户与目标服务器之间,但是对于用户而言,反向代理服务器就相当于目标服务器,即用户直接访问反向代理服务器就可以获得目标服务器的资源。同时,用户不需要知道目标服务器的地址,也无须在用户端作任何设定。反向代理服务器通常可用来作为Web加速,即使用反向代理作为Web服务器的前置机来降低网络和服务器的负载,提高访问效率 负载均衡 负载均衡是高可用网络基础架构的关键组件,通常用于将工作负载分布到多个服务器来提高网站、应用、数据库或其他服务的性能和可靠性。 Round Robin(轮询):为第一个请求选择列表中的第一个服务器,然后按顺序向下移动列表直到结尾,然后循环。 Least Connections(最小连接):优先选择连接数最少的服务器,在普遍会话较长的情况下推荐使用。 Source:根据请求源的 IP 的散列(hash)来选择要转发的服务器。这种方式可以一定程度上保证特定用户能连接到相同的服务器。 动静分离 在Web开发中,通常来说,动态资源其实就是指那些后台资源,而静态资源就是指HTML,JavaScript,CSS,img等文件。 一般来说,都需要将动态资源和静态资源分开,将静态资源部署在Nginx上,当一个请求来的时候,如果是静态资源的请求,就直接到nginx配置的静态资源目录下面获取资源,如果是动态资源的请求,nginx利用反向代理的原理,把请求转发给后台应用去处理,从而实现动静分离。 高可用 二、Nginx常用命令123456789101112131415161718192021222324252627# 查看版本号nginx -v# 查看进程号ps -ef|grep nginx# 启动nginxnginx# 重启nginxnginx -s reload# 关闭Nginx# 从容停止# 找到 master 进程号kill -quit 25462# 或者kill -int 25462# 快速停止# 找到 master 进程号kill -term 25462# 或者kill -int 25462# 强制停止pkill -9 nginx 三、Nginx配置文件nginx 目录: /usr/local/etc/nginxnginx配置文件模块结构 main events 主要影响Nginx服务器与用户的网络连接 http http全局块 包括文件引入、MIME-TYPE定义、日志自定义、连接超时时间、单链接请求数上限等 server 和虚拟主机有密切关系 全局server块 location块 upstream块 nginx配置文件示例 Nginx配置参数详解123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121#user nobody;worker_processes 1;#error_log logs/error.log;#error_log logs/error.log notice;#error_log logs/error.log info;#pid logs/nginx.pid;#worker_rlimit_nofile 1024 指定一个nginx进程可以打开的最多文件描述符数目events { #Nginx支持的工作模式有select、poll、kqueue、epoll、rtsig和/dev/poll。其中select和poll都是标准的工作模式,kqueue和epoll是高效的工作模式,不同的是epoll用在Linux平台上,而kqueue用在BSD系统中,因为Mac基于BSD,所以Mac也得用这个模式,对于Linux系统,epoll工作模式是首选。 #use kqueue; worker_connections 1024; #每个进程最大连接数}http { include mime.types; default_type application/octet-stream; #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 logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; server { listen 8080; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /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; # } #} include servers/*;} 四、Nginx解决跨域配置示例1234567891011121314151617181920212223242526272829303132333435worker_processes 1;events { worker_connections 1024;}http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 8000; server_name 192.168.1.6; #location / { # proxy_pass http://localhost:8080 #} location /api/ { proxy_pass http://server.com } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } include servers/*;} 五、Nginx反向代理配置示例 监听nginx 8000端口路径127.0.0.1:8000/edu 代理至 127.0.0.1:8001/edu路径127.0.0.1:8000/aop 代理至 127.0.0.1:8002/aop 123456789101112131415161718192021222324252627282930313233343536worker_processes 1;events { worker_connections 1024;}http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 8000; server_name 192.168.1.6; # ~ 表示使用正则(区分大小写) ~*使用正则不区分大小写 location ~ /edu/ { proxy_pass http:127.0.0.1:8001 } location ~ /aop/ { proxy_pass http:127.0.0.1:8002 } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } include servers/*;} 六、负载均衡配置示例 将192.168.1.6:8080的请求负载均衡到192.168.1.6:8001、192.168.1.6:8002两个服务器 123456789101112131415161718192021222324252627282930313233343536373839404142434445worker_processes 1;events { worker_connections 1024;}http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; upstream myserver { # 默认轮旬分配 # ip_hash; ip hash 分配 # fair; 根据响应时间分配 server 192.168.1.6:8001; server 192.168.1.6:8002; # 权重算法分配 # server 192.168.1.6:8001 weight = 5; # server 192.168.1.6:8002 weight = 10; } server { listen 8080; server_name 192.168.1.6; location / { proxy_pass http://myserver; root html; index index.html index.html } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } include servers/*;} 七、高可用","link":"/2022/05/07/nginx/"}],"tags":[{"name":"promise","slug":"promise","link":"/tags/promise/"},{"name":"generator","slug":"generator","link":"/tags/generator/"},{"name":"js","slug":"js","link":"/tags/js/"},{"name":"docker","slug":"docker","link":"/tags/docker/"},{"name":"nginx","slug":"nginx","link":"/tags/nginx/"}],"categories":[{"name":"js","slug":"js","link":"/categories/js/"},{"name":"docker","slug":"docker","link":"/categories/docker/"},{"name":"nginx","slug":"nginx","link":"/categories/nginx/"}]}