在工作中碰到了这样的一个问题,签电子合同,发送短信往往都是多个服务提供商,但是使用每个服务商的权重不同,每个业务线可能使用的服务提供商也会不同,像这种服务提供商的路由选择,可以通过设计表来进行实现,不过我用了另外一种方式实现。
这个小工具的实现使用的zookeeper, 支持动态的配置各个权重。下面是具体的实现过程。
1.为了降低处理的复杂度,并没有使用注解,而是自定义一个spring命名空间,就像dubbo一样.
public class ThorNamespaceHandler extends NamespaceHandlerSupport{
@Override
public void init() {
registerBeanDefinitionParser("routing", new RuleBeanDefinitionParser());
}
}
public class RuleBeanDefinitionParser implements BeanDefinitionParser{
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
if(element == null) {
return null;
}
RoutingSchemaBean routing = new RoutingSchemaBean();
routing.setCode(element.getAttribute("code"));
routing.setDefaultValue(element.getAttribute("default"));
NodeList nodeList = element.getChildNodes();
setRoutingRules(routing, nodeList);
RoutingRuleSchemaContext.put(routing);
return null;
}
private void setRoutingRules(RoutingSchemaBean routing, NodeList nodeList){
if(nodeList == null) {
return;
}
for(int i=0;i<nodeList.getLength();i++){
Node node = nodeList.item(i);
if(node == null) {
continue;
}
if(!node.getNodeName().contains("rules")){
continue;
}
NodeList subList = node.getChildNodes();
if(subList == null) {
continue;
}
for(int j=0;j<subList.getLength();j++){
Node sub = subList.item(j);
if(sub == null) {
continue;
}
if(!sub.getNodeName().contains("rule")){
continue;
}
RuleSchemaBean rule = getRule(sub);
routing.addRules(rule);
}
}
}
private RuleSchemaBean getRule(Node sub){
RuleSchemaBean bean = new RuleSchemaBean();
NamedNodeMap attrs = sub.getAttributes();
for(int i=0;i<attrs.getLength();i++) {
Node each = attrs.item(i);
try {
BeanUtilEx.setProperty(bean, each.getNodeName(), each.getNodeValue());
} catch (Exception e) {
e.printStackTrace();
}
}
return bean;
}
}
上面的代码就是实现自定义spring命名空间(就是自定义标签)的代码部分,后面会贴出一个示例
2. 将配置数据存储到zookeeper上,这里要处理zookeeper链接断开的问题,因为连接一旦断开,会话就失效了,所以这个时候要重新创建客户端示例,重新监听所有节点数据的变更。
下面时zookeeper的一些封装处理:
public abstract class ConnectionWatcher implements Watcher , AutoCloseable {
protected static Logger logger = LoggerFactory.getLogger(ConnectionWatcher.class);
private CountDownLatch countDownLatch = new CountDownLatch(1);
// 10秒 会话时间,避免频繁的session expired
private static final int SESSION_TIMEOUT = 10000;
// 3秒 连接超时时间
private static final int CONNECT_TIMEOUT = 5000;
// 重试间隔时间
protected static final int RETRY_PERIOD_INTERVAL = 2;
protected ZooKeeper zk;
private String host;
@Override
public void process(WatchedEvent event) {
// 连接状态处理
keeperStateResolve(event);
// 节点事件处理
if(event.getType() != null && !event.getType().equals(Event.EventType.None)) {
nodeEventResolve(event);
}
}
@Override
public void close() throws InterruptedException {
zk.close();
}
/**
* 连接zk server
* @throws Exception
*/
protected void connect(String host) throws Exception {
this.host = host;
zk = new ZooKeeper(host,SESSION_TIMEOUT,this);
// 可能连接超时
countDownLatch.await(CONNECT_TIMEOUT,TimeUnit.MILLISECONDS);
}
/**
* 事件处理业务逻辑
* @param path 节点
* @param eventType 事件类型
*/
protected abstract void eventHandle(String path, Event.EventType eventType);
/**
* 处理连接状态
*/
protected abstract void keeperStatHandle(WatchedEvent event);
/**
* 重新建立zk客户端
*/
private void reConnect() {
logger.info("zookeeper 会话(session)过期,即将重新建立连接");
int retries = 0;
while (true) {
try {
if(zk != null && !ZooKeeper.States.CLOSED.equals(zk.getState())) {
break;
}
// 关闭连接
close();
// 创建连接
connect(this.host);
} catch (Exception e) {
logger.error("第 {} 次重试异常,异常信息:{}",(++retries),e.getMessage(),e);
try {
logger.info("sleep {}s",RETRY_PERIOD_INTERVAL);
TimeUnit.SECONDS.sleep(RETRY_PERIOD_INTERVAL);
} catch (Exception ex) {
// nothing to do
}
}
}
}
/**
* 处理连接状态
* @param event 事件类型
*/
private void keeperStateResolve(WatchedEvent event) {
switch (event.getState()) {
case SyncConnected:
keeperStatHandle(event);
countDownLatch.countDown();
logger.info("Connect zk server success");
break;
case Expired:
/*session expired: 会话过期 此场景下需创建新的zk实例,因旧实例已不再有效,无法再与server建立连接.*/
logger.warn("Session expired to zk server,will reconnect.");
reConnect();
break;
case Disconnected:
/*
* 未与任何server建立连接.
* (可能zk server 宕机,需要等待其重启.但首次)
*/
logger.error("zk disconnected");
break;
}
}
/**
* 处理节点事件
* @param event 事件类型
*/
private void nodeEventResolve(WatchedEvent event) {
if(event.getType() != null) {
logger.info("Got Event:{}",event.getType().toString());
eventHandle(event.getPath(),event.getType());
}
}
}
/**
* Zookeeper客户端(单例)
* @author caoawei
*/
public class ZkClient extends ConnectionWatcher {
// 最大重试次数
private static final int MAX_RETRIES = 3;
private static final Charset CHARSET = Charset.forName("UTF-8");
private static class SingletonHolder {
private static ZkClient zkClient = new ZkClient();
}
private AtomicBoolean alive = new AtomicBoolean(false);
private WatcherDispatcher watcherDispatcher;
private ZkClient () {}
public static ZkClient getInstance() {
return SingletonHolder.zkClient;
}
/**
* 初始化ZkClient:
* 初始化方法抛出异常时,建议使用者
* 在应用启动时终止虚拟机的启动,待zookeeper正常启动后
* 再启动应用系统.
*
* @param host zookeeper 地址
* @throws Exception 初始化异常
*/
public ZkClient init(String host) throws Exception {
if(alive.get()) {
return this;
}
synchronized (this) {
if(alive.get()) {
return this;
}
if(watcherDispatcher == null) {
watcherDispatcher = new WatcherDispatcher();
}
connect(host);
alive.set(true);
return this;
}
}
/**
* 注册节点事件处理器
* @param eventHandler 节点事件处理器
*/
public ZkClient registerHandler(EventHandler eventHandler) {
synchronized (this) {
if(watcherDispatcher == null) {
watcherDispatcher = new WatcherDispatcher();
}
watcherDispatcher.registerHandler(eventHandler);
}
return this;
}
/**
* zk节点是否存在
* @param path 节点
* @param watch 是否监控
* @return boolean
* @throws KeeperException zookeeper异常
* @throws InterruptedException zookeeper异常
*/
public boolean exists(String path,boolean watch) throws KeeperException,InterruptedException {
int retries = 0;
while (true) {
try {
Stat stat = zk.exists(path,watch);
return stat != null;
} catch (KeeperException.SessionExpiredException e) {
throw e;
} catch (KeeperException e) {
logger.error("The method exists execute error,error info is "+ e.toString());
if(retries++ == ZkClient.MAX_RETRIES) {
throw e;
}
try {
int sleepTime = ConnectionWatcher.RETRY_PERIOD_INTERVAL * retries;
logger.warn("Method exists 第 {} 次重试,sleep {}s",retries,sleepTime);
TimeUnit.SECONDS.sleep(sleepTime);
} catch (Exception ex) {
// nothing to do
}
}
}
}
/**
* 创建zk节点
* @param path 节点
* @param data 节点数据
* @param createMode 节点类型
* @return 节点路径
* @throws KeeperException zookeeper异常
* @throws InterruptedException zookeeper异常
*/
public String create(String path,String data,CreateMode createMode) throws KeeperException,InterruptedException {
int retries = 0;
while (true) {
try {
Stat stat = zk.exists(path,false);
byte[] val = data == null ? "".getBytes(CHARSET) : data.getBytes(CHARSET);
if(stat == null) {
zk.create(path,val, ZooDefs.Ids.OPEN_ACL_UNSAFE,createMode);
} else {
zk.setData(path,val,stat.getVersion());
}
return path;
} catch (KeeperException.SessionExpiredException e) {
throw e;
} catch (KeeperException e) {
logger.error("The method create execute failure,the error info is "+e.getMessage(),e);
if(retries++ == ZkClient.MAX_RETRIES) {
throw e;
}
try {
int sleepTime = ConnectionWatcher.RETRY_PERIOD_INTERVAL * retries;
logger.warn("Method create 第 {} 次重试,sleep {}s",retries,sleepTime);
TimeUnit.SECONDS.sleep(sleepTime);
} catch (Exception ex) {
// nothing to do
}
}
}
}
/**
* 获取zk节点数据
* @param path 节点
* @param watch 是否监控
* @param stat 当前节点元数据
* @return 节点数据
* @throws KeeperException zookeeper异常
* @throws InterruptedException zookeeper异常
*/
public String getData(String path,boolean watch,Stat stat) throws KeeperException,InterruptedException {
byte[] data = zk.getData(path,watch,stat);
return new String(data,CHARSET);
}
/**
* 设置zk节点数据
* (如果节点不存在则创建节点并设置数据)
* @param path 节点
* @param data 节点数据
* @throws KeeperException zookeeper异常
* @throws InterruptedException zookeeper异常
*/
public void setData(String path, String data) throws KeeperException,InterruptedException {
int retries = 0;
while (true) {
try {
Stat stat = zk.exists(path,false);
byte[] val = data == null ? "".getBytes(CHARSET) : data.getBytes(CHARSET);
if(stat == null) {
zk.create(path,val, ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
} else {
zk.setData(path,val,stat.getVersion());
}
break;
} catch (KeeperException.SessionExpiredException e) {
throw e;
} catch (KeeperException e) {
logger.error("The method setData execute failure,the error info is "+e.getMessage(),e);
if(retries++ == ZkClient.MAX_RETRIES) {
throw e;
}
try {
int sleepTime = ConnectionWatcher.RETRY_PERIOD_INTERVAL * retries;
logger.warn("Method setData 第 {} 次重试,sleep {}s",retries,sleepTime);
TimeUnit.SECONDS.sleep(sleepTime);
} catch (Exception ex) {
// nothing to do
}
}
}
}
/**
* 获取path的子节点
* @param path 父节点
* @param watch 是否监控子节点变化(增删)
* @return 子节点列表(if any)
*/
public List<String> getChildren(String path,boolean watch) {
List<String> rs = new ArrayList<>();
try {
rs = zk.getChildren(path,watch);
} catch (KeeperException e) {
logger.error(e.toString());
e.printStackTrace();
} catch (InterruptedException e) {
logger.error(e.toString());
e.printStackTrace();
}
return rs;
}
/**
* 删除节点
* @param path 节点
* @param version 版本
*/
public void delete(String path,int version) {
try {
zk.delete(path,version);
} catch (KeeperException e) {
logger.error("{},delete error: {}",path,e.getMessage(),e);
} catch (InterruptedException e) {
logger.error("{},delete error: {},interrupted",path,e.getMessage(),e);
}
}
@Override
public void close() {
try {
super.close();
} catch (Exception e) {
logger.error("ZkClient close failure.");
} finally {
watcherDispatcher.clear();
}
}
@Override
protected final void eventHandle(String path, Event.EventType eventType) {
/**
* Adjusting for requirement in the feature,if need.
*
* eg: Using the way of asynchronous to invoke EventHandler,or with timeout.
*/
watcherDispatcher.doDispatch(path,eventType);
}
@Override
protected void keeperStatHandle(WatchedEvent event) {
watcherDispatcher.doDispatch(event);
}
}
public class WatcherDispatcher {
private static Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private List<EventHandler> eventHandlers = new ArrayList<>(1);
private ReentrantLock lock = new ReentrantLock();
/**
* 执行节点事件处理器
* @param nodePath 节点路径
* @param eventType 事件类型
*/
public void doDispatch(String nodePath,Watcher.Event.EventType eventType) {
if(eventType == null || eventType.equals(Watcher.Event.EventType.None)) {
return;
}
logger.info("EventHandler处理器: zk事件: {},关联路径: {}",eventType.toString(),nodePath);
if(eventHandlers.size() > 0) {
try {
for (EventHandler eh : eventHandlers) {
switch (eventType) {
case NodeCreated:
eh.onCreate(nodePath);
break;
case NodeDataChanged:
eh.onDataChanged(nodePath);
break;
case NodeChildrenChanged:
eh.onChildrenChanged(nodePath);
break;
case NodeDeleted:
eh.onDelete(nodePath);
break;
}
}
} catch (Exception e) {
logger.error("EventHandler invoke failure,error info is {}",e.getMessage(),e);
}
}
}
/**
* 执行连接状态事件处理器
* @param event 连接事件
*/
public void doDispatch(WatchedEvent event) {
if(eventHandlers == null || eventHandlers.isEmpty()) {
return;
}
logger.info("EventHandler处理器: zk事件: {}",event.toString());
for (EventHandler eh : eventHandlers) {
eh.onConnection();
}
}
/**
* 注册节点事件Handler
* @param eventHandler
*/
public void registerHandler(EventHandler eventHandler) {
if(eventHandler == null) {
return;
}
lock.lock();
try {
eventHandlers.add(eventHandler);
} finally {
lock.unlock();
}
}
/**
* 清空handlers
*/
public void clear() {
if(eventHandlers.isEmpty()) {
return;
}
lock.lock();
try {
eventHandlers.clear();
} finally {
lock.unlock();
}
}
}
public interface EventHandler {
/**
* 连接建立
* 连接重新建立也会调用此方法
*/
void onConnection();
/**
* 创建新节点事件
* @param path 节点路径
* @see org.apache.zookeeper.ZooKeeper#exists(String, boolean)
*/
void onCreate(String path);
/**
* 节点数据变更事件
* @param path 节点路径
* @see org.apache.zookeeper.ZooKeeper#getData(String, boolean, Stat)
*/
void onDataChanged(String path);
/**
* 节点子节点变更事件
* @param path 节点路径
* @see org.apache.zookeeper.ZooKeeper#getChildren(String, boolean)
*/
void onChildrenChanged(String path);
/**
* 节点删除事件
* @param path 节点路径
* @see org.apache.zookeeper.ZooKeeper#exists(String, boolean)
*/
void onDelete(String path);
}
EventHandler 是用来处理zk事件的
WatcherDispatcher 是用来统一管理EventHandler及调用EventHandler的,这个名字取的不是很贴切.
ConnectWatcher 是为了确保zk连接,监听事件用的,当会话失效,需要重新建立连接. 连接重新建立成功的话,也就是会发生onConnection事件,因此EventHandler里的onConnection方法里要做重新监听所有节点的事情,不这样做的话,那么后续的动态修改便不会再有通知
ZkClient 对操作的一些封装.
这个小工具的策略实现有三种,基于内存,基于缓存(redis),基于zookeeper
剩下的代码就是关于配置的解析和存储数据到zookeeper的代码了,暂时就补贴代码了,代码比较多,下面的代码是基于zookeeper的一个类
public abstract class AbstractZookeeperRuleMethodRouting extends AbstractRemoteRuleMethodRouting implements DisposableBean {
Logger logger = LoggerFactory.getLogger(AbstractZookeeperRuleMethodRouting.class);
// 提取ip的正则表达式
private String dubboRegistry = OptimusConfig.getValue("dubbo.registry");
private String dubboRegistryBack = OptimusConfig.getValue("dubbo.provider.backup");
private String appName = OptimusConfig.getValue("systemInfo.appName", "thor.api");
private int timeout = OptimusConfig.getValue("dubbo.timeout", Integer.class, 2000);
private ThorRemoteConnectionManager thorRemoteConnectionManager;
private String zkAddressForRouting;
private String defaultRootPath;
private ZkClient zkClient;
@Override
public void afterPropertiesSet() throws Exception {
initZkClient();
super.afterPropertiesSet();
}
@Override
public void destroy() {
if(zkClient != null) {
zkClient.close();
}
}
protected void resetAppEntry(List<RoutingRule> list){
String path = getZKPath(appName);
try {
if(!zkClient.exists(path, false)){
createPath(path, null);
}
List<String> nodes = getNodes(path);
if(nodes == null || nodes.isEmpty()){
return;
}
if(list == null || list.isEmpty()){
return;
}
List<String> forCreate = Lists.transform(list, new Function<RoutingRule, String>(){
@Override
public String apply(RoutingRule rule) {
return getZKPath(rule.getZkPath());
}
});
for(String each : nodes){
if(!forCreate.contains(each)){
zkClient.delete(each,-1);
}
}
} catch (Exception e) {
logger.error(e.getMessage());
throw Exceptions.fault(ErrorMessage.errorMessage("thor zk lost", e.getMessage()), e);
}
}
protected void addZK(final RoutingRule rule){
if(rule == null) {
return;
}
if(rule.getId() == null){
rule.setId(UUIDUtil.getID());
}
FutureTask<RoutingRule> future = new FutureTask<>(new Callable<RoutingRule>() {
@Override
public RoutingRule call() throws Exception {
String data = JsonUtils.toJson(rule);
String path = getZKPath(rule.getZkPath());
if(!zkClient.exists(path, false)){
createPath(path, data);
} else {
zkClient.setData(path, data);
}
return rule;
}
});
postRemote(future);
}
protected RoutingRule watch(final RoutingRule rule){
if(rule == null) {
return null;
}
String path = getZKPath(rule.getZkPath());
getData(path);
return rule;
}
/**
* 获取节点事件回调
* @return
*/
protected EventHandler getEventHandler() {
// Subclass could override this method
return new DefaultEventHandler();
}
/**
* 可重写此方法:以适应用户需求
* 但建议保证再启动时zk地址有效且可成功连接的
* @throws Exception
*/
private void initZkClient() throws Exception {
// 建立zk连接
String host = getZkAddress();
zkClient = ZkClient.getInstance().registerHandler(getEventHandler()).init(host);
}
/**
* 此方法目前的用途主要是用来监听应用节点的变化,
* 以达到动态的添加新的配置目的
* (前提条件是,应用中已写好相应代码,否则虽能感知到新配置,却无法真实的生效)
*/
private void afterConnection() {
String appPath = getZKPath(appName);
try {
// 监听节点创建事件
if(ZkClient.getInstance().exists(appPath,true)) {
// 监听节点子节点变化事件
ZkClient.getInstance().getChildren(appPath,true);
}
} catch (KeeperException | InterruptedException e) {
logger.error("afterInit invoke failure,error info is {}",e.getMessage(),e);
throw new RuntimeException(e);
}
}
private void initConnectionManager(){
if(thorRemoteConnectionManager == null) {
if(StringUtil.isEmpty(dubboRegistry)) {
throw new RuntimeException("property dubbo.registry is not set in *.properties");
}
ReferenceConfig<ThorRemoteConnectionManager> reference = new ReferenceConfig<>();
reference.setApplication(new ApplicationConfig(appName));
reference.setRegistry(new RegistryConfig(dubboRegistry));
reference.setInterface(ThorRemoteConnectionManager.class);
reference.setTimeout(timeout);
thorRemoteConnectionManager = reference.get();
if(thorRemoteConnectionManager == null){
reference.setRegistry(new RegistryConfig(dubboRegistryBack));
thorRemoteConnectionManager = reference.get();
}
}
}
private String getDefaultRootPath(){
if(StringUtil.isNotEmpty(defaultRootPath)){
return defaultRootPath;
}
initConnectionManager();
defaultRootPath = thorRemoteConnectionManager.getDefaultPath();
return defaultRootPath;
}
private List<String> getNodes(String path){
List<String> result = Lists.newArrayList();
List<String> child = zkClient.getChildren(path, false);
for(String each : child){
String subPath = path+ "/" +each;
List<String> sub = zkClient.getChildren(subPath, false);
if(sub == null || sub.isEmpty()) {
result.add(subPath);
} else {
List<String> subList = getNodes(subPath);
result.addAll(subList);
}
}
return result;
}
private void createPath(String path, String value){
String[] pathArray = path.split("/");
if(pathArray.length < 1){
return;
}
String pathForCreate = "";
for(String each : pathArray){
if(StringUtil.isEmpty(each)){
continue;
}
// 层层创建节点
try {
pathForCreate += "/"+each;
if(!zkClient.exists(pathForCreate, false)){
if(pathForCreate.equals(path)){
zkClient.create(pathForCreate, value,CreateMode.PERSISTENT);
} else {
zkClient.create(pathForCreate, null,CreateMode.PERSISTENT);
}
}
} catch (KeeperException | InterruptedException e) {
logger.error(e.getMessage());
throw Exceptions.fault(ErrorMessage.errorMessage("thor zk lost", e.getMessage()), e);
}
}
}
private void getData(final String path) {
try{
String data = zkClient.getData(path,true,null);
if(StringUtil.isEmpty(data)) {
return;
}
RoutingRule rule = JsonUtils.fromJson(data, RoutingRule.class);
if(rule != null) {
putLocal(rule);
}
} catch (KeeperException | InterruptedException e) {
logger.error(e.getMessage());
throw Exceptions.fault(ErrorMessage.errorMessage("thor zk lost", e.getMessage()), e);
}
}
private String getZkAddress() {
if(StringUtil.isEmpty(zkAddressForRouting)) {
if(thorRemoteConnectionManager == null) {
initConnectionManager();
}
zkAddressForRouting = thorRemoteConnectionManager.getZkRemoteConnectionString();
if(StringUtil.isEmpty(zkAddressForRouting)) {
throw new RuntimeException("zookeeper address for routing no config");
}
}
return zkAddressForRouting;
}
private String getZKPath(String path){
return getDefaultRootPath()+"/"+path;
}
private class DefaultEventHandler extends EventHandlerAdapter {
@Override
public void onConnection() {
// 防止session失效后,各种监听失效
logger.info("Zk EventHandler处理器,事件: 连接建立");
List<RoutingRule> rules = getRoutings();
if(rules != null && rules.size() > 0) {
for (RoutingRule rule : rules) {
watch(rule);
}
}
afterConnection();
}
@Override
public void onCreate(String path) {
/*
try {
logger.info("Zk EventHandler处理器,事件: 创建节点, node path: {}",path);
int level = nodeLevel(path);
if(level == 4) {
getData(path);
} else {
zkClient.getChildren(path,true);
// 防止因网络延迟,导致无法监听某些子节点的变化
onChildrenChanged(path);
}
} catch (Exception e) {
logger.error("节点:{},获取zk数据异常:{}",path,e.getMessage(),e);
}
*/
// FIXME 以上代码待完善
}
@Override
public void onDataChanged(String path) {
int level = nodeLevel(path);
if(level == 4) {
logger.info("Zk EventHandler处理器,事件: 节点数据变更, node path: {}",path);
getData(path);
}
}
@Override
public void onChildrenChanged(String path) {
/*
List<String> childrenNode = getChildren(path);
if(childrenNode == null || childrenNode.isEmpty()) {
return;
}
int level = nodeLevel(path);
logger.info("Zk EventHandler处理器,事件: 子节点变更, parent node path: {}",path);
try {
for (String node : childrenNode) {
final String nodePath = path + "/" + node;
logger.info("Zk EventHandler处理器,事件: 子节点变更, child node path: {}",nodePath);
if(level == 3) {
getData(nodePath);
} else {
// 非四层节点,继续监听其子节点变化
asynExecute(new Runnable() {
@Override
public void run() {
List<String> childrenNode = ZkClient.getInstance().getChildren(nodePath,true);
if(childrenNode.isEmpty()) {
return;
}
}
});
}
}
} catch (Exception e) {
logger.error("节点:{},获取zk数据异常:{}",path,e.getMessage(),e);
}
*/
// FIXME 以上代码待完善
}
private List<String> getChildren(String path) {
return zkClient.getChildren(path,true);
}
private int nodeLevel(String path) {
if(path == null || "".equals(path)) {
return 0;
}
return StringUtil.delimitedListToStringArray(path.substring(1),"/").length;
}
}
}
public interface MethodRouting {
/**
* 初始化路由规则
*/
void init();
/**
* 重置路由规则成配置文件中的配置
*/
void reset();
/**
* 设置一个路由
* @param rule 路由规则
*/
void setRouting(RoutingRule rule);
/**
* 更新规则
* @param id
* @param percent
*/
void updateRouting(String id, Integer percent);
/**
* 更新一条规则
* @param id
* @param sharding
*/
void updateRouting(String id, String sharding);
/**
* 查看一个路由配置
* @param code
* @return
*/
String getRouting(String code);
/**
* 根据某个值判断路由
* @param code
* @param sharding
* @return
*/
String getRouting(String code, String sharding);
/**
* 获取所有的路由规则, method, routing, rule
* @return
*/
List<RoutingRule> getRoutings();
}
ThorRemoteConnectionManager是一个外部应用需要使用的一个api, 为的是获得这个配置所存储的zookeeper的地址及配置的默认根目录
MethodRouting 也是一个外部接口, 引入此程序的jar包, 在Spring里配置一个实现类,就是上面提到的三个中的一个.
下面一个实际当中使用时的示例:
<thor:routing code="orderAccountList" default="orderQueryService">
<thor:rules>
<thor:rule routing="orderQueryServiceEmpty" percent="0" sharding="20181101000000-20181102235959"/>
<thor:rule routing="orderQueryService" percent="100" sharding="20181115000000-20991231235959"/>
</thor:rules>
</thor:routing>
<thor:routing code="orderUpdatedAccountList" default="orderQueryService">
<thor:rules>
<thor:rule routing="orderQueryServiceEmpty" percent="0" sharding="20181101000000-20181102235959"/>
<thor:rule routing="orderQueryService" percent="100" sharding="20181115000000-20991231235959"/>
</thor:rules>
</thor:routing>
code属性是方法名, default 是默认选择的bean名称
routing 是bean名称, percent是权重,sharding 这个属性没有统一的定义,使用起来比较灵活,可以使用此属性表达不同的逻辑,在这里这个sharding的所表达的意思使用bean的时间段,会忽略percent的配置
到此就结束了.这个小程序还在完善中。