如何启动ZooKeeper

周明耀

2017-09-20


启动ZooKeeper

ZooKeeper服务的启动方式分为三种,即单机模式、伪分布式模式、分布式模式,这里针对三种模式均做逐一讲解。

Tips

调试过程建议尽量使用分布式模式,单机模式不推荐在生产环境下使用,伪分布式模式实质上是在一个进程内派生多个线程模拟分布式形态,由于操作系统的内部结构设计,容易造成一些问题,建议与其解决问题不如切换到分布式模式。生产环境下建议一定采用分布式模式,如果机器不够,推荐采用虚拟机方式。

(1). Module1 单机模式

采用单机模式,意味着只有一台机器或者一个节点,因此流程较为简单。首先,在conf目录下面可以通过自己创建zoo.cfg文件的方式完成ZooKeeper的配置,如清单1-7所示,ZooKeeper服务会读取该配置文件,具体的读取代码会在第四章介绍。

注意,ZooKeeper自带了zoo_sample.cfg文件,这个是配置文件的模板文件,可以打开看看具体的内容,也可以作为zoo.cfg的创建内容范例。

清单1-7 ZooKeeper配置文件

[root@localhost zookeeper-3.4.7]# cd conf
[root@localhost conf]# ls -rlt
total 12
-rw-rw-r--. 1 1000 1000  922 Nov 1022:32 zoo_sample.cfg
-rw-rw-r--. 1 1000 1000 2161 Nov 10 22:32 log4j.properties
-rw-rw-r--. 1 1000 1000  535 Nov 1022:32 configuration.xsl
[root@localhost conf]# cat zoo_sample.cfg
# 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=/tmp/zookeeper
# the port at which the clients will connect
clientPort=2181
# 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

上面是自带的示例配置,与我们相关的三个配置项是tickTime、dataDir和clientPort。

tickTime:这个参数主要是用来针对ZooKeeper服务端和客户端的会话控制,包括心跳控制,一般来说,会话超时时间是该值的两倍,它的单位是毫秒,我们设置为2000毫秒。

dataDir:这个目录用来存放数据库的镜像和操作数据库的日志。注意,如果这个文件夹不存在,需要手动创建一个并赋予读写权限,我们设置为/tmp/zookeeper,不用手动创建这个文件夹,系统运行后会自动创建或覆盖。

clientPort:ZooKeeper服务端监听客户端的端口,默认是2181,这里沿用默认设置。

接下来通过bin目录下面的zkServer.sh脚本启动ZooKeeper服务,如果不清楚具体参数,可以直接调用脚本查看输出,如清单1-8所示。

Tips

ZooKeeper采用的是Bourne Shell。Shell基本上是一个命令解释器,类似于DOS下的command。它接收用户命令(如ls等),然后调用相应的应用程序。较为通用的shell有标准的Bourne shell、C shell和Korn shell。

清单1-8 调用zkServer.sh脚本

[root@localhost bin]# ./zkServer.sh
ZooKeeper JMX enabled by default
Using config: /home/zhoumingyao/zookeeper/zookeeper-3.4.7/bin/../conf/zoo.cfg
Usage: ./zkServer.sh{start|start-foreground|stop|restart|status|upgrade|print-cmd}

输出中可以看到有start等选项,具体每个选项的意义我们放在第四章解释,这里先启动ZooKeeper服务,如清单1-9所示。

清单1-9 启动ZooKeeper

[root@localhost bin]# ./zkServer.sh start
ZooKeeper JMX enabled by default
Using config:/home/zhoumingyao/zookeeper/zookeeper-3.4.7/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED

ZooKeeper服务是否启动成功,可以通过ps或者jps命令查看,如清单1-10所示。

清单1-10 查看ZooKeeper服务

[root@localhost bin]# jps
2737 QuorumPeerMain
2751 Jps
[root@localhost bin]# ps -ef | grep zookeeper | grep -v grep | awk '{print$2}'
2608

这里我们看到的进程号为2737的进程QuorumPeerMain代表了ZooKeeper服务。我们也可以通过ZooKeeper启动脚本自带的参数“Status”来查看ZooKeeper进程状态,如清单1-11所示。

清单1-11 查看ZooKeeper进程状态

[root@localhost bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config:/home/zhoumingyao/zookeeper/zookeeper-3.4.7/bin/../conf/zoo.cfg
Mode: standalone

ZooKeeper服务运行以后我们可以通过命令行工具去访问它,默认是Java命令行脚本。我们可以通过以下命令方式启动ZooKeeper命令行Shell,运行输出如清单1-12所示。

清单1-12 ZKCli运行输出

[root@localhost bin]# ./zkCli.sh -server localhost:2181
Connecting to localhost:2181
2015-12-20 23:22:10,620 [myid:] - INFO [main:Environment@100] - Clientenvironment:zookeeper.version=3.4.7-1713338, built on 11/09/2015 04:32 GMT
2015-12-20 23:22:10,645 [myid:] - INFO [main:Environment@100] - Client environment:host.name=localhost
2015-12-20 23:22:10,645 [myid:] - INFO [main:Environment@100] - Client environment:java.version=1.8.0_51
2015-12-20 23:22:10,657 [myid:] - INFO [main:Environment@100] - Client environment:java.vendor=OracleCorporation
2015-12-20 23:22:10,658 [myid:] - INFO [main:Environment@100] - Clientenvironment:java.home=/usr/lib/jdk1.8.0_51/jre
2015-12-20 23:22:10,658 [myid:] - INFO [main:Environment@100] - Client environment:java.class.path=/home/zhoumingyao/zookeeper/zookeeper-3.4.7/bin/../build/classes:/home/zhoumingyao/zookeeper/zookeeper-3.4.7/bin/../build/lib/*.jar:/home/zhoumingyao/zookeeper/zookeeper-3.4.7/bin/../lib/slf4j-log4j12-1.6.1.jar:/home/zhoumingyao/zookeeper/zookeeper-3.4.7/bin/../lib/slf4j-api-1.6.1.jar:/home/zhoumingyao/zookeeper/zookeeper-3.4.7/bin/../lib/netty-3.7.0.Final.jar:/home/zhoumingyao/zookeeper/zookeeper-3.4.7/bin/../lib/log4j-1.2.16.jar:/home/zhoumingyao/zookeeper/zookeeper-3.4.7/bin/../lib/jline-0.9.94.jar:/home/zhoumingyao/zookeeper/zookeeper-3.4.7/bin/../zookeeper-3.4.7.jar:/home/zhoumingyao/zookeeper/zookeeper-3.4.7/bin/../src/java/lib/*.jar:/home/zhoumingyao/zookeeper/zookeeper-3.4.7/bin/../conf:
2015-12-20 23:22:10,660 [myid:] - INFO [main:Environment@100] - Clientenvironment:java.library.path=/usr/java/packages/lib/i386:/lib:/usr/lib
2015-12-20 23:22:10,665 [myid:] - INFO [main:Environment@100] - Client environment:java.io.tmpdir=/tmp
2015-12-20 23:22:10,665 [myid:] - INFO [main:Environment@100] - Client environment:java.compiler=<NA>
2015-12-20 23:22:10,666 [myid:] - INFO [main:Environment@100] - Client environment:os.name=Linux
2015-12-20 23:22:10,666 [myid:] - INFO [main:Environment@100] - Client environment:os.arch=i386
2015-12-20 23:22:10,667 [myid:] - INFO [main:Environment@100] - Clientenvironment:os.version=2.6.32-504.el6.i686
2015-12-20 23:22:10,668 [myid:] - INFO [main:Environment@100] - Client environment:user.name=root
2015-12-20 23:22:10,668 [myid:] - INFO [main:Environment@100] - Client environment:user.home=/root
2015-12-20 23:22:10,668 [myid:] - INFO [main:Environment@100] - Clientenvironment:user.dir=/home/zhoumingyao/zookeeper/zookeeper-3.4.7/bin
2015-12-20 23:22:10,693 [myid:] - INFO [main:ZooKeeper@438] - Initiating client connection,connectString=localhost:2181 sessionTimeout=30000watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@b07fd3
Welcome to ZooKeeper!
2015-12-20 23:22:10,953 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1032] - Openingsocket connection to server localhost/0:0:0:0:0:0:0:1:2181. Will not attempt toauthenticate using SASL (unknown error)
JLine support is enabled
2015-12-20 23:22:11,342 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@876] - Socketconnection established to localhost/0:0:0:0:0:0:0:1:2181, initiating session
2015-12-20 23:22:11,672 [myid:] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1299] - Sessionestablishment complete on server localhost/0:0:0:0:0:0:0:1:2181, sessionid =0x151c241c15b0000, negotiated timeout = 30000
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0]

光标停留在[zk: localhost:2181(CONNECTED) 0]这一行,我们可以通过help请求来查看所有的支持命令,如清单1-13所示。

清单1-13 ZKCli help命令

[zk: localhost:2181(CONNECTED) 0] help
ZooKeeper -server host:port cmd args
        stat path [watch]
        set path data [version]
        ls path [watch]
        delquota [-n|-b] path
        ls2 path [watch]
        setAcl path acl
        setquota -n|-b val path
        history
        redo cmdno
        printwatches on|off
        delete path [version]
        sync path
        listquota path
        rmr path
        get path [watch]
        create [-s] [-e] path dataacl
        addauth scheme auth
        quit
        getAcl path
        close
        connect host:port

关于ZKCli命令我们会在后续章节详细解释,这里只做一个简单的演示,我们可以使用类似于Linux的ls命令打印ZooKeeper内部ZNode列表,如清单1-14所示。
清单1-14 打印ZNode列表

[zk: localhost:2181(CONNECTED) 1] ls /
[zookeeper]

上面示例返回一个字符串zookeeper,这是一个ZooKeeper的ZNode(ZooKeeper术语),我们可以通过脚本创建一个ZNode,如清单1-15所示。

清单1-15 创建一个ZNode

[zk: localhost:2181(CONNECTED) 3] create /HelloWorld ""
Created /HelloWorld
[zk: localhost:2181(CONNECTED) 4] ls /
[zookeeper, HelloWorld]

(2). Module2 伪分布式模式

上面演示了如何启动单机模式,现在我们来演示设置伪分布式模式。

我们可以在一台机器上创建模拟的ZooKeeper集群服务,假如我们需要3个节点,需要创建3个cfg文件,分别命名为zoo1.cfg,zoo2.cfg,zoo3.cfg,此外我们还需要创建3个不同的数据文件夹,分别是zoo1,zoo2和zoo3,目录位于/var/lib/zookeeper,如1-16、1-17和1-18三个配置清单所示。

清单1-16 配置文件zoo1内容

[root@localhost conf]# cat zoo1.cfg
# 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=/var/lib/zookeeper/zoo1
# the port at which the clients will connect
clientPort=2181
# 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
server.1=localhost:2666:3666
server.2=localhost:2667:3667
server.3=localhost:2668:3668

清单1-17 配置文件zoo2内容

[root@localhost conf]# cat zoo2.cfg
# 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=dataDir=/var/lib/zookeeper/zoo2
# the port at which the clients will connect
clientPort=2182
# 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
server.1=localhost:2666:3666
server.2=localhost:2667:3667
server.3=localhost:2668:3668

清单1-18 配置文件zoo3内容

[root@localhost conf]# cat zoo3.cfg
# 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=/var/lib/zookeeper/zoo3
# the port at which the clients will connect
clientPort=2183
# 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
server.1=localhost:2666:3666
server.2=localhost:2667:3667
server.3=localhost:2668:3668

注意,每一个虚拟机器都对应一个自己的zoo{$}.cfg,其中的{$}需要通过清单1-19所示命令来进行设置。

Tips

由于采用的是“>”,所以命令会创建文件,如果采用“>>”,则Linux会在原有文件基础上新增内容。

清单1-19 设置myid

[root@localhost conf]# echo 1 > /var/lib/zookeeper/zoo1/myid
[root@localhost conf]# echo 2 > /var/lib/zookeeper/zoo2/myid
[root@localhost conf]# echo 3 > /var/lib/zookeeper/zoo3/myid

接下来我们开始启动ZooKeeper的3个实例(虚拟的3台机器),需要调用三次zkServer.sh的Start命令,采用不同的配置文件,如清单1-20所示命令及输出。

清单1-20 启动伪分布式集群服务

[root@localhost bin]# ./zkServer.sh start/home/zhoumingyao/zookeeper/zookeeper-3.4.7/conf/zoo1.cfg
ZooKeeper JMX enabled by default
Using config: /home/zhoumingyao/zookeeper/zookeeper-3.4.7/conf/zoo1.cfg
Starting zookeeper ... STARTED
[root@localhost bin]# ./zkServer.sh start/home/zhoumingyao/zookeeper/zookeeper-3.4.7/conf/zoo2.cfg
ZooKeeper JMX enabled by default
Using config: /home/zhoumingyao/zookeeper/zookeeper-3.4.7/conf/zoo2.cfg
Starting zookeeper ... STARTED
[root@localhost bin]# ./zkServer.sh start/home/zhoumingyao/zookeeper/zookeeper-3.4.7/conf/zoo3.cfg
ZooKeeper JMX enabled by default
Using config: /home/zhoumingyao/zookeeper/zookeeper-3.4.7/conf/zoo3.cfg
Starting zookeeper ... STARTED

清单1-21 查看服务

[root@localhost bin]# jps
5537 QuorumPeerMain
5617 Jps
5585 QuorumPeerMain

确认服务都正常启动,我们就可以通过zkCli.sh脚本方式连接到ZooKeeper集群,命令为./zkCli.sh -server localhost:2181,localhost:2182,localhost:2183,效果和单机模式一样。

(3). Module3 分布式模式

由于ZooKeeper单机模式不支持单点失败保护,所以不推荐在生产环境下使用。

ZooKeeper有另外一种支持多台机器的模式,即真正的分布式模式,这多台包含在一个应用体内的集群机器被称为quorum,这些机器最小配置为3台,最佳配置为5台,其中包含1台Leader(领导者)机器,由5台机器内部选举产生,另外4台机器就立即成为Follower(跟随者)机器,一旦Leader宕机,剩余的Follower就会重新选举出Leader。

从配置文件内部的字段定义上来说,分布式模式的ZooKeeper与单机模式的ZooKeeper有一些差距,例如下面三个字段:

Ø  initLimit:follower对于Leader的初始化连接timeout时间;
Ø  syncLimit:follower对于Leader的同步timeout时间;
Ø  timeout的计算公式是initLimit*tickTime,syncLimit*tickTime。

此外,我们需要把组成quorum的所有机器也都列在这个配置文件里面。假设我们有两个端口,第一个端口2889用于Follower和Leader之间的通信,通信方式是采用TCP方式,第二个端口3889是为选举Leader用的,用于quorum内部的Leader选举响应。那么我们配置文件如清单1-22所示。

清单1-22 分布式模式配置文件

server.1=node1:2889:3889
server.2=node2:2889:3889
server.3=node3:2889:3889

注意,分布式模式也需要设置myid,这个和伪分布式模式基本一样,只需要在每一台机器上实现一个myid,例如第一台机器是1,第二台机器上设置为2,第三台机器上设置为3,以此类推。

分布式模式的启动方式和单机唯一的差距是每一台机器上都需要启动ZooKeeper服务,即运行命令./zkServer.sh start。

ZooKeeper服务端运行后,我们可以通过在每台机器上运行./zkServer.sh status来查看选举结果,其中Follower节点的运行结果如清单所示,Leader节点的运行结果如清单1-23所示。

清单1-23 Follower节点的运行结果

[root@node3 bin]# ./zkServer.sh status
JMX enabled by default
Using config: /usr/lib/zookeeper-3.4.6/bin/../conf/zoo.cfg
Mode: follower

清单1-24 Leader节点的运行结果

[root@node2 bin]# ./zkServer.sh status
JMX enabled by default
Using config: /usr/lib/zookeeper-3.4.6/bin/../conf/zoo.cfg
Mode: leader

差距就在于Mode这一栏。接下来可以通过zkCli命令行访问ZooKeeper服务,假如我们访问node2节点,如清单1-25所示。

清单1-25 访问ZooKeeper服务及输出

[root@localhost bin]# ./zkCli.sh -server node2:2182
Connecting to node2:2182
2016-01-19 16:15:06,702 [myid:] - INFO [main:Environment@100] - Clientenvironment:zookeeper.version=3.4.7-1713338, built on 11/09/2015 04:32 GMT
2016-01-19 16:15:06,710 [myid:] - INFO [main:Environment@100] - Client environment:host.name=node2
2016-01-19 16:15:06,710 [myid:] - INFO [main:Environment@100] - Client environment:java.version=1.7.0_79
2016-01-19 16:15:06,714 [myid:] - INFO [main:Environment@100] - Client environment:java.vendor=OracleCorporation
2016-01-19 16:15:06,714 [myid:] - INFO [main:Environment@100] - Client environment:java.home=/usr/lib/jdk1.7.0_79/jre
2016-01-19 16:15:06,715 [myid:] - INFO [main:Environment@100] - Clientenvironment:java.class.path=/home/zhoumingyao/zookeeper-3.4.7/bin/../build/classes:/home/zhoumingyao/zookeeper-3.4.7/bin/../build/lib/*.jar:/home/zhoumingyao/zookeeper-3.4.7/bin/../lib/slf4j-log4j12-1.6.1.jar:/home/zhoumingyao/zookeeper-3.4.7/bin/../lib/slf4j-api-1.6.1.jar:/home/zhoumingyao/zookeeper-3.4.7/bin/../lib/netty-3.7.0.Final.jar:/home/zhoumingyao/zookeeper-3.4.7/bin/../lib/log4j-1.2.16.jar:/home/zhoumingyao/zookeeper-3.4.7/bin/../lib/jline-0.9.94.jar:/home/zhoumingyao/zookeeper-3.4.7/bin/../zookeeper-3.4.7.jar:/home/zhoumingyao/zookeeper-3.4.7/bin/../src/java/lib/*.jar:/home/zhoumingyao/zookeeper-3.4.7/bin/../conf:.:/usr/lib/jdk1.7.0_79/lib:/usr/lib/jdk1.7.0_79/jre/lib:
2016-01-19 16:15:06,715 [myid:] - INFO [main:Environment@100] - Clientenvironment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
2016-01-19 16:15:06,715 [myid:] - INFO [main:Environment@100] - Client environment:java.io.tmpdir=/tmp
2016-01-19 16:15:06,715 [myid:] - INFO [main:Environment@100] - Client environment:java.compiler=<NA>
2016-01-19 16:15:06,716 [myid:] - INFO [main:Environment@100] - Client environment:os.name=Linux
2016-01-19 16:15:06,716 [myid:] - INFO [main:Environment@100] - Client environment:os.arch=amd64
2016-01-19 16:15:06,716 [myid:] - INFO [main:Environment@100] - Clientenvironment:os.version=3.10.0-123.el7.x86_64
2016-01-19 16:15:06,716 [myid:] - INFO [main:Environment@100] - Client environment:user.name=root
2016-01-19 16:15:06,717 [myid:] - INFO [main:Environment@100] - Client environment:user.home=/root
2016-01-19 16:15:06,717 [myid:] - INFO [main:Environment@100] - Client environment:user.dir=/home/zhoumingyao/zookeeper-3.4.7/bin
2016-01-19 16:15:06,720 [myid:] - INFO [main:ZooKeeper@438] - Initiating client connection,connectString=node2:2182 sessionTimeout=30000watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@5dc6bb75
Welcome to ZooKeeper!
2016-01-19 16:15:06,774 [myid:] - INFO [main-SendThread(node2:2182):ClientCnxn$SendThread@1032] - Openingsocket connection to server node2/172.10.201.56:2182. Will not attempt toauthenticate using SASL (unknown error)
2016-01-19 16:15:06,783 [myid:] - INFO [main-SendThread(node2:2182):ClientCnxn$SendThread@876] - Socketconnection established to node2/172.10.201.56:2182, initiating session
JLine support is enabled
2016-01-19 16:15:06,820 [myid:] - INFO [main-SendThread(node2:2182):ClientCnxn$SendThread@1299] - Sessionestablishment complete on server node2/172.10.201.56:2182, sessionid =0x25258f06e1f0000, negotiated timeout = 30000

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[zk: node2:2182(CONNECTED) 0] help
ZooKeeper -server host:port cmd args
        connect host:port
        get path [watch]
        ls path [watch]
        set path data [version]
        rmr path
        delquota [-n|-b] path
        quit
        printwatches on|off
        create [-s] [-e] path dataacl
        stat path [watch]
        close
        ls2 path [watch]
        history
        listquota path
        setAcl path acl
        getAcl path
        sync path
        redo cmdno
        addauth scheme auth
        delete path [version]
        setquota -n|-b val path
[zk: node2:2182(CONNECTED) 1]

以上就证明分布式模式启动成功,这里不多加描述,和伪分布式方式基本一样。

欢迎关注麦克叔叔每晚十点说,感兴趣的朋友可以关注公众号,一起交流。

读者评论

相关博文

  • 社区使用反馈专区

    陈晓猛 2016-10-04

    尊敬的博文视点用户您好: 欢迎您访问本站,您在本站点访问过程中遇到任何问题,均可以在本页留言,我们会根据您的意见和建议,对网站进行不断的优化和改进,给您带来更好的访问体验! 同时,您被采纳的意见和建议,管理员也会赠送您相应的积分...

    陈晓猛 2016-10-04
    1418 235 1 4
  • 迎战“双12”!《Unity3D实战核心技术详解》独家预售开启!

    陈晓猛 2016-12-05

    时隔一周,让大家时刻挂念的《Unity3D实战核心技术详解》终于开放预售啦! 这本书不仅满足了很多年轻人的学习欲望,并且与实际开发相结合,能够解决工作中真实遇到的问题。预售期间优惠多多,实在不容错过! Unity 3D实战核心技术详解 ...

    陈晓猛 2016-12-05
    1363 34 0 1
  • 新书试读员征集

    陈晓猛 2016-12-01

    活动获奖名单公布 恭喜以下10位读者朋友获得本次试读员征集的名额,请私信 陈晓猛 将您的QQ号发给小编,以便加入读者VIP群,给您送出图书! 获奖名单 程续缘 悠悠的一杯茶 313150284@qq.com llaomao 落伍特青年 ...

    陈晓猛 2016-12-01
    435 12 0 0