Hyperledger Fabric 1.0 实战 - 集合版本

       学习搭建Hyperledger Fabric 1.0 ,所以记录了整个搭建过程中所有相关信息以便后续回顾和分享给大家,让大家批评指导~(持续更新)

区块链网络搭建步骤:

1.环境构建

       测试环境用到的宿主机环境是Centos ,版本为Centos7.4,通过Docker 容器来运行Fabric的节点,版本为v1.0。因此,启动Fabric网络中的节点需要先安装Docker、Docker-compose和Go语言环境,然后在网上拉取相关的Docker镜像,再通过配置compose文件来启动各个节点。

1.1.虚拟机安装

       目前Docker安装需要Centos7.0以上的系统,目前CentOS最新版为7.4,采用7.4的版本(http://mirrors.163.com/centos/7.4.1708/isos/x86_64/CentOS-7-x86_64-DVD-1708.iso

1.2.更换Linux的yum源

a.备份原来yum源
    cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak

b.设置阿里yum源
    wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

c.清理缓存并生成新的缓存
    yum clean all
    yum makecache
    
d.更新yum库
    yum update   

1.3.Docker安装

1.3.1.网络安装
    a.如果服务骑上有旧版的docker,需要先卸载操作,如下:
        yum remove docker docker-common docker-selinux docker-engine
        
    b.随后安装Docker CE
        yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
        yum-config-manager --enable docker-ce-edge
        yum-config-manager --enable docker-ce-test
        yum-config-manager --disable docker-ce-edge
        yum makecache fast
        yum install docker-ce
    
    c.安装完成,查看docker版本号
        docker --version
    
    d.设置docker开启自启
        chkconfig docker on
        
    e.设置docker启动
        service docker start
1.3.2.离线安装
    a.下载安装包,地址:https://download.docker.com/linux/centos/7/x86_64/stable/Packages/
        cd /home/soft/
        wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-17.12.1.ce-1.el7.centos.x86_64.rpm
    
    b.安装docker
        yum install docker-ce-17.12.1.ce-1.el7.centos.x86_64.rpm
        
    c.安装完成后,查看是否安装成功
        docker --version
        
    d.设置docker开启自启
        chkconfig docker on
        
    e.设置docker启动
        service docker start
1.3.3.Docker常用命令
    a.docker ps 查看当前正在运行的容器

    b.docker ps -a 查看所有容器的状态

    c.docker start/stop id/name 启动/停止某个容器

    d.docker attach id 进入某个容器(使用exit退出后容器也跟着停止运行)

    e.docker exec -ti id 启动一个伪终端以交互式的方式进入某个容器(使用exit退出后容器不停止运行)

    f.docker images 查看本地镜像

    g.docker rm id/name 删除某个容器

    h.docker rmi id/name 删除某个镜像

    i.docker run --name test -ti ubuntu /bin/bash  复制ubuntu容器并且重命名为test且运行,然后以伪终端交互式方式进入容器,运行bash

    j.docker build -t soar/centos:7.1 .  通过当前目录下的Dockerfile创建一个名为soar/centos:7.1的镜像

    k.docker run -d -p 2222:22 --name test soar/centos:7.1  以镜像soar/centos:7.1创建名为test的容器,并以后台模式运行,并做端口映射到宿主机2222端口,P参数重启容器宿主机端口会发生改变

1.4Docker-Compose安装

a.到github项目里下载最新版的docker-compose-Linux-x86_64,随后将其上传至/机票,/home/soft/下。
    github地址:https://github.com/docker/compose/releases
    cd /home/soft/
    wget https://github.com/docker/compose/releases/download/1.20.0-rc2/docker-compose-Linux-x86_64
    
b.安装
    mv docker-compose-Linux-x86_64 /usr/local/bin/docker-compose
    chmod +x /usr/local/bin/docker-compose

c.查看安装情况
    docker-compose --version

1.5.Go语言安装

a.下载安装包
    https://www.golangtc.com/download
    curl -O https://www.golangtc.com/static/go/1.9.2/go1.9.2.linux-amd64.tar.gz
    
b.解压go1.9.2.linux-amd64.tar.gz到/usr/local/目录下,执行如下操作:
    tar -C /usr/local -xzf go1.9.2.linux-amd64.tar.gz
    
c.创建go语言的路径
    mkdir /opt/gopath/
    
d.配置go环境变量
    vi /etc/profile
    export PATH=$PATH:/usr/local/go/bin
    export GOPATH=/opt/gopath
    
    source /etc/profile
    
e.查看环境变量
    echo $PATH
    
f.查看go安装情况
    go version

1.6.Docker中国镜像设置

a.创建daemon.json
    vi /etc/docker/daemon.json 
    内容如下:
    { 
        "registry-mirrors":["https://registry.docker-cn.com"] 
    }
b.重启Docker
    systemctl daemon-reload 
    systemctl restart docker

1.7.Git安装

a.linux安装git
    yum install git

2.Fabric源码及镜像文件处理

2.1源码下载-手动方式(2.1或者2.2任选一种)

    a.创建Fabric目录
        mkdir -p /opt/gopath/src/github.com/hyperledger
        
    b.通过手动下载fabric源代码
        浏览器打开:https://github.com/hyperledger/fabric/tree/release-1.0
        点击download下载压缩包:release-1.0.zip
        
    c.FTP上传到/opt/gopath/src/github.com/hyperledger目录下
    
    d.解压缩
        unzip ./release-1.0.zip
        
    e.重命名
        mv ./release-1.0 ./fabric

2.2源码下载-git方式(速度慢2.1或者2.2任选一种)

    a.创建Fabric目录
        mkdir -p /opt/gopath/src/github.com/hyperledger
        
    b.进入hyperledger目录
        cd /opt/gopath/src/github.com/hyperledger
    
    c.克隆fabric v1.0.0代码
        git clone -b v1.0.0 https://github.com/hyperledger/fabric.git

2.3拉取fabric所需要的镜像,目前版本为v1.0.0,具体的内容如下

    a.上DockerHub官方镜像网站:
        https://hub.docker.com/u/hyperledger/?page=1
    
    b.搜索依赖名称,执行如下命令:
        docker pull hyperledger/fabric-tools:x86_64-1.0.0
        docker pull hyperledger/fabric-couchdb:x86_64-1.0.0
        docker pull hyperledger/fabric-kafka:x86_64-1.0.0
        docker pull hyperledger/fabric-zookeeper:x86_64-1.0.0
        docker pull hyperledger/fabric-orderer:x86_64-1.0.0
        docker pull hyperledger/fabric-javaenv:x86_64-1.0.0
        docker pull hyperledger/fabric-ccenv:x86_64-1.0.0
        docker pull hyperledger/fabric-ca:x86_64-1.0.0
        docker pull hyperledger/fabric-baseos:x86_64-0.3.1
        docker pull hyperledger/fabric-baseimage:x86_64-0.3.1
        docker pull hyperledger/fabric-membersrvc:latest
        
    c.所有的镜像文件及版本号修改完成后,执行如下命令:
        docker images

    d.为了方便docker-compose的配置,我们将所有的镜像tag都改为latest,执行如下格式的命令
        docker tag IMAGEID(镜像id) REPOSITORY:TAG(仓库:标签)
        docker tag 0403fd1c72c7 docker.io/hyperledger/fabric-tools:latest
        
    e.镜像备份和拷贝
        1.查看所有的镜像
            docker images
            
        2.保存镜像
            docker save 6830dcd7b9b5> /tmp/docker/fabric-images/peer.tar
            
        3.加载镜像
            docker load < /tmp/docker/fabric-peer.tar
            镜像加载完成后,可根据之前的方案,将镜像tag都改为lastest。

3.运行测试e2e

3.1fabric-sample下载-手动下载

    a.github上下载fabric-sample,(3.1或3.2任选一种)
        https://github.com/hyperledger/fabric-samples/tree/release-1.0
    
    b.下载release-1.0.zip

    c.上传到ftp目录/opt/gopath/src/github.com/hyperledger上

    d.解压缩
        unzip ./release-1.0.zip
    
    e.重命名
        mv ./release-1.0 ./fabric-samples

3.2fabric-sample下载-git clone,(3.1或3.2任选一种)

    a.进入目录
        cd /opt/gopath/src/github.com/hyperledger
        
    b.克隆
        git clone -b v1.0.0 https://github.com/hyperledger/fabric-samples.git

3.3fabric-sample下载二进制执行文件

    a.进入下载网址
        https://nexus.hyperledger.org/content/repositories/releases/org/hyperledger/fabric/hyperledger-fabric/linux-amd64-1.0.0/
        
    b.下载hyperledger-fabric二进制bin
        https://nexus.hyperledger.org/content/repositories/releases/org/hyperledger/fabric/hyperledger-fabric/linux-amd64-1.0.0/hyperledger-fabric-linux-amd64-1.0.0.tar.gz
        
    c.下载完成后,解压文件到以下目录,会有一个/bin目录出来,包含cryptogen、configtxgen、configtxlator以及peer
        /opt/gopath/src/github.com/hyperledger/fabric-samples

3.4运行e2e_cli项目

    a.进入e2e_cli目录
        cd /opt/gopath/src/github.com/hyperledger/fabric/examples/e2e_cli
    
    b.文件修改成可执行
        chmod 775 ./network_setup.sh
        
    c.测试环境开启
        bash network_setup.sh up
        如果环境正常最后会出现END_E2E
        
    d.测试网络关闭
        bash network_setup.sh down

4. fabric-samples --基础(也即3.1)

4.1 git clone https://github.com/hyperledger/fabrci-samples.git
cd fabric-samples/basic-network
docker-compose f docker-compose.yml up -d
result: 
    Creating orderer.example.com ... done
    Creating peer0.org1.example.com ... done
    Creating couchdb ... 
    Creating orderer.example.com ... 
    Creating cli ... 
    Creating peer0.org1.example.com ... 
#docker ps  ---> for check

# 切换管理员用户,进入peer节点服务 peer0.org1.example.com 管理员才可以进行创建通道
docker exec -it -e "CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp/users/Admin@org1.example.com/msp" peer0.org1.example.com bash
# 创建通道
peer channel create -o orderer.example.com:7050 -c mychannel -f /etc/hyperledger/configtx/channel.tx 
# 加入通道
peer channel  join -b mychannel.block 
# 退出peer节点容器peer0.org1.example.com
exit
#进入cli
docker exec -it cli /bin/bash
#给peer节点容器peer0.org1.example.com安装链码(智能合约),注意其中有两种方式go/node
peer chaincode install -n mycc -v v0 -p github.com/chaincode_example02/go/
#实例化链码
peer chaincode instantiate -o orderer.example.com:7050 -C mychannel -n mycc -v v0 -c '{"Args":["init","a","100","b","200"]}'
#链码调用和查询
peer chaincode query -C mychannel -n mycc -v v0 -c '{"Args":["query","a"]}'

5. 基于 /fabric/examples/e2e_cli/架构搭建 4个peer 1个order 的多服务器架构 --深入

目标机器:
    10.0.200.111    orderer.lychee.com
    10.0.200.113    peer0.org1.lychee.com
    10.0.200.114    peer1.org1.lychee.com
    10.0.200.115    peer0.org2.lychee.com
    10.0.200.116    peer1.org2.lychee.com

5.1 工具了解(会自动编译)

    configtxlator   配置文件生成工具
    cryptogen   证书生成工具

5.2 创建证书与配置文件 文件配置需要格式严格设置

    # 参数通道名称,涉及文件:crypto-config.yaml实体证书信息, configtx.yaml创始区块配置文件,docker-compose-e2e.yaml 证书服务器docker配置,域、节点数等修改时,需要同时修改这几个文件。
    ./generateArtifacts.sh lycheechannel
5.2.1 generateArtifacts.sh 脚本文件说明
        generateCerts 证书生成
        replacePrivateKey 私钥替换
        generateChannelArtifacts 创始区块配置文件生成

        cryptogen generate --config=./crypto-config.yaml #根据这个文件可以为组织和其中的成员生成数字证书和签名密钥,生成的文件都保存到crypto-config文件夹

        export FABRIC_CFG_PATH=$PWD
        configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/genesis.block   #生成排序服务创世区块
        #TwoOrgsOrdererGenesis为configtx.yaml中的profiles之一。
        #./channel-artifacts/genesis.block为生成的创世块的文件名及保存位置。
        
        configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID mychannel  #生成通道配置创世区块
        #mychannel为通道名称
        #TwoOrgsChannel为configtx.yaml中的profiles之一。
        
        configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID mychannel -asOrg Org1MSP  #生成组织锚节点,锚节点负责代表组织与其他组织中的节点进行 Gossip 通信
        configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID mychannel -asOrg Org2MSP
        其中Org1MSP,Org2MSP为组织名称,在configtx.yaml中有设置。
        最终,我们在channel-artifacts文件夹中,应该是能够看到4个文件。
        channel-artifacts/ 
        ├── channel.tx 
        ├── genesis.block 
        ├── Org1MSPanchors.tx 
        └── Org2MSPanchors.tx
5.2.2 crypto-config.yaml 证书文件说明
MSP证书是超级账本网络实体的身份标识,实体在通信和交易时使用证书进行签名和验证。生产证书需要crypto-config.yaml文件。定义所有的组织,OrdererOrgs排序服务组织、PeerOrgs节点组织;主要是包括服务所在域名、名称、包含的节点数等,详见文件;
5.2.3 configtx.yaml 创始区块配置文件
生成创世区块依赖文件configtx.yaml。configtx.yaml这个文件里面配置了由2个Org参与的Orderer共识配置TwoOrgsOrdererGenesis,以及由2个Org 参与的Channel配置:TwoOrgsChannel。Orderer可以设置共识的算法是Solo还是Kafka,以及共识时区块大小,超时时间 等,我们使用默认值即可,不用更改。而Peer节点的配置包含了MSP的配置,锚节点的配置。如果我们有更多的Org,或者有更多的Channel,那么 就可以根据模板进行对应的修改。
5.2.4 docker-compose-e2e.yaml 文件说明
此文件为docker-compose创建启动docker的配置文件;定义了两个ca证书服务容器;关于docker启动配置参数说明,见docker-compose配置文件说明.txt。在多机部署的时候,不再需要此文件;单机部署的时候可以直接用这个文件编译:
    ./network_setup.sh up 
关于network_setup.sh脚本,up即启动容器,down则使用docker ps 枚举后在用docker rm。5台节点的启动看后续配置文件说明。  

5.3 docker-compose常用命令

  docker-compose up ,这两个容器都是在前台运行的。我们可以指定-d命令以daemon的方式启动容器。除此之外,docker-compose还支持下面参数:
        --verbose :输出详细信息
        -f 制定一个非docker-compose.yml命名的yaml文件
        -p 设置一个项目名称(默认是directory名)
    docker-compose的动作包括:
        build :构建服务
        kill -s SIGINT :给服务发送特定的信号。
        logs :输出日志
        port :输出绑定的端口
        ps :输出运行的容器
        pull :pull服务的image
        rm :删除停止的容器
        run : 运行某个服务,例如docker-compose run web python manage.py shell
        start :运行某个服务中存在的容器。
        stop :停止某个服务中存在的容器。
        up :create + run + attach容器到服务。
        scale :设置服务运行的容器数量。例如:docker-compose scale web=2 worker=3   

5.4 分别配置docker-compose.yaml文件

5.4.1 配置orderer节点yaml
    cp docker-compose-cli.yaml docker-compose-orderer.yaml
    #orderer服务器上我们只需要保留order设置,其他peer和cli设置都可以删除。orderer可以不设置extra_hosts。注意域、名称等一致性。详见实例文件
5.4.2 配置peer节点的yaml
    cp docker-compose-cli.yaml docker-compose-peer.yaml
    #修改docker-compose-peer.yaml,去掉orderer的配置,只保留一个peer和cli,因为我们要多级部署,节点与节点之前又是通过主机名通讯,所以需要修改容器中的host文件,也就是extra_hosts设置,修改后的peer配置如下:
        peer0.org1.example.com:
            container_name: peer0.org1.example.com
            extends:
                    file:  base/docker-compose-base.yaml
                    service: peer0.org1.example.com
            extra_hosts:
                - "orderer.example.com:10.174.13.185" # host:ip 在peer容器的/etc/hosts增加记录。             
    cli也需要能够和各个节点通讯,所以cli下面也需要添加extra_hosts设置,去掉无效的依赖,否则容易启动失败,启动顺序非常重要,并且去掉command这一行(调用scripts.sh命令,这个是创建通道、部署链码测试脚本),因为我们是每个peer都会有个对应的客户端,也就是cli,所以我只需要去手动执行一次命令,而不是自动运行(创建通道等只需要运行一次)。主要定义了一些环境变量、卷轴挂载、网络说明等。
    注意路径映射,否则容易找不到文件。其他peer节点复制后将名称、地址修改即可。
5.4.3 docker-compose-base.yaml 文件说明
    所有peer和orderer都依赖这个文件,这个是基础的extends扩展;主要定义一些环境变量msp和tls位置、msp和tls卷轴映射、端口映射、gossip地址等。这个文件也要修改,因为是多机部署,所以端口映射可以改为一样。
5.5 多节点分发配置
每个机器都要求安卓docker等工具,所以建议在一个环境配置好后,保存镜像,然后再用此镜像创建其他节点。
分发后,需要修改其他peer节点的docker-compose-peer.yaml文件,主要是修改名称和依赖。

5.6 启动Fabric

5.6.1 启动orderer节点
    docker-compose -f docker-compose-orderer.yaml up -d
    #启动后,可以用docker ps 查看启动的容器,会有一个为orderer.lychee.com的节点
5.6.2 启动peer节点
    docker-compose -f docker-compose-peer.yaml up –d
    #启动后,可以看到两个容器,peer0.org1.lychee.com和lycheecli两个容器。
    #登陆其他节点,同样的命令启动。

5.7 创建通道和部署、实例化、测试链码

5.7.1 创建channel测试chaincode
    选择任意一个peer,执行一下命令:
    docker exec -it lycheecli bash  #切入到lycheecli容器中;结果类似:
    root@b41e67d40583:/opt/gopath/src/github.com/hyperledger/fabric/peer#
        
    ./scripts/script.sh mychannel   #创建名称为mychannel的通道并测试链码
5.7.2 script.sh脚本文件说明(摘取部分命令说明,详见配置文件)
    #在orderer上创建一个channel,channel配置文件为./channel-artifacts/channel.tx
    peer channel create -o orderer.lychee.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx >&log.txt 
        
    #更新通道上锚节点信息
    peer channel update -o orderer.lychee.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/${CORE_PEER_LOCALMSPID}anchors.tx >&log.txt
        
    #加入通道,尝试5次
    peer channel join -b $CHANNEL_NAME.block  >&log.txt
        
    #安装链码,先要配置环境
    peer chaincode install -n mycc -v 1.0 -p github.com/hyperledger/fabric/lychees/chaincode/go/chaincode_lychee02 >&log.txt

    #实例化链码,先要配置环境
    peer chaincode instantiate -o orderer.lychee.com:7050 --tls $CORE_PEER_TLS_ENABLED --cafile $ORDERER_CA -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a","100","b","200"]}' -P "OR       ('Org1MSP.member','Org2MSP.member')" >&log.txt

    #链码调用,查询
    peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' >&log.txt

    #链码调用,invoke
    peer chaincode invoke -o orderer.lychee.com:7050 -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}' >&log.txt

5.8 结果

    ===================== All GOOD, End-2-End execution completed ===================== 
    出现以上内容,则这简单的5个节点的多机部署就OK了

参考文章
http://www.cnblogs.com/studyzy/p/7237287.html #Fabric 1.0 多机部署
https://blog.csdn.net/liguangxianbin/article/details/79492866 #dokcer-compose 命令及其配置文件解说
https://blog.csdn.net/yulei_qq/article/details/52987333 #dokcer-compose 命令及其配置文件解说
https://blog.csdn.net/binbinxyz/article/details/79491254 #增加zookeeper集群和kafka集群
http://www.cnblogs.com/aberic/p/8289257.html #增加zookeeper集群和kafka集群
https://www.cnblogs.com/aberic/p/7527831.html
https://www.ibm.com/developerworks/cn/java/j-chaincode-for-java-developers/index.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容