上一篇使用自定義xsd定義了發佈、訂閱的標籤
這一篇就是用標籤的解析並想zk上註冊服務
廢話不多說,直接上註冊中心的代碼
package com.kaer.rpc.netty.register.zk;
import java.util.Iterator;
import java.util.concurrent.ConcurrentMap;
import org.apache.curator.framework.CuratorFramework;
...
import org.slf4j.LoggerFactory;
import com.kaer.rpc.demo.config.ProviderConfig;
/**
* Zookeeper註冊中心處理類
*
* @author kaer
*
*/
public class ZkRegistryCenter {
public static final Logger logger = LoggerFactory.getLogger(ZkRegistryCenter.class);////日誌打印
private static String basePath = "/kaer-rpc";//zk是以目錄的形式創建節點
private static CuratorFramework client;//Curator操作zk的客戶端
private final ConcurrentMap<String, ProviderConfig> services = Maps.newConcurrentMap();//服務的集合,暫時只發佈一個服務,後面可擴展
private static final ConnectionStateListener connectionStateListener;//監聽器對zk的狀態實時處理,暫不實現
static {
connectionStateListener = new ConnectionStateListener() {
@Override
public void stateChanged(CuratorFramework client, ConnectionState newState) {
// 狀態的變化處理
logger.info("zkClient state change to " + newState);
}
};
}
public static void start(String zookeeperUrl) {
//序列化工具
//初始化連接客戶端,即連接到了本地zk的127.0.0.1:2181
client = CuratorFrameworkFactory.newClient(zookeeperUrl, new ExponentialBackoffRetry(1000, 3));
client.getConnectionStateListenable().addListener(ZkRegistryCenter.connectionStateListener);
client.start();//zk客戶端啓動
try {
//zk是以目錄的形式存儲數據,而且頂向下一節一節實現的
if((Stat) client.checkExists().forPath(basePath) == null) {
((ACLBackgroundPathAndBytesable<?>) client.create().creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)).forPath(basePath);
}
} catch (Exception e) {
e.printStackTrace();
}
logger.info("start service register......");
}
//註冊服務,這個發生的時間是在容器的bean加載,還記得之前自定義的標籤麼,有對provider標籤的解析類ProviderBean
public static void registerService(String serviceId, String alias, byte[] info) {
//創建目錄節點,類似kaer-rpc/{api},數據目錄爲kaer-rpc/{api}/data
String nodePath = ZKPaths.makePath(basePath, serviceId);
String fullPath = ZKPaths.makePath(nodePath, "data");
//存放目錄數據
try {
Stat stat = (Stat) client.checkExists().forPath(nodePath);
if(stat == null) {
((ACLBackgroundPathAndBytesable<?>) client.create().creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)).forPath(nodePath);
((ACLBackgroundPathAndBytesable<?>) client.create().creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)).forPath(fullPath);
}else {
stat = (Stat) client.checkExists().forPath(fullPath);
if(stat == null) {
((ACLBackgroundPathAndBytesable<?>) client.create().creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)).forPath(fullPath);
}
}
client.setData().forPath(fullPath, info);//這裏的info使用Json序列化的,具體要見ProviderConfig
} catch (Exception e) {
e.printStackTrace();
}
}
//刪除服務,暫不實現
public void unRegisterService(String serviceId) {
String path = basePath + "serviceId";
try {
((ChildrenDeletable) client.delete().guaranteed()).forPath(path);
} catch (Exception e) {
e.printStackTrace();
logger.error("Could not unregister instance: s%", serviceId);
}
}
public void close() {
ExceptionAccumulator accumulator = new ExceptionAccumulator();
Iterator<ProviderConfig> arg2 = this.services.values().iterator();
while (arg2.hasNext()) {
ProviderConfig service = (ProviderConfig) arg2.next();
unRegisterService(service.getApi());
}
client.getConnectionStateListenable().removeListener(ZkRegistryCenter.connectionStateListener);
CloseableUtils.closeQuietly(client);
accumulator.propagate();
}
}
由上一篇中分別對三個自定義標籤進行了解析處理
下面是註冊服務的處理邏輯
package com.kaer.rpc.demo.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSON;
import com.kaer.rpc.netty.register.zk.ZkRegistryCenter;
/**
* 服務提供者配置
*
* @author kaer
*
*/
public class ProviderConfig {
public static Logger logger = LoggerFactory.getLogger(ProviderConfig.class);
private String api;
private String mapper;
private String alias;
//發佈,假設模擬一個服務
protected void doExport() {
logger.info("生產者信息:端口[" + api + "],映射[" + mapper + "],別名[" + alias + "]。");
RpcProviderConfig rpcProviderConfig = new RpcProviderConfig();
rpcProviderConfig.setApi(api);
rpcProviderConfig.setMapper(mapper);
rpcProviderConfig.setAlias(alias);
rpcProviderConfig.setHost("127.0.0.1");
rpcProviderConfig.setPort(9999);
//註冊生產者
// long count = RedisRegistryCenter.registryProvider(api, alias, JSON.toJSONString(rpcProviderConfig));
ZkRegistryCenter.registerService(api, alias, JSON.toJSONBytes(rpcProviderConfig));
logger.info("註冊生產者:{} {} {}", api, alias, null);
}
public String getApi() {
return api;
}
public void setApi(String api) {
this.api = api;
}
public String getMapper() {
return mapper;
}
public void setMapper(String mapper) {
this.mapper = mapper;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
}
消費者的配置(ConsumerConfig)類似提供者的這個配置,暫不實現。
/**
* 實現服務提供的依賴注入
* @author kaer
*
*/
public class ProviderBean extends ProviderConfig implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// 發佈
doExport();
}
}
其中有需要依賴的包(Curator對ZK的支持):
<!-- Curator對ZK的支持 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.0</version>
</dependency>
本地啓動zookeeper
測試類如下:
public class ApiTest {
@SuppressWarnings("resource")
public static void main(String[] args) throws UnknownHostException, IOException {
String[] configs = { "rpc/test.xml" };
new ClassPathXmlApplicationContext(configs);
}
}
執行完了,通過可視化界面去看看是否註冊成功(當然也可以用命令去操作,zkCli.exe)。
這裏用到了zkUI的一個工具,需要的在附件中獲取。
可以登陸進去可以看到有代碼中創建的目錄了
一層一層點進去,可以看到最終註冊上去的服務的信息,在實際開發中,應獲取應用本機的ip和端口,再附加服務的唯一性數據,訂閱就是個獲取並解析然後再進行netty通訊的步驟
本文結束,後面使用consul進行服務註冊玩玩
附件:zkUI可視化工具