33 changed files with 414 additions and 76 deletions
@ -0,0 +1,19 @@ |
|||||||
|
package net.sopod.soim.common.constant; |
||||||
|
|
||||||
|
/** |
||||||
|
* DeviceEnum |
||||||
|
* TODO 登录时客户端传递设备类型 |
||||||
|
* http登录接口token中包含当前账号在线顺序 |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-05-02 14:09 |
||||||
|
*/ |
||||||
|
public enum DeviceEnum { |
||||||
|
|
||||||
|
PC, |
||||||
|
|
||||||
|
ANDROID, |
||||||
|
|
||||||
|
IOS |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
package net.sopod.soim.entry.config; |
||||||
|
|
||||||
|
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.dubbo.DubboProtocol; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
|
||||||
|
/** |
||||||
|
* DubboExporterListener |
||||||
|
* 获取服务注册地址 im-entry |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-05-02 09:55 |
||||||
|
*/ |
||||||
|
public class DubboImEntryExporterListener implements ExporterListener { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(DubboImEntryExporterListener.class); |
||||||
|
|
||||||
|
@Override |
||||||
|
public void exported(Exporter<?> exporter) throws RpcException { |
||||||
|
URL invokerUrl = exporter.getInvoker().getUrl(); |
||||||
|
if (DubboProtocol.NAME.equals(invokerUrl.getProtocol())) { |
||||||
|
if (ApplicationContextHolder.getDubboAppServiceAddr() == null) { |
||||||
|
logger.info("im-entry dubbo app serverAddr: {}", invokerUrl.getAddress()); |
||||||
|
ApplicationContextHolder.setDubboAppServiceAddr(invokerUrl.getAddress()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void unexported(Exporter<?> exporter) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
im_entry_export_listener=net.sopod.soim.entry.config.DubboImEntryExporterListener |
||||||
@ -0,0 +1,45 @@ |
|||||||
|
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 服务"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
package net.sopod.soim.entry.api.service; |
||||||
|
|
||||||
|
/** |
||||||
|
* OnlineUserService |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-05-02 22:44 |
||||||
|
*/ |
||||||
|
public interface OnlineUserService { |
||||||
|
|
||||||
|
String getImEntryAddrByUid(Long uid); |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
im_entry_loadbalance=net.sopod.soim.entry.api.route.ImEntryServerAddressLoadBalance |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
|
||||||
|
其他服务到 router 的服务,通过 uid 进行一致性 hash 负载均衡, |
||||||
|
|
||||||
|
router 到 entry 的服务,通过指定 user 所登录的 entry 节点 serverAddr 地址进行指向性路由负载均衡。 |
||||||
|
|
||||||
@ -0,0 +1,37 @@ |
|||||||
|
package net.sopod.soim.router.cache; |
||||||
|
|
||||||
|
import net.sopod.soim.router.api.model.RouterUser; |
||||||
|
|
||||||
|
import java.util.Map; |
||||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||||
|
|
||||||
|
/** |
||||||
|
* ImUserCache |
||||||
|
* 在线用户属性缓存 |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-05-02 14:07 |
||||||
|
*/ |
||||||
|
public class SoImUserCache { |
||||||
|
|
||||||
|
private static final SoImUserCache INSTANCE = new SoImUserCache(); |
||||||
|
|
||||||
|
private final ConcurrentHashMap<Long, RouterUser> routerUserMap = new ConcurrentHashMap<>(128); |
||||||
|
|
||||||
|
public static SoImUserCache getInstance() { |
||||||
|
return INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
public void put(Long uid, RouterUser routerUser) { |
||||||
|
routerUserMap.put(uid, routerUser); |
||||||
|
} |
||||||
|
|
||||||
|
public RouterUser get(Long uid) { |
||||||
|
return routerUserMap.get(uid); |
||||||
|
} |
||||||
|
|
||||||
|
public Map<Long, RouterUser> getRouterUserMap() { |
||||||
|
return routerUserMap; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,49 @@ |
|||||||
|
package net.sopod.soim.router.config; |
||||||
|
|
||||||
|
import net.sopod.soim.common.constant.DubboConstant; |
||||||
|
import net.sopod.soim.router.api.model.RouterUser; |
||||||
|
import net.sopod.soim.router.cache.SoImUserCache; |
||||||
|
import org.apache.dubbo.rpc.*; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
/** |
||||||
|
* InvokeImEntryFilter |
||||||
|
* 调用 im-entry 服务 serverAddr 上下文设置 |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-05-02 14:56 |
||||||
|
*/ |
||||||
|
public class InvokeImEntryFilter implements Filter { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(InvokeImEntryFilter.class); |
||||||
|
|
||||||
|
@Override |
||||||
|
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { |
||||||
|
String serviceInterface = invoker.getUrl().getServiceInterface(); |
||||||
|
logger.info("invoke interface: {}", serviceInterface); |
||||||
|
// 判断是 im-entry 服务接口
|
||||||
|
if (serviceInterface.startsWith(DubboConstant.IM_ENTRY_SERVICE_API_PACK)) { |
||||||
|
String imEntryServerAddr = null; |
||||||
|
// 获取请求链路用户id
|
||||||
|
String ctxUid = invocation.getAttachment(DubboConstant.CTX_UID); |
||||||
|
if (ctxUid != null) { |
||||||
|
SoImUserCache soImUserCache = SoImUserCache.getInstance(); |
||||||
|
RouterUser routerUser = soImUserCache.get(Long.valueOf(ctxUid)); |
||||||
|
if (routerUser != null) { |
||||||
|
imEntryServerAddr = routerUser.getImEntryAddr(); |
||||||
|
} |
||||||
|
} |
||||||
|
if (imEntryServerAddr == null) { |
||||||
|
throw new IllegalCallerException("调用im-entry服务接口,上下文服务地址未指定"); |
||||||
|
} |
||||||
|
String uid2 = RpcContext.getServerContext().getAttachment(DubboConstant.CTX_UID); |
||||||
|
String uid3 = RpcContext.getServiceContext().getAttachment(DubboConstant.CTX_UID); |
||||||
|
logger.info("ctxUid: {}, {}, {}", ctxUid, uid2, uid3); |
||||||
|
// 设置 im-entry 服务地址
|
||||||
|
invocation.setAttachment(DubboConstant.IM_ENTRY_ADDR, imEntryServerAddr); |
||||||
|
} |
||||||
|
return invoker.invoke(invocation); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,39 +0,0 @@ |
|||||||
package net.sopod.soim.router.listener; |
|
||||||
|
|
||||||
import java.rmi.*; |
|
||||||
import java.rmi.registry.Registry; |
|
||||||
|
|
||||||
/** |
|
||||||
* ImRouterServiceRegistry |
|
||||||
* |
|
||||||
* @author tmy |
|
||||||
* @date 2022-05-01 16:34 |
|
||||||
*/ |
|
||||||
public class ImRouterServiceRegistry implements Registry { |
|
||||||
|
|
||||||
@Override |
|
||||||
public Remote lookup(String s) throws RemoteException, NotBoundException, AccessException { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void bind(String s, Remote remote) throws RemoteException, AlreadyBoundException, AccessException { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void unbind(String s) throws RemoteException, NotBoundException, AccessException { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void rebind(String s, Remote remote) throws RemoteException, AccessException { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public String[] list() throws RemoteException, AccessException { |
|
||||||
return new String[0]; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,23 @@ |
|||||||
|
package net.sopod.soim.router.service; |
||||||
|
|
||||||
|
import net.sopod.soim.entry.api.service.OnlineUserService; |
||||||
|
import net.sopod.soim.router.api.model.RouterUser; |
||||||
|
import net.sopod.soim.router.cache.SoImUserCache; |
||||||
|
import org.apache.dubbo.config.annotation.DubboService; |
||||||
|
|
||||||
|
/** |
||||||
|
* SoImUserServiceImpl |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-05-02 22:45 |
||||||
|
*/ |
||||||
|
@DubboService |
||||||
|
public class OnlineUserServiceImpl implements OnlineUserService { |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getImEntryAddrByUid(Long uid) { |
||||||
|
RouterUser routerUser = SoImUserCache.getInstance().get(uid); |
||||||
|
return routerUser == null ? null : routerUser.getImEntryAddr(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
package net.sopod.soim.router.util; |
||||||
|
|
||||||
|
import net.sopod.soim.common.constant.DubboConstant; |
||||||
|
import net.sopod.soim.router.api.model.RouterUser; |
||||||
|
import net.sopod.soim.router.cache.SoImUserCache; |
||||||
|
import org.apache.dubbo.rpc.RpcContext; |
||||||
|
|
||||||
|
import java.util.Objects; |
||||||
|
|
||||||
|
/** |
||||||
|
* RpcContextUtil |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-05-02 22:27 |
||||||
|
*/ |
||||||
|
public class RpcContextUtil { |
||||||
|
|
||||||
|
public static boolean setImEntryRouteServerAddrByUid(Long uid) { |
||||||
|
Objects.requireNonNull(uid, "设置im-entry路由参数uid不能为空"); |
||||||
|
RpcContext.getServiceContext().setAttachment(DubboConstant.CTX_UID, uid); |
||||||
|
RouterUser routerUser = SoImUserCache.getInstance().get(uid); |
||||||
|
String imEntryAddr; |
||||||
|
if (routerUser == null |
||||||
|
|| null == (imEntryAddr = routerUser.getImEntryAddr())) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
RpcContext.getServiceContext().setAttachment(DubboConstant.IM_ENTRY_ADDR, imEntryAddr); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1 @@ |
|||||||
|
invoke_im_entry_filter=net.sopod.soim.router.config.InvokeImEntryFilter |
||||||
Loading…
Reference in new issue