51 changed files with 750 additions and 241 deletions
@ -1 +1 @@
|
||||
im_entry_export_listener=net.sopod.soim.entry.config.DubboImEntryExporterListener |
||||
im_entry_export_listener=net.sopod.soim.entry.config.ImEntryAPIExporterListener |
||||
@ -1,45 +0,0 @@
|
||||
package net.sopod.soim.entry.api.route; |
||||
|
||||
import net.sopod.soim.common.constant.DubboConstant; |
||||
import net.sopod.soim.common.util.StringUtil; |
||||
import org.apache.dubbo.common.URL; |
||||
import org.apache.dubbo.rpc.Invocation; |
||||
import org.apache.dubbo.rpc.Invoker; |
||||
import org.apache.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* ImEntryServerAddressLoadBalance |
||||
* |
||||
* @author tmy |
||||
* @date 2022-05-02 14:45 |
||||
*/ |
||||
public class ImEntryServerAddressLoadBalance extends AbstractLoadBalance { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ImEntryServerAddressLoadBalance.class); |
||||
|
||||
@Override |
||||
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) { |
||||
String invokeAddr = ""; |
||||
for (Invoker<T> invoker : invokers) { |
||||
invokeAddr += invoker.getUrl().getAddress() + ":" + invoker.getInterface() + ","; |
||||
} |
||||
logger.info("invokers: {}", invokeAddr); |
||||
String entryAddr = invocation.getAttachment(DubboConstant.IM_ENTRY_ADDR); |
||||
if (StringUtil.isEmpty(entryAddr)) { |
||||
throw new IllegalCallerException("上下文 im-entry 服务地址不能为空"); |
||||
} |
||||
logger.info("entryAddr: {}", entryAddr); |
||||
for (Invoker<T> invoker : invokers) { |
||||
if (entryAddr.equals(invoker.getUrl().getAddress())) { |
||||
logger.info("invoker address: {}", invoker.getUrl().getAddress()); |
||||
return invoker; |
||||
} |
||||
} |
||||
throw new IllegalCallerException("没有地址为 " + entryAddr + " im-entry 服务"); |
||||
} |
||||
|
||||
} |
||||
@ -1 +0,0 @@
|
||||
im_entry_loadbalance=net.sopod.soim.entry.api.route.ImEntryServerAddressLoadBalance |
||||
@ -0,0 +1,32 @@
|
||||
package net.sopod.soim.logic.common.util; |
||||
|
||||
import net.sopod.soim.common.constant.DubboConstant; |
||||
import net.sopod.soim.common.util.StringUtil; |
||||
import org.apache.dubbo.rpc.RpcContext; |
||||
|
||||
/** |
||||
* RpcContextUtil |
||||
* |
||||
* @author tmy |
||||
* @date 2022-05-04 22:44 |
||||
*/ |
||||
public class RpcContextUtil { |
||||
|
||||
public static void setContextUid(Long uid) { |
||||
setContextUid(StringUtil.toString(uid)); |
||||
} |
||||
|
||||
public static void setContextUid(String uid) { |
||||
RpcContext.getServiceContext().setAttachment(DubboConstant.CTX_UID, uid); |
||||
} |
||||
|
||||
public static String getContextUid() { |
||||
return RpcContext.getServiceContext().getAttachment(DubboConstant.CTX_UID); |
||||
} |
||||
|
||||
public static Long getContextUidNum() { |
||||
String uid = getContextUid(); |
||||
return uid == null ? null : Long.valueOf(uid); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,24 @@
|
||||
package net.sopod.soim.router.api.model; |
||||
|
||||
import lombok.Data; |
||||
import lombok.experimental.Accessors; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
/** |
||||
* RegistryRes |
||||
* |
||||
* @author tmy |
||||
* @date 2022-05-04 16:05 |
||||
*/ |
||||
@Data |
||||
@Accessors(chain = true) |
||||
public class RegistryRes implements Serializable { |
||||
|
||||
private static final long serialVersionUID = -4367288919640496421L; |
||||
|
||||
private Boolean success; |
||||
|
||||
private String imRouterId; |
||||
|
||||
} |
||||
@ -0,0 +1,44 @@
|
||||
package net.sopod.soim.router.api.route; |
||||
|
||||
import net.sopod.soim.common.constant.DubboConstant; |
||||
import net.sopod.soim.common.util.StringUtil; |
||||
import org.apache.dubbo.common.URL; |
||||
import org.apache.dubbo.rpc.Invocation; |
||||
import org.apache.dubbo.rpc.Invoker; |
||||
import org.apache.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* ImRouterDirectLoadBalance |
||||
* 通过上下文指定的 router 服务地址调用 im-router 服务接口 |
||||
* |
||||
* @author tmy |
||||
* @date 2022-05-04 15:26 |
||||
*/ |
||||
public class ImRouterDirectLoadBalance extends AbstractLoadBalance { |
||||
|
||||
public static final String NAME = "im_router_direct"; |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ImRouterDirectLoadBalance.class); |
||||
|
||||
@Override |
||||
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) { |
||||
String imRouterId = invocation.getAttachment(DubboConstant.IM_ROUTER_ID_KEY); |
||||
logger.info("invocation router id: {}", imRouterId); |
||||
if (StringUtil.isEmpty(imRouterId)) { |
||||
throw new IllegalCallerException("上下文im-router服务id不能为空"); |
||||
} |
||||
for (Invoker<T> invoker : invokers) { |
||||
String invokerId = invoker.getUrl().getParameter(DubboConstant.IM_ROUTER_ID_KEY); |
||||
logger.info("invoker router id: {}", invokerId); |
||||
if (imRouterId.equals(invokerId)) { |
||||
return invoker; |
||||
} |
||||
} |
||||
throw new IllegalCallerException("没有id为" + imRouterId + "的im-router服务"); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,62 @@
|
||||
package net.sopod.soim.router.api.route; |
||||
|
||||
import net.sopod.soim.common.util.HashAlgorithms; |
||||
|
||||
import java.util.Map; |
||||
import java.util.TreeMap; |
||||
|
||||
/** |
||||
* UidConsistentHashSelector |
||||
* |
||||
* @author tmy |
||||
* @date 2022-05-04 10:37 |
||||
*/ |
||||
public class UidConsistentHashSelector<V> { |
||||
|
||||
/** |
||||
* 一致性 hash 每个节点的虚拟节点数量 |
||||
*/ |
||||
private static final int VIRTUAL_NODE_SIZE = 120; |
||||
|
||||
private final TreeMap<Long, V> virtualNodeMap; |
||||
|
||||
private final int identityHashCode; |
||||
|
||||
public UidConsistentHashSelector(Map<String, V> serverAddressMap, int identityHashCode) { |
||||
this.identityHashCode = identityHashCode; |
||||
this.virtualNodeMap = new TreeMap<>(); |
||||
|
||||
// 构建虚拟节点一致性 hash 表
|
||||
for (Map.Entry<String, V> entry : serverAddressMap.entrySet()) { |
||||
String serverAddr = entry.getKey(); |
||||
V value = entry.getValue(); |
||||
for (int i = 0, len = VIRTUAL_NODE_SIZE / 4; i < len; i++) { |
||||
for (int h = 0; h < 4; h++) { |
||||
long hash = hash(serverAddr + i, h); |
||||
this.virtualNodeMap.put(hash, value); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public V select(String uid) { |
||||
if (uid == null) { |
||||
throw new IllegalStateException("im-router consistent hash route, ctx uid can not be null!"); |
||||
} |
||||
long hash = hash(uid, 0); |
||||
Map.Entry<Long, V> entry = virtualNodeMap.ceilingEntry(hash); |
||||
if (entry == null) { |
||||
entry = virtualNodeMap.firstEntry(); |
||||
} |
||||
return entry.getValue(); |
||||
} |
||||
|
||||
private static long hash(String value, int number) { |
||||
return HashAlgorithms.md5Hash(value, number); |
||||
} |
||||
|
||||
public int getIdentityHashCode() { |
||||
return identityHashCode; |
||||
} |
||||
|
||||
} |
||||
@ -1 +1,2 @@
|
||||
im_route_consistent_hash=net.sopod.soim.router.api.route.ImRouterConsistentHashRoute |
||||
im_router_direct=net.sopod.soim.router.api.route.ImRouterDirectLoadBalance |
||||
@ -0,0 +1,26 @@
|
||||
package net.sopod.soim.router.cache; |
||||
|
||||
/** |
||||
* BiSync |
||||
* |
||||
* @author tmy |
||||
* @date 2022-05-04 17:22 |
||||
*/ |
||||
public interface DataSync { |
||||
|
||||
/** 不是更新数据的方法开头 */ |
||||
String[] nonUpdateMethodStart = new String[]{"get", "select", "list"}; |
||||
|
||||
/** |
||||
* 如果是更新方法会同步数据,到新增节点或备份节点 |
||||
*/ |
||||
default boolean isUpdateMethod(String methodName) { |
||||
for (String nonUpdateStart : nonUpdateMethodStart) { |
||||
if (methodName.startsWith(nonUpdateStart)) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,66 @@
|
||||
package net.sopod.soim.router.cache; |
||||
|
||||
import net.sf.cglib.proxy.Enhancer; |
||||
import net.sf.cglib.proxy.MethodInterceptor; |
||||
import net.sf.cglib.proxy.MethodProxy; |
||||
import net.sopod.soim.common.util.Collects; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import java.io.Serializable; |
||||
import java.lang.reflect.Method; |
||||
|
||||
/** |
||||
* BiSyncProxyManager |
||||
* 设置属性的时候,将设置方法同步处理 |
||||
* |
||||
* @author tmy |
||||
* @date 2022-05-04 17:10 |
||||
*/ |
||||
public class DataSyncProxyFactory { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DataSyncProxyFactory.class); |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
public static <T extends DataSync> T newProxyInstance(Class<T> biSyncClazz) { |
||||
Enhancer enhancer = new Enhancer(); |
||||
enhancer.setSuperclass(biSyncClazz); |
||||
enhancer.setCallback(new DataSyncProxyCallback()); |
||||
return (T) enhancer.create(); |
||||
} |
||||
|
||||
public static class DataSyncProxyCallback implements MethodInterceptor { |
||||
|
||||
@Override |
||||
public Object intercept(Object instance, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { |
||||
String methodName = method.getName(); |
||||
// 是判断是否更新的方法跳过
|
||||
if ("isUpdateMethod".equals(methodName)) { |
||||
return methodProxy.invokeSuper(instance, args); |
||||
} |
||||
boolean isUpdateMethod = ((DataSync) instance).isUpdateMethod(methodName); |
||||
if (!isUpdateMethod) { |
||||
return methodProxy.invokeSuper(instance, args); |
||||
} |
||||
if (Collects.isNotEmpty(args)) { |
||||
// TODO 参数值可能为 null,检查参数可序列化放在生成代理对象时
|
||||
for (int i = 0; i < args.length; i++) { |
||||
if (!(args[i] instanceof Serializable)) { |
||||
logger.error("类:{} 更新方法:{} 第{}i个参数不可序列化", instance.getClass(), methodName, i+1); |
||||
} |
||||
} |
||||
} |
||||
// TODO 记录更新操作(方法和参数),查询数据订阅者,异步同步数据
|
||||
System.out.println("intercept invoke:" + methodName); |
||||
return methodProxy.invokeSuper(instance, args); |
||||
} |
||||
|
||||
} |
||||
|
||||
public static void main(String[] args) { |
||||
RouterUser routerUser = newProxyInstance(RouterUser.class); |
||||
routerUser.setAccount("日月光"); |
||||
System.out.println(routerUser.getAccount()); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,12 @@
|
||||
package net.sopod.soim.router.cache.annotation; |
||||
|
||||
/** |
||||
* BiSyncIgnore |
||||
* 忽略方法同步 |
||||
* |
||||
* @author tmy |
||||
* @date 2022-05-04 17:39 |
||||
*/ |
||||
public @interface DataSyncIgnore { |
||||
|
||||
} |
||||
@ -0,0 +1,50 @@
|
||||
package net.sopod.soim.router.config; |
||||
|
||||
import net.sopod.soim.common.util.HashAlgorithms; |
||||
import net.sopod.soim.common.util.StringUtil; |
||||
import org.apache.dubbo.common.URL; |
||||
|
||||
import java.util.List; |
||||
import java.util.concurrent.CopyOnWriteArrayList; |
||||
|
||||
/** |
||||
* ImRouterContextHolder |
||||
* im-router 应用上下文信息 |
||||
* TODO 启动指定 im-router 服务类型 service/backup |
||||
* TODO backup 指定备份 im-router 或随机备份未备份 im-router 服务 |
||||
* |
||||
* @author tmy |
||||
* @date 2022-05-04 09:21 |
||||
*/ |
||||
public class ImRouterAppContextHolder { |
||||
|
||||
/** |
||||
* 要注册的 provider 服务的 url 列表 |
||||
*/ |
||||
private static final List<URL> registryInvokerUrls = new CopyOnWriteArrayList<>(); |
||||
|
||||
private static String appServiceAddr; |
||||
|
||||
public static final String IM_ROUTER_ID; |
||||
|
||||
static { |
||||
IM_ROUTER_ID = String.valueOf(HashAlgorithms.md5Hash(StringUtil.randomUUID())); |
||||
} |
||||
|
||||
public static void addRegistryInvokerUrl(URL registryInvokerUrl) { |
||||
registryInvokerUrls.add(registryInvokerUrl); |
||||
} |
||||
|
||||
public static List<URL> getRegistryInvokerUrls() { |
||||
return registryInvokerUrls; |
||||
} |
||||
|
||||
public static void setAppServiceAddr(String appServiceAddr) { |
||||
ImRouterAppContextHolder.appServiceAddr = appServiceAddr; |
||||
} |
||||
|
||||
public static String getAppServiceAddr() { |
||||
return appServiceAddr; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,126 @@
|
||||
package net.sopod.soim.router.config; |
||||
|
||||
import com.alibaba.nacos.api.NacosFactory; |
||||
import com.alibaba.nacos.api.exception.NacosException; |
||||
import com.alibaba.nacos.api.naming.NamingService; |
||||
import com.alibaba.nacos.api.naming.pojo.Instance; |
||||
import net.sopod.soim.common.constant.AppConstant; |
||||
import net.sopod.soim.common.constant.DubboConstant; |
||||
import net.sopod.soim.common.util.Collects; |
||||
import net.sopod.soim.router.api.route.UidConsistentHashSelector; |
||||
import org.apache.dubbo.common.URL; |
||||
import org.apache.dubbo.registry.Registry; |
||||
import org.apache.dubbo.registry.support.RegistryManager; |
||||
import org.apache.dubbo.rpc.model.ApplicationModel; |
||||
import org.apache.dubbo.rpc.proxy.AbstractProxyInvoker; |
||||
import org.apache.dubbo.spring.boot.context.event.AwaitingNonWebApplicationListener; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.boot.context.event.ApplicationReadyEvent; |
||||
import org.springframework.context.ApplicationListener; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.core.Ordered; |
||||
|
||||
import java.util.*; |
||||
|
||||
/** |
||||
* AppcationInitialed |
||||
* 参考 dubbo {@link AwaitingNonWebApplicationListener} |
||||
* |
||||
* @author tmy |
||||
* @date 2022-05-04 09:51 |
||||
*/ |
||||
@Configuration |
||||
public class ImRouterAppOnReady implements ApplicationListener<ApplicationReadyEvent>, Ordered { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ImRouterAppOnReady.class); |
||||
|
||||
/** |
||||
* 同步一致性hash临近节点数据 |
||||
*/ |
||||
@Override |
||||
public void onApplicationEvent(ApplicationReadyEvent event) { |
||||
// 服务已可用进行注册
|
||||
this.doRegistry(); |
||||
} |
||||
|
||||
/** |
||||
* 注册 im-router 的API接口服务 |
||||
*/ |
||||
private void doRegistry() { |
||||
RegistryManager registryManager = ApplicationModel.defaultModel().getBeanFactory() |
||||
.getBean(RegistryManager.class); |
||||
Collection<Registry> registries = registryManager.getRegistries(); |
||||
List<URL> registryInvokerUrls = ImRouterAppContextHolder.getRegistryInvokerUrls(); |
||||
if (Collects.isNotEmpty(registries) |
||||
&& Collects.isNotEmpty(registryInvokerUrls)) { |
||||
for (Registry registry : registries) { |
||||
for (URL invokerUrl : registryInvokerUrls) { |
||||
// 添加 im-router 服务id参数,生成新的 url
|
||||
URL url = invokerUrl.addParameter( |
||||
DubboConstant.IM_ROUTER_ID_KEY, |
||||
ImRouterAppContextHolder.IM_ROUTER_ID |
||||
); |
||||
registry.register(url); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public int getOrder() { |
||||
return Ordered.HIGHEST_PRECEDENCE; |
||||
} |
||||
|
||||
/** |
||||
* 一致性hash,使用了虚拟节点会导致迁移多个数据节点 |
||||
* |
||||
* 应用启动后,进行新增 im-router 节点逻辑处理 |
||||
* 0.获取分布式全局锁,成功开始同步数据(TODO nacos不支持,想其他办法) |
||||
* 1.获取现有所有 im-router 节点,加上当前节点,生成新的一致性hash虚拟节点表 |
||||
* 2.计算所有需要迁移数据的节点,当前虚拟节点的顺时针临近节点 |
||||
* 3.依次调用数据迁移节点,分页拉取router缓存用户数据(如有数据节点获取失败,探测是否可用,不可用从第1步重新开始;) |
||||
* 如已拉取数据有更新需要双写??[CGLib], 服务注册前需要被调用,开netty http服务与dubbo服务端口偏移量1000 |
||||
* |
||||
* 4.数据迁移完成后,注册dubbo服务,依次调用所有迁移数据节点可清空用户数据(用户数据已移过来了失败不用管) |
||||
*/ |
||||
private void nameServerTestCode() { |
||||
String serverAddr = "124.222.131.236:3848"; |
||||
Properties properties = new Properties(); |
||||
properties.put("serverAddr", serverAddr); |
||||
|
||||
// 同步一致性 hash 临近节点数据
|
||||
try { |
||||
// 获取当前服务实例
|
||||
NamingService namingService = NacosFactory.createNamingService(properties); |
||||
List<Instance> imRouterInstances = namingService.getAllInstances(AppConstant.APP_IM_ROUTER_NAME); |
||||
|
||||
//namingService.registerInstance();
|
||||
Instance instance1 = new Instance(); |
||||
instance1.addMetadata("LOCK_VAL", "123123"); |
||||
// instance1.getMetadata();
|
||||
|
||||
logger.info("instances: {}", imRouterInstances); |
||||
if (!Collects.isEmpty(imRouterInstances)) { |
||||
Map<String, String> consistentHashNodeMap = new HashMap<>(); |
||||
Map<String, Instance> stringInstanceMap = Collects.collect2Map(imRouterInstances, |
||||
Instance::toInetAddr, |
||||
new HashMap<>(Collects.mapCapacity(imRouterInstances.size())) |
||||
); |
||||
Map<String, String> map = new HashMap<>(Collects.mapCapacity(imRouterInstances.size())); |
||||
for (Instance instance : imRouterInstances) { |
||||
String serverInetAddr = instance.toInetAddr();// 服务地址, 如: 192.168.56.1:3031
|
||||
map.put(serverInetAddr, serverInetAddr); |
||||
} |
||||
UidConsistentHashSelector<String> selector = new UidConsistentHashSelector<>(map, imRouterInstances.hashCode()); |
||||
|
||||
logger.info("instances addr: {}", imRouterInstances.get(0).toInetAddr()); |
||||
logger.info("instances addr: {}:{}", imRouterInstances.get(0).getIp(), imRouterInstances.get(0).getPort()); |
||||
} |
||||
logger.info("application ready event..."); |
||||
} catch (NacosException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,40 @@
|
||||
package net.sopod.soim.router.config.listener; |
||||
|
||||
import net.sopod.soim.router.config.ImRouterAppContextHolder; |
||||
import org.apache.dubbo.common.URL; |
||||
import org.apache.dubbo.rpc.Exporter; |
||||
import org.apache.dubbo.rpc.ExporterListener; |
||||
import org.apache.dubbo.rpc.RpcException; |
||||
import org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
/** |
||||
* ImRouterExportListener |
||||
* 监听需要注册的服务URL,存储到context,后根据需要注册到注册中心 |
||||
* |
||||
* @author tmy |
||||
* @date 2022-05-01 16:31 |
||||
*/ |
||||
public class ImRouterAPIExportListener implements ExporterListener { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ImRouterAPIExportListener.class); |
||||
|
||||
@Override |
||||
public void exported(Exporter<?> exporter) throws RpcException { |
||||
URL invokerUrl = exporter.getInvoker().getUrl(); |
||||
if (!InjvmProtocol.NAME.equals(invokerUrl.getProtocol())) { |
||||
ImRouterAppContextHolder.addRegistryInvokerUrl(invokerUrl); |
||||
if (ImRouterAppContextHolder.getAppServiceAddr() == null) { |
||||
ImRouterAppContextHolder.setAppServiceAddr(invokerUrl.getAddress()); |
||||
logger.info("im-router registry serverAddr: {}", invokerUrl.getAddress()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void unexported(Exporter<?> exporter) { |
||||
logger.info("unexported listener: {}", exporter.getInvoker().getInterface()); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,56 @@
|
||||
package net.sopod.soim.router.config.loadbalance; |
||||
|
||||
import net.sopod.soim.common.constant.DubboConstant; |
||||
import net.sopod.soim.common.util.StringUtil; |
||||
import net.sopod.soim.logic.common.util.RpcContextUtil; |
||||
import net.sopod.soim.router.cache.RouterUser; |
||||
import net.sopod.soim.router.cache.SoImUserCache; |
||||
import org.apache.dubbo.common.URL; |
||||
import org.apache.dubbo.rpc.Invocation; |
||||
import org.apache.dubbo.rpc.Invoker; |
||||
import org.apache.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* ImEntryServerAddressLoadBalance |
||||
* |
||||
* @author tmy |
||||
* @date 2022-05-02 14:45 |
||||
*/ |
||||
public class ImEntryServerAddressLoadBalance extends AbstractLoadBalance { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ImEntryServerAddressLoadBalance.class); |
||||
|
||||
@Override |
||||
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) { |
||||
// 调用 im-entry 服务的地址
|
||||
String entryAddr = null; |
||||
// 获取请求链路用户id
|
||||
// String ctxUid = invocation.getAttachment(DubboConstant.CTX_UID);
|
||||
String ctxUid = RpcContextUtil.getContextUid(); |
||||
if (StringUtil.isEmpty(ctxUid)) { |
||||
throw new IllegalCallerException("调用im-entry服务接口,上下文uid未指定"); |
||||
} |
||||
SoImUserCache soImUserCache = SoImUserCache.getInstance(); |
||||
RouterUser routerUser = soImUserCache.get(Long.valueOf(ctxUid)); |
||||
if (routerUser != null) { |
||||
entryAddr = routerUser.getImEntryAddr(); |
||||
} |
||||
if (entryAddr == null) { |
||||
throw new IllegalCallerException("调用im-entry服务接口,上下文entry服务地址未指定"); |
||||
} |
||||
logger.info("invoke im-entry: uid={}, im-entry addr={}", ctxUid, entryAddr); |
||||
// 返回地址为 entryAddr 的 invoker
|
||||
for (Invoker<T> invoker : invokers) { |
||||
if (entryAddr.equals(invoker.getUrl().getAddress())) { |
||||
return invoker; |
||||
} |
||||
} |
||||
// TODO 自定义异常,用户重新登录
|
||||
throw new IllegalCallerException("没有地址为 " + entryAddr + " im-entry 服务"); |
||||
} |
||||
|
||||
} |
||||
@ -1,25 +0,0 @@
|
||||
package net.sopod.soim.router.listener; |
||||
|
||||
import org.apache.dubbo.rpc.Exporter; |
||||
import org.apache.dubbo.rpc.ExporterListener; |
||||
import org.apache.dubbo.rpc.RpcException; |
||||
|
||||
/** |
||||
* ImRouterExportListener |
||||
* |
||||
* @author tmy |
||||
* @date 2022-05-01 16:31 |
||||
*/ |
||||
public class ImRouterExportListener implements ExporterListener { |
||||
|
||||
@Override |
||||
public void exported(Exporter<?> exporter) throws RpcException { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void unexported(Exporter<?> exporter) { |
||||
|
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1 @@
|
||||
im_router_api_export_listener=net.sopod.soim.router.config.listener.ImRouterAPIExportListener |
||||
@ -1 +1 @@
|
||||
invoke_im_entry_filter=net.sopod.soim.router.config.InvokeImEntryFilter |
||||
invoke_im_entry_filter=net.sopod.soim.router.config.filter.InvokeImEntryFilter |
||||
@ -0,0 +1 @@
|
||||
im_entry_loadbalance=net.sopod.soim.router.config.loadbalance.ImEntryServerAddressLoadBalance |
||||
Loading…
Reference in new issue