目录
- 一、准备环境
- 1、购买阿里云服务器
- 2、搭建java环境
- 3、搭建Maven环境
- 4、安装配置Tomcat
- 5、安装配置zookeeper
- 6、zookeeper的可视化工具
- 7、安装配置dubbo-admin
- 8、总结环境搭建所遇问题
- 二、在idea上实现一个简单分布式服务demo
- 1、创建父maven工程
- 2、创建服务接口 dubbo-api
- 3、创建服务提供者dubbo-provider
- 4、创建服务消费者 dubbo-consumer
- 5、测试
一、准备环境
话说看代码不如写代码,写完代码也要跑起来看看效果才行,不然搭建了demo,没有环境运行效果,也是没有太好的体会。所以首先需要准备环境才行。 搭建的是dubbo框架的分布式服务demo,以下是我的环境准备。
环境准备:1、一台服务器 2、服务器上搭建java环境,安装maven、tomcat、zookeeper、dubbo-admin。
虽然搭建的是简单的分布式服务,但是成功地在服务器上跑起来还是花了一些时间,主要是在搭建环境中遇到了一些坑,为了让其他同学能够快速地搭建一个简单的分布式服务,我将自己搭建的每一个步骤都详细地书写了,方便大家自己搭建。
1、购买阿里云服务器
首先需要一个云服务器,没有云服务器的话也可以在自己电脑上使用vmware,用户可在单一的桌面上同时运行不同的操作系统,进行开发。但是这样做太费内存了,电脑卡的不行,所以我还是选择服务器。
网上有许多的服务器,耳熟能详的就是阿里云和腾讯云,同学们可以自己去官网对比一下,从价格、性能、活动优惠内容这些自己选择。下面是我购买阿里云服务器一些流程,对于还没有用过服务器的同学,我觉得还是有帮助的。
(1)登录阿里云官网(https://www.aliyun.com/),如果没有注册阿里云会员的,可以先注册一个。选择云服务器ECS。为什么叫ECS服务器,大家可以去问问度娘,这里就不解释了。
这里就注意了,阿里云网页是有活动的,所以不要点立即购买,点击下面的活动。(土豪请无视)
进入活动页面,找到适合自己的,当前活动我选择的是下面红框中的。有同学可能想问为什么不选突发性能型t5,我对比了一下,t5是由性能限制的,具体的自己对比下。点击立即购买。
购买后出现下面的页面,这时候需要选择地域,有些同学会奇怪为什么没有我所在的地域,是不是不能用,并不是这样的。这些地域是布置有服务器的地域,你选择离你所在地域比较近的就行了。而且实例必须你是有的,没有实例的是灰色的。带宽可以自己选择。最后点击立即购买。
购买后,进入阿里云主页,选择ECS云服务器——管理控制台——实例——更多,重置实例密码,重置后要重启服务器才会生效。
使用Xshell或者MobaXterm进入服务器中,服务器的ip就是公网显示的ip。为什么用公网ip?服务器公网ip可以用于域名解析ip,服务器远程登录ip,是最主要的服务器ip地址。私网ip不能用于域名解析。不可以直接用于服务器远程登录,其主要作用是:跟当前帐号下的其他同集群的机器通信。
2、搭建java环境
服务器有了,就可以开始搭建环境。
(1)查看当前linux操作系统版本
cat /etc/redhat-release 是系统安装时默认的发行版本。
[root@iZbp13xqpgsk8aqlm2wr1zZ ~]# cat /etc/redhat-release
CentOS Linux release 8.1.1911 (Core)
cat /proc/version 和 uname -a 显示的内容相同,显示linux内核版本号(开源版本)
[root@iZbp13xqpgsk8aqlm2wr1zZ ~]# cat /proc/version
Linux version 4.18.0-147.5.1.el8_1.x86_64 (mockbuild@kbuilder.bsys.centos.org) (gcc version 8.3.1 20190507 (Red Hat 8.3.1-4) (GCC)) #1 SMP Wed Feb 5 02:00:39 UTC 2020
[root@iZbp13xqpgsk8aqlm2wr1zZ ~]# uname -a
Linux iZbp13xqpgsk8aqlm2wr1zZ 4.18.0-147.5.1.el8_1.x86_64 #1 SMP Wed Feb 5 02:00:39 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
查看当前Linux版本是多少位
[root@iZbp13xqpgsk8aqlm2wr1zZ ~]# getconf LONG_BIT
64
(2)安装之前先查看一下有无系统自带jdk
rpm -qa |grep java
rpm -qa |grep jdk
rpm -qa |grep gcj
没有信息输出就是无jdk,如果有就使用批量卸载命令
rpm -qa | grep java | xargs rpm -e --nodeps
(3)下载安装JDK1.8(有两种办法)
1)第一种办法是在Oracle官网https://www.oracle.com/java/technologies/javase-jdk8-downloads.html下载jdk1.8包
将jdk包传到服务器中任意位置,在/usr/lib/下创建jvm文件夹(可以自己命名文件夹名字及位置)
解压jdk包至/usr/lib/jvm/下,使用命令
tar -zxvf jdk-8u171-linux-x64.tar.gz -C /usr/lib/jvm
配置环境变量,进入/etc/profile
vim /etc/profile
进入文本编辑,用按键"shift+g"定位到文件尾,点击’i’ 进入编辑,将下面命令写至文件末尾。
export JAVA_HOME=/usr/lib/jvm/jdk1.8.0_171 (根据自己的完整路径修改)
export PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/jre/lib
让环境变量生效
source /etc/profile
然后执行以下命令验证是否安装成功
java -version
2)在linux通过yum命令直接下载安装jdk1.8包。这样下载特别快。
首先检索包含java的列表
yum list java*
再yum安装1.8.0版本openjdk
yum install java-1.8.0-openjdk* -y
以下是安装好的状态。
查看版本
java -version
openjdk version "1.8.0_242"
OpenJDK Runtime Environment (build 1.8.0_242-b08)
OpenJDK 64-Bit Server VM (build 25.242-b08, mixed mode)
设置环境变量
默认jre jdk 安装路径是/usr/lib/jvm 下面
JAVA_HOME指向一个含有java可执行程序的目录,直接用export命令将 JAVA_HOME 指向这个链接.jre-1.8.0-openjdk-1.8.0.242.b08-0.el8_1.x86_64。
使所有用户生效的配置
vim /etc/profile
#set java environment
export JAVA_HOME=/usr/lib/jvm/jre-1.8.0-openjdk-1.8.0.242.b08-0.el8_1.x86_64
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/jre/lib/rt.jar
export PATH=$PATH:$JAVA_HOME/bin
使得配置生效,查看变量
[root@iZbp13xqpgsk8aqlm2wr1zZ jvm]# source /etc/profile
[root@iZbp13xqpgsk8aqlm2wr1zZ jvm]# echo $CLASSPATH
.:/usr/lib/jvm/jre-1.8.0-openjdk-1.8.0.242.b08-0.el8_1.x86_64/lib/dt.jar:/usr/lib/jvm/jre-1.8.0-openjdk-1.8.0.242.b08-0.el8_1.x86_64/lib/tools.jar:/usr/lib/jvm/jre-1.8.0-openjdk-1.8.0.242.b08-0.el8_1.x86_64/jre/lib/rt.jar
[root@iZbp13xqpgsk8aqlm2wr1zZ jvm]# java -version
openjdk version "1.8.0_242"
OpenJDK Runtime Environment (build 1.8.0_242-b08)
OpenJDK 64-Bit Server VM (build 25.242-b08, mixed mode)
java环境搭建成功。
3、搭建Maven环境
1)下载maven
下载地址:https://mirrors.cnnic.cn/apache/maven/maven-3/
选择版本以后再选择红框中的,不要问为什么,自己可以去试试…
2)安装maven以及配置环境变量
在liunx上解压maven的tar包
tar xf apache-maven-3.3.9-bin.tar.gz -C /usr/local/
配置环境变量
vim /etc/profile
在/etc/profile文件内容最后添加以下内容
#set maven3.6.3 enviroment
export MAVEN_HOME=/usr/local/apache-maven-3.3.9
export PATH=${PATH}:${MAVEN_HOME}/bin
刷新配置文件
source /etc/profile
验证maven
[root@iZbp13xqpgsk8aqlm2wr1zZ apache-maven-3.3.9]# mvn -version
Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00)
Maven home: /usr/local/apache-maven-3.3.9
Java version: 1.8.0_242, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-0.el8_1.x86_64/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.18.0-147.5.1.el8_1.x86_64", arch: "amd64", family: "unix"
注意:在选择版本的时候,我开始下载的是3.6.3版本,结果一顿操作下来,没有mvn命令,反复看哪里没操作对,结果没发现。不甘心的我看了网上用3.3.9版本的多,最后换了版本就好了,蛋蛋的忧伤。。。。。。
4、安装配置Tomcat
1)下载Tomcat(其实就和maven的前两步一样操作)
tomcat下载连接:https://tomcat.apache.org/
2)安装tomcat以及配置环境变量
在liunx上解压tomcat的tar包
tar xf apache-tomcat-8.5.51.tar.gz -C /usr/local/
配置环境变量
vim /etc/profile
在/etc/profile文件内容最后添加以下内容
#set tomcat
export TOMCAT_HOME=/usr/local/apache-tomcat-8.5.51
export PATH=${PATH}:${TOMCAT_HOME}/bin
刷新配置文件
source /etc/profile
3)启动tomcat并验证页面
#查看验证tomcat版本
[root@iZbp13xqpgsk8aqlm2wr1zZ apache-tomcat-8.5.51]# cd /usr/local/apache-tomcat-8.5.51
[root@iZbp13xqpgsk8aqlm2wr1zZ apache-tomcat-8.5.51]# cd bin/
[root@iZbp13xqpgsk8aqlm2wr1zZ bin]# ./version.sh
Using CATALINA_BASE: /usr/local/apache-tomcat-8.5.51
Using CATALINA_HOME: /usr/local/apache-tomcat-8.5.51
Using CATALINA_TMPDIR: /usr/local/apache-tomcat-8.5.51/temp
Using JRE_HOME: /usr/lib/jvm/jre-1.8.0-openjdk-1.8.0.242.b08-0.el8_1.x86_64
Using CLASSPATH: /usr/local/apache-tomcat-8.5.51/bin/bootstrap.jar:/usr/local/apache-tomcat-8.5.51/bin/tomcat-juli.jar
Server version: Apache Tomcat/8.5.51
Server built: Feb 5 2020 22:26:25 UTC
Server number: 8.5.51.0
OS Name: Linux
OS Version: 4.18.0-147.5.1.el8_1.x86_64
Architecture: amd64
JVM Version: 1.8.0_242-b08
JVM Vendor: Oracle Corporation
#启动tomcat
[root@iZbp13xqpgsk8aqlm2wr1zZ bin]# ./startup.sh
Using CATALINA_BASE: /usr/local/apache-tomcat-8.5.51
Using CATALINA_HOME: /usr/local/apache-tomcat-8.5.51
Using CATALINA_TMPDIR: /usr/local/apache-tomcat-8.5.51/temp
Using JRE_HOME: /usr/lib/jvm/jre-1.8.0-openjdk-1.8.0.242.b08-0.el8_1.x86_64
Using CLASSPATH: /usr/local/apache-tomcat-8.5.51/bin/bootstrap.jar:/usr/local/apache-tomcat-8.5.51/bin/tomcat-juli.jar
Tomcat started.
网页查看:http://服务器IP:8080/
5、安装配置zookeeper
1)下载安装zookeeper,我们移步到国内镜像下载呢。
镜像地址1: http://apache.fayea.com/zookeeper/
镜像地址2: http://mirrors.hust.edu.cn/apache/zookeeper/
以上可以随机选一个下载地址 然后下载zookeeper(我下载zookeeper-3.4.14.tar.gz版本),再放入服务器中。
2)解压zookeeper的包并进行配置(可以自己选择位置,看自己)。
切换到zookeeper目录下的conf目录下,重新复制一份 zoo_sample.cfg文件并命名为zoo.cfg(只能是这个名字)
tar xf zookeeper-3.4.14.tar.gz -C /usr/local/
mv /usr/local/zookeeper-3.4.14/ /usr/local/zookeeper
cd /usr/local/zookeeper
cd conf
cp zoo_sample.cfg zoo.cfg
对zoo.cfg进行配置。这是最重要的配置,zookeeper连接配置都是在这里配置的。
vi zoo.cfg
配置dataDir ZK启动目录配置dataLogDir 日志目录
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/usr/local/zookeeper/data
dataLogDir=/usr/local/zookeeper/log
# the port at which the clients will connect
clientPort=2181
server.1=ip地址:2888:3888
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
注意:
1、更改成自己服务器的IP,具体几个server可根据需要添加,由于服务器性能原因,我这里只保留一个,就是单机模式
2、上面的dataDir和dataLogDir文件,先手动去确认一下是否存在,没有就手动mkdir一个。
3、没有myid文件,需要在上图的dataDir路径下生成一个myid文件,同时写入上面的server.X中 的 X,集群模式下除了多个zookeeper外,在myid文件中也需要添加server.X中的X。直接vi myid,在里面写入1。
Server.1=Master:3333:4444 就在myid中写个1
Server.1=Master:3333:4444 Server.2=slave1:3333:4444 就写1 2
配置环境变量
vi /etc/profile
在/etc/profile文件内容最后添加以下内容
#set zookeeper environment
export ZOOKEEPER_HOME=/usr/local/zookeeper
export PATH=$PATH:$ZOOKEEPER/bin
刷新配置文件
source /etc/profile
启动zookeeper 任务目录下执行以下命令
[root@iZbp13xqpgsk8aqlm2wr1zZ bin]# ./zkServer.sh start 启动zookeeper服务端
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@iZbp13xqpgsk8aqlm2wr1zZ bin]# ./zkServer.sh status 查看服务端运行状态
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Mode: standalone 单机模式
[root@iZbp13xqpgsk8aqlm2wr1zZ bin]# ./zkServer.sh stop 停止zookeeper服务端
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Stopping zookeeper ... STOPPED
连接zookeeper
./zkCli.sh -server localhost:2181
./zkCli.sh start -server ip:端口号
出现以下界面说明连接zookeeper成功。
6、zookeeper的可视化工具
zkui是基于java语言开发,使用zkui来搭建zookeeper的可视化web界面,下载zkui工具
下载地址:https://github.com/DeemOpen/zkui.git
在idea中修改config.cfg,zh,主要修改port和zkserver。
再package打包,生成两个jar包:
zkui-2.0-SNAPSHOT.jar和zkui-2.0-SNAPSHOT-jar-with-dependencies.jar,用的是第二个,将第二个上传至服务器,并且将config.cfg文件也放到和jar包相同的目录下。
执行jar包
java -jar zkui-2.0-SNAPSHOT-jar-with-dependencies.jar
查看网页,浏览器中运行http://自己的ip:9090,账号密码 admin/manager
7、安装配置dubbo-admin
为了让使用户更好的管理或监控众多的dubbo服务,dubbo官方提供了可视化的监控程序——Dubbo Admin。不过不安装监控中心也不会影响dubbo的使用。
看了网上好多搭建dubbo admin的,好多方法还是把war包部署到tomcat中。现在不用生成war包,不用tomcat。在idea上生成jar包后,直接在linux上运行即可。
(1)使用dubbo-admin jar包
1)下载dubbo admin
dubbo admin 官方地址 https://github.com/apache/incubator-dubbo-ops
2)在idea中导入dubbo admin源码进行配置,生成jar包
在src/main/resources目录,修改application.properties中的配置(默认使用zookeeper作为注册中心,一般不需要改动,默认配置即可使用)
#application.properties默认配置:
server.port=7002 端口号
spring.velocity.cache=false
spring.velocity.charset=UTF-8
spring.velocity.layout-url=/templates/default.vm
spring.messages.fallback-to-system-locale=false
spring.messages.basename=i18n/message
spring.root.password=root root用户的密码
spring.guest.password=guest guest用户的密码
dubbo.registry.address=zookeeper://127.0.0.1:2181 注册中心ip和端口
#我的zookeeper和dubbo-admin部署在阿里云服务器,所以地址默认服务器的ip就可以。
在idea上先clean项目,然后package打包,生成的jar包是放在源码文件夹下面的。
将jar包放入服务器中,执行jar包
java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
4)在网页上登录dubbo admin控制台
(2)使用dubbo-admin war包,这里就不做详细介绍了。
8、总结环境搭建所遇问题
1、因为使用的是阿里云服务器,所以必须要在阿里云服务器添加安全组规则。这是为了能够使用端口访问,不然访问不了的,不信的话你可以试试。这里就不细说了,可以看:
部署tomcat、zookeeper这些会涉及到端口、防火墙的时候。可以使用systemctl工具来管理服务程序,它包括了service和chkconfig。
[root@iZbp13xqpgsk8aqlm2wr1zZ bin]# systemctl list-unit-files|grep enabled
aliyun.service enabled
atd.service enabled
auditd.service enabled
autovt@.service enabled
chronyd.service enabled
cloud-config.service enabled
cloud-final.service enabled
cloud-init-local.service enabled
cloud-init.service enabled
crond.service enabled
dbus-org.freedesktop.NetworkManager.service enabled
dbus-org.freedesktop.nm-dispatcher.service enabled
dbus-org.freedesktop.resolve1.service enabled
dbus-org.freedesktop.timedate1.service enabled
PS:一些systemctl命令:
启动一个服务:systemctl start firewalld.service
关闭一个服务:systemctl stop firewalld.service
重启一个服务:systemctl restart firewalld.service
显示一个服务的状态:systemctl status firewalld.service
在开机时启用一个服务:systemctl enable firewalld.service
在开机时禁用一个服务:systemctl disable firewalld.service
查看服务是否开机启动:systemctl is-enabled firewalld.service;echo $?
查看已启动的服务列表:systemctl list-unit-files|grep enabled
对于端口开放,还可以添加INPUT链,INPUT链的默认规则是DROP,所以我们就写需要ACCETP(通过)的链,为了能采用远程SSH登陆,我们要开启22和8080端口。
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 22 -j ACCEPT
二、在idea上实现一个简单分布式服务demo
1、创建父maven工程
在idea中创建一个maven项目,单击file——new——project,这里不要选择Create from archetype复选框。
命名dubboDemo,用来存放后面创建的dubbo-api,dubbo-provider,dubbo-consumer文件。
finish生成maven项目
2、创建服务接口 dubbo-api
(1)在idea中,再次创建一个Module的maven项目,单击file——new——Module。
(2)创建HelloService接口类
(3)使用install 成jar包,便于其它项目使用
3、创建服务提供者dubbo-provider
(1)创建一个Module的spring项目,单击file——new——Module。选择Spring Initializr
(2)在dubbo-provider中的pom.xml添加dubbo、zookeeper、接口依赖
<!--引入dubbo-->
<dependency>
<groupId>com.alibaba.spring.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!--引入zookeeper-->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
<!--引入接口-->
<dependency>
<groupId>com.lxy</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
(3)实现dubbo-provider接口,注意提供者必须添加Component和Service的注解。Component是Spring bean注解,负责将bean注入到Spring容器中,Service是dubbo的注解(不要和spring bean的service注解弄混),负责暴露服务。
(4)在dubbo-provider中的resources 目录下application.properties 配置的dubbo信息
server.port=8090
#dubbo服务名
spring.dubbo.application.name=dubbo-provider
#注册中心
spring.dubbo.registry.address=zookeeper://zookeeper注册的ip:2181
#dubbo的RPC协议
spring.dubbo.protocol.name=dubbo
#dubbo的RPC端口
spring.dubbo.protocol.port=20880
#远程服务超时时间
spring.dubbo.provider.timeout=5000
#发布服务扫描的路径
spring.dubbo.base-package=com.helloDubbo.dubboprovider.service.impl
(5)服务提供者启动类,添加EnableDubboConfiguration注解。@EnableDubboConfiguration, 表示要开启dubbo功能.
4、创建服务消费者 dubbo-consumer
(1)创建dubbo-consumer的springboot项目 ,与创建dubbo-provider项目一样。就不多做介绍了。
(2)在dubbo-consumer 添加下面代码框中的依赖(细心的同学会发现和provider的一样)
<!--引入dubbo-->
<dependency>
<groupId>com.alibaba.spring.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!--引入zookeeper-->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
<!--引入接口-->
<dependency>
<groupId>com.lxy</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
(3)编写controller调用远程服务,Reference注解引用配置,用于创建一个远程服务代理,一个引用可以指向多个注册中心。在需要调用服务的类(Controller) 中使用 @Reference 注解来创建 Bean。
注意:在服务启动的时候,dubbo注解的扫描必须要先于spring的注解,如果不做处理的话,先扫描controller注解,再扫描reference会造成项目启动没问题,但是在实现功能的时候会报service空指针。因为这个reference没有被注入到controller中去。
(4)同样在dubbo-provider中的resources 目录下application.properties 配置的dubbo信息
#防止与zookeeper端口号重复
server.port=8091
#dubbo服务名
spring.dubbo.application.name=dubbo-consumer
#注册中中心
spring.dubbo.registry.address=zookeeper://116.62.180.224:2181
#dubbo的RPC协议
spring.dubbo.protocol.name=dubbo
#dubbo的RPC端口
spring.dubbo.protocol.port=20880
#远程服务超时时间
spring.dubbo.provider.timeout=5000
#发布服务扫描的路径
spring.dubbo.base-package=com.helloDubbo.dubboconsumer.controller
(6)服务消费者启动类,添加EnableDubboConfiguration注解。
5、测试
主要在dubbo-admin的平台查看是否有服务。也可以在zookeeper的可视化界面或者节点上查看。
(1)首先启动dubbo-admin的jar包
(2)启动dubbo-provider
(2)在浏览器中输入dubbo-admin的网址http://自己设置的ip:7002/。不清楚的可以看看上面dubbo-admin安装时的配置。在首页上可以看出已成功注册provider服务。
(3)启动dubbo-consumer,选择comsumers查看是否有消费者
(4)最终测试,在浏览器中输入localhost:8091/hello(8091是自己配置的消费者的端口号),若在屏幕显示Hello world,表示成功,控制台同样输出Hello lxy
控制台输出
大功告成!