Promise含义

  • Promise 对象,用来传递异步操作的消息。代表了某个未来才会知道结果的事件(通常是一个异步操作)。这个事件提供统一的API,可供进一步处理
  • 可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易
  • 无法取消Promise。如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

基本用法

Promise实例:

1
2
3
4
5
6
7
8
9
var promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});

resolve和reject是两个函数,由JavaScript引擎提供

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function test(object) {
return new Promise((resolve, reject) => {
//resolve("success");
reject("error");
});
}
test().then((value) => {
//resolve
console.log(value);
},function(value){
//reject
console.log(value);
});

then()–Promise.prototype.then()

  • Promise实例有then方法,then方法定义在原型对象Promise.prototype上.返回的是一个新的Promise实例
  • 可以采用链式(chain)写法,即then方法后面再调用另一个then方法,后一个then的回调函数,会等待新Promise对象的状态发生变化,才会被调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function funcA(comments) {
console.log("Resolved: ", comments);
}, function funcB(err){
console.log("Rejected: ", err);
});
// 第二个then的回调函数,会等待这个新的Promise对象状态发生变化。如果变为Resolved,就调用funcA
function(post) {
return getJSON(post.commentURL);
}
//等于
post => getJSON(post.commentURL)

catch()–Promise.prototype.catch()

建议总是使用catch方法,而不使用then方法的第二个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) { //cb
// success
})
.catch(function(err) {
// error
});

catch方法返回的还是一个Promise对象,因此后面还可以接着调用then方法。

如果没有使用catch方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应

有必要时,可以在回调链最后加done(),保证抛出任何可能出现的错误

Promise.all()

用于将多个Promise实例,包装成一个新的Promise实例。适合用于事务

p的状态由p1、p2、p3决定,分成两种情况。

(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

1
2
3
4
5
6
7
8
9
10
// 生成一个Promise对象的数组
var promises = [2, 3, 5, 7, 11, 13].map(function(id){
return getJSON("/post/" + id + ".json");
});
Promise.all(promises).then(function(posts) {
// ...
}).catch(function(reason){
// ...
});

Promise.race()

同样是将多个Promise实例,包装成一个新的Promise实例。可用于超时判断 setTimeout
超时判断可用bluebird的.timeout(
int ms,
[String message=”operation timed out”]
) -> Promise

只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的回调函数

1
2
3
4
5
6
7
8
var p = Promise.race([
fetch('/resource-that-may-take-a-while'),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
])
p.then(response => console.log(response))
p.catch(error => console.log(error))
  • bluebird Promise.some 可以传入count,来确定返回几个,promise.any则返回一个
1
2
3
4
5
6
7
8
Promise.some([
ping("ns1.example.com"),
ping("ns2.example.com"),
ping("ns3.example.com"),
ping("ns4.example.com")
], 2).spread(function(first, second) {
console.log(first, second);
});

Promise.resolve()

将现有对象转为Promise对象

参数是一个Promise实例,将不做变动的返回

参数是thenable对象 将这个对象转为Promise对象,然后就立即执行thenable对象的then方法

参数是一个原始值,或者是一个不具有then方法的对象 返回一个新的Promise对象,状态为Resolved

不带有任何参数 直接返回一个Resolved状态的Promise对象

1
2
3
4
5
6
7
8
9
10
11
12
13
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three

Promise.resolve()在本轮“事件循环”(event loop)结束时执行,setTimeout(fn, 0)在下一轮“事件循环”开始时执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//无论执行失败还是成功 最后都会回调,依照nodejs回调规范,第一个参数error,第二个成功结果。
bluebird .asCallback(
[function(any error, any value) callback],
[Object {spread: boolean=false} options]
) -> this
//spread 用于遍历拆分数组
Promise.resolve([1,2,3]).asCallback(function(err, a, b, c) {
// err == null
// a == 1
// b == 2
// c == 3
}, {spread: true});

Nova的四个核心服务:

  1. API —> 进入Nova的HTTP接口 有Policy保护 可以直接操作数据库
  2. Compute —> 与VMM交互运行和管理虚拟机
  3. Conductor —> 为数据库访问提供安全保障 建议数据库操作都通过Condutor
  4. Scheduler —> 负责挑选合适的计算节点创建虚拟机实例 只读取数据库

创建虚拟机的流程

  • 用户通过执行·client提供的命令,创建虚拟机。client,对原生RESTful Nova API封装、简化用户使用,将用户请求转换成HTTP请求
  • API监听HTTP请求,并将其转换成AMQP消息 然后通过消息队列调用Conductor
  • Conductor汇总参数,再、通过消息队列通知Scheduler去选择一个合适的计算节点,等拿到Scheduler提供的节点后,通知Compute创建虚拟机

删除虚拟机

  • 不需要Scheduler,API通过消息队列通知Compute删除指定虚拟机,Compute通过Condutor更新数据库

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
"use strict";
var net = require('net');
var fs = require('fs');
var name = "",//send-email-address
pwd = "",//email-password
host = "smtp.exmail.qq.com",
port = 25,
to = "";to-email-address
var argx = {
title: "test",
attachment: [{
name: "1.png",
path: "1.png"
}, {
name: "2.txt",
path: "2.txt"
}],
content: '内容'
}
send(argx)
function send(arg) {
var title = "test";
var f = fs.open("1.png");
var buffer = f.readAll();
var content = buffer.base64();
var f1 = fs.open("2.txt");
var content1 = f1.readAll().base64();
var _smtp = new net.Smtp();
_smtp.connect(host, port);
_smtp.hello();
_smtp.login(name, pwd);
_smtp.from(name);
var data = "from:" + name + "\r\n";
data += "to:" + to + "\r\n";
data += "subject:" + arg.title + "\r\n";
data += "MIME-Version: 1.0 \r\nContent-Type: multipart/mixed;\r\n "
data += "boundary=#qwertyuiop#;\r\n"
data += "\r\n--#qwertyuiop#\r\n"
data += "Content-Type: text/plain;charset=utf8\r\nContend˙t-Transfer-Encoding:7bit\r\n"
data += "\r\n" + arg.content + "\r\n"
arg.attachment.forEach(function(o) {
data += "\r\n--#qwertyuiop#\r\n"
data += "Content-Type: application/octet-stream;"
data += "name=" + o.name + "\r\n"
data += "Content-Transfer-Encoding: base64\r\n"
data += "\r\n" + fs.open(o.path).readAll().base64()
})
data += "\r\n--#qwertyuiop#\r\n"
f.dispose();
_smtp.to(to);
_smtp.data(data);
_smtp.quit();
}

  1. chmod +x ./test.sh #使脚本具有执行权限
  2. 变量名和等号之间不能有空格
  3. $ 表示当前Shell进程的ID,即pid $echo $$
  4. $ 和 $@ 都表示传递给函数或脚本的所有参数,当它们被双引号(“ “)包含时,”$“ 会将所有的参数作为一个整体,以”$1 $2 … $n”的形式输出所有参数;”$@” 会将各个参数分开,以”$1” “$2” … “$n” 的形式输出所有参数。
  5. -e 表示对转义字符进行替换 echo -e “Value of a is $a \n”1;

  6. 使用 echo 命令的 -E 选项禁止转义,默认也是不转义的;使用 -n 选项可以禁止插入换行符。

  7. 命令替换是指Shell可以先执行命令 DATE=date
变量 含义
$0 当前脚本的文件名
$n 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。
$# 传递给脚本或函数的参数个数。
$* 传递给脚本或函数的所有参数。
$@ 传递给脚本或函数的所有参数。被双引号(“ “)包含时,与 $* 稍有不同,下面将会讲到。
$? 上个命令的退出状态,或函数的返回值。
$$ 当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
echo "File Name: $0"
echo "First Parameter : $1"
echo "First Parameter : $2"
echo "Quoted Values: $@"
echo "Quoted Values: $*"
echo "Total Number of Parameters : $#"
$./test.sh Zara Ali
File Name : ./test.sh
First Parameter : Zara
Second Parameter : Ali
Quoted Values: Zara Ali
Quoted Values: Zara Ali
Total Number of Parameters : 2
转义 含义
\ 反斜杠
\a 警报,响铃
\b 退格(删除键)
\f 换页(FF),将当前位置移到下页开头
\n 换行
\r 回车
\t 水平制表符(tab键)
\v 垂直制表符

变量替换:

形式 说明
${var} 变量本来的值
${var:-word} 如果变量 var 为空或已被删除(unset),那么返回 word,但不改变 var 的值。
${var:=word} 如果变量 var 为空或已被删除(unset),那么返回 word,并将 var 的值设置为 word。
${var:?message} 如果变量 var 为空或已被删除(unset),那么将消息 message 送到标准错误输出,可以用来检测变量 var 是否可以被正常赋值。若此替换出现在Shell脚本中,那么脚本将停止运行。
${var:+word} 如果变量 var 被定义,那么返回 word,但不改变 var 的值。

VMM(Virtual Machine Monitor 虚拟机监视器)

  • 运行在硬件平台 Xen
  • 运行在操作系统里 KVM

完全虚拟化:VMM虚拟现实存在的平台,客户机感觉不到,操作系统不用修改

Intel的VTx 在处理器上引入一个新的执行模式用于虚拟机,当虚拟机运行在这个模式中,它的任何特权操作都会被处理器截获并报告VMM.VMM运行在正常模式下,接收到处理器报告后,解码目标指令,找到相应的虚拟化模块进行模拟,并把效果反映在特殊模式的坏境中

类虚拟化

VMM虚拟现实不存在的平台,虚拟平台对客户机操作系统进行修改,客户机也会主动适应。通过在源代码级别修改指令来避免虚拟化漏洞,对呀X86中难以虚拟化的指令,修改内核来避免,并自定义基于事务的高度优化的I/O协议,达到近似物理机的性能

  • 动态迁移:

    • 将虚拟机从一个物理机快速移到另一个,程序和网络保持连接。在目的服务器建立相同配置的虚拟机,同步各种内部状态,难点是内存同步,有脏页。如果有VTd设备(网卡),需要使用Bonding Drive,一个网络接口绑定多个网卡和hotplug 热插拔
  • 虚拟机快照:

    • 保存虚拟机某一时刻的硬盘、内存、CPU等信息
  • 虚拟机克隆:

    • 把一个虚拟机的状态完全复制到另一个虚拟机 但要修改某些配置 如MAC 避免冲突
  • P2V( Physical to Virtual Machine):

    • 将物理机的操作系统、数据从物理硬盘移到虚拟机 降低服务器虚拟化的使用门槛
  • Xen:

    • 起源于剑桥 最初基于类虚拟化,修改linux内核来实现处理器和内存虚拟化,引入I/O的前端/后端驱动架构实现设备虚拟化,能达到接近物理机的效果。之后支持硬件完全虚拟化,如Intel的VT AMD-V

管理一个节点上的虚拟机 支持

  • LXC,轻量级Linux容器
  • OpenVZ,基于Linxu内核的轻量级Linux容器
  • KVM/QEMU,基于Linux的类型2(宿主)的VMM
  • Xen,开源的类型1(运行在硬件)的VMM
  • User-mode Linux(UML),系统调用级别的Linux虚拟机
  • VirtualBox,类型2的Vmm
  • VMwarte ESX and GSX ,VMware 虚拟化的服务器版
  • VMware Workstation and Player,VMware虚拟化的桌面版
  • Hyper-V 微软开发的VMM
  • PowerVM IBM开发的VMM 运行在AIX Linux
  • Bhyve FreeBSD 9+上的VMM

基于驱动的架构 每种VMM要提供一个Driver

体积优化

  • 全局图片开关管控,针对商品、店铺、页头、入口图等图片通过开关全局系数裁剪压缩处理,降低页面图片整体体积;
  • zCache打包,js和css离线化,减少固定大资源阻塞和请求时间耗损;

请求优化

  • 通过全局开关控制,针对走节点懒加载模块图片做域名收敛管控,降低Mobile端的http建连和dns握手的成本;
  • 常用图标iconfont化,减少请求;
  • 节点懒加载接入,避免非首屏dom载入;
  • 空背景图请求修复,避免资源耗损;
  • 模块小图片base64化,减少不必要的请求;

渲染优化

  • gif动画去处和部分模块高度计算有误兼容避免引起重绘性能耗损;

动态降频(页面渲染wormhole)

  • 整个渲染策略就是,定时备份页面到OSS集群,每次请求过来,都会去判断当前系统Load是否过载,若果过载则直接读取上次备份的页面返回,而不使用模板引擎渲染,达到动态降低系统负载,快速响应的目的。 如果发生了最极端的情况,源站全部挂掉,由当天的值班人员,手工切换CDN指向已经备份了的OSS文件,保障页面可访问。

CDN根据终端类型(使用user-agent判断)转发到node渲染服务

模块开发者需要编写一份模块需要数据的 JSON Schema 描述

每一个页面都变成了以下几个部分:

  • 一份页面的描述文件,声明了这个页面依赖的所有模块,以及渲染这些模块所需的数据的地址。
  • 一系列相互独立的模块。
  • 一份包含页面上所有模块需要的数据的数据文件。

node优化

对于 node 应用自身而言,我们首先要保证它有充足的测试,通过 mocha + istanbul ,尽可能让测试覆盖每一个功能点和边缘情况。

  • 没有单点 多个节点,主备读取,同时对所有的文件都加上磁盘文件容灾,每一个依赖都不能够出现单点问题
  • 弱化依赖 对于每个依赖异常都有容灾的方案

  • 预案自动化 对于每一个可能出现问题的环节,都需要有针对性的预案,做到自动化,通过健康检查自动剔除

  • 视窗内渲染,就是只渲染可视区域的元素,以减少绘制消耗

  • 懒加载,就是图片资源一开始是不加载的,在用户滚动到附近区域的时候才加载,减少网络请求

  • 加载限流 限制同时只能加载4个图片,并实时调整加载顺序,优先加载用户当前可见区域的图片

对于多尺寸图片

自动处理图片的gulp任务,可以自动生成多种尺寸的图片,然后压缩、上传到cdn,最后生成一个imgs.js的文件,使用只需要依赖这个js,然后以原始文件名引用即可