原因: 由于每次开发时都需要开启多个服务,非常占用内存与效率,而我们开发者一般只针对单一模块(服务)进行开发,所以希望能够实现控制dubbo的路由(服务器上的服务与本地服务)
实现逻辑:
利用dubbo的路由规则扩展,如果请求上下文中存在已经配置的ip,即-Ddubbo.protocol.host,则会优先路由到同ip的服务,即你本地服务,如果没有,则匹配其他非本地服务
步骤:
1. 扩展路由规则
创建GrayscaleRouter类, 重点看route方法
import java.util.ArrayList;
import java.util.List;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.cluster.Router;
/**
* 同ip访问同一个服务
*
*
*/
public class GrayscaleRouter implements Router, Comparable<Router> {
private URL url;
private final int priority;
public GrayscaleRouter(URL url) {
this.url = url;
this.priority = url.getParameter(Constants.PRIORITY_KEY, 0);
}
@Override
public int compareTo(Router o) {
if (o == null || o.getClass() != GrayscaleRouter.class) {
return 1;
}
GrayscaleRouter c = (GrayscaleRouter)o;
return this.priority == c.priority ? url.toFullString().compareTo(c.url.toFullString())
: (this.priority > c.priority ? 1 : -1);
}
@Override
public URL getUrl() {
return url;
}
/**
* 实现:添加一个路由规则,即本地服务路由到本地,其他服务调用zk上的
* 1.在启动本地服务时设置环境变量dubbo.protocol.host(本地ip),然后注册到服务器zk上
* 2.新路由规则直接获取本地ip配置,然后遍历所有的invoker(服务),检测如果invoker上的ip是本地ip(本代码本地服务ip是10开头的),
* 将其加入到一个List<Invoker<T>>并返回。
*/
@Override
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL tempUrl, Invocation invocation)
throws RpcException {
String ip = RpcContext.getContext().getAttachment("client_ip");
if (ip == null) {
ip = System.getProperty("dubbo.protocol.host");
}
if (ip == null) {
return excludeLocalService(invokers);
} else {
RpcContext.getContext().setAttachment("client_ip", ip);
}
List<Invoker<T>> result = new ArrayList<Invoker<T>>();
for (Invoker<T> invoker : invokers) {
if (ip.equals(invoker.getUrl().getIp())) {
result.add(invoker);
}
}
if (result.isEmpty()) {
return excludeLocalService(invokers);
}
return result;
}
/**
* 排除本地服务
*
* @param invokers
* @return
*/
private <T> List<Invoker<T>> excludeLocalService(List<Invoker<T>> invokers) {
List<Invoker<T>> result = new ArrayList<Invoker<T>>();
invokers.forEach(invoker -> {
String serviceIp = invoker.getUrl().getIp();
if (!serviceIp.startsWith("10.")) {
result.add(invoker);
}
});
return result;
}
@Override
public boolean equals(Object object) {
return super.equals(object);
}
@Override
public int hashCode(){
return super.hashCode();
}
}
创建GrayscaleRouterFactory类
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.rpc.cluster.Router;
import com.alibaba.dubbo.rpc.cluster.RouterFactory;
/**
* 根据ip访问特定接口
*
*/
public class GrayscaleRouterFactory implements RouterFactory {
@Override
public Router getRouter(URL url) {
return new GrayscaleRouter(url);
}
}
2. zk上添加路由规则
创建并执行DubboConfig类,给所有服务添加路由规则,需自己指定注册中心,新增一个服务就需要执行一次,会自动给所有服务添加路由规则
import java.util.List;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.registry.Registry;
import com.alibaba.dubbo.registry.RegistryFactory;
import com.wu.trade.common.exceptions.CheckException;
/**
* dubbo的配置,路由规则加载
*
*
*/
@Component
public class DubboConfig {
static Logger logger = LoggerFactory.getLogger(DubboConfig.class);
/**
* 添加路由规则
*
* @param args
* @throws Exception
*/
public static void main(String[] args) {
// 服务器zk地址
String zkServerPath = "192.168.16.203:2181";
List<String> paths = lookup(zkServerPath, "/trade");
paths.forEach(System.out::println);
paths.forEach(path -> deleteAll(zkServerPath, String.format("/trade/%s/routers", path)));
register(paths, zkServerPath);
}
/**
* 删除所有zk节点
*
* @throws Exception
*/
public static void deleteAll(String zkServerPath, String path) {
CuratorFramework zkClient =
CuratorFrameworkFactory.newClient(zkServerPath, new ExponentialBackoffRetry(1000, 3));
zkClient.start();
try {
zkClient.delete().guaranteed().deletingChildrenIfNeeded().forPath(path);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
/**
* 检索所有服务
*
* @return
* @throws Exception
*/
public static List<String> lookup(String zkServerPath, String path) {
try {
CuratorFramework zkClient =
CuratorFrameworkFactory.newClient(zkServerPath, new ExponentialBackoffRetry(1000, 3));
zkClient.start();
return zkClient.getChildren().forPath(path);
} catch (Exception e) {
throw new CheckException(e);
}
}
/**
* 注册规则
*
* @param paths
*/
public static void register(List<String> paths, String zkServerPath) {
RegistryFactory registryFactory =
ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://" + zkServerPath + "?group=trade"));
paths.forEach(path -> {
registry.unregister(URL.valueOf("routers://0.0.0.0/" + path + "?name=gray"
+ "&category=routers&router=grayscaleRouter&dynamic=false&version=1.0&runtime=true"));
registry.register(URL.valueOf("routers://0.0.0.0/" + path + "?name=gray"
+ "&category=routers&router=grayscaleRouter&dynamic=false&version=1.0&runtime=true"));
});
}
}
3. 设置系统变量,启动service
-Ddubbo.protocol.host="10.13.130.109" //设置你本地IP
然后就可以实现了
参考资料:
http://dubbo.apache.org/zh-cn/docs/dev/impls/router.html
https://blog.csdn.net/u012345283/article/details/51789196