Fabric1.4.1单机版环境搭建(first-network)

安装软件

更新源

sudo apt-get update

安装Go

cd /usr/local

上传go的压缩包

sudo rz

解压压缩包

sudo tar -zxf  go1.14.4.linux-amd64.tar.gz

配置环境

vim ~/.bashrc

export GOROOT=/usr/local/go

export GOPATH=/opt/gopath

export PATH=$PATH:$GOROOT/bin/:$GOPATH/bin

source ~/.bashrc

go version

安装Docker

sudo apt-get install -y docker.io

修改当前用户(我使用的用户叫fish)权限

sudo usermod -aG docker fish

注销并重新登录(或关闭当前黑屏窗口重新进入),然后添加阿里云的Docker Hub镜像:

sudo mkdir -p /etc/docker

sudo tee /etc/docker/daemon.json <<-'EOF'

{

  "registry-mirrors": ["https://obou6wyb.mirror.aliyuncs.com"]

}

EOF

cd /etc/docker

more daemon.json

sudo systemctl daemon-reload

sudo systemctl restart docker

docker -v

安装Docker-compose

Docker-compose是支持通过模板脚本批量创建Docker容器的一个组件。

sudo apt-get install -y docker-compose

docker-compose --version

Fabric环境搭建

https://github.com/hyperledger/fabric.git

https://github.com/hyperledger/fabric-sdk-go.git

https://github.com/hyperledger/fabric-samples.git

sudo mkdir -p /opt/gopath/src/github.com/hyperledger/

Fabric1.4.1源码

cd /opt/gopath/src/github.com/hyperledger/

git clone https://github.com/hyperledger/fabric-samples.git

切换fabric版本:

git tag

sudo git checkout -b v1.4.1

下载fabric1.4.1的docker镜像

vim docker_images.sh

#!/bin/bash

docker pull hyperledger/fabric-ca:1.4.1

docker tag hyperledger/fabric-ca:1.4.1 hyperledger/fabric-ca:latest

docker pull hyperledger/fabric-tools:1.4.1

docker tag hyperledger/fabric-tools:1.4.1 hyperledger/fabric-tools:latest

docker pull hyperledger/fabric-ccenv:1.4.1

docker tag hyperledger/fabric-ccenv:1.4.1 hyperledger/fabric-ccenv:latest

docker pull hyperledger/fabric-orderer:1.4.1

docker tag hyperledger/fabric-orderer:1.4.1 hyperledger/fabric-orderer:latest

docker pull hyperledger/fabric-peer:1.4.1

docker tag hyperledger/fabric-peer:1.4.1 hyperledger/fabric-peer:latest

docker pull hyperledger/fabric-javaenv:1.4.1

docker tag hyperledger/fabric-javaenv:1.4.1 hyperledger/fabric-javaenv:latest

docker pull hyperledger/fabric-zookeeper:0.4.15

docker tag hyperledger/fabric-zookeeper:0.4.15 hyperledger/fabric-zookeeper:latest

docker pull hyperledger/fabric-kafka:0.4.15

docker tag hyperledger/fabric-kafka:0.4.15 hyperledger/fabric-kafka:latest

docker pull hyperledger/fabric-couchdb:0.4.15

docker tag hyperledger/fabric-couchdb:0.4.15 hyperledger/fabric-couchdb:latest

docker pull hyperledger/fabric-baseos:0.4.15

docker tag hyperledger/fabric-baseos:0.4.15 hyperledger/fabric-baseos:latest

chmod 777 docker_images.sh

执行

./docker_images.sh

需要一段时间。。。。

查看

docker images

下载fabric1.4.1可执行文件

可以选择编译二进制文件的方式生成可执行文件,但出错的概率很大,个人建议去官网下载。

第一种:make release方式,执行make release拉取二进制源码工具,执行完成后,在fabric/release/linux-amd64/bin的目录下就会有下载好的二进制源码工具。

第二种:make方式,会在.build/bin目录下生成二进制工具(.build/bin是隐藏文件,在make时第一行会有提示)

make configtxgen

make cryptogen

make configtxlator

下载可执行文件,https://github.com/hyperledger/fabric/releases/tag/v1.4.1

上传configtxgen、cryptogen、configtxlator 到 /usr/local/bin目录下, 这样fabric的这些可执行程序就可以在全局范围内使用

cd /usr/local/bin

sudo chmod 777 *

到此,万事具备,只欠东风了。。。

etcdraft网络测试

cd /opt/gopath/src/github.com/hyperledger/fabric-samples/first-network

用脚本生成生成公私钥和证书、创世区块

sudo ./byfn.sh -m generate -o etcdraft

Generating certs and genesis block for channel 'mychannel' with CLI timeout of '10' seconds and CLI delay of '3' seconds
Continue? [Y/n] y
proceeding ...
/usr/local/bin/cryptogen

##########################################################
##### Generate certificates using cryptogen tool #########
##########################################################
+ cryptogen generate --config=./crypto-config.yaml
org1.example.com
org2.example.com
+ res=0
+ set +x

/usr/local/bin/configtxgen
##########################################################
#########  Generating Orderer Genesis block ##############
##########################################################
CONSENSUS_TYPE=etcdraft
+ '[' etcdraft == solo ']'
+ '[' etcdraft == kafka ']'
+ '[' etcdraft == etcdraft ']'
+ configtxgen -profile SampleMultiNodeEtcdRaft -channelID byfn-sys-channel -outputBlock ./channel-artifacts/genesis.block
2020-07-02 13:12:03.873 CST [common.tools.configtxgen] main -> INFO 001 Loading configuration
2020-07-02 13:12:04.043 CST [common.tools.configtxgen.localconfig] completeInitialization -> INFO 002 orderer type: etcdraft
2020-07-02 13:12:04.044 CST [common.tools.configtxgen.localconfig] completeInitialization -> INFO 003 Orderer.EtcdRaft.Options unset, setting to tick_interval:"500ms" election_tick:10 heartbeat_tick:1 max_inflight_blocks:5 snapshot_interval_size:20971520 
2020-07-02 13:12:04.044 CST [common.tools.configtxgen.localconfig] Load -> INFO 004 Loaded configuration: /opt/gopath/src/github.com/hyperledger/fabric-samples/first-network/configtx.yaml
2020-07-02 13:12:04.189 CST [common.tools.configtxgen.localconfig] completeInitialization -> INFO 005 orderer type: solo
2020-07-02 13:12:04.195 CST [common.tools.configtxgen.localconfig] LoadTopLevel -> INFO 006 Loaded configuration: /opt/gopath/src/github.com/hyperledger/fabric-samples/first-network/configtx.yaml
2020-07-02 13:12:04.200 CST [common.tools.configtxgen] doOutputBlock -> INFO 007 Generating genesis block
2020-07-02 13:12:04.201 CST [common.tools.configtxgen] doOutputBlock -> INFO 008 Writing genesis block
+ res=0
+ set +x

#################################################################
### Generating channel configuration transaction 'channel.tx' ###
#################################################################
+ configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID mychannel
2020-07-02 13:12:04.269 CST [common.tools.configtxgen] main -> INFO 001 Loading configuration
2020-07-02 13:12:04.434 CST [common.tools.configtxgen.localconfig] Load -> INFO 002 Loaded configuration: /opt/gopath/src/github.com/hyperledger/fabric-samples/first-network/configtx.yaml
2020-07-02 13:12:04.594 CST [common.tools.configtxgen.localconfig] completeInitialization -> INFO 003 orderer type: solo
2020-07-02 13:12:04.594 CST [common.tools.configtxgen.localconfig] LoadTopLevel -> INFO 004 Loaded configuration: /opt/gopath/src/github.com/hyperledger/fabric-samples/first-network/configtx.yaml
2020-07-02 13:12:04.594 CST [common.tools.configtxgen] doOutputChannelCreateTx -> INFO 005 Generating new channel configtx
2020-07-02 13:12:04.616 CST [common.tools.configtxgen] doOutputChannelCreateTx -> INFO 006 Writing new channel tx
+ res=0
+ set +x

#################################################################
#######    Generating anchor peer update for Org1MSP   ##########
#################################################################
+ configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP
2020-07-02 13:12:04.688 CST [common.tools.configtxgen] main -> INFO 001 Loading configuration
2020-07-02 13:12:04.829 CST [common.tools.configtxgen.localconfig] Load -> INFO 002 Loaded configuration: /opt/gopath/src/github.com/hyperledger/fabric-samples/first-network/configtx.yaml
2020-07-02 13:12:04.966 CST [common.tools.configtxgen.localconfig] completeInitialization -> INFO 003 orderer type: solo
2020-07-02 13:12:04.971 CST [common.tools.configtxgen.localconfig] LoadTopLevel -> INFO 004 Loaded configuration: /opt/gopath/src/github.com/hyperledger/fabric-samples/first-network/configtx.yaml
2020-07-02 13:12:04.971 CST [common.tools.configtxgen] doOutputAnchorPeersUpdate -> INFO 005 Generating anchor peer update
2020-07-02 13:12:04.972 CST [common.tools.configtxgen] doOutputAnchorPeersUpdate -> INFO 006 Writing anchor peer update
+ res=0
+ set +x

#################################################################
#######    Generating anchor peer update for Org2MSP   ##########
#################################################################
+ configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID mychannel -asOrg Org2MSP
2020-07-02 13:12:05.035 CST [common.tools.configtxgen] main -> INFO 001 Loading configuration
2020-07-02 13:12:05.190 CST [common.tools.configtxgen.localconfig] Load -> INFO 002 Loaded configuration: /opt/gopath/src/github.com/hyperledger/fabric-samples/first-network/configtx.yaml
2020-07-02 13:12:05.339 CST [common.tools.configtxgen.localconfig] completeInitialization -> INFO 003 orderer type: solo
2020-07-02 13:12:05.339 CST [common.tools.configtxgen.localconfig] LoadTopLevel -> INFO 004 Loaded configuration: /opt/gopath/src/github.com/hyperledger/fabric-samples/first-network/configtx.yaml
2020-07-02 13:12:05.341 CST [common.tools.configtxgen] doOutputAnchorPeersUpdate -> INFO 005 Generating anchor peer update
2020-07-02 13:12:05.343 CST [common.tools.configtxgen] doOutputAnchorPeersUpdate -> INFO 006 Writing anchor peer update
+ res=0
+ set +x

启动etcdraft网络

sudo ./byfn.sh -m up -o etcdraft

Starting for channel 'mychannel' with CLI timeout of '10' seconds and CLI delay of '3' seconds
Continue? [Y/n] y
proceeding ...
LOCAL_VERSION=1.4.1
DOCKER_IMAGE_VERSION=1.4.1
Creating network "net_byfn" with the default driver
Creating volume "net_orderer.example.com" with default driver
Creating volume "net_orderer2.example.com" with default driver
Creating volume "net_peer0.org2.example.com" with default driver
Creating volume "net_peer0.org1.example.com" with default driver
Creating volume "net_peer1.org1.example.com" with default driver
Creating volume "net_peer1.org2.example.com" with default driver
Creating volume "net_orderer5.example.com" with default driver
Creating volume "net_orderer4.example.com" with default driver
Creating volume "net_orderer3.example.com" with default driver
Creating orderer.example.com
Creating orderer3.example.com
Creating peer0.org2.example.com
Creating peer1.org2.example.com
Creating orderer4.example.com
Creating peer0.org1.example.com
Creating peer1.org1.example.com
Creating orderer5.example.com
Creating orderer2.example.com
Creating cli
CONTAINER ID        IMAGE                               COMMAND             CREATED             STATUS                  PORTS                      NAMES
d7eeeab81a70        hyperledger/fabric-tools:latest     "/bin/bash"         5 seconds ago       Up Less than a second                              cli
8b44bf864563        hyperledger/fabric-orderer:latest   "orderer"           33 seconds ago      Up 5 seconds            0.0.0.0:11050->7050/tcp    orderer5.example.com
978c541e7072        hyperledger/fabric-peer:latest      "peer node start"   33 seconds ago      Up 5 seconds            0.0.0.0:8051->8051/tcp     peer1.org1.example.com
03588b64dd2d        hyperledger/fabric-orderer:latest   "orderer"           33 seconds ago      Up 4 seconds            0.0.0.0:8050->7050/tcp     orderer2.example.com
68505592e320        hyperledger/fabric-peer:latest      "peer node start"   33 seconds ago      Up 6 seconds            0.0.0.0:7051->7051/tcp     peer0.org1.example.com
f93f04ee282a        hyperledger/fabric-peer:latest      "peer node start"   33 seconds ago      Up 6 seconds            0.0.0.0:10051->10051/tcp   peer1.org2.example.com
1bd634c587dd        hyperledger/fabric-orderer:latest   "orderer"           33 seconds ago      Up 9 seconds            0.0.0.0:10050->7050/tcp    orderer4.example.com
59dc81ae24e1        hyperledger/fabric-peer:latest      "peer node start"   33 seconds ago      Up 6 seconds            0.0.0.0:9051->9051/tcp     peer0.org2.example.com
f034f6b98a59        hyperledger/fabric-orderer:latest   "orderer"           33 seconds ago      Up 7 seconds            0.0.0.0:9050->7050/tcp     orderer3.example.com
d452fab38842        hyperledger/fabric-orderer:latest   "orderer"           33 seconds ago      Up 12 seconds           0.0.0.0:7050->7050/tcp     orderer.example.com
Sleeping 15s to allow etcdraft cluster to complete booting

 ____    _____      _      ____    _____ 
/ ___|  |_   _|    / \    |  _ \  |_   _|
\___ \    | |     / _ \   | |_) |   | |  
 ___) |   | |    / ___ \  |  _ <    | |  
|____/    |_|   /_/   \_\ |_| \_\   |_|  

Build your first network (BYFN) end-to-end test

+ peer channel create -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/channel.tx --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
Channel name : mychannel
Creating channel...
+ res=0
+ set +x
2020-07-02 05:13:20.877 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2020-07-02 05:13:20.990 UTC [cli.common] readBlock -> INFO 002 Got status: &{NOT_FOUND}
2020-07-02 05:13:20.992 UTC [channelCmd] InitCmdFactory -> INFO 003 Endorser and orderer connections initialized
2020-07-02 05:13:21.267 UTC [cli.common] readBlock -> INFO 004 Got status: &{SERVICE_UNAVAILABLE}
2020-07-02 05:13:21.280 UTC [channelCmd] InitCmdFactory -> INFO 005 Endorser and orderer connections initialized
2020-07-02 05:13:21.487 UTC [cli.common] readBlock -> INFO 006 Got status: &{SERVICE_UNAVAILABLE}
2020-07-02 05:13:21.498 UTC [channelCmd] InitCmdFactory -> INFO 007 Endorser and orderer connections initialized
2020-07-02 05:13:21.712 UTC [cli.common] readBlock -> INFO 008 Got status: &{SERVICE_UNAVAILABLE}
2020-07-02 05:13:21.721 UTC [channelCmd] InitCmdFactory -> INFO 009 Endorser and orderer connections initialized
2020-07-02 05:13:21.921 UTC [cli.common] readBlock -> INFO 00a Got status: &{SERVICE_UNAVAILABLE}
2020-07-02 05:13:21.923 UTC [channelCmd] InitCmdFactory -> INFO 00b Endorser and orderer connections initialized
2020-07-02 05:13:22.124 UTC [cli.common] readBlock -> INFO 00c Got status: &{SERVICE_UNAVAILABLE}
2020-07-02 05:13:22.125 UTC [channelCmd] InitCmdFactory -> INFO 00d Endorser and orderer connections initialized
2020-07-02 05:13:22.358 UTC [cli.common] readBlock -> INFO 00e Received block: 0
===================== Channel 'mychannel' created ===================== 

Having all peers join the channel...
+ peer channel join -b mychannel.block
+ res=0
+ set +x
2020-07-02 05:13:22.520 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2020-07-02 05:13:23.139 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
===================== peer0.org1 joined channel 'mychannel' ===================== 
+ peer channel join -b mychannel.block

+ res=0
+ set +x
2020-07-02 05:13:26.285 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2020-07-02 05:13:26.592 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
===================== peer1.org1 joined channel 'mychannel' ===================== 

+ peer channel join -b mychannel.block
+ res=0
+ set +x
2020-07-02 05:13:29.724 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2020-07-02 05:13:29.954 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
===================== peer0.org2 joined channel 'mychannel' ===================== 

+ peer channel join -b mychannel.block
+ res=0
+ set +x
2020-07-02 05:13:33.085 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2020-07-02 05:13:33.338 UTC [channelCmd] executeJoin -> INFO 002 Successfully submitted proposal to join channel
===================== peer1.org2 joined channel 'mychannel' ===================== 

Updating anchor peers for org1...
+ peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org1MSPanchors.tx --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
+ res=0
+ set +x
2020-07-02 05:13:36.435 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2020-07-02 05:13:36.461 UTC [channelCmd] update -> INFO 002 Successfully submitted channel update
===================== Anchor peers updated for org 'Org1MSP' on channel 'mychannel' ===================== 

Updating anchor peers for org2...
+ peer channel update -o orderer.example.com:7050 -c mychannel -f ./channel-artifacts/Org2MSPanchors.tx --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
+ res=0
+ set +x
2020-07-02 05:13:39.839 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
2020-07-02 05:13:39.886 UTC [channelCmd] update -> INFO 002 Successfully submitted channel update
===================== Anchor peers updated for org 'Org2MSP' on channel 'mychannel' ===================== 

Installing chaincode on peer0.org1...
+ peer chaincode install -n mycc -v 1.0 -l golang -p github.com/chaincode/chaincode_example02/go/
+ res=0
+ set +x
+ peer chaincode install -n mycc -v 1.0 -l golang -p github.com/chaincode/chaincode_example02/go/
2020-07-02 05:13:43.180 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2020-07-02 05:13:43.180 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
2020-07-02 05:13:44.883 UTC [chaincodeCmd] install -> INFO 003 Installed remotely response:<status:200 payload:"OK" > 
===================== Chaincode is installed on peer0.org1 ===================== 

Install chaincode on peer0.org2...
+ res=0
+ set +x
2020-07-02 05:13:45.012 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2020-07-02 05:13:45.012 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
2020-07-02 05:13:45.340 UTC [chaincodeCmd] install -> INFO 003 Installed remotely response:<status:200 payload:"OK" > 
===================== Chaincode is installed on peer0.org2 ===================== 

Instantiating chaincode on peer0.org2...
+ peer chaincode instantiate -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc -l golang -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P 'AND ('\''Org1MSP.peer'\'','\''Org2MSP.peer'\'')'
+ res=0
+ set +x
2020-07-02 05:13:45.467 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2020-07-02 05:13:45.467 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
===================== Chaincode is instantiated on peer0.org2 on channel 'mychannel' ===================== 

Querying chaincode on peer0.org1...
===================== Querying on peer0.org1 on channel 'mychannel'... ===================== 
Attempting to Query peer0.org1 ...3 secs
+ peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
+ res=0
+ set +x

100
===================== Query successful on peer0.org1 on channel 'mychannel' ===================== 
Sending invoke transaction on peer0.org1 peer0.org2...
+ peer chaincode invoke -o orderer.example.com:7050 --tls true --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n mycc --peerAddresses peer0.org1.example.com:7051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses peer0.org2.example.com:9051 --tlsRootCertFiles /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt -c '{"Args":["invoke","a","b","10"]}'
+ res=0
+ set +x
2020-07-02 05:15:28.922 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 001 Chaincode invoke successful. result: status:200 
===================== Invoke transaction successful on peer0.org1 peer0.org2 on channel 'mychannel' ===================== 

Installing chaincode on peer1.org2...
+ peer chaincode install -n mycc -v 1.0 -l golang -p github.com/chaincode/chaincode_example02/go/
+ res=0
+ set +x
2020-07-02 05:15:28.979 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 001 Using default escc
2020-07-02 05:15:28.979 UTC [chaincodeCmd] checkChaincodeCmdParams -> INFO 002 Using default vscc
2020-07-02 05:15:29.151 UTC [chaincodeCmd] install -> INFO 003 Installed remotely response:<status:200 payload:"OK" > 
===================== Chaincode is installed on peer1.org2 ===================== 

Querying chaincode on peer1.org2...
===================== Querying on peer1.org2 on channel 'mychannel'... ===================== 
Attempting to Query peer1.org2 ...3 secs
+ peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'
+ res=0
+ set +x
90
===================== Query successful on peer1.org2 on channel 'mychannel' ===================== 

========= All GOOD, BYFN execution completed =========== 
 _____   _   _   ____   
| ____| | \ | | |  _ \  
|  _|   |  \| | | | | | 
| |___  | |\  | | |_| | 
|_____| |_| \_| |____/  

docker ps -a

docker exec -it cli bash

peer chaincode query -C mychannel -n mycc -c '{"Args":["query","a"]}'

重启etcdraft网络

sudo ./byfn.sh -m restart -o etcdraft

停止etcdraft网络

sudo ./byfn.sh -m down -o etcdraft

就这样,搭建fabric1.4.1的raft共识单机版集群环境完成了。。。

猜你喜欢

转载自blog.csdn.net/yuch_hong/article/details/107336759
今日推荐