Netty游戏服务器实战开发(6):Netty整合Zookeeper实现分布式服务发现与注册
1:Zookeeper基础安装zookeeper。当然,很多时候我们会在Windows上开发,所以,我们需要在本地搭建一个zookeeper环境。方便开发过程中的测试。首先我们去Apache上下载zookeeper。https://zookeeper.apache.org/下载下来后解压得到如下文件打开conf里面的zoo_simple.cfg ,拷贝一份,重新命名为zoo.cfg修改如...
1:Zookeeper基础
安装zookeeper。当然,很多时候我们会在Windows上开发,所以,我们需要在本地搭建一个zookeeper环境。方便开发过程中的测试。
首先我们去Apache上下载zookeeper。https://zookeeper.apache.org/
下载下来后解压得到如下文件
打开conf里面的zoo_simple.cfg ,拷贝一份,重新命名为zoo.cfg修改如下地方,适合你自己的环境路径
保存文件。
进入bin目录下,点击启动zkServer.cmd
当出现如下图片的时候。证明zookeeper正常启动,否者不正常启动的话需要检查具体原因,一般是java环境变量没有设置好等问题。
启动好zookeeper之后我们登录zookeeper客户端进行测试。
当列举出节点的时候,我们大概就证明zookeeper能够成功激动了。到此准备工作完成。我们需要将zookeeper整合到Netty游戏服务器中,来实现分布式服务发现和注册的功能。
2:Netty整合zookeeper:实现分布式服务发现和注册
项目模式还是基于上一篇 https://blog.csdn.net/baidu_23086307/article/details/82755939
中的模式。
要向使用zookeeper aip,我们需要在maven工程里面添加zookeeper相关的组件。
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
上面添加的zookeeper核心组件库。下面是使用apache的curator 用来实现服务动态发现的功能。
所需要添加的库
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
相关的环境和jar都准备好之后我们需要添加一个配置文件。来统一配置zookeeper服务器的基本信息。
我们在项目中的resource下添加一个zookeeper.properties文件
内容如下
zookeeper.timeout=5000
zookeeper.registry.path=/rpc_registry
zookeeper.data.path=/data
zookeeper.port=2181
zookeeper.ip=0.0.0.0
# 服务发现时间
zookeeper.elapsedTimeMs=10000
一些简单基础的配置。足以说明问题。
首先我们来编写一个读取properties文件的配置类,我们将bean对象丢给spring做管理,所以我们能够轻松的在任何地方使用我们的这个对象。
ZookeeperConfig
package com.twjitm.core.common.config.global;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* Created by IntelliJ IDEA.
* User: 文江 Date: 2018/8/19 Time: 13:29
* https://blog.csdn.net/baidu_23086307
*/
@Service
public class ZookeeperConfig {
private final Logger logger = LoggerFactory.getLogger(ZookeeperConfig.class);
private int timeOut;
private String dataPath;
private String registryPath;
private int serverPort;
private String serverHost;
private int elapsedTimeMs;
private Properties properties;
public void init() {
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(GlobalConstants.ConfigFile.ZOOKEEPER_PROPERTIES_FILE_PATH);
try {
properties = new Properties();
properties.load(in);
timeOut = Integer.parseInt(properties.getProperty("zookeeper.timeout"));
dataPath = properties.getProperty("zookeeper.data.path");
registryPath = properties.getProperty("zookeeper.registry.path");
serverPort = Integer.parseInt(properties.getProperty("zookeeper.port"));
serverHost = properties.getProperty("zookeeper.ip");
elapsedTimeMs = Integer.parseInt(properties.getProperty("zookeeper.elapsedTimeMs"));
} catch (IOException e) {
e.printStackTrace();
logger.error("error", e);
}
}
public String getRegistryAddress() {
return this.getServerHost() + ":" + this.getServerPort();
}
public String getRegistryPath() {
return registryPath;
}
public int getTimeOut() {
return timeOut;
}
public int getServerPort() {
return serverPort;
}
public void setServerPort(int serverPort) {
this.serverPort = serverPort;
}
public String getServerHost() {
return serverHost;
}
public int getElapsedTimeMs() {
return elapsedTimeMs;
}
}
有了这个对象我们能够在应用程序里面对zookeeper发起连接请求,创建节点和更新删除节点请求等一系列的操作。所以这个类对于zookeeper这一模块来说是比较重要的。
2.1:服务注册
当项目启动成功的时候。我们要将本服务器的基础信息告诉zookeeper。通过zookeeper来通知别的服务器。这样就不用去管每台服务器的连接信息,只通过zookeeper获取别的服务器信息。这样有助于管理和动态分配。
服务注册即将服务器节点信息发布到zookeeper中。和zookeeper建立连接关系。
在这个地方我们模拟将要注入三种角色的节点:
1:world服务器
2; game服务器
3:db服务器
所以我们需要一个枚举类来记录每个节点下的根目录
package com.twjitm.core.common.zookeeper;
import com.twjitm.core.common.enums.NettyGameTypeEnum;
/**
* @author twjitm - [Created on 2018-08-22 10:42]
* zookeeper 节点枚举
*/
public enum NettyZookeeperNodeNettyGameTypeEnum {
WORLD(NettyGameTypeEnum.WORLD, "/registry_world_address"),
GAME(NettyGameTypeEnum.GAME, "/registry_game_address"),
DB(NettyGameTypeEnum.DB, "/registry_db_address"),;
private NettyGameTypeEnum nettyGameTypeEnum;
private String rootPath;
NettyZookeeperNodeNettyGameTypeEnum(NettyGameTypeEnum nettyGameTypeEnum, String rootPath) {
this.nettyGameTypeEnum = nettyGameTypeEnum;
this.rootPath = rootPath;
}
public NettyGameTypeEnum getNettyGameTypeEnum() {
return nettyGameTypeEnum;
}
public void setNettyGameTypeEnum(NettyGameTypeEnum nettyGameTypeEnum) {
this.nettyGameTypeEnum = nettyGameTypeEnum;
}
public String getRootPath() {
return rootPath;
}
public void setRootPath(String rootPath) {
this.rootPath = rootPath;
}
}
有了节点枚举类。我们需要将本服务器注入到zookeeper中。下面代码演示了如何将节点注入到zookeeper系统中去。
首先我们要连接到zookeeper,连接通之后我们在注入节点,当节点信息存在的时候我们就不用注册了
当节点信息不存在的时候我们才注入。
package com.twjitm.core.common.zookeeper;
import com.twjitm.core.common.config.global.NettyGameServiceConfig;
import com.twjitm.core.common.config.global.NettyGameServiceConfigService;
import com.twjitm.core.common.config.global.ZookeeperConfig;
import com.twjitm.core.common.config.rpc.RpcServerConfig;
import com.twjitm.core.common.service.IService;
import com.twjitm.core.common.service.rpc.server.NettySdRpcServiceProvider;
import com.twjitm.core.spring.SpringServiceManager;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
/**
* @author twjtim - [Created on 2018-08-22 11:16]
* @jdk java version "1.8.0_77"
*/
public class NettyZookeeperRpcServiceRegistryService implements IService {
private static final Logger logger = LoggerFactory.getLogger(NettyZookeeperRpcServiceRegistryService.class);
private CountDownLatch countDownLatch = new CountDownLatch(1);
private ZooKeeper zooKeeper;
@Override
public String getId() {
return NettyZookeeperRpcServiceRegistryService.class.getSimpleName();
}
@Override
public void startup() throws Exception {
logger.info("STARTUP ZOOKEEPER SERVER BEGINNING");
NettyGameServiceConfigService gameServiceConfigService = SpringServiceManager.getSpringLoadService().getNettyGameServiceConfigService();
boolean isOpen = gameServiceConfigService.getNettyGameServiceConfig().isZookeeperOpen();
if (isOpen) {
registryZookeeper();
registryZookeeperNode();
}
}
/**
* 在启动服务器的时候,需要将服务器信息注册到zookeeper,提供给其他服务器使用,当服务器不可达时候,zookeeper会将
* 服务器信息清楚,达到容灾策略。
*/
private void registryZookeeperNode() {
NettyGameServiceConfigService gameConfig = SpringServiceManager.getSpringLoadService().getNettyGameServiceConfigService();
RpcServerConfig rpcConfig = gameConfig.getRpcServerConfig();
NettyGameServiceConfig gameServiceConfig = gameConfig.getNettyGameServiceConfig();
NettySdRpcServiceProvider nettySdRpcServiceProvider = rpcConfig.getProvider();
boolean worldOpen = nettySdRpcServiceProvider.isWorldOpen();
if (worldOpen) {
NettyZookeeperNodeInfo zookeeperNodeInfo = new NettyZookeeperNodeInfo(NettyZookeeperNodeNettyGameTypeEnum.WORLD,
gameServiceConfig.getServerId(),
gameServiceConfig.getServerHost(),
gameServiceConfig.getServerPort());
registry(zookeeperNodeInfo);
logger.info("注册world节点到zookeeper");
}
if (nettySdRpcServiceProvider.isGameOpen()) {
NettyZookeeperNodeInfo zookeeperNodeInfo = new NettyZookeeperNodeInfo(NettyZookeeperNodeNettyGameTypeEnum.GAME,
gameServiceConfig.getServerId(),
gameServiceConfig.getServerHost(),
gameServiceConfig.getServerPort());
registry(zookeeperNodeInfo);
logger.info("注册game节点到zookeeper");
}
if (nettySdRpcServiceProvider.isDbOpen()) {
NettyZookeeperNodeInfo zookeeperNodeInfo = new NettyZookeeperNodeInfo(NettyZookeeperNodeNettyGameTypeEnum.DB,
gameServiceConfig.getServerId(),
gameServiceConfig.getServerHost(),
gameServiceConfig.getServerPort());
registry(zookeeperNodeInfo);
logger.info("注册db节点到zookeeper");
}
}
private void registry(NettyZookeeperNodeInfo zookeeperNodeInfo) {
String rootPath = zookeeperNodeInfo.getNettyZookeeperNodeNettyGameTypeEnum().getRootPath();
String nodePath = zookeeperNodeInfo.getZookeeperNodePath();
String nodeData = null;
try {
nodeData = zookeeperNodeInfo.serialize();
} catch (IOException e) {
e.printStackTrace();
}
if (!StringUtils.isEmpty(rootPath)) {
if (zooKeeper != null) {
addRootNode(zooKeeper, rootPath);
try {
if (zooKeeper.exists(nodePath, false) != null) {
deleteNode(zooKeeper, nodePath);
logger.info("DELETE ZOOKEEPER NODE ", nodeData);
}
} catch (KeeperException e) {
} catch (InterruptedException e) {
}
createNode(zooKeeper, nodePath, nodeData);
logger.info("CREATE ZOOKEEPER NODE " + nodeData);
}
}
}
private void registryZookeeper() {
if (zooKeeper == null) {
zooKeeper = connectZookeeperServer();
}
}
/**
* 链接到zookeeper 服务器
*
* @return
*/
private ZooKeeper connectZookeeperServer() {
ZooKeeper zk = null;
try {
NettyGameServiceConfigService gameServerConfigService = SpringServiceManager.getSpringLoadService().getNettyGameServiceConfigService();
ZookeeperConfig zooKeeperConfig = gameServerConfigService.getZookeeperConfig();
String hostAndPort = zooKeeperConfig.getServerHost() + ":" + zooKeeperConfig.getServerPort();
zk = new ZooKeeper(hostAndPort, zooKeeperConfig.getTimeOut(),
event -> countDownLatch.countDown());
} catch (Exception e) {
logger.error(e.toString(), e);
}
return zk;
}
private void addRootNode(ZooKeeper zk, String rootPath) {
try {
Stat s = zk.exists(rootPath, false);
if (s == null) {
createRootNode(rootPath, new byte[0]);
logger.info("CREATE ZOOKEEPER ROOT NODE " + rootPath);
}
} catch (Exception e) {
logger.error(e.toString(), e);
}
}
private void createNode(ZooKeeper zk, String nodePath, String nodeData) {
try {
byte[] bytes = nodeData.getBytes();
String path = create(nodePath, bytes);
logger.debug("CREATE ZOOKEEPER NODE ({} => {})", path, bytes);
} catch (Exception e) {
logger.error(e.toString(), e);
}
}
/**
* <b>function:</b>创建持久态的znode,比支持多层创建.比如在创建/parent/child的情况下,无/parent.无法通过
*
* @param path
* @param data
* @throws KeeperException
* @throws InterruptedException
*/
public String createRootNode(String path, byte[] data) throws Exception {
/**
* 此处采用的是CreateMode是PERSISTENT 表示The znode will not be automatically deleted upon client's disconnect.
* EPHEMERAL 表示The znode will be deleted upon the client's disconnect.
*/
return this.zooKeeper.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
/**
* <b>function:</b>创建持久态的znode,比支持多层创建.比如在创建/parent/child的情况下,无/parent.无法通过
*
* @param path
* @param data
*/
public String create(String path, byte[] data) throws Exception {
/**
* 此处采用的是CreateMode是PERSISTENT 表示The znode will not be automatically deleted upon client's disconnect.
* EPHEMERAL 表示The znode will be deleted upon the client's disconnect.
*/
return this.zooKeeper.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
}
public void deleteNode(ZooKeeper zk, String nodePath) {
try {
zk.delete(nodePath, -1);
logger.debug("delete zookeeper node path c ({} => {})", nodePath);
} catch (Exception e) {
logger.error(e.toString(), e);
}
}
@Override
public void shutdown() throws Exception {
if (zooKeeper != null) {
zooKeeper.close();
}
}
}
在项目启动完成的时候我们调用startup方法,既可以注入节点到zookeeper。
2.2:服务发现
想要获取zookeeper关注的节点,我们需要借助Apache 开源的客户端Curator 不知道Curator使用的可以进行再这查看。
https://blog.csdn.net/baidu_23086307/article/details/82745545 更多的还得去看看官方教程。
通过curator这个库,我们能够动态的获取zookeeper的节点信息,当zookeeper节点信息发生改变的时候能够及时通知到关注的节点。
zookeeper服务发现的核心代码
package com.twjitm.core.common.zookeeper;
import com.twjitm.core.common.config.global.NettyGameServiceConfig;
import com.twjitm.core.common.config.global.NettyGameServiceConfigService;
import com.twjitm.core.common.config.global.ZookeeperConfig;
import com.twjitm.core.common.service.IService;
import com.twjitm.core.common.service.rpc.service.NettyRpcClientConnectService;
import com.twjitm.core.spring.SpringServiceManager;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.ACLProvider;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* 采用Apache Curator 来发现zookeeper服务。
*
* @author twjitm - [Created on 2018-08-22 15:57]
* @jdk java version "1.8.0_77"
* zookeeper 服务发现
*/
public class NettyZookeeperRpcServiceDiscoveryService implements IService {
private Logger logger = LoggerFactory.getLogger(NettyZookeeperRpcServiceDiscoveryService.class);
private volatile ConcurrentHashMap<NettyZookeeperNodeNettyGameTypeEnum, List<NettyZookeeperNodeInfo>> nettyZookeeperNodeMap;
/**
* zookeeper连接客户端 https://blog.csdn.net/dc_726/article/details/46475633
*/
private CuratorFramework client;
@Override
public String getId() {
return NettyZookeeperRpcServiceDiscoveryService.class.getSimpleName();
}
public List<NettyZookeeperNodeInfo> getNodeList(final NettyZookeeperNodeNettyGameTypeEnum zooKeeperNodeBoEnum) {
return nettyZookeeperNodeMap.get(zooKeeperNodeBoEnum);
}
@Override
public void startup() throws Exception {
nettyZookeeperNodeMap = new ConcurrentHashMap<>();
NettyGameServiceConfigService gameConfig = SpringServiceManager.getSpringLoadService().getNettyGameServiceConfigService();
NettyGameServiceConfig gameServiceConfig = gameConfig.getNettyGameServiceConfig();
boolean openZookeeper = gameServiceConfig.isZookeeperOpen();
//创建一个连接
if (openZookeeper) {
try {
client = createCuratorClient();
} catch (Exception e) {
logger.error("ERROR IN NettyZookeeperRpcServiceDiscoveryService startup()", e);
}
NettyZookeeperNodeNettyGameTypeEnum[] nodeNettyGameTypeEnum = NettyZookeeperNodeNettyGameTypeEnum.values();
for (NettyZookeeperNodeNettyGameTypeEnum temp : nodeNettyGameTypeEnum) {
discoveryService(temp);
}
}
}
private void discoveryService(NettyZookeeperNodeNettyGameTypeEnum nettyZookeeperNodeNettyGameTypeEnum) {
if (client == null) {
client = createCuratorClient();
}
try {
setCuratorListener(client, nettyZookeeperNodeNettyGameTypeEnum);
} catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("CURATORFRAMEWORK LISTENING EXCEPTION:" + e.getMessage());
}
}
}
/**
* 设置一个监听器,负责监听zookeeper信息变化回调
* Apache Curator
*
* @param client
* @param nettyZookeeperNodeNettyGameTypeEnum
*/
private void setCuratorListener(CuratorFramework client, NettyZookeeperNodeNettyGameTypeEnum nettyZookeeperNodeNettyGameTypeEnum) throws Exception {
List<String> childRenList = client.getChildren().forPath(nettyZookeeperNodeNettyGameTypeEnum.getRootPath());
List<NettyZookeeperNodeInfo> tempNodeList = new ArrayList<>();
for (String node : childRenList) {
NettyZookeeperNodeInfo zooKeeperNodeInfo = new NettyZookeeperNodeInfo();
byte[] bytes = client.getData().forPath(nettyZookeeperNodeNettyGameTypeEnum.getRootPath() + "/" + node);
if (bytes != null) {
zooKeeperNodeInfo.deserialize(new String(bytes));
tempNodeList.add(zooKeeperNodeInfo);
}
}
if (logger.isDebugEnabled()) {
logger.debug("NODE DATA: {}", tempNodeList);
}
nettyZookeeperNodeMap.put(nettyZookeeperNodeNettyGameTypeEnum, tempNodeList);
if (logger.isDebugEnabled()) {
logger.debug("SERVICE DISCOVERY TRIGGERED UPDATING CONNECTED SERVER NODE.");
}
NettyRpcClientConnectService rpcClientConnectService = SpringServiceManager.getSpringLoadService().getNettyRpcClientConnectService();
rpcClientConnectService.notifyConnect(nettyZookeeperNodeNettyGameTypeEnum, nettyZookeeperNodeMap.get(nettyZookeeperNodeNettyGameTypeEnum));
TreeCache cache = new TreeCache(client, nettyZookeeperNodeNettyGameTypeEnum.getRootPath());
cache.getListenable().addListener((client1, event) -> {
ChildData data = event.getData();
if (data != null) {
switch (event.getType()) {
case NODE_ADDED:
if (logger.isDebugEnabled()) {
logger.debug("NODE_ADDED : " + data.getPath() + " DATA:" + new String(data.getData()));
}
break;
case NODE_REMOVED:
if (logger.isDebugEnabled()) {
logger.debug("NODE_REMOVED : " + data.getPath() + " DATA:" + new String(data.getData()));
}
break;
case NODE_UPDATED:
if (logger.isDebugEnabled()) {
logger.debug("NODE_UPDATED : " + data.getPath() + " DATA:" + new String(data.getData()));
}
break;
default:
break;
}
} else {
switch (event.getType()) {
case CONNECTION_SUSPENDED:
if (logger.isDebugEnabled()) {
logger.debug("DATA IS NULL : " + "CONNECTION_SUSPENDED");
}
break;
case CONNECTION_RECONNECTED:
if (logger.isDebugEnabled()) {
logger.debug("DATA IS NULL : " + "CONNECTION_RECONNECTED");
}
break;
case CONNECTION_LOST:
if (logger.isDebugEnabled()) {
logger.debug("DATA IS NULL : " + "CONNECTION_LOST");
}
break;
default:
break;
}
}
});
// 开始监听
cache.start();
}
private CuratorFramework createCuratorClient() {
/**
* ACL 提供者
*/
ACLProvider aclProvider = new ACLProvider() {
private List<ACL> acl;
@Override
public List<ACL> getDefaultAcl() {
if (acl == null) {
ArrayList<ACL> acl = ZooDefs.Ids.CREATOR_ALL_ACL;
acl.clear();
acl.add(new ACL(ZooDefs.Perms.ALL, new Id("auth", "admin:admin")));
this.acl = acl;
}
return acl;
}
@Override
public List<ACL> getAclForPath(String path) {
return acl;
}
};
int connectionTimeoutMs = SpringServiceManager.getSpringLoadService().getNettyGameServiceConfigService().getZookeeperConfig().getTimeOut();
NettyGameServiceConfigService gameServerConfigService = SpringServiceManager.getSpringLoadService().getNettyGameServiceConfigService();
ZookeeperConfig zooKeeperConfig = gameServerConfigService.getZookeeperConfig();
String registryAddress = zooKeeperConfig.getRegistryAddress();
CuratorFramework client = CuratorFrameworkFactory.builder()
.aclProvider(aclProvider)
.connectionTimeoutMs(connectionTimeoutMs)
.connectString(registryAddress)
.retryPolicy(
new RetryNTimes(Integer.MAX_VALUE, zooKeeperConfig.getElapsedTimeMs())
).build();//绑定
//启动
client.start();
return client;
}
@Override
public void shutdown() throws Exception {
if (client != null) {
try {
client.close();
} catch (Exception e) {
logger.error(e.toString(), e);
}
}
}
}
定时检测zookeeper上的节点是否发生改变,若是发生改变了需要刷新本地缓存的服务器上的列表。
最后我们来测试一下zookeeper分布式服务发现和注册
编写一个测试类来测试一下:
package com.twjitm.rpc.zookeeper;
import com.twjitm.TestSpring;
import com.twjitm.core.bootstrap.Bootstrap;
import com.twjitm.core.common.zookeeper.NettyZookeeperNodeInfo;
import com.twjitm.core.common.zookeeper.NettyZookeeperNodeNettyGameTypeEnum;
import com.twjitm.core.common.zookeeper.NettyZookeeperRpcServiceDiscoveryService;
import com.twjitm.core.common.zookeeper.NettyZookeeperRpcServiceRegistryService;
import com.twjitm.core.spring.SpringServiceManager;
import java.util.List;
/**
* @author twjtim - [Created on 2018-08-23 11:48]
*/
public class NettyZookeeperTest {
private static NettyZookeeperRpcServiceRegistryService registryService;
private static NettyZookeeperRpcServiceDiscoveryService discoveryService;
public static void main(String[] args) {
TestSpring.initSpring();
Bootstrap.startServer();
registryService = SpringServiceManager.getSpringLoadService().getNettyZookeeperRpcServiceRegistryService();
discoveryService = SpringServiceManager.getSpringLoadService().getNettyZookeeperRpcServiceDiscoveryService();
test();
}
private static void test() {
List<NettyZookeeperNodeInfo> list = discoveryService.getNodeList(NettyZookeeperNodeNettyGameTypeEnum.WORLD);
System.out.println(list);
}
}
运行效果:
能够将本地的服务器信息注册到zookeeper服务器上,也能够获取到zookeeper服务器上已经注入的别的服务器信息。
因此我们使用zookeeper实现一个分布式服务发现和注册相对来说是一个简单高效的方式,特别实在游戏服务器架构方面来说简直是一大福利,我们少去了许多服务器配置,而且要是服务器宕机,客户端服务器没有办法及时检测到。所以zookeeper的使用能够帮助我们解决不少问题。
更多推荐
所有评论(0)