Node.js
一种事件循环架构
- Node.js的异步事件驱动到底是什么?怎样工作的?
- JavaScript 关键特性讲解
- Node.js的事件队列
- 用otto引擎实现一个Node.js
- 在脉脉平台,目前我们前端是怎么用Node.js的?
- Node.js擅长哪些领域?
带着问题开始
JavaScript
high-level
object-oriented
just-in-time
curly-bracket syntax
prototype-based object-orientation
dynamic typing
first-class functions
示例0.0:函数
var call = fn => fn(3)
var doSomething = (n) => {
console.log(n)
}
// 函数可以作为参数传给传给函数
call(doSomething)
// 使用函数很方便,可以直接匿名使用
call(() => {
console.log(2)
})依赖宿主环境
Host
JavaScript Codes
Engine(v8...)
胶水语言
...
v8(c++)
otto(golang)
动态性极好
网络script资源
nodejs动态加载
两段不同的代码可以在不同时机被执行,且上下文可以互相侵入
示例0.1:侵入上下文
var a = 1
// eval 在当前作用域调用一段js代码
eval('a = 2')
console.log(a)Jsonp
示例0.2:独立上下文
const vm = require('vm');
const x = 1;
const sandbox = { x: 2 };
vm.createContext(sandbox); // Contextify the sandbox.
const code = 'x += 40; var y = 17;';
// `x` and `y` are global variables in the sandboxed environment.
// Initially, x has the value 2 because that is the value of sandbox.x.
vm.runInContext(code, sandbox);
console.log(sandbox.x); // 42
console.log(sandbox.y); // 17
console.log(x); // 1; y is not defined.- 灵活,迭代快,发版不受限
- 难维护,难排查
- 安全性差,性能差
衍生特点
Node.js
asynchronous event-driven
JavaScript runtime
不是lib不是框架,与浏览器并列,一种新的宿主环境
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
——nodejs.org
Node.js 能做什么?
asynchronous event-driven
示例1.0:异步事件、计时器
console.time('timeout')
// setTimeout第一个参数,是一个函数表达式
// 第二个参数时毫秒数,经过指定的延迟调用表达式
// 计时器是一种典型的宿主提供的能力
setTimeout(() => {
// 100.671ms
// console.time/timeEnd是计时api
console.timeEnd('timeout')
}, 100)
一个定时器就是一个异步事件
跟定时器类似,Node.js中存在多种异步事件,不同种事件的回调函数会加入不同的队列,这些队列之间存在优先级关系。事件以单线程的形式被排队调用

示例1.1:单线程
console.time('timeout')
setTimeout(() => {
// 由于线程忙,所以即时时间到了,也只能排队
// 1001.671ms
console.timeEnd('timeout')
}, 100)
// 线程阻塞
const block = ms => {
const start = Date.now()
while(Date.now() - start < ms) {}
}
block(1000)
// no data-race示例1.2:形象演示队列优先级
const fs = require('fs')
const readable = fs.createReadStream('./text')
// poll
readable.on('data', () => {
console.log('poll: ', Date.now())
})
// close
readable.on('close', () => {
console.log('close: ', Date.now())
})
// timer
setTimeout(() => {
console.log('timers: ', Date.now())
}, 1000)
// 当前线程阻塞
const block = ms => {
const start = Date.now()
while(Date.now() - start < ms) {}
}
block(3000)
// 开始处理事件队列的callback
console.log('todo queued jobs: ', Date.now())
// todo queued jobs: 1575476371564
// timers: 1575476371566
// poll: 1575476371570
// close: 1575476371571otto引擎试玩
一个“nodejs”它是怎么工作的?
getting started
-> go version
go version go1.13.5 darwin/amd64
-> go get fknsrs.biz/p/ottoext
-> go get github.com/robertkrimen/otto
-> go get github.com/GeertJohan/go.rice
-> go get gopkg.in/readline.v1
-> cd ~/go/src/fknsrs.biz/p/ottoext/cmd/ottoext
-> go build main.go
-> ./main example.js一个golang实现的“nodejs”的架构
otto
ottocmd
ottoext
大前提:otto能做什么?
执行js脚本
向js注入函数
获取并执行js方法
准备js与宿主环境交互的“钩子”,如timer,网络请求...这部分在ottoext的各种包中定义
在准备了各种与宿主环境的钩子之后,它已经是一个成熟的js runtime了,在后面的js脚本中就可以使用宿主注入的方法了,这部分代码在ottocmd中
根据异步的特点,宿主环境提供的“钩子”往往是需要回调的,引擎需要获取回调函数,并且可以执行它,这部分在ottoext/loop
大前提:otto能做什么?(2)



1)
2)
3)


回想一下刚刚提到的nodejs的核心特征
asynchronous event-driven
EventLoop (1 数据结构)




EventLoop (2 循环运行)


EventLoop (3 执行任务)



参考源码
Node.js在平台的应用
-
客服平台
-
周报平台
-
开发者平台
-
...
主要技术选型
-
koa
-
mongodb
-
pm2
koa
洋葱模型

示例 1.0 koa服务器 - 洋葱模型
const Koa = require('koa');
const app = new Koa();
// logger
app.use(async (ctx, next) => {
await next();
const rt = ctx.response.get('X-Response-Time');
console.log(`${ctx.method} ${ctx.url} - ${rt}`);
});
// x-response-time
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.set('X-Response-Time', `${ms}ms`);
});
// response
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);(示例代码来自 https://koa.bootcss.com/)
ctx
next
1,贯穿整个生命周期的一个对象,包含request对象,response对象。与请求和响应的一切活动皆围绕ctx对象进行
2,它可以被任意删减修改属性,正如JavaScript的灵活性,同时也具备风险,所以中间件是危险的,引入一个中间件的同时可能会影响下游中间件
1,泛指下游所有中间件,执行可对下游所有中间件进行迭代
2,默认下游操作是一个异步的过程
async function
简单来讲,使用async function,我们可以使用同步的代码结构,编写异步的逻辑
异步的JavaScript代码结构衍化
asyncCall1(res1 => {
asyncCall2(res2 => {
asyncCall3(res3 => {
/// ...
})
})
})asyncCall1().then(res1 => {
return asyncCall2()
}).then(res2 => {
return asyncCall3()
}).then(res3 => {
/// ...
})async function asyncCalls() {
const res1 = await asyncCall1()
const res2 = await asyncCall2()
const res3 = await asyncCall3()
// ...
}phase 1: callback
phase 2: promise
phase 3: async function

平台前端nodejs主要模块及作用
node.js擅长领域
-
前端自动化
-
webpack
-
gulp
-
...
-
-
headless browser
-
BFF
-
...
前端自动化
-
打包构建
-
webpack,gulp
-
-
预处理,后处理
-
ts,scss,postcss
-
-
模版生成
-
...
headless browser
-
截图服务
-
爬虫
-
搜索引擎优化
-
...
示例 2.0 headless browser 截图
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
console.log(await page.content());
await page.screenshot({path: 'screenshot.png'});
await browser.close();
(示例代码来自https://try-puppeteer.appspot.com )
BFF
-
微服务发展的产物,并不局限于nodejs
-
nodejs(前端)来做BFF的优势
-
api本是面向ui,服务端来做会有重复的理解成本和沟通成本
-
http request方和response方统一负责,降低理解沟通成本
-
服务端渲染
-
...
-
BFF在某条某部门
-
BFF层生成框架
- 基于 protobuf 定义api接口,并且自动生成代码架子
- protobuf 生成成 typescript Interface定义
-
go service层生成框架
- 自动生成代码架子
- 使用 thrift 协议
-
基于k8s的服务调度
- instance无状态
- 运维全部自动化

thx
nodejs
By shaomingquan
nodejs
- 627