最近在看《架构探险-从零开始写分布式服务框架》,对于分布式框架的入门级选手还是挺合适的,扫盲。对分布式服务框架中的基本概念:RPC、SOA、序列化、Spring集成RPC、ZooKeeper、I/O模型、Netty、软负载、服务治理做了系统介绍。手写了RPC框架、使用ZooKeeper实现注册中心、Netty服务端和客户端,以及前面各个概念的代码实现。总之,概念讲的比较清楚,代码也比较多,容易理解,好上手。
书上的代码只给了主要实现部分,我自己敲了一遍,有3个问题导致没有运行起来:
主要维护两个数据,一个服务方使用的提供服务的方法列表providerServiceMap,一个消费方使用的服务列表serviceMetaMap4Consume。
具体代码如下:
public interface IRegisterCenter4Invoker {
/**
* 消费端初始化服务提供者信息本地缓存
*/
public void initProviderMap();
/**
* 消费端获取服务提供者信息
*/
public Map> getServiceMetaDataMap4Consume();
/**
* 消费端将消费者信息注册到ZK对应的节点下
*/
public void registerInvoker(final InvokerService invoker);
}
public interface IRegisterCenter4Provider {
/**
* 服务端将服务提供者信息注册到ZK对应的节点下
*/
public void registerProvider (final List serviceMetaData);
/**
* 服务端获取服务提供者信息
*
* 返回对象:key:服务提供者接口,value:服务提供者服务方法列表
*/
public Map> getProviderServiceMap();
}
package ZookeeperInterfaceImpl;
import Service.InvokerService;
import Service.ProviderService;
import ZookeeperInterface.IRegisterCenter4Invoker;
import ZookeeperInterface.IRegisterCenter4Provider;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
/**
* 注册中心实现
*
* @author zhangna
* @date 2018-04-05 18:23
*/
public class RegisterCenter implements IRegisterCenter4Invoker, IRegisterCenter4Provider {
private static RegisterCenter registerCenter = new RegisterCenter();
//服务提供者列表,key:服务提供者接口 value:服务提供者服务方法列表
private static final Map> providerServiceMap = Maps.newConcurrentMap();
//服务端ZK服务元信息,选择服务(第一次直接从ZK拉取,后续由ZK的监听机制主动更新)
private static final Map> serviceMetaDataMap4Consume = Maps.newConcurrentMap();
//从配置文件中获取ZK的服务地址列表
private static String ZK_SERVICE = PropertyConfigeHelper.getZkService();
//从配置文件中获取ZK会话超时时间配置
private static int ZK_SESSION_TIME_OUT = PropertyConfigeHelper.getZKSessionTimeout();
//从配置文件中获取ZK连接超时时间配置
private static int ZK_CONNECTION_TIME_OUT = PropertyConfigeHelper.getZKConnectionTimeout();
//组装ZK根路径/APPKEY路径
private static String ROOT_PATH = "/config_register/" + PropertyConfigeHelper.getAppName();
public static String PROVIDER_TYPE = "/provider";
public static String INVOKE_TYPE = "/consumer";
private static volatile ZkClient zkClient = null;
private RegisterCenter(){}
public static RegisterCenter singleton(){
return registerCenter;
}
public void registerProvider(final List serviceMetaData) {
if(CollectionUtils.isEmpty(serviceMetaData)){
return;
}
//连接ZK,注册服务
synchronized (RegisterCenter.class){
for(ProviderService provider : serviceMetaData){
//获得接口名称
String serviceItfKey = provider.getServiceItf().getName();
//获得提供者列表
List providers = providerServiceMap.get(serviceItfKey);
if(providers == null){
providers = Lists.newArrayList();
}
providers.add(provider);
providerServiceMap.put(serviceItfKey,providers);
if(zkClient == null){
zkClient = new ZkClient(ZK_SERVICE,ZK_SESSION_TIME_OUT,ZK_CONNECTION_TIME_OUT,new SerializableSerializer());
}
//创建ZK命名空间/当前部署应用APP命名空间
boolean exist= zkClient.exists(ROOT_PATH);
if(!exist){
zkClient.createPersistent(ROOT_PATH,true);
}
//创建服务提供者节点,将服务提供者信息作为ZK子节点写入ZK
for(Map.Entry> entry : providerServiceMap.entrySet()){
//创建服务提供者节点
String serviceNode = entry.getKey();
String servicePath = ROOT_PATH + "/" + serviceNode + PROVIDER_TYPE;
exist = zkClient.exists(servicePath);
if(!exist){
zkClient.createPersistent(servicePath,true);
}
//创建当前服务器节点
int serverPort = entry.getValue().get(0).getServerPort();
String localIp = IPHelper.localIP();
String currentServiceIpNode = servicePath + "/" + localIp + "|" + serverPort;
exist = zkClient.exists(currentServiceIpNode);
if(!exist){
//注意,这里创建的是临时节点
zkClient.createEphemeral(currentServiceIpNode);
}
//监听注册服务的变化(将子节点路径注册监听器),同时更新数据到本地缓存
zkClient.subscribeChildChanges(servicePath, new IZkChildListener() {
@Override
public void handleChildChange(String parentPath, List currentChilds) throws Exception {
if(currentChilds == null){
currentChilds = Lists.newArrayList();
}
//存活的服务IP列表
List activityServiceIPList = Lists.newArrayList(
Lists.transform(currentChilds,new Function(){
@Override
public String apply(String input){
return StringUtils.split(input,"|")[0];
}
}));
//更新本地服务者缓存信息
refreshActivityService(activityServiceIPList);
}
});
}
}
}
}
@Override
public void initProviderMap() {
if(MapUtils.isEmpty(serviceMetaDataMap4Consume)){
serviceMetaDataMap4Consume.putAll(fetchOrUpdateServiceMetaData());
}
}
@Override
public Map> getServiceMetaDataMap4Consume() {
return serviceMetaDataMap4Consume;
}
@Override
public Map> getProviderServiceMap() {
return providerServiceMap;
}
@Override
public void registerInvoker(InvokerService invoker) {
if(invoker == null){
return;
}
//连接ZK,注册服务
synchronized (RegisterCenter.class){
if(zkClient == null){
zkClient = new ZkClient(ZK_SERVICE,ZK_SESSION_TIME_OUT,ZK_CONNECTION_TIME_OUT,new SerializableSerializer());
}
//创建ZK命名空间/当前部署应用APP命名空间
boolean exist = zkClient.exists(ROOT_PATH);
if(!exist){
zkClient.createPersistent(ROOT_PATH,true);
}
//创建服务消费者节点
String serviceNode = invoker.getServiceItf().getName();
String servicePath = ROOT_PATH + "/" +serviceNode + INVOKE_TYPE;
exist = zkClient.exists(servicePath);
if(!exist){
zkClient.createPersistent(servicePath);
}
//创建当前服务器节点
String localIp = IpHelper.localIp();
String currentServiceIpNode = servicePath + "/" +localIp;
exist = zkClient.exists(currentServiceIpNode);
if(!exist){
//注意,这里创建的是临时节点
zkClient.createEphemeral(currentServiceIpNode);
}
}
}
//利用ZK自动刷新当前存活的服务提供者列表数据
private void refreshActivityService(List serviceIpList){
if(serviceIpList == null){
serviceIpList = Lists.newArrayList();
}
Map> currentServiceMetaDataMap = Maps.newHashMap();
for(Map.Entry> entry : providerServiceMap.entrySet()){
String key = entry.getKey();
List providerServices = entry.getValue();
List serviceMetaDataModelList = currentServiceMetaDataMap.get(key);
if(serviceMetaDataModelList == null){
serviceMetaDataModelList = Lists.newArrayList();
}
for(ProviderService serviceMetaData : providerServices){
if(serviceIpList.contains(serviceMetaData.getServerIp())){
serviceMetaDataModelList.add(serviceMetaData);
}
}
currentServiceMetaDataMap.put(key,serviceMetaDataModelList);
}
providerServiceMap.putAll(currentServiceMetaDataMap);
}
//刷新服务员数据列表(与上个方法唯一不同的地方是把数据putAll到serviceMetaDataMap4Consume)
private void refreshServiceMetaDataMap(List serviceIpList){
if(serviceIpList == null){
serviceIpList = Lists.newArrayList();
}
Map> currentServiceMetaDataMap = Maps.newHashMap();
for(Map.Entry> entry:serviceMetaDataMap4Consume.entrySet()){
String serviceItfKey = entry.getKey();
List serviceList = entry.getValue();
List providerServiceList = currentServiceMetaDataMap.get(serviceItfKey);
if(providerServiceList == null){
providerServiceList = Lists.newArrayList();
}
for(ProviderService serviceMetaData : serviceList){
if(serviceIpList.contains(serviceMetaData.getServerIp())){
providerServiceList.add(serviceMetaData);
}
}
currentServiceMetaDataMap.put(serviceItfKey,providerServiceList);
}
serviceMetaDataMap4Consume.putAll(currentServiceMetaDataMap);
System.out.println("serviceMetaDataMap4Consume:" + JSON.toJSONString(serviceMetaDataMap4Consume);
}
private Map> fetchOrUpdateServiceMetaData(){
final Map> providerServiceMap = Maps.newConcurrentMap();
//连接ZK
synchronized (RegisterCenter.class){
if(zkClient == null){
zkClient = new ZkClient(ZK_SERVICE,ZK_SESSION_TIME_OUT,ZK_CONNECTION_TIME_OUT,new SerializableSerializer());
}
}
//从ZK获取服务提供者列表
String providePath = ROOT_PATH;
List providerServices = zkClient.getChildren(providePath);
for(String serviceName : providerServices){
String servicePath = providePath + "/" + serviceName + PROVIDER_TYPE;
List ipPathList = zkClient.getChildren(servicePath);
for(String ipPath : ipPathList){
String serverIp = StringUtils.split(ipPath,"|")[0];
String serverPort = StringUtils.split(ipPath,"|")[1];
List providerServiceList = providerServiceMap.get(serviceName);
if(providerServiceList == null){
providerServiceList = Lists.newArrayList();
}
ProviderService providerService = new ProviderService();
try{
providerService.setServiceItf(ClassUtils.getClass(serviceName));
}catch (ClassNotFoundException e){
throw new RuntimeException(e);
}
providerService.setServerIp(serverIp);
providerService.setServerPort(Integer.parseInt(serverPort));
providerServiceList.add(providerService);
providerServiceMap.put(serviceName,providerServiceList);
}
//监听注册服务的变化,同时更新数据到本地缓存
zkClient.subscribeChildChanges(servicePath, new IZkChildListener() {
@Override
public void handleChildChange(String parentPath, List currentChilds) throws Exception {
if(currentChilds == null){
currentChilds = Lists.newArrayList();
}
currentChilds = Lists.newArrayList(Lists.transform(currentChilds, new Function() {
@Override
public String apply(String input) {
return StringUtils.split(input,"|")[0];
}
}));
}
});
}
return providerServiceMap;
}
}
另外,两个容易记忆模糊的关键字:
final的使用(Java编程思想):
修饰基本类型:使数值恒定不变。
修饰对象应用:使引用恒定不变。一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象,然而,对象其自身却是可以被修改的。
修饰参数:无法在方法中更改参数引用所指向的对象。
volatile的使用:
Java内部两种同步机制,一种是synchronized另一种是volatile。线程能够自动发现volatile变量的最新值。(https://www.ibm.com/developerworks/cn/java/j-jtp06197.html)