redis通配符删除

redis通配符删除

批量删除Key

Redis 中有删除单个 Key 的指令 DEL,但好像没有批量删除 Key 的指令,不过我们可以借助 Linux 的 xargs 指令来完成这个动作

1
redis-cli keys "*" | xargs redis-cli del
1
2
3
4
5
//如果要指定 Redis 数据库访问密码,使用下面的命令
redis-cli -a password keys "*" | xargs redis-cli -a password del

//下面的命令指定数据序号为0,即默认数据库
redis-cli -n 0 keys "*" | xargs redis-cli -n 0 del
1
2
3
4
//批量删除redis 数据库中redis key的方法如下:
bin/redis-cli –h <your_redis_server_IP>
-p 6379 -n <your database ID>
-a <your_auth_keys> keys "mykeys*" | xargs bin/redis-cli -n <your database ID> del
1
2
3
4
5
//删除所有Key,可以使用Redis的flushdb和flushall命令
//删除当前数据库中的所有Key
flushdb
//删除所有数据库中的key
flushall

程序猿灯谜

灯谜

  1. 老会计喝二锅(打一热门技术)

  2. 梦中交谈(打一热门技术)

  3. 连胜六场又赢了(打一知名操作系统)

  4. 小米大合唱(打一著名互联网厂商)

  5. 男女生都一样(打一技术术语)

  6. 不达目的誓不罢休(打一著名网络解决方案提供商)

  7. 席卷天下,包举宇内,囊括四海,并吞八荒(打一互联网技术)

  8. 话又说回来了(打一网络安全术语)

  9. 禽流感(打一常见的PC/服务器故障)

  10. 深夜造访(打一网络安全术语)

  11. 屡屡破记录(打一技术术语)

  12. 单个花生超产(打一芯片技术)

  13. 驴友上路多郁闷(打一网络设备)

  14. 月老难扯二人姻缘(打一网络技术)

  15. 光芒照四方(打一安全厂商)

  16. 网管抓狂(打一网络设备)

  17. 悟空出了五指山(打一技术术语)

  18. 不听话就得打(打一网络安全术语)

  19. 整个界面都是阿凡达(打一PC/服务器故障现象)

  20. E(打一开发语言)

  21. 拳王的金腰带(打一网络技术)

  22. 方便月老配对象(打一网络设备)

  23. 走麦城(打一网络安全设备)

  24. 皇帝绷着脸(打一设备)

  25. 太上老君的金丹(打一网络安全术语)

  26. 王老吉面壁(打一网络安全设备)

高性能web平台OpenResty

高性能web平台OpenResty

OpenResty®

OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

OpenResty® 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。

OpenResty® 的目标是让你的Web服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。

OpenResty(又称:ngx_openresty) 是一个基于 NGINX 的可伸缩的 Web 平台,由中国人章亦春发起,提供了很多高质量的第三方模块。
OpenResty 是一个强大的 Web 应用服务器,Web 开发人员可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,更主要的是在性能方面,OpenResty可以 快速构造出足以胜任 10K 以上并发连接响应的超高性能 Web 应用系统。

下载安装

macOS/Mac OS X

强烈建议 Mac OS X 或者 macOS 系统用户通过 homebrew 包管理器安装 OpenResty,像下面这样:

1
2
brew tap openresty/brew
brew install openresty

如果你之前是从 homebrew/nginx 安装的 OpenResty,请先执行:

1
brew untap homebrew/nginx

Hello World实例

安装成功后,可以使用openresty直接输出html页面。
首先可以创建一个工作目录

1
2
3
mkdir /home/www
cd /home/www/
mkdir logs/ conf/

其中,logs目录用于存放日志,conf用于存放配置文件。

在conf目录下创建一个nginx.conf文件 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
worker_processes  1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
server {
listen 9000;
location / {
default_type text/html;
content_by_lua '
ngx.say("<p>Hello, World!</p>")
';
}
}
}

启动openresty

默认情况下 openresty 安装在 /usr/local/openresty 目录中,启动命令为:

1
2
cd /home/www
/usr/local/openresty/nginx/sbin/nginx -p `pwd`/ -c conf/nginx.conf

如果没有任何输出,说明启动成功,-p 指定我们的项目目录,-c 指定配置文件。

接下来我们可以使用 curl 来测试是否能够正常范围:

curl http://localhost:9000/

输出结果为:

<p>Hello, World!</p>

OpenResty 的目标是让你的 Web 服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL,PostgreSQL,~Memcaches 以及 ~Redis 等都进行一致的高性能响应。
所以对于一些高性能的服务来说,可以直接使用 OpenResty 访问 Mysql或Redis等,而不需要通过第三方语言(PHP、Python、Ruby)等来访问数据库再返回,这大大提高了应用的性能。

对称加密&非对称加密

对称加密 - DES

  1. 真正的加密算法,带密钥,加密和解密使用相同的密钥
  2. 对称加密优点是算法公开、计算量小、加密速度快、加密效率高
  3. 双方都保存秘钥,其次如果一方的秘钥被泄露,那么加密信息也就不安全了。另外,每对用户每次使用对称加密算法时,都需要使用其他人不知道的唯一秘钥,这会使得收、发双方所拥有的钥匙数量巨大,密钥管理成为双方的负担
  4. DES使用56位密钥,以现代计算能力,24小时内即可被破解。虽然如此,在某些简单应用中,我们还是可以使用DES加密算法
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
public class DES {  
/**
* 加密
*
* @param datasource byte[]
* @param password String
* @return byte[]
*/
public static byte[] encrypt(byte[] datasource, String password) {
try {
SecureRandom random = new SecureRandom();
DESKeySpec desKey = new DESKeySpec(password.getBytes());
//创建一个密匙工厂,然后用它把DESKeySpec转换成
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey securekey = keyFactory.generateSecret(desKey);
//Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance("DES");
//用密匙初始化Cipher对象,Cipher.ENCRYPT_MODE代表编码模式
cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
//现在,获取数据并加密
//正式执行加密操作
return cipher.doFinal(datasource);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}

/**
* 解密
*
* @param src byte[]
* @param password String
* @return byte[]
* @throws Exception
*/
public static byte[] decrypt(byte[] src, String password) throws Exception {
// DES算法要求有一个可信任的随机数源
SecureRandom random = new SecureRandom();
// 创建一个DESKeySpec对象
DESKeySpec desKey = new DESKeySpec(password.getBytes());
// 创建一个密匙工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
// 将DESKeySpec对象转换成SecretKey对象
SecretKey securekey = keyFactory.generateSecret(desKey);
// Cipher对象实际完成解密操作
Cipher cipher = Cipher.getInstance("DES");
// 用密匙初始化Cipher对象
cipher.init(Cipher.DECRYPT_MODE, securekey, random);
// 真正开始解密操作
return cipher.doFinal(src);
}
}

非对称加密 - RSA

1、用RSA算法生成一对密钥,公钥发放给外部客户,私钥自己保管;有以下应用场景:
【公钥加密、私钥解密】或者【私钥签名、公钥验证】
2、非对称加解密的理解:

  • 小明想秘密给小英发送消息
  • 小英手里有一个盒子(public key),这个盒子只有小英手里的钥匙(private key)才打得开
  • 小英把盒子送给小明(分发公钥)
  • 小明写好消息放进盒子里,锁上盒子(公钥加密)
  • 小明把盒子寄给小英(密文传输)
  • 小英用手里的钥匙打开盒子,得到小明的消息(私钥解密)
  • 假设小刚劫持了盒子,因为没有小英的钥匙,他也打不开

与DES不同,RSA算法中,每个通信主体都有两个钥匙,一个公钥一个私钥。
就是有2把钥匙

  1. 使用publicKey可以对数据进行加密
  2. 使用Key才能对数据进行解密

单方向传输
用公钥加密的数据,只有私钥能解开(可用于加密);
同时,使用私钥加密的数据,只有公钥能解开(签名)。但是速度很慢(比私钥加密慢100到1000倍),
公钥的主要算法有RSA,还包括Blowfish,Diffie-Helman等。

公钥与私钥

  1. 权威数字认证机构(CA)给所有通信主体(个人或组织)颁发公钥和私钥,彼此配对,分别唯一。
  2. 私钥好比数字指纹,同时具有解密和加密功能。个人保管,不公开。
  3. 公钥好比安全性极高的挂号信箱地址,公开。

公私钥加解密举例

设若甲有一份需保密的数字商业合同发给乙签署。经过如下步骤:

  1. 甲用乙的公钥对合同加密。
  2. 密文从甲发送到乙。
  3. 乙收到密文,并用自己的私钥对其解密。
  4. 解密正确,经阅读,乙用自己的私钥对合同进行签署。
  5. 乙用甲的公钥对已经签署的合同进行加密。
  6. 乙将密文发给甲。
  7. 甲用自己的私钥将已签署合同解密。
  8. 解密正确,确认签署。

公私钥加解密说明
从以上步骤,我们知道:

  1. 用公钥加密的密文能且只能用与其唯一配对的私钥才能解开。
  2. 如果某份密文被解开,那么肯定是密文的目标信息主体解开的。
  3. 私钥因其唯一标识所有者的属性,被用于数字签名,具有法律效力。

一。 公私钥生成

  1. 随机选定两个大素数p, q.
  2. 计算公钥和私钥的公共模数 n = pq .
  3. 计算模数n的欧拉函数 φ(n) .
  4. 选定一个正整数e, 使1 < e < φ(n) , 且e与φ(n)互质.
  5. 计算d, 满足 de ≡ 1 (mod φ(n) ), (k为某个正整数).
    6.n与e决定公钥, n与d决定私钥.

二。加解密
该过程为小张给小李发消息,公钥为小李的公钥(n & e), 私钥为小李的私钥(n & d).

  1. 小张欲给小李发一个消息M, 他先把M转换为一个大数m < n, 然后用小李的公钥(n & e)把m加密为另一个大数:
    c = me mod n
  2. 小李收到小张发来的大数c, 着手解密. 通过自己的私钥(n & d), 得到原来的大数m:
    m = cd mod n
    3.再把m转换为M, 小李即得到小张的原始消息.

这个过程之所以能通过, 是因为有如下等式:
cd ≡(me)d ≡med (mod n)

参考

MD5、SHA1、HMAC、HMAC_SHA1区别

MD5、SHA1、HMAC、HMAC_SHA1区别

MD5

MD5 – message-digest algorithm 5(信息-摘要算法)缩写,是一种不可逆的加密算法,对任何字符串都可以加密成一段唯一的固定长度的代码。可以是128位。
MD5码可以唯一地代码原信息的特征,通常用于密码的加密存储,数字签名,文件完整性验证等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**  
* MD5加密
*
* @param data
* @return
* @throws Exception
*/
public static byte[] encryptMD5(byte [] data) throws Exception {

MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
md5.update(data);

return md5.digest();
}

SHA1

SHA(Secure Hash Algorithm,安全散列算法),较MD5更安全。
SHA1是由NISTNSA设计,对长度小于264的输入,产生长度为160bit的散列值,可穷举性(brute-force
)更好。SHA-1是由美国标准技术局(NIST)颁布的国家标准,是一种应用最为广泛的Hash函数算法,也是
目前最先进的加密技术,被政府部门和私营业主用来处理敏感信息。也常用于验证文件有没有被篡改。

1
2
3
4
5
6
7
public static byte[] encryptSHA(byte[] data) throws Exception {

MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
sha.update(data);

return sha.digest();
}

HMAC_SHA1

HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code, 散列消息鉴别码),HMAC运算利用哈希算法,以一个密钥和一个消息为输入,生产一个消息摘要作为输出。消息鉴别码实现鉴别的原理是,
用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。使用一个密钥生成一个
固定大小的小数据块,即MAC,并将其加入到消息中,然后传输。接收方利用与发送方共享的密钥进行鉴别认证等。

HMAC_SHA1需要一个密钥,而SHA1不需要。

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
/**  
* 初始化HMAC密钥
*
* @return
* @throws Exception
*/
public static String initMacKey() throws Exception {
Keygenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);

SecretKey secretKey = keyGenerator.geterateKey();
return encryptBASE64(secretKey.getEncoded());
}

/**
* HMAC加密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encryptHMAC(byte[] data, String key) throws Exception {

SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);

return mac.doFinal(data);
}

CRC

CRC的全称为CyclicRedundancyCheck,中文名称为循环冗余校验。它是一类重要的线性分组码,编码和解码方法
简单,检错和纠错能力强,在通信领域广泛地用于实现差错控制。

BASE64

按照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种
不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent
brbitrary sequences of octets in a form that need not be humamly readable.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**  
* BASE64解密
*
* @param key
* @return
* @throws Exception
*/
public static byte[] decryptBASE64(String key) throws Exception {
return (new BASE64Decoder()).decodeBuffer(key);
}

/**
* BASE64加密
*
* @param key
* @return
* @throws Exception
*/
public static String encryptBASE64(byte[] key) throws Exception {
return (new BASE64Encoder()).encodeBuffer(key);
}

## hash算法的作用

    1. 文件校验

MD5Hash算法的“数字指纹”,使它成为目前应用最广泛的一种文件完整性校验和(Checksum)算法,不少Unix
系统有提供计算md5checksum的命令。

    1. 数字签名

Hash算法也是现代密码体系中的一个重要组成部分,由于非对称算法的运算速度较慢,所以在数字签名协议中,
单项散列函数扮演了一个重要的角色。对Hash值,又称”数字摘要”进行数字签名,在统计上可以任务与对文件
本身进行数字签名是等效的。

    1. 鉴权协议

完整代码

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
import java.security.MessageDigest;    

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
* 基础加密组件
*
* @author jiaxf
* @version 1.0
* @since 1.0
*/
public abstract class Coder {
public static final String KEY_SHA = "SHA";
public static final String KEY_MD5 = "MD5";

/**
* MAC算法可选以下多种算法
*
* <pre>
* HmacMD5
* HmacSHA1
* HmacSHA256
* HmacSHA384
* HmacSHA512
* </pre>
*/
public static final String KEY_MAC = "HmacMD5";

/**
* BASE64解密
*
* @param key
* @return
* @throws Exception
*/
public static byte[] decryptBASE64(String key) throws Exception {
return (new BASE64Decoder()).decodeBuffer(key);
}

/**
* BASE64加密
*
* @param key
* @return
* @throws Exception
*/
public static String encryptBASE64(byte[] key) throws Exception {
return (new BASE64Encoder()).encodeBuffer(key);
}

/**
* MD5加密
*
* @param data
* @return
* @throws Exception
*/
public static byte[] encryptMD5(byte[] data) throws Exception {

MessageDigest md5 = MessageDigest.getInstance(KEY_MD5);
md5.update(data);

return md5.digest();

}

/**
* SHA加密
*
* @param data
* @return
* @throws Exception
*/
public static byte[] encryptSHA(byte[] data) throws Exception {

MessageDigest sha = MessageDigest.getInstance(KEY_SHA);
sha.update(data);

return sha.digest();

}

/**
* 初始化HMAC密钥
*
* @return
* @throws Exception
*/
public static String initMacKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);

SecretKey secretKey = keyGenerator.generateKey();
return encryptBASE64(secretKey.getEncoded());
}

/**
* HMAC加密
*
* @param data
* @param key
* @return
* @throws Exception
*/
public static byte[] encryptHMAC(byte[] data, String key) throws Exception {

SecretKey secretKey = new SecretKeySpec(decryptBASE64(key), KEY_MAC);
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);

return mac.doFinal(data);

}
}

测试类

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
import  static org.junit.Assert.*;

import org.junit.Test;

/**
*
* @author jiaxf
* @version 1.0
* @since 1.0
*/
public class CoderTest {

@Test
public void test() throws Exception {
String inputStr = "简单加密";
System.err.println("原文:/n" + inputStr);

byte[] inputData = inputStr.getBytes();
String code = Coder.encryptBASE64(inputData);

System.err.println("BASE64加密后:/n" + code);

byte[] output = Coder.decryptBASE64(code);

String outputStr = new String(output);

System.err.println("BASE64解密后:/n" + outputStr);

// 验证BASE64加密解密一致性
assertEquals(inputStr, outputStr);

// 验证MD5对于同一内容加密是否一致
assertArrayEquals(Coder.encryptMD5(inputData), Coder
.encryptMD5(inputData));

// 验证SHA对于同一内容加密是否一致
assertArrayEquals(Coder.encryptSHA(inputData), Coder
.encryptSHA(inputData));

String key = Coder.initMacKey();
System.err.println("Mac密钥:/n" + key);

// 验证HMAC对于同一内容,同一密钥加密是否一致
assertArrayEquals(Coder.encryptHMAC(inputData, key), Coder.encryptHMAC(
inputData, key));

BigInteger md5 = new BigInteger(Coder.encryptMD5(inputData));
System.err.println("MD5:/n" + md5.toString(16));

BigInteger sha = new BigInteger(Coder.encryptSHA(inputData));
System.err.println("SHA:/n" + sha.toString(32));

BigInteger mac = new BigInteger(Coder.encryptHMAC(inputData, inputStr));
System.err.println("HMAC:/n" + mac.toString(16));
}
}

参考

  1. 加密算法中BASE64、MD5、SHA、HMAC等之间的区别

2018-05-02-Node.js10和npm6发布

Node.js10及npm6发布

Node.js 发布最新版本 Node.js 10.0.0,同时,npm 6 也随之发布。据了解,今年 10 月,Node.js 10.x 将成为长期支持版本,该版本专注于稳定性、扩展支持以及为各类应用提供一个可靠稳定的平台。

Node.js 10.x 将是第一个支持 OpenSSL 1.1.0 的版本。该版本配备了 Google V8 6.6 JavaScript 引擎,性能会增强,错误处理和跟踪诊断能力也将会提升。
此版本还将推出 Node.js API(N-API),N-API 是一个稳定的 API 模块,它独立于 V8,这样就不会阻碍模块在不重新编译的情况下运行新版本的 Node.js。

关于 N-API

N-API 提高了 Node.js 的 ABI 稳定性,有助于模块的部署和维护。
Node.js 10 将 ABI 的稳定模块 API(N-API)作为官方支持的 API 层。N-API 旨在解决当今生态系统中的两个问题,一是降低本地模块的维护成本,二是在升级 Node.js 版本时,降低模块使用者之间的摩擦。

升级到最新的 Node.js 版本后,Node.js 版本之间的模块损坏将不再成为 N-API 模块的问题,这对于开发者和消费者来说都是双赢的。为了提高此功能的实用性,N-API 也将被移植到 Node.js 8.x 和 6.x 中,还包括下一版本。

现代化的加密

Node.js 10.x 是第一代支持 OpenSSL 1.1.0 的版本,Node.js 现在能够充分利用由 OpenSSL 团队在代码质量、清理和现代化上提供的服务。

Node.js 现在可以将其加密支持扩展到对称加密算法 ChaCha20 和身份认证算法 Poly1305 上,它们共同构成了现代加密系统,增加了 Node.js 使用“ AEAD ”密码套件的可能性。
伴随着最近 TLS 1.3 规范的完成,网络安全迈出了一大步,OpenSSL 团队正准备发布 1.1.1 版本,其主要特性是支持 TLS 1.3,而支持 OpenSSL 1.1.1 的 Node.js 10 将可以轻松实现 API 和 ABI 的稳定升级。

错误处理能力提升

Node.js 10.x 在利用错误代码以缓解持续性的错误检验上取得了很好的进展。过去,更改文本里的错误都需要等到 semver 主版本更新后,这也意味着只有等到下一个主版本的 Node.js 才能对错误进行更改,而 Node.js 主版本每六个月发布一次。使用错误代码将可以在不中断应用程序的情况下更新文本。

性能改进

最新的 V8 在 Promise、异步生成器和阵列性能(array performance)有了很大的改进,Promise 和异步函数的改进消除了异步函数和 desugarded promise 链之间的隔阂,这有利于提高使用 Node.js 构建的应用程序性能。

诊断跟踪和Post mortem

在 Node.js 10 中,新的跟踪事件由 performance API 发布,提高了代码的透明性。此外,它还将引入了新的 API,允许用户在代码运行时按需启用和禁用跟踪事件,从而提高了运行时诊断 Node.js 应用程序问题的灵活性。

Node.js 10.x引入npm6

Node.js 10.0.0 附带 npm 5.7.x, 但是,预计在 Node.js 10.x 生命周期的早期会更新为 npm 6。第 6 版将侧重于性能、稳定性和安全性,与先前版本的 npm 相比,性能提高 1700%。
以后,如果使用具有已知安全问题的代码,npm Registry 的用户会收到警告通知。npm 将自动检查针对 NSP 数据库的安装请求,并在代码包含漏洞时发出警告通知。

有关npm6的更多信息,可访问https://go.npm.me/npm6

注意: Node.js 10 版本可能还会发生其他变动,可参考https://medium.com/the-node-js-collection/the-node-js-project-introduces-latest-release-line-node-js-10-x-bf07abfa9076

相关链接

  1. Node.js 10文档
  2. Node.js API 中文文档

PWA简介

PWA简介

Progressive Web Apps,简称PWA.
渐进式提升Web App原生体验的技术方案,能给用户原生应用的体验。

PWA能做到原生应用的体验不是靠特指某一项技术,而是经过应用一些新技术进行改进,在安全、性能和体验三个方面都有很大提升,PWA本质上是WebApp,借助一些新技术也具备NativeApp的一些特性,兼具WebApp和NativeApp的优点。

PWA具有的一些特点及特性

PWA特点:

  • 可靠 即使在不稳定的网络环境下,也能瞬间加载并展现
  • 体验 快速响应,并且有平滑的动画响应用户的操作
  • 粘性 像设备上的原生应用,具有沉浸式饿得用户体验,用户可以添加到桌面

PWA具有特性

  • 渐进式 - Progressive 适用于所有浏览器,因为它以渐进式增强作为宗旨开发
  • 连接无关性 - 能够借助Service Worker在离线或这网络较差情况下正常访问
  • 类似应用 - 由于在App Shell模型基础上开发,因为应具有NativeApp的交互和导航,给用户NativeApp的体验
  • 持续更新 - 始终是最新的,无版本和更新问题
  • 安全 - 通过HTTPS协议提供服务,防止窥探和确保内容不被篡改
  • 可索引 - 应用清单文件和ServiceWorker可以让搜索引擎引到,从而将其识别为应用
  • 粘性 - 通过推送离线通知等,可以让用户回流
  • 免安装 - 用户可以添加常用的webapp到桌面,免去应用商店下载的麻烦
  • 可链接 - 通过链接即可分享内容,无需下载安装

  • Web App Manifest(主屏图标)

  • ServiceWorker(离线可用)
  • Notification API & Push API(离线通知)
  • App Shell & App Skeleton设计模型
  • PRPL Pattern(Push, Render, Pre-cache, Lazy-load)
  • 安全HTTPS
  • 交互 & 动画
  • PWA通常是SPA 通常采用AppShell设计模型

ServiceWorker

什么是Service Worker

W3C 组织早在 2014 年 5 月就提出过 Service Worker 这样的一个 HTML5 API ,主要用来做持久的离线缓存。
浏览器中的 javaScript 都是运行在一个单一主线程上的,在同一时间内只能做一件事情。随着 Web 业务不断复杂,我们逐渐在 js 中加了很多耗资源、耗时间的复杂运算过程,这些过程导致的性能问题在 WebApp 的复杂化过程中更加凸显出来。

W3C 组织早早的洞察到了这些问题可能会造成的影响,这个时候有个叫 Web Worker 的 API 被造出来了,这个 API 的唯一目的就是解放主线程,Web Worker 是脱离在主线程之外的,将一些复杂的耗时的活交给它干,完成后通过 postMessage 方法告诉主线程,而主线程通过 onMessage 方法得到 Web Worker 的结果反馈。

Service Worker 在 Web Worker 的基础上加上了持久离线缓存能力。
Service Worker 有以下功能和特性:

  • 一个独立的 worker 线程,独立于当前网页进程,有自己独立的 worker context。
  • 一旦被 install,就永远存在,除非被 uninstall
  • 需要的时候可以直接唤醒,不需要的时候自动睡眠(有效利用资源,此处有坑)
  • 可编程拦截代理请求和返回,缓存文件,缓存的文件可以被网页进程取到(包括网络离线状态)
  • 离线内容开发者可控
  • 能向客户端推送消息
  • 不能直接操作 DOM
  • 出于安全的考虑,必须在 HTTPS 环境下才能工作
  • 异步实现,内部大都是通过 Promise 实现

Service Worker生命周期

MDN 给出了详细的 Service Worker 生命周期图:

生命周期状态:

  • 安装( installing ):这个状态发生在 Service Worker 注册之后,表示开始安装,触发 install 事件回调指定一些静态资源进行离线缓存。

    install 事件回调中有两个方法:

    • event.waitUntil():传入一个 Promise 为参数,等到该 Promise 为 resolve 状态为止。
    • self.skipWaiting():self 是当前 context 的 global 变量,执行该方法表示强制当前处在 waiting 状态的 Service Worker 进入 activate 状态。
    • 安装后( installed ):Service Worker 已经完成了安装,并且等待其他的 Service Worker 线程被关闭。
    • 激活( activating ):在这个状态下没有被其他的 Service Worker 控制的客户端,允许当前的 worker 完成安装,并且清除了其他的 worker 以及关联缓存的旧缓存资源,等待新的 Service Worker 线程被激活。
      activate 回调中有两个方法:
    • event.waitUntil():传入一个 Promise 为参数,等到该 Promise 为 resolve 状态为止。
    • self.clients.claim():在 activate 事件回调中执行该方法表示取得页面的控制权, 这样之后打开页面都会使用版本更新的缓存。旧的 Service Worker 脚本不再控制着页面,之后会被停止。
    • 激活后( activated ):在这个状态会处理 activate 事件回调 (提供了更新缓存策略的机会)。并可以处理功能性的事件 fetch (请求)、sync (后台同步)、push (推送)。
    • 废弃状态 ( redundant ):这个状态表示一个 Service Worker 的生命周期结束。

    进入废弃 (redundant) 状态的原因可能为这几种:

    • 安装 (install) 失败
    • 激活 (activating) 失败
    • 新版本的 Service Worker 替换了它并成为激活状态

Service Worker支持的事件

MDN 也列出了 Service Worker 所有支持的事件:

怎么使用Service worker

  1. 注册

安装serviceWoker ,通常需要在js主线程(常规页面的js)注册SeriveWorker来启动安装,这个过程将会通知浏览器我们的Service Worker线程的javascript文件在什么地方呆着。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('/sw.js', {scope: '/'})
.then(function (registration) {

// 注册成功
console.log('ServiceWoker registration successful with scope:', registration.scope);
})
.catch(function (err) {
// 注册失败
console.log('ServiceWoker registration failed:', err);
});
});
}

注:查看是否注册成功,可以用chrome浏览器,输入 chrome://inspect/#service-workers

  1. 安装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 监听 service worker的install事件
this.addEventListener('install', function(event){
// 如果监听到了service worker 已经安装成功,就会调用event.waitUntil回调函数
event.waitUntil(
// 安装成功后操作 CacheStorage缓存,使用之前需要先通过caches.open()打开对应缓存空间
cache.open('my-test-cache-v1').then(function(cache) {
// 通过cache缓存对象的addAll方法添加precache缓存
return cache.addAll([
'/',
'/index.html',
'/main.css',
'/main.js',
'/image.jpg'
])
});
);
});
  1. 自定义请求响应

任何被Service Worker控制的资源被请求时,都会触发fetch事件,这些资源包括了指定的scope内的html文档,和这些html文档内引用的其他任何资源。
实现思路: Serivice Worker代理服务,给Service Woker添加一个fetch的事件监听器,接着调用event上的respondWith()方法来劫持HTTP响应,然后来更新他们。

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
this.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
// 来来来,代理可以搞一些代理的事情

// 如果Service Worker有自己的返回,就直接返回,减少一次http请求
if(response){
return response;
}

// 如果service worker没有返回,那就得直接请求真实远程服务
var request = event.request.clone(); //把原始请求拷过来
return fetch(request).then(function(httpRes) {
// http请求的返回已被抓到,可以处置

// 请求失败了,直接返回失败的结果就好
if(!httpRes || httpRes.status !== 200){
return httpRes;
}

// 请求成功的话,将请求缓存起来
var responseClone = httpRes.clone();
cache.open('my-test-cache-v1').then(function (cache){
cache.put(event.request, responseClone);
});

return httpRes;
});

})
);
});
  1. Service Worker版本更新

    • 自动更新所有页面

      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
      // 安装阶段跳过等待,直接进入active
      self.addEventListener('install', function(event) {
      event.waitUntil(self.skipWaiting());
      });

      self.addEventListener('activate', function(event) {
      event.waitUntil(
      Promise.all([
      // 更新客户端
      self.clients.claim(),

      // 清理旧版本
      caches.keys().then(function(cacheList) {
      return Promise.all(
      cacheList.map(function(cacheName) {
      if(cacheName !== 'my-test-cache-v1'){
      return caches.delete(cacheName);
      }
      })
      );
      })

      ])
      );
      });
    • 手动更新Service Worker

在页面中可以手动借助Registration.update()更新。

1
2
3
4
5
6
7
8
var version = '1.0.1';
navigator.serviceWorker.register('/sw.js').then(function(reg) {
if(localStorage.getItem('sw_version') !== version){
reg.update().then(function() {
localStorage.setItem('sw_version', version)
});
}
});
  • debug时更新

    Service Worker被载入后立即激活可以保证每次/sw.js为最新。

    1
    2
    3
    self.addEventListener('install', function(){
    self.skipWaiting();
    });
  • 意外惊喜

Service Worker的特殊之处除了由浏览器触发更新之外,还应用了特殊的缓存策略:如果该文件已24小时没有更新,当update触发时会强制更新。意外着最坏情况下Service Worker会每天更新一次。

ServiceWorker如何更新?

  • 浏览器每天至少更新一次ServiceWorker
  • 注册新的Service Worker,带上版本号,如: /sw.js?t=201709091920
  • 手动更新resistration.update()
  • 逐字节对比新的sw文件和旧的sw,有区别才更新
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// index.html
navigator.serviceWoker.addEventListener('message', function(e){
if(e.data === 'sw.update'){
//提醒用户刷新
}
});

// sw.js
self.clients.matchAll().then(function(clients){
if(clients && clients.length){
clients.forEach(function(client){
client.postMessage('sw.update');
})
}
})

PWA SEO可以服务器端渲染Server Side Rendering(SSR).
SSR中如何正确使用Service Worker

参考

  1. LAVAS百度
  2. 个人分享PWA简介
  3. QCon2018-《Lavas:PWA的探索与最佳实践》-彭星

区块链的几个基本概念

区块链的几个基本概念

区块链(blockchain)本质上是一种特殊的分布式数据库。

首先,区块链的主要作用是储存信息。任何需要保存的信息,都可以写入区块链,也可以从里面读取,所以它是数据库。

其次,任何人都可以架设服务器,加入区块链网络,成为一个节点。区块链的世界里面,没有中心节点,每个节点都是平等的,都保存着整个数据库。你可以向任何一个节点,写入/读取数据,因为所有节点最后都会同步,保证区块链一致。

区块链是去中心化的,没有管理员,是彻底无中心的。

区块

区块链是由一个个区块(block)组成。区块很像数据库的记录,每次写入数据,就是创建一个区块。

每个区块包含两个部分

  • 区块头(Head): 记录当前区块的特征值(当前时间、上一个区块的Hash、区块体的Hash)
  • 区块体(body): 实际数据

两个重要推论:

推论1: 每个区块的哈希都是不一样的,可以通过哈希标识区块。

推论2:如果区块的内容变了,它的哈希一定会改变。

采矿

添加新区块需要一定的计算,只有通过极其大量的计算,才能得到当前区块的有效哈希,从而把新区块添加到区块链。
这个过程就叫做采矿(mining),因为计算有效哈希的难度,好比在全世界的沙子里面,找到一粒符合条件的沙子。
计算哈希的机器就叫做矿机,操作矿机的人就叫做矿工。

难度系数

区块头包含一个难度(difficulty),这个值决定了计算哈希的难度。
区块链协议规定,使用一个常量除以难度系数,可以得到目标值(target)。难度系数越大,目标值就越小。

区块链的适用场景

  1. 不存在所有成员都信任的管理当局
  2. 希尔的数据不要求实时使用
  3. 挖矿的收益能弥补本身成本

非对称加密

加密和解密需要两把钥匙:一把公钥和一把私钥。公钥是公开的,任何人都可以获取。私钥是保密的,只有拥有者才能使用。
他人使用你的公钥加密信息,然后发送给你,你用私钥解密,取出信息。反过来,你也可以用私钥加密信息,别人用你的公钥
解开,从而证明这个信息是你发的,且未被篡改,这就叫数字签名。

比特币特点

  1. 不会轻易被偷走
  2. 无法伪造
  3. 无法大批生成

区块链的作用

区块链就是一个数据库,记载了所有的交易,用作中央记账系统,分布在无数个节点之上。

数字货币的本质就是一条可信的数据库记录。

比特币协议规定,分支点之后最先达到6个区块的那个分支被认定为正式的区块链,其他分支都被放弃。由于区块
的生成速度由计算能力决定,所以到底哪一笔交易最后会被写入区块链,完全由它所在的分支能吸引多少计算能力决定。
隐藏的逻辑是,如果大多数人(计算能力)选择相信某一笔交易,那么它就应该是真的。

函数式编程

函数式编程

Javascript的箭头函数

ECMAScript2015引入箭头表达式。箭头函数其实是匿名函数,基本语法如下:

(param1, param2, …, paramN) => { statements }

(param1, param2, …, paramN) => expression

// 等于 : => { return expression; }

// 只有一个参数时,括号才可以不加:

(singleParam) => { statements }

singleParam => { statements }

//如果没有参数,就一定要加括号:

() => { statements }

示例:

1
2
3
4
5
6
7
8
9
10
var simple = a => a > 15 ? 15 : a;
simple(16); // 15
simple(10); // 10

let max = (a, b) => a > b ? a : b;

var arr = [5, 6, 13, 0, 1, 18, 23];
var sum = arr.reduce((a, b) => a + b); //66
var even = arr.filter(v => v % 2 == 0); // [6, 0, 18]
var double = arr.map(v => v * 2); // [10, 12, 26, 0, 36, 46]

有些时候,某些函数在声明的时候就是调用的时候,尤其是函数式编程中,一个函数还对外返回函数的时候。

1
2
3
4
5
6
7
8
9
10
11
function MakePowerFn(power) {
return function PowerFn(base) {
return Math.pow(base, power);
}
}

power3 = MakePowerFn(3); // 制造一个X的3次方的函数
power2 = MakePowerFn(2); // 制造一个X的2次方的函数

console.log(power3(10)); // 10的3次方 1000
console.log(power2(10)); // 10的2次方 100

用箭头函数

1
2
3
4
5
6
7
8
9
10
11
12
13
MakePowerFn = power => {
return base => {
return Math.pow(base, power);
}
}

// 简化
MakePowerFn = power => base => Math.pow(base, power)

// 加上括号
MakePowerFn = (power) => (
(base) => (Math.pow(base, power))
)

匿名函数的递归

函数式编程立志于用函数表达式来消除有状态的函数,以及for/while循环,所以在函数式编程里不应该用for/while循环,而要改用递归(递归的性能很差,所以,一般用尾递归来做优化,也就是把函数的计算状态当参数一层一层往下传递,这样语言的编译器后解释器就需要用函数栈来帮你保存函数的内部变量的状态了)。

递归的代码就是函数自己调用自己,比如求阶乘

1
2
3
function fact(n){
return n == 0 ? 1 : n * fact(n-1);
}

对于匿名函数,可以把匿名函数当成一个参数传给另外一个函数,因为函数的参数有名字,所以就可以调用自己了。

1
2
3
4
5
6
function combinator(func) {
func(func);
}

// 箭头函数式的匿名函数
func) => (func(func))

阶乘代码重构

1
2
3
4
5
6
7
8
9
10
11
12
13
funtion fact(func, n){
return n == 0 ? 1: n * func(func, n-1);
}

// 匿名函数版
var fact = (func, n) => (n == 0 ? 1 : func(func, n-1));
fact(fact, 5)

// 函数体声明时调用自己
func, x) => func(func, x)(
(func, n) => ( n ==0 ? 1 : n * func(func, n-1)), // 第一个调用参数
5 // 第二个调用参数
);

动态高级函数的递归

递归版高阶函数

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
HighOrderFact = function(func){
return function(n){
return n == 0 ? 1 : func(func)(n-1);
};
}

// 需要一个函数做参数,然后返回这个函数的递归版本
fact = HighOrderFact(HighOrderFact);
fact(5);

HighOrderFact(HighOrderFact)(5);

fact = function(hifunc) {
return hifunc(hifunc);
}(
// 调用参数是一个函数
function(func){
return function(n){
return n == 0 ? 1 : n * func(func)(n-1);
};
}
);
// 调用
fact(5);

// 箭头函数重构
fact = (highfunc => highfunc(highfunc)) (
func => n => n == 0 ? 1 : n * func(func)(n-1)
);

重构之前的程序

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
 // 查找数组正常版本
function find(x, y) {
for(let i = 0; i< x.length; i++){
if(x[i] == y) return i;
}
return null;
}

// 干掉for,搞成递归版本
funcion find(x, y, i=0){
if( i >= x.length) return null;
if( x[i] == y) return i;
return find(x, y, i+1);
}

// 继续重构 带实参的匿名函数版本
((func, x, y, i) => func(func, x,y i)) ( // 函数体
(func, x, y, i=0) => (
i>= x.length ? null :
x[i] == y ? i : func(func, x, y, i+1)
), // 第一个调用参数
arr, // 第二个调用参数
2 // 第三个调用参数
)
// 引入高级函数,去除实参

const find = (highfunc => highfunc(highfunc))(
func => (x, y, i =0 ) => (
i >= x.length ? null :
x[i] == y ? i : func(func)(x, y, i+1)
)
)
;

可以参考以下两篇文章

Thinking in Ramda 1

Thinking in Ramda: getting started

Getting Started

I’ll be using the Ramda JavaScript library fro this series,though many of the ideas apply to other JavaScript libraries such as Underscore and Lodash as well as to other languages.

I’m going to stick to the lighter,less-academic end of functional programing.

Ramda

I find Ramda to be a nicely designed library that provides a lot of tools for doing functional programming in JavaScript in a clean, elegant way.

,
Fork me on GitHub