不会飞的章鱼

熟能生巧,勤能补拙;念念不忘,必有回响。

Open Horizon 快速入门

先决条件

两台服务器,配置如下:

  • 4GB以上的内存
  • 20GB以上的存储
  • Ubuntu Server 18.04 LTS

安装

确保是root权限

1
2
3
4
5
6
7
# 执行
curl -sSL https://raw.githubusercontent.com/open-horizon/devops/master/mgmt-hub/deploy-mgmt-hub.sh | bash

# 当出现以下信息,表示安装成功
----------- Summary of what was done:
1. Started Horizon management hub services: Agbot, CSS, Exchange, FDO, Mongo DB, Postgres DB, Postgres DB FDO, Vault
2. Created exchange resources: system organization (IBM) admin user, user organization (myorg) and admin user, and agbot

查看 CLI 和代理版本号

1
2
3
# hzn version
Horizon CLI version: 2.30.0-1491
Horizon Agent version: 2.30.0-1491

接下来做什么

自动化测试

1
./test-mgmt-hub.sh

查看边缘节点的状态

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
# hzn node list
{
"id": "node1",
"organization": "myorg",
"pattern": "",
"name": "node1",
"nodeType": "device",
"clusterNamespace": null,
"token_last_valid_time": "2024-03-11 02:17:27 +0000 UTC",
"token_valid": true,
"ha_group": "",
"configstate": {
"state": "configured",
"last_update_time": "2024-03-11 02:17:27 +0000 UTC"
},
"configuration": {
"exchange_api": "http://192.168.1.208:3090/v1/",
"exchange_version": "2.122.0",
"required_minimum_exchange_version": "2.90.1",
"preferred_exchange_version": "2.110.1",
"mms_api": "http://192.168.1.208:9443",
"architecture": "amd64",
"horizon_version": "2.31.0-1498"
}
}

查看为运行 helloworld 边缘服务示例而达成的协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# hzn agreement list
[
{
"name": "Policy for myorg/node1 merged with myorg/policy-ibm.helloworld_1.0.0",
"current_agreement_id": "be397e12deb1651d278739fefa9ff2780436878c3a9fc4f2c12b5ee4857ef5ff",
"consumer_id": "IBM/agbot",
"agreement_creation_time": "2024-03-11 02:17:39 +0000 UTC",
"agreement_accepted_time": "2024-03-11 02:17:43 +0000 UTC",
"agreement_finalized_time": "2024-03-11 02:17:43 +0000 UTC",
"agreement_execution_start_time": "2024-03-11 02:17:45 +0000 UTC",
"agreement_data_received_time": "",
"agreement_protocol": "Basic",
"workload_to_run": {
"url": "ibm.helloworld",
"org": "IBM",
"version": "1.0.0",
"arch": "amd64"
}
}
]

查看Horizo启动的边缘服务容器

1
2
3
4
5
6
7
8
9
10
11
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b6f00808639f openhorizon/ibm.helloworld_amd64 "/bin/sh -c /service…" 25 minutes ago Up 25 minutes be397e12deb1651d278739fefa9ff2780436878c3a9fc4f2c12b5ee4857ef5ff-ibm.helloworld
676698c249c6 openhorizon/fdo-owner-services:testing "/bin/sh -c $WORKDIR…" 29 minutes ago Up 29 minutes (healthy) 0.0.0.0:8042->8042/tcp, :::8042->8042/tcp, 0.0.0.0:9008->9008/tcp, :::9008->9008/tcp fdo-owner-services
3ca1dc37e978 openhorizon/amd64_agbot:latest "/bin/sh -c /usr/hor…" 29 minutes ago Up 29 minutes (healthy) 127.0.0.1:3110->8080/tcp, 192.168.1.208:3111->8083/tcp agbot
90265cba1f70 openhorizon/amd64_cloud-sync-service:latest "/usr/edge-sync-serv…" 29 minutes ago Up 29 minutes (healthy) 192.168.1.208:9443->8080/tcp css-api
a49f896ac063 openhorizon/amd64_vault:latest "entrypoint.sh server" 29 minutes ago Up 29 minutes (healthy) 192.168.1.208:8200->8200/tcp vault
3c99de720e83 postgres:13 "docker-entrypoint.s…" 29 minutes ago Up 29 minutes (healthy) 0.0.0.0:5433->5432/tcp, :::5433->5432/tcp postgres-fdo-owner-service
ec4293468239 openhorizon/amd64_exchange-api:latest "/bin/sh -c '/usr/bi…" 29 minutes ago Up 29 minutes (healthy) 8083/tcp, 192.168.1.208:3090->8080/tcp exchange-api
a45538b25f34 mongo:4.0.6 "docker-entrypoint.s…" 32 minutes ago Up 32 minutes (healthy) 27017/tcp mongo
5765c53f43be postgres:13 "docker-entrypoint.s…" 32 minutes ago Up 32 minutes (healthy) 0.0.0.0:5432->5432/tcp, :::5432->5432/tcp postgres

查看helloworld边缘服务的日志

1
2
3
4
5
6
7
8
9
10
# hzn service log -f ibm.helloworld
Found service ibm.helloworld with service id be397e12deb1651d278739fefa9ff2780436878c3a9fc4f2c12b5ee4857ef5ff.
Displaying log messages of container ibm.helloworld for service ibm.helloworld with service id be397e12deb1651d278739fefa9ff2780436878c3a9fc4f2c12b5ee4857ef5ff.
Use ctrl-C to terminate this command.
Mar 11 02:17:45 oh-3 workload-be397e12deb1651d278739fefa9ff2780436878c3a9fc4f2c12b5ee4857ef5ff_ibm.helloworld[3807]: node1 says: Hello from the EDGE!!!
Mar 11 02:17:48 oh-3 workload-be397e12deb1651d278739fefa9ff2780436878c3a9fc4f2c12b5ee4857ef5ff_ibm.helloworld[3807]: node1 says: Hello from the EDGE!!!
Mar 11 02:17:51 oh-3 workload-be397e12deb1651d278739fefa9ff2780436878c3a9fc4f2c12b5ee4857ef5ff_ibm.helloworld[3807]: node1 says: Hello from the EDGE!!!
Mar 11 02:17:54 oh-3 workload-be397e12deb1651d278739fefa9ff2780436878c3a9fc4f2c12b5ee4857ef5ff_ibm.helloworld[3807]: node1 says: Hello from the EDGE!!!
Mar 11 02:17:57 oh-3 workload-be397e12deb1651d278739fefa9ff2780436878c3a9fc4f2c12b5ee4857ef5ff_ibm.helloworld[3807]: node1 says: Hello from the EDGE!!!
Mar 11 02:18:00 oh-3 workload-be397e12deb1651d278739fefa9ff2780436878c3a9fc4f2c12b5ee4857ef5ff_ibm.helloworld[3807]: node1 says: Hello from the EDGE!!!

查看Horizon配置

1
2
3
4
5
6
7
# cat /etc/default/horizon
HZN_EXCHANGE_URL=http://192.168.1.208:3090/v1
HZN_FSS_CSSURL=http://192.168.1.208:9443/
HZN_AGBOT_URL=http://192.168.1.208:3111
HZN_FDO_SVC_URL=http://192.168.1.208:9008/api
HZN_DEVICE_ID=node1
ANAX_LOG_LEVEL=3

查看Horizon代理守护程序状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# systemctl status horizon
● horizon.service - Service for Horizon control system
Loaded: loaded (/lib/systemd/system/horizon.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2024-03-11 02:14:11 UTC; 34min ago
Main PID: 11686 (anax)
Tasks: 10 (limit: 4915)
CGroup: /system.slice/horizon.service
└─11686 /usr/horizon/bin/anax -v 3 -logtostderr -config /etc/horizon/anax.json

Mar 11 02:49:00 oh-3 anax[11686]: I0311 02:49:00.181841 11686 changes.go:270] Exchange RPC getting 1000 changes since change ID 100 in orgs []
Mar 11 02:49:00 oh-3 anax[11686]: I0311 02:49:00.196471 11686 changes.go:308] Exchange RPC found 0 changes since ID 100 with latest change ID 99 in orgs []
Mar 11 02:49:00 oh-3 anax[11686]: I0311 02:49:00.196560 11686 exchange_changes.go:82] Successfully saved exchange change state: Change State ID: 100, last updated: 2024-03-11T02:49:00Z[UTC]
Mar 11 02:49:00 oh-3 anax[11686]: I0311 02:49:00.197466 11686 changes_worker.go:307] Exchange Changes Worker: done looking for changes
Mar 11 02:49:00 oh-3 anax[11686]: I0311 02:49:00.197499 11686 worker.go:363] CommandDispatcher: ExchangeChanges command processor non-blocking for commands
Mar 11 02:49:00 oh-3 anax[11686]: I0311 02:49:00.858885 11686 governance.go:1478] GovernanceWorker: GovernanceWorker dispatching no work handler.
Mar 11 02:49:00 oh-3 anax[11686]: I0311 02:49:00.858928 11686 governance.go:405] GovernanceWorker: governing agreements
Mar 11 02:49:00 oh-3 anax[11686]: I0311 02:49:00.861252 11686 worker.go:363] CommandDispatcher: Governance command processor non-blocking for commands
Mar 11 02:49:02 oh-3 anax[11686]: I0311 02:49:02.514702 11686 worker.go:480] CommandDispatcher: Running subworker SurfaceExchErrors
Mar 11 02:49:02 oh-3 anax[11686]: I0311 02:49:02.516143 11686 worker.go:484] CommandDispatcher: Finished run of subworker SurfaceExchErrors

查看协议协商过程中执行的步骤

1
2
3
4
5
6
7
8
9
# hzn eventlog list
"2024-03-11 02:17:27: Start node configuration/registration for node node1.",
"2024-03-11 02:17:27: Complete node configuration/registration for node node1.",
"2024-03-11 02:17:39: Node received Proposal message using agreement be397e12deb1651d278739fefa9ff2780436878c3a9fc4f2c12b5ee4857ef5ff for service IBM/ibm.helloworld from the agbot IBM/agbot.",
"2024-03-11 02:17:43: Agreement reached for service ibm.helloworld. The agreement id is be397e12deb1651d278739fefa9ff2780436878c3a9fc4f2c12b5ee4857ef5ff.",
"2024-03-11 02:17:43: Start dependent services for IBM/ibm.helloworld.",
"2024-03-11 02:17:43: Start workload service for IBM/ibm.helloworld.",
"2024-03-11 02:17:44: Image loaded for IBM/ibm.helloworld.",
"2024-03-11 02:17:45: Workload service containers for IBM/ibm.helloworld are up and running."

查看导致部署 helloworld 服务的节点策略设置

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
# hzn policy list
{
"properties": [
{
"name": "openhorizon.example",
"value": "helloworld"
},
{
"name": "openhorizon.hardwareId",
"value": "36d3acd26462630855c99dedd33a56c04e443927"
},
{
"name": "openhorizon.operatingSystem",
"value": "ubuntu"
},
{
"name": "openhorizon.containerized",
"value": false
},
{
"name": "openhorizon.cpu",
"value": 4
},
{
"name": "openhorizon.arch",
"value": "amd64"
},
{
"name": "openhorizon.memory",
"value": 7976
},
{
"name": "openhorizon.allowPrivileged",
"value": false
}
],
"constraints": [],
"deployment": {
"properties": null,
"constraints": null
},
"management": {
"properties": null,
"constraints": null
}
}

查看 Horizon Exchange 中的资源

首先导出环境变量HZN_ORG_IDHZN_EXCHANGE_USER_AUTH

1
2
3
# 设置ORG_ID 为 myorg
export HZN_ORG_ID=myorg
export HZN_EXCHANGE_USER_AUTH=admin:<password>

查看所有hzn exchange可用的子命令

1
2
3
4
5
6
7
8
9
10
11
12
# hzn exchange --help
usage: hzn exchange | ex [<flags>] <command> [<args> ...]

List and manage Horizon Exchange resources.

Flags:
-h, --help Show context-sensitive help (also try --help-long and --help-man).
-v, --verbose Verbose output.
--dry-run When calling the Horizon or Exchange API, do GETs, but don't do PUTs, POSTs, or DELETEs.
-o, --org=ORG The Horizon exchange organization ID. If not specified, HZN_ORG_ID will be used as a default.
-u, --user-pw=USER:PW Horizon Exchange user credentials to query and create exchange resources. If not specified, HZN_EXCHANGE_USER_AUTH will be used as a default. If you don't prepend it with the user's org, it will automatically be prepended with the -o value. As an alternative to using
-o, you can set HZN_ORG_ID with the Horizon exchange organization ID

查看边缘服务示例

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
# hzn exchange service list IBM/
[
"IBM/ibm.cpu2evtstreams_1.4.3_amd64",
"IBM/ibm.hello-mms_1.1.0_arm",
"IBM/ibm.helloworld_1.0.0_amd64",
"IBM/ibm.cpu2evtstreams_1.4.3_arm",
"IBM/ibm.cpu_1.2.7_amd64",
"IBM/ibm.nginx-operator_2.0.0_ppc64le",
"IBM/ibm.helloworld_1.0.0_ppc64le",
"IBM/ibm.nginx-operator_2.0.0_amd64",
"IBM/ibm.hello-secret_1.0.0_amd64",
"IBM/ibm.hello-mms_1.1.0_arm64",
"IBM/ibm.helloworld_1.0.0_arm64",
"IBM/ibm.cpu_1.2.7_arm",
"IBM/ibm.hello-mms_1.1.0_amd64",
"IBM/ibm.hello-secret_1.0.0_arm",
"IBM/ibm.cpu_1.2.7_arm64",
"IBM/ibm.gps_2.1.4_amd64",
"IBM/ibm.gps_2.1.4_arm",
"IBM/ibm.gps_2.1.4_arm64",
"IBM/ibm.hello-secret_1.0.0_arm64",
"IBM/ibm.helloworld_1.0.0_arm",
"IBM/ibm.cpu2evtstreams_1.4.3_arm64",
"IBM/ibm.helloworld_1.0.0_s390x"
]

查看示例模式

1
2
3
4
5
6
7
8
# hzn exchange pattern list IBM/
[
"IBM/pattern-ibm.cpu2evtstreams",
"IBM/pattern-ibm.helloworld",
"IBM/pattern-ibm.nginx-operator",
"IBM/pattern-ibm.hello-secret",
"IBM/pattern-ibm.hello-mms"
]

查看部署策略示例

1
2
3
4
5
6
# hzn exchange deployment listpolicy
[
"myorg/policy-ibm.nginx-operator_2.0.0",
"myorg/policy-ibm.cpu2evtstreams_1.4.3",
"myorg/policy-ibm.helloworld_1.0.0"
]

验证导致部署 helloworld 服务的策略匹配

1
2
3
4
5
6
7
8
9
# hzn deploycheck all -b policy-ibm.helloworld_1.0.0
Neither node id nor node policy is specified. Getting node policy from the local node.
Neither node id nor node user input file is specified. Getting node user input from the local node.
{
"compatible": true,
"reason": {
"IBM/ibm.helloworld_1.0.0_amd64": "Compatible"
}
}

查看您的节点

1
2
3
4
# hzn exchange node list
[
"myorg/node1"
]

查看您组织中的用户

1
2
3
4
5
6
7
8
9
10
11
# hzn exchange user list
{
"myorg/admin": {
"password": "********",
"email": "not@used",
"admin": true,
"hubAdmin": false,
"lastUpdated": "2024-03-11T02:14:01.119884994Z[UTC]",
"updatedBy": "root/root"
}
}

使用 verbose 标志查看hzn命令调用的交换 REST API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# hzn exchange user list -v
[verbose] Reading configuration file: /etc/horizon/hzn.json
[verbose] Reading configuration file: /etc/default/horizon
[verbose] Config file does not exist: /root/.hzn/hzn.json.
[verbose] No project level configuration file found.
[verbose] The exchange url: http://192.168.1.208:3090/v1
[verbose] GET http://192.168.1.208:3090/v1/admin/version
[verbose] HTTP request timeout set to 30 seconds
[verbose] HTTP code: 200
[verbose] The exchange url: http://192.168.1.208:3090/v1
[verbose] GET http://192.168.1.208:3090/v1/orgs/myorg/users/admin
[verbose] HTTP request timeout set to 30 seconds
[verbose] HTTP code: 200
{
"myorg/admin": {
"password": "********",
"email": "not@used",
"admin": true,
"hubAdmin": false,
"lastUpdated": "2024-03-11T02:14:01.119884994Z[UTC]",
"updatedBy": "root/root"
}
}

agent-install.sh查看CSS中可用于在边缘节点上安装/注册代理的公共文件

1
2
3
4
5
6
7
8
9
# hzn mms -o IBM -u "$HZN_ORG_ID/$HZN_EXCHANGE_USER_AUTH" object list -d -t agent_files
Listing objects in org IBM:
[
{
"objectID": "agent-install.cfg",
"objectType": "agent_files",
"objectStatus": "ready"
}
]

创建彩信(MMS)文件

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
# cat << EOF > mms-meta.json
> {
> "objectID": "mms-file",
> "objectType": "stuff",
> "destinationOrgID": "$HZN_ORG_ID",
> "destinationType": "pattern-ibm.helloworld"
> }
> EOF

# echo -e "foo\nbar" > mms-file

# cat mms-file
foo
bar

# cat mms-meta.json
{
"objectID": "mms-file",
"objectType": "stuff",
"destinationOrgID": "myorg",
"destinationType": "pattern-ibm.helloworld"
}

# hzn mms object publish -m mms-meta.json -f mms-file
Digital sign with SHA1 will be performed for data integrity. It will delay the MMS object publish.
Start hashing the file...
Data hash is generated. Start digital signing with the data hash...
Digital sign finished.
Object mms-file added to org myorg in the Model Management Service

查看文件的元数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# hzn mms object list -d
Listing objects in org myorg:
[
{
"objectID": "mms-file",
"objectType": "stuff",
"objectStatus": "ready"
},
{
"objectID": "test-mgmt-hub-mms-file",
"objectType": "test-mgmt-hub",
"objectStatus": "ready"
}
]

获取文件

1
2
3
4
5
6
7
8
# hzn mms object download -t stuff -i mms-file -f mms-file.downloaded
Verifying data with digital signature....
Downloading: 100.00 %Verifying digital signature is done.
Data of object mms-file saved to file mms-file.downloaded

# cat mms-file.downloaded
foo
bar

切换到该组织中的管理员用户

通过切换到该组织中的管理员用户来查看系统组织中的更多资源

1
2
3
# 设置ORG_ID 为 IBM
export HZN_ORG_ID=IBM
export HZN_EXCHANGE_USER_AUTH=admin:<password>

查看系统组织中的用户

1
2
3
4
5
6
7
8
9
10
11
# hzn exchange user list
{
"IBM/admin": {
"password": "********",
"email": "not@used",
"admin": true,
"hubAdmin": false,
"lastUpdated": "2024-03-11T02:13:45.607816433Z[UTC]",
"updatedBy": "root/root"
}
}

查看 agbot

1
2
3
4
# hzn exchange agbot list
[
"IBM/agbot"
]

查看 agbot 正在服务的部署策略

1
2
3
4
5
6
7
8
9
# hzn exchange agbot listdeploymentpol agbot
{
"myorg_*_myorg": {
"businessPolOrgid": "myorg",
"businessPol": "*",
"nodeOrgid": "myorg",
"lastUpdated": "2024-03-11T02:13:48.974503620Z[UTC]"
}
}

查看 agbot 正在服务的模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# hzn exchange agbot listpattern agbot
{
"IBM_*_myorg": {
"lastUpdated": "2024-03-11T02:13:48.830249995Z[UTC]",
"nodeOrgid": "myorg",
"pattern": "*",
"patternOrgid": "IBM"
},
"myorg_*_myorg": {
"lastUpdated": "2024-03-11T02:13:48.892474770Z[UTC]",
"nodeOrgid": "myorg",
"pattern": "*",
"patternOrgid": "myorg"
}
}

切换到中心管理员

中心管理员可以管理 Horizon 组织(创建、读取、更新和删除它们)。

1
2
export HZN_ORG_ID=root
export HZN_EXCHANGE_USER_AUTH=hubadmin:<password>

列出组织

1
2
3
4
5
6
# hzn exchange org list
[
"root",
"IBM",
"myorg"
]

创建一个新组织

1
2
3
4
# hzn exchange org create -d 'my new org' -a IBM/agbot myneworg
Organization myneworg is successfully added to the Exchange.
Agbot IBM/agbot is responsible for deploying services in org myneworg
Organization myneworg is successfully added to MMS.

配置 agbot 使其能够使用该组织的示例服务

1
# hzn exchange agbot addpattern IBM/agbot IBM '*' myneworg

查看 agbot 正在服务的模式

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
# hzn exchange agbot listpattern IBM/agbot
{
"IBM_*_myneworg": {
"lastUpdated": "2024-03-11T03:25:26.840611649Z[UTC]",
"nodeOrgid": "myneworg",
"pattern": "*",
"patternOrgid": "IBM"
},
"IBM_*_myorg": {
"lastUpdated": "2024-03-11T02:13:48.830249995Z[UTC]",
"nodeOrgid": "myorg",
"pattern": "*",
"patternOrgid": "IBM"
},
"myneworg_*_myneworg": {
"lastUpdated": "2024-03-11T03:24:53.886134418Z[UTC]",
"nodeOrgid": "myneworg",
"pattern": "*",
"patternOrgid": "myneworg"
},
"myorg_*_myorg": {
"lastUpdated": "2024-03-11T02:13:48.892474770Z[UTC]",
"nodeOrgid": "myorg",
"pattern": "*",
"patternOrgid": "myorg"
}
}

查看 agbot 正在服务的部署策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# hzn exchange agbot listdeploymentpol IBM/agbot
{
"myneworg_*_myneworg": {
"businessPolOrgid": "myneworg",
"businessPol": "*",
"nodeOrgid": "myneworg",
"lastUpdated": "2024-03-11T03:24:53.894147006Z[UTC]"
},
"myorg_*_myorg": {
"businessPolOrgid": "myorg",
"businessPol": "*",
"nodeOrgid": "myorg",
"lastUpdated": "2024-03-11T02:13:48.974503620Z[UTC]"
}
}

添加更多边缘设备

在每个边缘节点上执行以下命令

1
2
3
4
5
export HZN_ORG_ID=myorg
export HZN_EXCHANGE_USER_AUTH=admin:<admin-pw>
export HZN_FSS_CSSURL=http://<mgmt-hub-ip>:9443/
export HZN_EXCHANGE_URL=http://<mgmt-hub-ip>:3090/v1
curl -sSL https://github.com/open-horizon/anax/releases/latest/download/agent-install.sh | bash -s -- -i anax: -k css: -c css: -p IBM/pattern-ibm.helloworld -w '*' -T 120

命令执行完成后,会在当前边缘节点下启动一个服务

1
2
3
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0b2c31b05629 openhorizon/ibm.helloworld_amd64 "/bin/sh -c /service…" 24 hours ago Up 24 hours 049af567e902ee6eb89ee574f9bd9e25ebb90ce7d89f0ffd9a96dd90db5254b8-ibm.helloworld

查看节点列表

1
2
3
4
5
# hzn exchange node list
[
"myorg/node1",
"myorg/oh-2" # 新注册进来的边缘节点
]

尝试FDO

运行 FDO 测试脚本

1

链接

open-horizon-devops

------ 本文结束------
如果本篇文章对你有帮助,可以给作者加个鸡腿~(*^__^*),感谢鼓励与支持!