seastar usage

启动

1
2
3
4
5
seastar::app_template app;
char *av[5] = {"--overprovisioned", "-c", "4", "--blocked-reactor-notify-ms", "25"};
app.run(sizeof av / sizeof *av, av, [] {
return seastar::make_ready_future<>();
});

std::move

1
2
3
4
5
6
seastar::future<int> slow_do_something(std::unique_ptr<T> obj) {
using namespace std::chrono_literals;
return seastar::sleep(10ms).then([obj = std::move(obj)] () mutable {
return do_something(std::move(obj));
});
}
  • mutable :

    • The [obj = ...] capture syntax we used here is new to C++14. This is the main reason why Seastar requires C++14, and does not support older C++11 compilers.

      The extra () mutable syntax was needed here because by default when C++ captures a value (in this case, the value of std::move(obj)) into a lambda, it makes this value read-only, so our lambda cannot, in this example, move it again. Adding mutable removes this artificial restriction.

异常和fail

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "core/future.hh"
#include <iostream>
#include <exception>

class my_exception : public std::exception {
virtual const char* what() const noexcept override { return "my exception"; }
};

seastar::future<> fail() {
return seastar::make_exception_future<>(my_exception());
}

seastar::future<> f() {
return fail().finally([] {
std::cout << "cleaning up\n";
});
}

抛出异常会导致整个 f() 执行失败,不建议抛出异常,应该返回fail future:

1
2
3
4
5
6
7
8
9
10
11
void inner() {
throw my_exception();
}
seastar::future<> fail() {
try {
inner();
} catch(...) {
return seastar::make_exception_future(std::current_exception());
}
return seastar::make_ready_future<>();
}

或者使用futurize_apply

1
2
3
4
5
6
7
8
seastar::future<> fail() {
throw my_exception();
}
seastar::future<> f() {
return seastar::futurize_apply(fail).finally([] {
std::cout << "cleaning up\n";
});
}

文档

seastar tutorail

seastar 入门, 基于这里 这里.

描述

seastar 使用两个概念 futures 和 continuations,提供复杂的异步编程库,使用非共享编程模型。

异步编程

大多数程序处理并行的请求,通常是每个连接使用隔离的操作系统级别的进程。这种技术也一直在进化,从一开始的每个请求都会创建一个进程,到创建一个线程池,最终进化到使用线程。不过,进化的共同点是在一个时刻,每个进程/线程处理一个连接。

异步/时间驱动的服务,通常一个核心对应一个线程。

seastar 是一个事件驱动的框架,允许你使用相对直接的方式编写非阻塞、异步代码。他的 API 基于 future,利用下面的概念达到极致性能。

  • 协同微任务调度器
  • 非共享 SMP 架构
  • 基于 future 的 APIs
  • 非共享 TCP 堆栈
  • 基于 DMA 的存储 APIs

开始

每个 seastar 应用必须定义和运行一个 app_template 对象,该对象启动主事件循环(seastar 引擎)在一个或多个 cpu 核心上,运行指定的函数。

方法返回一个 future,指示何时退出程序,如果是返回 make_ready_future<> 则会立刻退出程序。无论何时,C 的 exit() 方法都不应使用,这会阻止 seastar 或程序进行适当的清理工作。

线程和内存

线程

seastar 应用在每个核心上运行一个线程,每个线程运行自己的 event loop,可以使用 seastar::smp::count 打印运行的线程数。

getopts

参数

1
2
3
4
5
6
# opts
:前缀 忽略错误
:后缀 参数后必须有值

# example
:abc:de: 忽略参数错误,-c、-e后必须有值

示例

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
#!/bin/bash

# define usage info
usage() {
cat <<EOF
Usage: $0 [-a] [-b name] msg
EOF
}

while getopts ":ab:Bc:" opt; do
case $opt in
a) echo "found -a" ; a="hello" ;;
b) echo "found -b and value is: $OPTARG" ;;
c) echo "found -c and value is: $OPTARG" ;;
*) usage ;;
esac
done

shift $(($OPTIND - 1))

echo "MSG: $1"

if [ -n "${a}" ] ; then # or [ ! -z "${a}" ]
echo "-a exists : ${a}"
else
echo "-a not exists"
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/bash

usage() {
cat <<EOF
Usage: $0 [-h host] [-p port]
$0 [-a host:port]
EOF
}

while getopts "h:p:a:" opt; do
case $opt in
h) rhost="$OPTARG" ;;
p) rport="$OPTARG" ;;
a) raddress="$OPTARG" ;;
*) usage ; exit 1 ;;
esac
done

if [[ ( -z "$raddress" ) && ( -z "$rhost" || -z "$rport" ) ]]; then
usage
exit 1
fi

pyenv

安装

1
2
3
4
5
# brew
brew install pyenv

# 脚本
curl https://pyenv.run | bash

列出所有可安装版本

1
$ pyenv install -l

安装某个版本

1
2
3
$ pyenv install <version>  # e.g 3.8.9
# 使用国内源安装
$ v=3.8.9;wget https://npm.taobao.org/mirrors/python/$v/Python-$v.tar.xz -P ~/.pyenv/cache/;pyenv install $v

设置全局版本

1
pyenv global 3.8.9

查看 安装的版本

1
pyenv versions

回滚系统默认

1
pyenv global system

consul usage

启动

1
$ consul agent -dev -bind=0.0.0.0 -client=0.0.0.0 -advertise=127.0.0.1

高可用

接口

查询所有服务

1
curl -XGET http://localhost:8500/v1/catalog/services

查询服务所有监控实例

1
curl -XGET http://localhost:8500/v1/health/service/:servicename

列出所有节点

1
curl -XGET http://localhost:8500/v1/catalog/nodes

强制去除节点

1
curl -XPUT http://localhost:8500/v1/agent/force-leave/:node_name	

KV

导入导出

exportimport

1
2
3
4
consul kv export > data.json         # 导出所有
consul kv export online/ > data.json # 导出 online 前缀
# 指定 consul 地址
consul kv export -http-addr=http://10.20.10.3:8500 > data.json
1
2
consul kv import @data.json         # 文件导入, 需要在文件名前加 @
cat data.json | consul kv import - # 通过管道导入

查询

1
2
# 列出所有 key
curl --location --request GET 'http://127.0.0.1:8500/v1/kv/?keys'

注意

Blocking Query / Long Pull

指定参数 index={latest-index},latest-index 是相对于 kv prefix 而言,在 response 的 header 中 X-Consul-Index 返回。

使用 consul kv 做开关

1
2
3
4
5
6
function is_enable() {
consul_key_prefix="path/to/config"
config=$(curl "http://127.0.0.1:8500/v1/kv/${consul_key_prefix}?raw=true" | jq -r ".enable")
[ X"$config" = X"true" ] && return 0
return 1
}

openstack problems

无法删除卷

使用界面删除卷时,一致失败,查看 cinder-volumn 日志,错误是卷在使用。

1
2
3
4
5
6
...
2021-12-05 14:05:42.521 14117 ERROR oslo_messaging.rpc.server Command: sudo cinder-rootwrap /etc/cinder/rootwrap.conf lvremove --config activation { retry_deactivation = 1} devices { ignore_suspended_devices = 1} -f cinder-volumes/volume-0b85987d-350c-4faa-91bd-357a213c7eae
2021-12-05 14:05:42.521 14117 ERROR oslo_messaging.rpc.server Exit code: 5
2021-12-05 14:05:42.521 14117 ERROR oslo_messaging.rpc.server Stdout: ''
2021-12-05 14:05:42.521 14117 ERROR oslo_messaging.rpc.server Stderr: ' Logical volume cinder-volumes/volume-0b85987d-350c-4faa-91bd-357a213c7eae in use.\n'
...

查找占用卷的进程

1
2
3
4
5
6
7
root@srv:/home/wii# blkid
...
/dev/mapper/cinder--volumes-volume--0b85987d--350c--4faa--91bd--357a213c7eae: UUID="2021-08-24-09-09-05-00" LABEL="Ubuntu-Server 20.04.3 LTS amd64" TYPE="iso9660" PTUUID="7fcaeaa1" PTTYPE="dos"
...
root@srv:/home/wii# lsof /dev/mapper/cinder--volumes-volume--0b85987d--350c--4faa--91bd--357a213c7eae
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
tgtd 1887 root 17u BLK 253,10 0t0 442 /dev/mapper/../dm-10

可以看到,是 tgt 服务一直在占用。

1
root@srv: service stop tgt

再删除卷,然后启动 tgt 服务。

1
root@srv: service start tgt

系统盘使用高性能盘

如果存储服务区分 ssh 和 hdd,又希望系统盘能用 ssd,可以修改卷的类型扩展参数。

image-20211205224142960

LVM_NVME 要在 cinder 的配置进行配置,对应的是 ssd 磁盘。

linux problems

[toc]

挂载磁盘导致无法进入系统

拔出新加的磁盘,修改 /etc/fstab ,使用 UUID 区分盘符,示例如下。

使用 sudo 命令反应慢

  • /etc/hosts 中没有 hostname 的记录,在 /etc/hosts 中添加 127.0.0.1 <hostname>

ssh key 免密登录失败

生成公钥和密钥

1
2
3
ssh-keygen -t ras -C your@email.com

# ~/.ssh/id_rsa & ~/.ssh/id_rsa.pub

配置

将生成的 ~/.ssh/id_rsa.pub 内容附加到需要登录机器的 ~/.ssh/authorized_keys 文件后面。

登录

1
ssh remote-host

登录失败

1
2
3
4
5
6
# Permission denied (publickey).
## 查看远程机器文件权限
chmod 700 /home/$OS_USER/.ssh
chmod 600 /home/$OS_USER/.ssh/authorized_keys
chmod 600 /home/$OS_USER/.ssh/config
chmod 600 /home/$OS_USER/.ssh/id_rsa

设置语言和地区

~/.bash_profile~/.zshrc 中添加如下内容,具体文件视使用的 shell 而定。

1
2
3
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8

df 磁盘用满但 du 显示还有空间

1
2
3
#lsof | grep delete
COMMAND PID TID USER FD TYPE DEVICE SIZE/OFF NODE NAME
intercept 14265 root 3u REG 259,1 227432595897 54608901 /data/intercept/access.log (deleted)

/data/intercept/access.log 日志占用 210G 容量,但是已经被删除。

使用不同网关导致配合端口转发失效

Shell 登录慢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 调试
$ bash --login --verbose
...
export PS1="[\u@${Green}\H${ENDCOLOR}:${Brown}$(ips)${ENDCOLOR} \W]\\$" # 在这个位置卡很久

# 原因
## ips 命令卡住的可能性比较大, 手动执行 ips
## 果然会卡住

$ which ips
ips ()
{
curl -s http://169.254.169.254/latest/meta-data/public-ipv4
}

# 用的阿里云机器, 修改为下面的命令
$ sudo vim /etc/profile
# 修改 function ips() ... 为如下
function ips() {
GET http://100.100.100.200/latest/meta-data/eipv4
}

NVME 磁盘控制器挂掉

错误

1
nvme nvme0: controller is down; will reset: CSTS=0xffffffff, PCI_STATUS=0x10

解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 临时
$ sudo apt install nvme-cli
$ sudo nvme set-feature -f 0x0c -v=0 /dev/nvme0
# 验证
$ sudo nvme get-feature -f 0x0c -H /dev/nvme0
get-feature:0xc (Autonomous Power State Transition), Current value:00000000
Autonomous Power State Transition Enable (APSTE): Disabled

# 持久化
$ vim /etc/default/grub
# 修改 GRUB_CMDLINE_LINUX_DEFAULT
GRUB_CMDLINE_LINUX_DEFAULT="quiet nvme_core.default_ps_max_latency_us=0" # 多个值用空格分隔
# 更新 grub, 重启生效
$ sudo update-grub

参考

移动 /usr/lib64 导致命令失效

操作 mv /usr/lib64 /usr/lib64back 后,几乎所有命令都失效。

1
2
#mv lib64back lib64
-bash: /bin/mv: /lib64/ld-linux-x86-64.so.2: bad ELF interpreter: 没有那个文件或目录

使用如下命令恢复。

1
/usr/lib64back/ld-linux-x86-64.so.2 --library-path /usr/lib64back /usr/bin/cp /usr/lib64back /usr/lib64 -fr

rsa 加密解密

数据

公钥

1
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCOhQ81fJEKI/b0+iKtK0/WN7wcMJKuGL5QhzuRDyFLCADbKPkB7uGKfkQ1QDcntjZ8k4+mx2oGBKF5E1vSB+bD7SP7fkSgDmdCzGLSJX04Q9H95xuEfFsTXwW8dGh4uQDFt/dTIY2WAx8RnGhiJFKq6y9mj+tcofnzLbQNfy2PoQIDAQAB

私密

1
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAI6FDzV8kQoj9vT6Iq0rT9Y3vBwwkq4YvlCHO5EPIUsIANso+QHu4Yp+RDVANye2NnyTj6bHagYEoXkTW9IH5sPtI/t+RKAOZ0LMYtIlfThD0f3nG4R8WxNfBbx0aHi5AMW391MhjZYDHxGcaGIkUqrrL2aP61yh+fMttA1/LY+hAgMBAAECgYBuMZNA17+NB6G6aGzHV+WyzAU2Bphi497ChM0Zq4kial2/Fj7xr7HTUy2Jvszmd4xJZg579VOUs5/l7YHhMxrI2sUadaenkdJ/LmdHICRT4BAFdDnM78UsY1/YgtoA06r63ZkaE6PJ2UPMCgcYMqV5OkAFwt8oTDSGlBb4EZuUAQJBAMbR+tolF96tHPb96BKbowIytRQZpC3KMutnzY74davY4BvAnvzoUTs0d89Xi+1+YxmI0UahejGdyMEWOryNJYkCQQC3gf3B8Kmaic3hz1ghslsdY5n1sE8piJH/9owvToc54MVQc9sfFC9f+e6v8yjblHFeaoYTL6NUz9MUmeHAgatZAkB+VGnaNnuGR+UBo6/UMwROnz2jue8yESptnZVlZMYQHUu5FplvBYan4dzG6E/G5em+Dcs739qusB0hYyiLKfxRAkAiGKweqenJhgtUBqOYdzxIxKXpqZ272N1P0u6PJ6cmkOX4od438xcuXREFbkfMLNO3uFE7JWHSs17D+CejDjTZAkAWlJVrwvcgdPDun/xF2FK8YoonJFPJxFD/jrVPuoZ+HxYa9sytwuaAU8jTv8WDF4er1ZO4N3TENE+SedxuH0zW

数据串

1
hello world

加密

java

1
UgnRdVY7thSMN8dM5gg+wcMu7g8PmQclLFnoswkT9J8aB8RfU+LQhfKupRVifipg03LvVGedqQmXyF5Gz8AoDIW22q5kpLxpdS3sZdf2kQY6snDSHubtPszG+/jB05fYaCGCPvoJw1oD+HHWl6dF5GvTYvLcZykRAJtvr5vCjTg=

node

1
iQMK9U8wYsLdhssRVRVY0CA40SdM3PEP7KhuP74ICPA7AH0ckUw5Rq9C8RKAIBh0fO8hIYC+kPN9xUCKNUlFGy09xtBEOgRybg8JZZC8mEjeA0hHMbQePI4fxQa74IWYY646ihj7KCtaE55yE6G8yV7hRPFyjdPJieXEKhSJsmM=

js

1
hOv454LZNZhPoS2nbtnH7S1phhkdVVX0NtF7xx37H4tNKelTguQ0i/obZXBGVRC7FoP0CAP7lrqo/VlT276D9kLXHUGSl/9FW1Q4fNcj4iMZedjcQJaZNOSZ4Vlti/9cXUeuUXaGG6vS3V49EfMy/FELFy+zpbijcnrIAar6uh4=

解密

加密\解密 java node js
java 可以解密 可以解密 可以解密
node 可以解密 可以解密 可以解密
js 可以解密 可以解密 可以解密

Java

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
package pub.wii.cook.springboot.utils;

import lombok.SneakyThrows;

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class RSAUtil {

private final static String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCOhQ81fJEKI/b0+iKtK0/WN7wcMJKuGL5QhzuRDyFLCADbKPkB7uGKfkQ1QDcntjZ8k4+mx2oGBKF5E1vSB+bD7SP7fkSgDmdCzGLSJX04Q9H95xuEfFsTXwW8dGh4uQDFt/dTIY2WAx8RnGhiJFKq6y9mj+tcofnzLbQNfy2PoQIDAQAB";
private final static String privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAI6FDzV8kQoj9vT6Iq0rT9Y3vBwwkq4YvlCHO5EPIUsIANso+QHu4Yp+RDVANye2NnyTj6bHagYEoXkTW9IH5sPtI/t+RKAOZ0LMYtIlfThD0f3nG4R8WxNfBbx0aHi5AMW391MhjZYDHxGcaGIkUqrrL2aP61yh+fMttA1/LY+hAgMBAAECgYBuMZNA17+NB6G6aGzHV+WyzAU2Bphi497ChM0Zq4kial2/Fj7xr7HTUy2Jvszmd4xJZg579VOUs5/l7YHhMxrI2sUadaenkdJ/LmdHICRT4BAFdDnM78UsY1/YgtoA06r63ZkaE6PJ2UPMCgcYMqV5OkAFwt8oTDSGlBb4EZuUAQJBAMbR+tolF96tHPb96BKbowIytRQZpC3KMutnzY74davY4BvAnvzoUTs0d89Xi+1+YxmI0UahejGdyMEWOryNJYkCQQC3gf3B8Kmaic3hz1ghslsdY5n1sE8piJH/9owvToc54MVQc9sfFC9f+e6v8yjblHFeaoYTL6NUz9MUmeHAgatZAkB+VGnaNnuGR+UBo6/UMwROnz2jue8yESptnZVlZMYQHUu5FplvBYan4dzG6E/G5em+Dcs739qusB0hYyiLKfxRAkAiGKweqenJhgtUBqOYdzxIxKXpqZ272N1P0u6PJ6cmkOX4od438xcuXREFbkfMLNO3uFE7JWHSs17D+CejDjTZAkAWlJVrwvcgdPDun/xF2FK8YoonJFPJxFD/jrVPuoZ+HxYa9sytwuaAU8jTv8WDF4er1ZO4N3TENE+SedxuH0zW";

@SneakyThrows
public static PublicKey getPublicKey(String base64PublicKey) {
PublicKey publicKey;
X509EncodedKeySpec keySpec =
new X509EncodedKeySpec(Base64.getDecoder().decode(base64PublicKey.getBytes()));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
}

@SneakyThrows
public static PrivateKey getPrivateKey(String base64PrivateKey) {
PKCS8EncodedKeySpec keySpec =
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(base64PrivateKey.getBytes()));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}

@SneakyThrows
public static String encrypt(String data, String publicKey) {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey));
return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes()));
}

@SneakyThrows
public static String decrypt(byte[] data, PrivateKey privateKey) {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(data));
}

public static String decrypt(String data, String base64PrivateKey) {
return decrypt(Base64.getDecoder().decode(data.getBytes()), getPrivateKey(base64PrivateKey));
}

public static void main(String[] args) {
// 加密
String encryptedString = encrypt("hello world", publicKey);
System.out.println(encryptedString);
// 解密
String decryptedString = RSAUtil.decrypt(encryptedString, privateKey);
System.out.println(decryptedString);
}
}

Node

1
npm i node-rsa
1
2
3
4
5
6
7
8
9
10
11
12
const NodeRSA = require('node-rsa')
const privateKey = 'MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAI6FDzV8kQoj9vT6Iq0rT9Y3vBwwkq4YvlCHO5EPIUsIANso+QHu4Yp+RDVANye2NnyTj6bHagYEoXkTW9IH5sPtI/t+RKAOZ0LMYtIlfThD0f3nG4R8WxNfBbx0aHi5AMW391MhjZYDHxGcaGIkUqrrL2aP61yh+fMttA1/LY+hAgMBAAECgYBuMZNA17+NB6G6aGzHV+WyzAU2Bphi497ChM0Zq4kial2/Fj7xr7HTUy2Jvszmd4xJZg579VOUs5/l7YHhMxrI2sUadaenkdJ/LmdHICRT4BAFdDnM78UsY1/YgtoA06r63ZkaE6PJ2UPMCgcYMqV5OkAFwt8oTDSGlBb4EZuUAQJBAMbR+tolF96tHPb96BKbowIytRQZpC3KMutnzY74davY4BvAnvzoUTs0d89Xi+1+YxmI0UahejGdyMEWOryNJYkCQQC3gf3B8Kmaic3hz1ghslsdY5n1sE8piJH/9owvToc54MVQc9sfFC9f+e6v8yjblHFeaoYTL6NUz9MUmeHAgatZAkB+VGnaNnuGR+UBo6/UMwROnz2jue8yESptnZVlZMYQHUu5FplvBYan4dzG6E/G5em+Dcs739qusB0hYyiLKfxRAkAiGKweqenJhgtUBqOYdzxIxKXpqZ272N1P0u6PJ6cmkOX4od438xcuXREFbkfMLNO3uFE7JWHSs17D+CejDjTZAkAWlJVrwvcgdPDun/xF2FK8YoonJFPJxFD/jrVPuoZ+HxYa9sytwuaAU8jTv8WDF4er1ZO4N3TENE+SedxuH0zW'

var key = NodeRSA(privateKey, 'pkcs8-private-pem',
{'environment': 'node', 'encryptionScheme': 'pkcs1', 'signingScheme': 'sha256'});

// 加密
var encryptedData = key.encrypt('hello world', 'base64')
console.log(encryptedData)
// 解密
var decryptedData = key.decrypt(encryptedData, 'utf-8')
console.log(decryptedData)

JS

使用 openstack 实现的 jsencrypt.js

1
2
3
4
5
6
7
var encrypt=new JSEncrypt(); 
encrypt.setPrivateKey('MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAI6FDzV8kQoj9vT6Iq0rT9Y3vBwwkq4YvlCHO5EPIUsIANso+QHu4Yp+RDVANye2NnyTj6bHagYEoXkTW9IH5sPtI/t+RKAOZ0LMYtIlfThD0f3nG4R8WxNfBbx0aHi5AMW391MhjZYDHxGcaGIkUqrrL2aP61yh+fMttA1/LY+hAgMBAAECgYBuMZNA17+NB6G6aGzHV+WyzAU2Bphi497ChM0Zq4kial2/Fj7xr7HTUy2Jvszmd4xJZg579VOUs5/l7YHhMxrI2sUadaenkdJ/LmdHICRT4BAFdDnM78UsY1/YgtoA06r63ZkaE6PJ2UPMCgcYMqV5OkAFwt8oTDSGlBb4EZuUAQJBAMbR+tolF96tHPb96BKbowIytRQZpC3KMutnzY74davY4BvAnvzoUTs0d89Xi+1+YxmI0UahejGdyMEWOryNJYkCQQC3gf3B8Kmaic3hz1ghslsdY5n1sE8piJH/9owvToc54MVQc9sfFC9f+e6v8yjblHFeaoYTL6NUz9MUmeHAgatZAkB+VGnaNnuGR+UBo6/UMwROnz2jue8yESptnZVlZMYQHUu5FplvBYan4dzG6E/G5em+Dcs739qusB0hYyiLKfxRAkAiGKweqenJhgtUBqOYdzxIxKXpqZ272N1P0u6PJ6cmkOX4od438xcuXREFbkfMLNO3uFE7JWHSs17D+CejDjTZAkAWlJVrwvcgdPDun/xF2FK8YoonJFPJxFD/jrVPuoZ+HxYa9sytwuaAU8jTv8WDF4er1ZO4N3TENE+SedxuH0zW')

// 加密
var encryptedData = encrypt.encrypt('hello world')
// 解密
var decryptedData = encrypt.decrypt(encryptedData, 'utf-8')

参考