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_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