29 changed files with 664 additions and 201 deletions
@ -0,0 +1,39 @@ |
|||||||
|
package net.sopod.soim.common.util; |
||||||
|
|
||||||
|
import java.lang.reflect.Type; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* Reflects |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-04-10 23:54 |
||||||
|
*/ |
||||||
|
public class Reflects { |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取父类上的泛型 |
||||||
|
* @return 父类上的泛型 |
||||||
|
*/ |
||||||
|
public static List<String> getSuperclassGenericTypes(Class<?> clazz) { |
||||||
|
// 获取 handler 的泛型消息
|
||||||
|
Type superType = clazz.getGenericSuperclass(); |
||||||
|
String typeName = superType.getTypeName(); |
||||||
|
int idx = typeName.indexOf('<'); |
||||||
|
if (idx == -1) { |
||||||
|
// 父类没有泛型
|
||||||
|
return Collections.emptyList(); |
||||||
|
} |
||||||
|
String genericName = typeName.substring(idx + 1, typeName.length() - 1); |
||||||
|
// 父类只有一个泛型
|
||||||
|
if (!genericName.contains(",")) { |
||||||
|
return Collections.singletonList(genericName); |
||||||
|
} |
||||||
|
// 父类有多个泛型
|
||||||
|
String[] genericNames = genericName.split(", "); |
||||||
|
return Arrays.asList(genericNames); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,28 @@ |
|||||||
|
package net.sopod.soim.core.handler; |
||||||
|
|
||||||
|
import com.google.protobuf.MessageLite; |
||||||
|
import net.sopod.soim.core.session.Account; |
||||||
|
import net.sopod.soim.core.session.NetUser; |
||||||
|
|
||||||
|
/** |
||||||
|
* AccountMessageHandler |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-04-10 23:41 |
||||||
|
*/ |
||||||
|
public abstract class AccountMessageHandler<T> implements MessageHandler<T> { |
||||||
|
|
||||||
|
@Override |
||||||
|
public final void exec(NetUser netUser, T msg) { |
||||||
|
if (!netUser.isAccount()) { |
||||||
|
throw new IllegalStateException("NetUser is not account!" + netUser); |
||||||
|
} |
||||||
|
MessageLite res = handle((Account) netUser, msg); |
||||||
|
if (res != null) { |
||||||
|
netUser.write(res); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public abstract MessageLite handle(Account account, T msg); |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,15 @@ |
|||||||
|
package net.sopod.soim.core.handler; |
||||||
|
|
||||||
|
import net.sopod.soim.core.session.NetUser; |
||||||
|
|
||||||
|
/** |
||||||
|
* MessageHandler |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-04-10 23:40 |
||||||
|
*/ |
||||||
|
public interface MessageHandler<T> { |
||||||
|
|
||||||
|
void exec(NetUser netUser, T msg); |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,24 @@ |
|||||||
|
package net.sopod.soim.core.handler; |
||||||
|
|
||||||
|
import com.google.protobuf.MessageLite; |
||||||
|
import net.sopod.soim.core.session.NetUser; |
||||||
|
|
||||||
|
/** |
||||||
|
* NetUserMessageHandler |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-04-10 23:40 |
||||||
|
*/ |
||||||
|
public abstract class NetUserMessageHandler<T> implements MessageHandler<T> { |
||||||
|
|
||||||
|
@Override |
||||||
|
public final void exec(NetUser netUser, T msg) { |
||||||
|
MessageLite res = handle(netUser, msg); |
||||||
|
if (res != null) { |
||||||
|
netUser.write(res); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public abstract MessageLite handle(NetUser netUser, T msg); |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
package net.sopod.soim.core.handler; |
||||||
|
|
||||||
|
import com.google.protobuf.MessageLite; |
||||||
|
import net.sopod.soim.core.session.NetUser; |
||||||
|
|
||||||
|
/** |
||||||
|
* ProtoMessageHandler |
||||||
|
* <T extends MessageLite> |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-04-10 19:19 |
||||||
|
*/ |
||||||
|
public abstract class ProtoMessageHandler<T> { |
||||||
|
|
||||||
|
public final void exec(NetUser netUser, T msg) { |
||||||
|
MessageLite res = handle(msg); |
||||||
|
if (res != null) { |
||||||
|
netUser.write(res); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public abstract Class<T> type(); |
||||||
|
|
||||||
|
public abstract MessageLite handle(T msg); |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,21 @@ |
|||||||
|
package net.sopod.soim.core.net; |
||||||
|
|
||||||
|
import io.netty.util.AttributeKey; |
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||||
|
|
||||||
|
/** |
||||||
|
* AttributeKeys |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-04-10 23:26 |
||||||
|
*/ |
||||||
|
public interface AttributeKeys { |
||||||
|
|
||||||
|
/** channel 写失败次数 */ |
||||||
|
AttributeKey<AtomicInteger> WRITE_FAIL_TIMES = AttributeKey.valueOf("WRITE_FAIL_TIMES"); |
||||||
|
|
||||||
|
/** channel 登录失败次数 */ |
||||||
|
AttributeKey<AtomicInteger> LOGIN_FAIL_TIMES = AttributeKey.valueOf("LOGIN_FAIL_TIMES"); |
||||||
|
|
||||||
|
} |
||||||
@ -1,52 +0,0 @@ |
|||||||
package net.sopod.soim.core.net; |
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf; |
|
||||||
import io.netty.channel.ChannelHandlerContext; |
|
||||||
import io.netty.channel.CombinedChannelDuplexHandler; |
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder; |
|
||||||
import io.netty.handler.codec.MessageToByteEncoder; |
|
||||||
import net.sopod.soim.data.serialize.ImMessage; |
|
||||||
import org.slf4j.Logger; |
|
||||||
import org.slf4j.LoggerFactory; |
|
||||||
|
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* ImEntryCodec |
|
||||||
* |
|
||||||
* @author tmy |
|
||||||
* @date 2022-03-28 11:29 |
|
||||||
*/ |
|
||||||
public class ImEntryCodec extends CombinedChannelDuplexHandler<ImEntryCodec.ImDecoder, ImEntryCodec.ImEncoder> { |
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ImEntryCodec.class); |
|
||||||
|
|
||||||
public ImEntryCodec() { |
|
||||||
super(new ImDecoder(), new ImEncoder()); |
|
||||||
} |
|
||||||
|
|
||||||
public static class ImDecoder extends ByteToMessageDecoder { |
|
||||||
@Override |
|
||||||
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> list) throws Exception { |
|
||||||
ImMessage message = ImMessage.read(byteBuf); |
|
||||||
boolean isMagicError; |
|
||||||
if ((isMagicError = (message == ImMessage.MAGIC_ERROR)) |
|
||||||
|| message == ImMessage.PROTOCOL_ERROR) { |
|
||||||
logger.warn("decode im message error: {}, remote={}, closing channel.", |
|
||||||
isMagicError ? "MagicError" : "ProtocolError", |
|
||||||
ctx.channel().remoteAddress()); |
|
||||||
ctx.channel().close(); |
|
||||||
return; |
|
||||||
} |
|
||||||
list.add(message); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public static class ImEncoder extends MessageToByteEncoder<ImMessage> { |
|
||||||
@Override |
|
||||||
protected void encode(ChannelHandlerContext ctx, ImMessage imMessage, ByteBuf byteBuf) throws Exception { |
|
||||||
imMessage.write(byteBuf); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,76 @@ |
|||||||
|
package net.sopod.soim.core.net; |
||||||
|
|
||||||
|
import com.google.protobuf.MessageLite; |
||||||
|
import io.netty.buffer.ByteBuf; |
||||||
|
import io.netty.buffer.Unpooled; |
||||||
|
import io.netty.channel.ChannelHandlerContext; |
||||||
|
import io.netty.channel.CombinedChannelDuplexHandler; |
||||||
|
import io.netty.handler.codec.ByteToMessageDecoder; |
||||||
|
import io.netty.handler.codec.MessageToByteEncoder; |
||||||
|
import net.sopod.soim.data.proto.ProtoMessageManager; |
||||||
|
import net.sopod.soim.data.serialize.ImMessage; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import java.lang.reflect.Type; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* ImEntryCodec |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-03-28 11:29 |
||||||
|
*/ |
||||||
|
public class ImMessageCodec //extends MessageToMessageCodec<ByteBuf, MessageLite> {
|
||||||
|
extends CombinedChannelDuplexHandler<ImMessageCodec.ProtoMsgDecoder, ImMessageCodec.ProtoMsgEncoder> { |
||||||
|
public static void main(String[] args) throws ClassNotFoundException { |
||||||
|
|
||||||
|
Type superType = ImMessageCodec.class.getGenericSuperclass(); |
||||||
|
String typeName = superType.getTypeName(); |
||||||
|
int idx = typeName.indexOf('<'); |
||||||
|
String genericName = typeName.substring(idx + 1, typeName.length() - 1); |
||||||
|
System.out.println(genericName.trim()); |
||||||
|
System.out.println(Arrays.toString(genericName.split(", "))); |
||||||
|
} |
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ImMessageCodec.class); |
||||||
|
|
||||||
|
public ImMessageCodec() { |
||||||
|
super(new ProtoMsgDecoder(), new ProtoMsgEncoder()); |
||||||
|
} |
||||||
|
|
||||||
|
public static class ProtoMsgDecoder extends ByteToMessageDecoder { |
||||||
|
@Override |
||||||
|
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> list) throws Exception { |
||||||
|
ImMessage message = ImMessage.read(byteBuf); |
||||||
|
boolean isMagicError; |
||||||
|
if ((isMagicError = (message == ImMessage.MAGIC_ERROR)) |
||||||
|
|| message == ImMessage.PROTOCOL_ERROR) { |
||||||
|
logger.warn("decode im message error: {}, remote={}, closing channel.", |
||||||
|
isMagicError ? "MagicError" : "ProtocolError", |
||||||
|
ctx.channel().remoteAddress()); |
||||||
|
ctx.channel().close(); |
||||||
|
return; |
||||||
|
} |
||||||
|
// 解码 protobuf 消息体
|
||||||
|
int serviceNo = message.getServiceNo(); |
||||||
|
byte[] protoByte = message.getBody(); |
||||||
|
MessageLite protoClass = ProtoMessageManager.getProtoInstance(serviceNo); |
||||||
|
MessageLite protoMsg = protoClass.getParserForType().parseFrom(protoByte); |
||||||
|
list.add(protoMsg); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static class ProtoMsgEncoder extends MessageToByteEncoder<MessageLite> { |
||||||
|
@Override |
||||||
|
protected void encode(ChannelHandlerContext ctx, MessageLite message, ByteBuf byteBuf) throws Exception { |
||||||
|
Integer serialNo = ProtoMessageManager.getSerialNo(message.getClass()); |
||||||
|
// TODO unknow class serialNo
|
||||||
|
ImMessage imMessage = new ImMessage() |
||||||
|
.setServiceNo(serialNo) |
||||||
|
.setBody(message.toByteArray()); |
||||||
|
imMessage.write(byteBuf); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,28 +0,0 @@ |
|||||||
package net.sopod.soim.core.net; |
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext; |
|
||||||
import io.netty.channel.SimpleChannelInboundHandler; |
|
||||||
import net.sopod.soim.data.constant.SerializeType; |
|
||||||
import net.sopod.soim.data.serialize.ImMessage; |
|
||||||
|
|
||||||
import java.util.Map; |
|
||||||
|
|
||||||
/** |
|
||||||
* ImMessageHandler |
|
||||||
* |
|
||||||
* @author tmy |
|
||||||
* @date 2022-03-28 13:27 |
|
||||||
*/ |
|
||||||
public class ImMessageHandler extends SimpleChannelInboundHandler<ImMessage> { |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ImMessage imMessage) throws Exception { |
|
||||||
byte[] body = imMessage.getBody(); |
|
||||||
int serviceNo = imMessage.getServiceNo(); |
|
||||||
|
|
||||||
SerializeType serialize = SerializeType.getSerializeByOrdinal(imMessage.getSerializeType()); |
|
||||||
Map data = serialize.getSerializer().deserialize(body, Map.class); |
|
||||||
System.out.println(data); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,75 @@ |
|||||||
|
package net.sopod.soim.core.registry; |
||||||
|
|
||||||
|
import net.sopod.soim.common.util.ImClock; |
||||||
|
import net.sopod.soim.common.util.Reflects; |
||||||
|
import net.sopod.soim.core.handler.MessageHandler; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
|
||||||
|
import javax.annotation.Nullable; |
||||||
|
import java.util.Collection; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.concurrent.CountDownLatch; |
||||||
|
|
||||||
|
/** |
||||||
|
* ProtoMessageDispatcher |
||||||
|
* implements ApplicationContextAware |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-04-10 19:15 |
||||||
|
*/ |
||||||
|
public class ProtoMessageHandlerRegistry { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ProtoMessageHandlerRegistry.class); |
||||||
|
|
||||||
|
private static final Map<Class<?>, MessageHandler<?>> TYPE_HANDLER_MAP = new HashMap<>(32); |
||||||
|
|
||||||
|
private static final CountDownLatch CONTEXT_AWARE_AWAIT = new CountDownLatch(1); |
||||||
|
|
||||||
|
/** |
||||||
|
* spring ioc 容器中获取 msgType handler |
||||||
|
* @param context spring ioc 上下文 |
||||||
|
*/ |
||||||
|
public static synchronized void registerHandlerWithApplicationContext(ApplicationContext context) { |
||||||
|
if (CONTEXT_AWARE_AWAIT.getCount() <= 0) { |
||||||
|
throw new IllegalStateException("proto message registry already initialed!"); |
||||||
|
} |
||||||
|
logger.info("proto message registry initial..."); |
||||||
|
long start = ImClock.millis(); |
||||||
|
Map<String, MessageHandler> beansOfType = context.getBeansOfType(MessageHandler.class); |
||||||
|
Collection<MessageHandler> handlers = beansOfType.values(); |
||||||
|
for (MessageHandler<?> handler : handlers) { |
||||||
|
// 获取 handler 泛型
|
||||||
|
List<String> genericTypes = Reflects.getSuperclassGenericTypes(handler.getClass()); |
||||||
|
try { |
||||||
|
Class<?> type = genericTypes.size() == 0 ? Object.class : Class.forName(genericTypes.get(0)); |
||||||
|
MessageHandler<?> existHandler = TYPE_HANDLER_MAP.putIfAbsent(type, handler); |
||||||
|
if (existHandler != null) { |
||||||
|
// 消息类型有重复的 handler!
|
||||||
|
throw new IllegalStateException("msg type " + type + " handler duplicate; " + |
||||||
|
"[" + existHandler.getClass() + "] and [" + handler.getClass() + "]"); |
||||||
|
} |
||||||
|
} catch (ClassNotFoundException e) { |
||||||
|
logger.warn("handler msgType class not found!", e); |
||||||
|
} |
||||||
|
} |
||||||
|
CONTEXT_AWARE_AWAIT.countDown(); |
||||||
|
logger.info("proto message registry complete, {} handlers at {}ms.", handlers.size(), ImClock.millis() - start); |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
public static <T> MessageHandler<T> getTypeHandler(Class<T> type) { |
||||||
|
if (CONTEXT_AWARE_AWAIT.getCount() > 0) { |
||||||
|
try { |
||||||
|
CONTEXT_AWARE_AWAIT.await(); |
||||||
|
} catch (InterruptedException e) { |
||||||
|
logger.error("proto message type dispatcher, wait context ready error!", e); |
||||||
|
} |
||||||
|
} |
||||||
|
return (MessageHandler<T>) TYPE_HANDLER_MAP.get(type); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,41 +0,0 @@ |
|||||||
package net.sopod.soim.core.registry; |
|
||||||
|
|
||||||
import net.sopod.soim.core.service.ReqHandler; |
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentHashMap; |
|
||||||
import java.util.concurrent.atomic.AtomicInteger; |
|
||||||
|
|
||||||
/** |
|
||||||
* ServiceRegistry |
|
||||||
* |
|
||||||
* @author tmy |
|
||||||
* @date 2022-03-28 14:30 |
|
||||||
*/ |
|
||||||
public class ServiceRegistry { |
|
||||||
|
|
||||||
private static final ConcurrentHashMap<Integer, Class<?>> serviceIdParamTypeMap; |
|
||||||
|
|
||||||
private static final ConcurrentHashMap<Class<?>, Integer> paramTypeServiceIdMap; |
|
||||||
|
|
||||||
private static final ConcurrentHashMap<Integer, ReqHandler<?>> serviceIdHandlers; |
|
||||||
|
|
||||||
static { |
|
||||||
serviceIdParamTypeMap = new ConcurrentHashMap<>(); |
|
||||||
paramTypeServiceIdMap = new ConcurrentHashMap<>(); |
|
||||||
serviceIdHandlers = new ConcurrentHashMap<>(); |
|
||||||
} |
|
||||||
|
|
||||||
private static final AtomicInteger serviceIdGen = new AtomicInteger(10000); |
|
||||||
|
|
||||||
private static <T> void registry(Class<T> paramType, ReqHandler<T> handler) { |
|
||||||
int serviceId = serviceIdGen.getAndIncrement(); |
|
||||||
serviceIdParamTypeMap.put(serviceId, paramType); |
|
||||||
paramTypeServiceIdMap.put(paramType, serviceId); |
|
||||||
serviceIdHandlers.put(serviceId, handler); |
|
||||||
} |
|
||||||
|
|
||||||
public void aaa() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,13 +0,0 @@ |
|||||||
package net.sopod.soim.core.service; |
|
||||||
|
|
||||||
/** |
|
||||||
* ReqHandler |
|
||||||
* |
|
||||||
* @author tmy |
|
||||||
* @date 2022-03-28 14:33 |
|
||||||
*/ |
|
||||||
public interface ReqHandler<T> { |
|
||||||
|
|
||||||
Object handle(T param); |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,23 @@ |
|||||||
|
package net.sopod.soim.core.session; |
||||||
|
|
||||||
|
import io.netty.channel.Channel; |
||||||
|
import io.netty.util.AttributeKey; |
||||||
|
|
||||||
|
public class Account extends NetUser { |
||||||
|
|
||||||
|
public static final AttributeKey<Account> ACCOUNT_KEY = AttributeKey.valueOf(Account.class, "ACCOUNT"); |
||||||
|
|
||||||
|
private long accountId; |
||||||
|
|
||||||
|
private String name; |
||||||
|
|
||||||
|
public Account(Channel channel) { |
||||||
|
super(channel); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isAccount() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,50 @@ |
|||||||
|
package net.sopod.soim.core.session; |
||||||
|
|
||||||
|
import io.netty.channel.Channel; |
||||||
|
import io.netty.util.AttributeKey; |
||||||
|
|
||||||
|
import java.lang.ref.WeakReference; |
||||||
|
|
||||||
|
public class NetUser { |
||||||
|
|
||||||
|
/** channel 绑定 netUser 对象 */ |
||||||
|
public static final AttributeKey<NetUser> NET_USER_KEY = AttributeKey.valueOf(NetUser.class, "NET_USER"); |
||||||
|
|
||||||
|
private final WeakReference<Channel> channel; |
||||||
|
|
||||||
|
public NetUser(Channel channel) { |
||||||
|
this.channel = new WeakReference<>(channel); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isAccount() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public void write(Object message) { |
||||||
|
write(message, false); |
||||||
|
} |
||||||
|
|
||||||
|
public void writeNow(Object message) { |
||||||
|
write(message, true); |
||||||
|
} |
||||||
|
|
||||||
|
private void write(Object message, boolean now) { |
||||||
|
Channel channel = this.channel.get(); |
||||||
|
if (channel == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (now) { |
||||||
|
channel.writeAndFlush(message); |
||||||
|
} else { |
||||||
|
channel.write(message); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return "NetUser{" + |
||||||
|
"channel=" + channel + |
||||||
|
'}'; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,12 +0,0 @@ |
|||||||
package net.sopod.soim.data.proto; |
|
||||||
|
|
||||||
/** |
|
||||||
* MessageHolder |
|
||||||
* |
|
||||||
* @author tmy |
|
||||||
* @date 2022-04-08 18:02 |
|
||||||
*/ |
|
||||||
public class ProtoMessageHolder { |
|
||||||
public static final String protoSerialNoTableName = "protoSerialNoTable.txt"; |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,100 @@ |
|||||||
|
package net.sopod.soim.data.proto; |
||||||
|
|
||||||
|
import com.google.protobuf.GeneratedMessageV3; |
||||||
|
import com.google.protobuf.MessageLite; |
||||||
|
import net.sopod.soim.data.msg.hello.HelloPB; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
|
||||||
|
import javax.annotation.Nullable; |
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.io.InputStreamReader; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||||
|
|
||||||
|
/** |
||||||
|
* MessageHolder |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-04-08 18:02 |
||||||
|
*/ |
||||||
|
public class ProtoMessageManager { |
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ProtoMessageManager.class); |
||||||
|
|
||||||
|
public static final String protoSerialNoTableName = "protoSerialNoTable.txt"; |
||||||
|
|
||||||
|
private static final Map<Integer, String> serialNoTypeMap = new HashMap<>(32); |
||||||
|
private static final Map<String, Integer> typeSerialNoMap = new HashMap<>(32); |
||||||
|
private static Map<String, MessageLite> typeNameClazzMap = new ConcurrentHashMap<>(); |
||||||
|
|
||||||
|
static { |
||||||
|
try { |
||||||
|
init(); |
||||||
|
} catch (IOException e) { |
||||||
|
throw new IllegalStateException("protobuf序列号列表初始化失败", e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static void init() throws IOException { |
||||||
|
InputStream in = ProtoMessageManager.class.getClassLoader().getResourceAsStream(protoSerialNoTableName); |
||||||
|
if (in == null) { |
||||||
|
throw new IllegalStateException("classpath:" + protoSerialNoTableName + " 文件未找到"); |
||||||
|
} |
||||||
|
InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8); |
||||||
|
BufferedReader bufReader = new BufferedReader(reader); |
||||||
|
String line; |
||||||
|
while (null != (line = bufReader.readLine())) { |
||||||
|
int idx = line.indexOf('='); |
||||||
|
String clazz = line.substring(0, idx); |
||||||
|
Integer num = Integer.valueOf(line.substring(idx + 1)); |
||||||
|
serialNoTypeMap.put(num, clazz); |
||||||
|
typeSerialNoMap.put(clazz, num); |
||||||
|
} |
||||||
|
bufReader.close(); |
||||||
|
reader.close(); |
||||||
|
in.close(); |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
public static MessageLite getProtoInstance(Integer serialNo) { |
||||||
|
String clazz = serialNoTypeMap.get(serialNo); |
||||||
|
if (clazz == null) { |
||||||
|
logger.error("protoMsgDict serialNo proto class not found: {}", serialNo); |
||||||
|
return null; |
||||||
|
} |
||||||
|
return typeNameClazzMap.computeIfAbsent(clazz, c -> { |
||||||
|
try { |
||||||
|
Class<?> type = Class.forName(c); |
||||||
|
if (!MessageLite.class.isAssignableFrom(type)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return getDefaultInstance((Class<? extends MessageLite>) type); |
||||||
|
} catch (ClassNotFoundException e) { |
||||||
|
logger.error("protoMsgDict proto class not found: {}, {}", serialNo, c); |
||||||
|
return null; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
public static Integer getSerialNo(Class<? extends MessageLite> type) { |
||||||
|
return typeSerialNoMap.get(type.getName()); |
||||||
|
} |
||||||
|
|
||||||
|
private static MessageLite getDefaultInstance(Class<? extends MessageLite> clazz) { |
||||||
|
try { |
||||||
|
Method getDefaultInstance = clazz.getDeclaredMethod("getDefaultInstance"); |
||||||
|
return (MessageLite)getDefaultInstance.invoke(null); |
||||||
|
} catch (Exception e) { |
||||||
|
System.out.println("get instance exception:" + clazz.getName()); |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,2 +1,2 @@ |
|||||||
net.sopod.soim.data.msg.hello.HelloPB$World=10000 |
net.sopod.soim.data.msg.hello.HelloPB$Hello=10000 |
||||||
net.sopod.soim.data.msg.hello.HelloPB$Hello=10001 |
net.sopod.soim.data.msg.hello.HelloPB$World=10001 |
||||||
|
|||||||
@ -0,0 +1,24 @@ |
|||||||
|
package net.sopod.soim.entry.config; |
||||||
|
|
||||||
|
import net.sopod.soim.core.registry.ProtoMessageHandlerRegistry; |
||||||
|
import org.springframework.beans.BeansException; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
import org.springframework.context.ApplicationContextAware; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
|
||||||
|
/** |
||||||
|
* ApplicationContextInitialed |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-04-10 22:20 |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
public class ApplicationContextInitialed implements ApplicationContextAware { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
||||||
|
// 注册 protobuf 消息 handler
|
||||||
|
ProtoMessageHandlerRegistry.registerHandlerWithApplicationContext(applicationContext); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
package net.sopod.soim.entry.handler; |
||||||
|
|
||||||
|
import com.google.protobuf.MessageLite; |
||||||
|
import net.sopod.soim.core.handler.NetUserMessageHandler; |
||||||
|
import net.sopod.soim.core.session.NetUser; |
||||||
|
import net.sopod.soim.data.msg.hello.HelloPB; |
||||||
|
import org.springframework.stereotype.Service; |
||||||
|
|
||||||
|
/** |
||||||
|
* HelloHandler |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-04-10 19:19 |
||||||
|
*/ |
||||||
|
@Service |
||||||
|
public class HelloHandler extends NetUserMessageHandler<HelloPB.Hello> { |
||||||
|
|
||||||
|
@Override |
||||||
|
public MessageLite handle(NetUser netUser, HelloPB.Hello msg) { |
||||||
|
System.out.println("get hello message"); |
||||||
|
System.out.println(msg); |
||||||
|
System.out.println(msg.getStr()); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,16 +0,0 @@ |
|||||||
package net.sopod.soim.entry.model; |
|
||||||
|
|
||||||
import lombok.Data; |
|
||||||
|
|
||||||
/** |
|
||||||
* A |
|
||||||
* |
|
||||||
* @author tmy |
|
||||||
* @date 2022-03-27 17:01 |
|
||||||
*/ |
|
||||||
@Data |
|
||||||
public class A { |
|
||||||
|
|
||||||
private String name; |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,53 @@ |
|||||||
|
package net.sopod.soim.entry.server; |
||||||
|
|
||||||
|
import com.google.protobuf.MessageLite; |
||||||
|
import io.netty.channel.Channel; |
||||||
|
import io.netty.channel.ChannelHandlerContext; |
||||||
|
import io.netty.channel.SimpleChannelInboundHandler; |
||||||
|
import io.netty.util.Attribute; |
||||||
|
import net.sopod.soim.core.handler.MessageHandler; |
||||||
|
import net.sopod.soim.core.net.AttributeKeys; |
||||||
|
import net.sopod.soim.core.registry.ProtoMessageHandlerRegistry; |
||||||
|
import net.sopod.soim.core.session.NetUser; |
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||||
|
|
||||||
|
/** |
||||||
|
* MessageLiteHandler |
||||||
|
* |
||||||
|
* @author tmy |
||||||
|
* @date 2022-04-10 22:40 |
||||||
|
*/ |
||||||
|
public class InboundImMessageHandler extends SimpleChannelInboundHandler<MessageLite> { |
||||||
|
|
||||||
|
/** |
||||||
|
* channel 建立连接,设置初始属性 |
||||||
|
* TODO 登录倒计时 5s 断开连接, 登录失败次数 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void channelActive(ChannelHandlerContext ctx) throws Exception { |
||||||
|
Channel channel = ctx.channel(); |
||||||
|
channel.attr(NetUser.NET_USER_KEY).set(new NetUser(channel)); |
||||||
|
channel.attr(AttributeKeys.WRITE_FAIL_TIMES).set(new AtomicInteger()); |
||||||
|
channel.attr(AttributeKeys.LOGIN_FAIL_TIMES).set(new AtomicInteger()); |
||||||
|
|
||||||
|
ctx.fireChannelActive(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void channelInactive(ChannelHandlerContext ctx) { |
||||||
|
|
||||||
|
ctx.fireChannelInactive(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void channelRead0(ChannelHandlerContext ctx, MessageLite messageLite) throws Exception { |
||||||
|
Attribute<NetUser> netUserAttr = ctx.channel().attr(NetUser.NET_USER_KEY); |
||||||
|
NetUser netUser = netUserAttr.get(); |
||||||
|
// TODO dispatcher
|
||||||
|
MessageHandler<MessageLite> typeHandler = (MessageHandler<MessageLite>) ProtoMessageHandlerRegistry |
||||||
|
.getTypeHandler(messageLite.getClass()); |
||||||
|
typeHandler.exec(netUser, messageLite); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,28 @@ |
|||||||
|
package net.sopod.soim.entry.util; |
||||||
|
|
||||||
|
import io.netty.util.concurrent.FastThreadLocalThread; |
||||||
|
|
||||||
|
import java.util.concurrent.ThreadFactory; |
||||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||||
|
|
||||||
|
public class FastThreadLocalThreadFactory implements ThreadFactory { |
||||||
|
private String name; |
||||||
|
private int priority; |
||||||
|
private AtomicInteger counter; |
||||||
|
|
||||||
|
public FastThreadLocalThreadFactory(String name, int priority) { |
||||||
|
this.name = name; |
||||||
|
this.priority = priority; |
||||||
|
this.counter = new AtomicInteger(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Thread newThread(Runnable runnable) { |
||||||
|
FastThreadLocalThread thread = new FastThreadLocalThread( |
||||||
|
runnable, |
||||||
|
String.format(name, counter.incrementAndGet()) |
||||||
|
); |
||||||
|
thread.setPriority(priority); |
||||||
|
return thread; |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue