之前写的东西因为有些netty的知识了解不够多,导致写的有点乱,这次花了一点时间重新整理了一下,写了此次版本,可能后续还可能优化,因为不足之处还有点多。。。
此次主要实现了对应的,心跳的机制,对应的断线重连,服务自动加入,并且能够自动的进行对应的随机访问的负载的功能。还实现了对应的自动扫描接口,并生产对应的代理类注入到对应的ioc容器中,并加上对应的自动DI操作,服务端对应的消息的分发模式,代码更加简单明白,网络部分,加上了对应的tcp粘包,json编解码。具体代码分析:
首先cient端:
package edu.mbb.config;
import edu.mbb.netty.ServerInfo;
import edu.mbb.netty.ServerSelector;
import edu.mbb.util.RedisUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.util.*;
@Component
public class RpcClientConfiguration implements ApplicationListener<ContextRefreshedEvent>, BeanDefinitionRegistryPostProcessor, ApplicationContextAware, DisposableBean {
private NettyConfig nettyConfig;
@Autowired
private ServerSelector serverSelector;
public static ApplicationContext applicationContext;
private volatile boolean contextStart = false;
/**
* 扫包地址 多个包请逗号分割
*/
@Value("${rpc.basePackage}")
private String basePackage = "edu.mbb.service";
@PostConstruct
public void init() {
//把对应的服务注册进入对应的环境中
System.out.println("启动完成");
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
RpcInterfaceScanner scanner = new RpcInterfaceScanner(registry);
scanner.scan(basePackage.split(","));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
RpcClientConfiguration.applicationContext = applicationContext;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
//容器启动完成时,开始定时任务
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!contextStart) {
//启动定时任务,同步redis注册中心的服务
System.out.println("开始进行对应的redis数据同步任务。一直从redis中获取最新的服务列表信息");
SynServer();
contextStart = true;
}
}
@Override
public void destroy() throws Exception {
//关闭socket服务
nettyConfig.close();
}
public NettyConfig getNettyConfig() {
return nettyConfig;
}
public void setNettyConfig(NettyConfig nettyConfig) {
this.nettyConfig = nettyConfig;
}
public String getBasePackage() {
return basePackage;
}
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
public void SynServer() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("同步");
try {
Thread.sleep(1000 * 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
RedisUtil redisUtil = RpcClientConfiguration.applicationContext.getBean(RedisUtil.class);
Set<Object> serverNames = redisUtil.sGet(Constant.REDIS_SERVER_LIST);
Set<String> serverList = ServerInfo.serverList;
/**
* 同步思路解析:
* 首页我们肯定只同步我们本地用的到的服务 也就是本地扫描包下面的对应的bean
* 所以我们的接口名称必须保持一直
* 因为我们只有按照一个标准 都按照 首字母小的方式 当作key
*我们从redis 上面 同步到 我们本地不不存在的 我们不需要的服务
* 我们不管他我们忽略这样的 服务 我们同步的也就是 增加ip
*/
for (Object serverName : serverNames) {
//所以第一步我们过滤出我们需要的服务
//考虑后续加上namespace
if (ServerInfo.clientBeanNames.contains(serverName)) {
if (serverList.contains(serverName)) {
//如果服务器上面没有服务,从服务器列表删除
//虽然存在服务名称 没有ip和port
//有可能提供的服务的 机器已经下线了 我们做下线处理
long size = redisUtil.sGetSetSize(serverName.toString());
//移除对应的 服务名称
if (size == 0) {
System.out.println("服务器上面当前的服务名称已经没有ip了,进行对应的移除name操作");
redisUtil.del(serverName.toString());
//本地移除对应的服务
ServerInfo.clientBeanNames.remove(serverName);
ServerInfo.serverList.remove(serverName);
ServerInfo.serverBeanNameMap.remove(serverName);
ServerInfo.serverNameMap.remove(serverName);
continue;
} else {
//也放入对应的beanName
String beanName = redisUtil.get(Constant.SERVER_NAME_BEAN + serverName).toString();
if (StringUtils.isEmpty(beanName)) {
System.out.println("当前服务找不到远程的包地址进行移除" + serverName);
redisUtil.setRemove(Constant.REDIS_SERVER_LIST, serverName);
ServerInfo.clientBeanNames.remove(serverName);
ServerInfo.serverList.remove(serverName);
ServerInfo.serverBeanNameMap.remove(serverName);
ServerInfo.serverNameMap.remove(serverName);
continue;
}
//看当前服务名称下面的ip 有没有新的
//有新的 进行链接
Set<Object> IPAndPorts = redisUtil.sGet(serverName.toString());
IPAndPorts.stream()
.forEach(ip -> {
System.out.println("ip同步开始" + ip);
//查看当前通道有没有这个ip
if (!ServerInfo.serverChannels.containsKey(ip)) {
//新增了ip
System.out.println("有服务上线了,开始同步ip" + ip);
if (ServerInfo.newServerNameMap.size() > 0 && ServerInfo.newServerNameMap.containsKey(serverName)) {
ServerInfo.newServerNameMap.get(serverName).add(ip.toString());
} else {
Set<String> ips = new HashSet<>();
ips.add(ip.toString());
ServerInfo.newServerNameMap.put(serverName.toString(), ips);
}
}
});
//同步服务列表
}
}
}
}
System.out.println("一次同步完成!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
}
ApplicationListener接口,为了在容器启动之后,启动对应的数据同步线程,而且只启动一次,加上对应的判断条件,
BeanDefinitionRegistryPostProcessor接口(重要接口mybatis-spring整合也是实现了这个接口哦)。Spring对外提供的扫包接口,可以自己指定对应的扫描包路径并且可以指定对应的类的规则,还有对应的加入到ioc容器并且,可以支持Autowired注解,此接口提供是为了外部的组件能够注册进入到spring中。
ApplicationContextAware接口,为了把Spring的Ioc容器注入到方法中,为了从ioc中取类方便使用。
DisposableBean接口:为了当容器销毁的时候,释放对应的链接,当前只有优雅的推出才有用。。。。。
basePackage:扫描的接口路径,就是在需要链接远端的接口
package edu.mbb.config;
import edu.mbb.netty.ServerInfo;
import edu.mbb.proxy.RpcInterfaceProxyFactroyBean;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
import java.util.Set;
public class RpcInterfaceScanner extends ClassPathBeanDefinitionScanner {
public RpcInterfaceScanner(BeanDefinitionRegistry registry) {
super(registry, false);
}
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
addFliter();
Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
//记录存在的连接端 接口
if (beanDefinitionHolders!=null&&beanDefinitionHolders.size()>0){
//生成接口的动态代理类,类似MyBatis为mapper生成代理类
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
String beanName = beanDefinitionHolder.getBeanName();
ServerInfo.clientBeanNames.add(beanName);
GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
//通过FactoryBean创建
beanDefinition.setBeanClass(RpcInterfaceProxyFactroyBean.class);
beanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
return beanDefinitionHolders;
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return metadata.isInterface()&&metadata.isIndependent();
}
private void addFliter(){
addIncludeFilter(new TypeFilter() {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return true;
}
});
}
}
RpcInterfaceScanner类。对应的自定义的扫包类,继承了对应的地址类扫描类ClassPathBeanDefinitionScanner 其中isCandidateComponent是为了过滤对应的类的规则,比如我定义了对应的必须是接口,没有进行继承的接口;
重要的实现:doScan 扫描类的关键,此处是吧对应的类变为对应的BeanDefinition的关键,
//把对应的当前的类的class 当成构造参数传入对应的 构建类的参数,所以我们可以在对应的factory中直接把当前的类的class 当成构造参数可以取到要构造的类的class beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
//指定对应的工厂类
beanDefinition.setBeanClass(RpcInterfaceProxyFactroyBean.class);
//使当前类支持对应的autoWired
beanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
//代表我们需要链接远端的类的接口总和,因为可能远端很多服务,我们只关心当前系统需要关心的服务。
ServerInfo.clientBeanNames.add(beanName);
public class RpcInterfaceProxyFactroyBean<T> implements FactoryBean {
private Class<T> clzz;
public RpcInterfaceProxyFactroyBean(Class<T> clzz) {
this.clzz = clzz;
}
@Override
public T getObject() throws Exception {
RedisUtil redisUtil = RpcClientConfiguration.applicationContext.getBean(RedisUtil.class);
RpcClientConfiguration rpcClientConfiguration = RpcClientConfiguration.applicationContext.getBean(RpcClientConfiguration.class);
NettyConfig nettyConfig=rpcClientConfiguration.getNettyConfig();
//初始化配置
if (nettyConfig==null){
nettyConfig=new NettyConfig();
rpcClientConfiguration.setNettyConfig(nettyConfig);
}
//获取可以提供该接口服务的IP地址和端口
//缓存起来
List<ServerInfo> serverIpList=new ArrayList<>();
try {
synchronized (ServerInfo.isInit){
//如果服务器列表没有获取过,则先在redis获取服务器列表
if (!ServerInfo.isInit){
//获取服务列表
Set<Object> servers = redisUtil.sGet(Constant.REDIS_SERVER_LIST);
ServerInfo.isInit=true;
if (servers==null||servers.size()==0){
throw new RuntimeException("has no servers in center!");
}
Set<String> serversList=new HashSet<>();
servers.stream()
.forEach(
key->{
if(ServerInfo.clientBeanNames.contains(key.toString())){
serversList.add(key.toString());
}
}
);
ServerInfo.serverList.addAll(serversList);
}
}
//获取对应的 serverName
for (String serverName : ServerInfo.serverList) {
//如果服务列表没有获取过,则从redis服务器中获取
if (!ServerInfo.serverNameMap.containsKey(serverName)){
Set<Object> IPPortLists = redisUtil.sGet(serverName);
if (IPPortLists==null||IPPortLists.size()==0){
redisUtil.setRemove(Constant.REDIS_SERVER_LIST, serverName);
}
Set<String> iplist=new HashSet<>();
for(Object key : IPPortLists){
iplist.add(key.toString());
}
ServerInfo.serverNameMap.put(serverName,iplist);
}
if (serverName!=null&&serverName.equalsIgnoreCase(clzz.getSimpleName())){
Set<String> strings = ServerInfo.serverNameMap.get(serverName);
Iterator<String> iterator = strings.iterator();
while (iterator.hasNext()){
String ipAndPort = iterator.next();
String[] analysisIpAndPort = StringUtils.analysisIpAndPort(ipAndPort);
//分解服务器IP和端口成功
if (ipAndPort != null) {
//如果该客户端已经和该服务器连接,则不发起连接
if (ServerInfo.serverChannels.containsKey(ipAndPort)) {
ServerInfo serverInfo = new ServerInfo(analysisIpAndPort[0], Integer.parseInt(analysisIpAndPort[1]));
if(serverIpList.contains(serverInfo)){
System.out.println("当前信道中已经存在此链接不在新增此链接");
continue;
}
serverIpList.add(new ServerInfo(analysisIpAndPort[0], Integer.parseInt(analysisIpAndPort[1])));
} else {//否则和服务器发起连接
try {
nettyConfig.clientInit(analysisIpAndPort[0], Integer.parseInt(analysisIpAndPort[1]));
}catch (Exception e){
iterator.remove();
e.printStackTrace();
System.out.println("链接失败,选择下一个对应的服务列表数据");
continue;
}
serverIpList.add(new ServerInfo(analysisIpAndPort[0], Integer.parseInt(analysisIpAndPort[1])));
String s = redisUtil.get(Constant.SERVER_NAME_BEAN + serverName).toString();
System.out.println(s);
ServerInfo.serverBeanNameMap.put(serverName, s);
}
}
}
Iterator<Map.Entry<String, Set<String>>> iterator1 = ServerInfo.serverNameMap.entrySet().iterator();
while (iterator1.hasNext()){
Set<String> value = iterator1.next().getValue();
if(value==null && value.size()==0){
iterator1.remove();
}
}
}
}
}catch (Exception e){
e.printStackTrace();
}
if (serverIpList.size()==0){
throw new RuntimeException("["+clzz.getName() +"] has no implements in register center! ");
}
//生成连接对象
Object o = Proxy.newProxyInstance(clzz.getClassLoader(), new Class[]{clzz}, new ClientProxyInvocation(serverIpList,clzz));
return (T)o;
}
@Override
public Class<T> getObjectType() {
return clzz;
}
@Override
public boolean isSingleton() {
return true;
}
public Class<T> getItfClass() {
return clzz;
}
public void setItfClass(Class<T> itfClass) {
this.clzz = itfClass;
}
}
//对应的接口的工厂bean
//因为在前面我们指定了 扫描的接口的class类为构造函数注入进来我们需要对应的构造函数接入对应的class类
public RpcInterfaceProxyFactroyBean(Class<T> clzz) {
this.clzz = clzz;
}
对应的结构图:
//对应原子变量加锁,防止对此初始化
synchronized (ServerInfo.isInit){
//如果服务器列表没有获取过,则先在redis获取服务器列表
if (!ServerInfo.isInit){
//获取服务列表
//对应的就是serverName 的集合
Set<Object> servers = redisUtil.sGet(Constant.REDIS_SERVER_LIST);
//表示已经初始化不在初始化
ServerInfo.isInit=true;
if (servers==null||servers.size()==0){
//假如不存在服务端服务,直接报错,不在进行初始化和启动
throw new RuntimeException("has no servers in center!");
}
Set<String> serversList=new HashSet<>();
servers.stream()
.forEach(
key->{
//我们只关心我们当前系统中要调用的服务。
if(ServerInfo.clientBeanNames.contains(key.toString())){
serversList.add(key.toString());
}
}
);
//把服务列表中远端上的服务
ServerInfo.serverList.addAll(serversList);
}
}
通过上面一部我们已经筛选出我们需要的服务名称列表了
下面需要把 提供这些服务名称的ip和port的进行链接;
ServerInfo.serverNameMap 记录对应的name 和ipPort的对应关系
假如当前的类和对应的serverName能对应上(保证接口和服务端接口名字一致。。。。。)
serverName!=null&&serverName.equalsIgnoreCase(clzz.getSimpleName())
//因为可能一个服务器提供了很多的服务,假如之前的服务也在这台服务器上,因为之前已经链接过了,此时不在进行链接,直接记录服务信息即可
if (ServerInfo.serverChannels.containsKey(ipAndPort))
通过ip和port 进行链接
nettyConfig.clientInit(analysisIpAndPort[0], Integer.parseInt(analysisIpAndPort[1]));
解读连接服务器方法
package edu.mbb.config;
import edu.mbb.handler.ClientJsonDecoder;
import edu.mbb.handler.ClientJsonEncoder;
import edu.mbb.handler.ResponseHandler;
import edu.mbb.listener.CollectionListener;
import edu.mbb.netty.ServerInfo;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class NettyConfig {
private int workerGroupCount = Constant.NETTY_WORKERROUP_DEFAULT;
//业务线程池相关配置
private volatile NioEventLoopGroup worker = new NioEventLoopGroup(workerGroupCount);
public NioEventLoopGroup getWorker() {
return worker;
}
public void setWorker(NioEventLoopGroup worker) {
this.worker = worker;
}
//客户端连接服务初始化
public synchronized void clientInit(String ip, int port) {
RpcClientConfiguration rpcClientConfiguration = RpcClientConfiguration.applicationContext.getBean(RpcClientConfiguration.class);
NioEventLoopGroup worker = getWorker();
rpcClientConfiguration.getNettyConfig().setWorker(worker);
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(worker)
.channel(NioSocketChannel.class)
//tcp/ip模式
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 10);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
//加上对应的 心跳保活机制
//读写超时时间 为 1分钟
pipeline.addLast(new IdleStateHandler(1, 1, 1, TimeUnit.MINUTES));
//参考我的 之前的文档对 tcp 粘包和分包的讲解
//代表最大为长度为4的长度数据 只有一个length字段没有其他请求头字段
pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
//编码的时候编上请求头 4个字节的长度字段
pipeline.addLast(new LengthFieldPrepender(4));
//入站消息从上往下 出站消息从下往上
//入站消息 先解码为String类型
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
//Json编解码器
pipeline.addLast(new ClientJsonDecoder());
pipeline.addLast(new ClientJsonEncoder());
/**
* 此处流程为: 首先一个消息进来了 这个消息长成这个样子
*
* |——————————————————————|————————————————|
* | length字段代表后面的报文长度(4个字节) |报文内容 |
* |—————————————————————|—————————————————|
* 报文进来后-》LengthFieldBasedFrameDecoder——>报文内容(byteBuf)->StringDecoder(byteBuf->String)->ClientJsonDecoder(json String->ResponseImpl)->
*ResponseHandler(ResponseImpl)->
* ClientJsonEncoder(ResponseImpl->Json String)->
* StringEncoder(String)->LengthFieldPrepender(String->ByteBuf,并在这一步加上了length字段)
*
*
*/
pipeline.addLast(new ResponseHandler());
}
});
try {
//回执接口
ChannelFuture channelFuture = bootstrap.connect(ip, port).sync();
//监听回执接口
channelFuture.addListener(new CollectionListener(channelFuture.channel(), ip, port));
} catch (Exception ie) {
throw new RuntimeException("connect server[" + ip + ":" + port + "] error!", ie);
}
}
protected void close() {
if (this.getWorker() != null) {
this.getWorker().shutdownGracefully();
System.out.println("客户端已经关闭对应的通过进行关闭");
}
}
}
catch (Exception e){
iterator.remove();
e.printStackTrace();
System.out.println("链接失败,选择下一个对应的服务列表数据");
continue;
}
//一定要catch 因为可能redis 上面有一些服务无法进行连接了,此时要把不能连接的服务剔除,并且能尝试下一个ip和port 并不能添加到通道list
/**
添加连接到对应的连接list
因为我远程的服务端的对应的接口包名和对应的client的包名是不一致的,我这里记录下对应的对应的关系
为了发给远端使用、
*/
serverIpList.add(new
ServerInfo(analysisIpAndPort[0],Integer.parseInt(analysisIpAndPort[1])));
String s = redisUtil.get(Constant.SERVER_NAME_BEAN + serverName).toString(); System.out.println(s);
ServerInfo.serverBeanNameMap.put(serverName, s);
为接口生成对应的代理对象:
参看代码,注释写的很多
服务端:值得说的是
对应的一个分发器的模式,因为我们要处理的消息类型有很多。我们通过对应的消息类型分发到不同的消息处理器中去,
private Map<MessageType, MessageHandler> handlerHashMap= new HashMap<>();
/**
* 把消息处理类和消息类型进行绑定
* @param messageType 消息类型
* @param messageHandler 消息类型处理类
*/
public void resit(MessageType messageType, MessageHandler messageHandler){
handlerHashMap.put(messageType,messageHandler);
}
/**
* 通过消息类型 得到对应的处理类
* @param messageType 消息类型
* @return
*/
public MessageHandler getMessageHandler(MessageType messageType){
return handlerHashMap.get(messageType);
}
采用的是map的方式,key是对应的消息类型,而对应的处理类是实现了统一接口的各种处理类,他们有共同的功能,处理的方法和对应的标识他是什么类型的处理类。
/**
* 对应的消息类型的处理类方法
* @param channelHandlerContext 通道
* @param request 消息
*/
public void handler(ChannelHandlerContext channelHandlerContext,RequestImpl request);
/**
* 得到当前类的消息类型
* @return
*/
public MessageType getMessageType();
当对应的处理类实现了该方法,所有的类型消息,都能得到对应的处理类,当客户端来消息的时候。
private ApplicationContext applicationContext;
/* @Autowired
@Qualifier(value = "MessageServerType")
private MessageHandler messageServerType;
@Autowired
@Qualifier(value = "MessageHeartType")
private MessageHandler messageHeartType;*/
@Bean
public MessageTypeHandlerMap messageTypeHandlerMap(){
MessageTypeHandlerMap messageTypeHandlerMap=new MessageTypeHandlerMap();
/* messageTypeHandlerMap.resit(MessageType.HEARTBEAT,messageHeartType);
messageTypeHandlerMap.resit(MessageType.HEARTBEAT,messageHeartType);*/
/**
* 通过类型加载出所有的 当前类型的bean
*/
Map<String, MessageHandler> beansOfType = applicationContext.getBeansOfType(MessageHandler.class);
for (String key: beansOfType.keySet()){
MessageHandler messageHandler = beansOfType.get(key);
//得到当前类的消息
MessageType messageType = messageHandler.getMessageType();
//注册到 map中
messageTypeHandlerMap.resit(messageType,messageHandler);
}
return messageTypeHandlerMap;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
注册采用的是注入对应的ioc容器 ,从容器中取出所有的实现该接口的实现类,并根据他们的对应类型注册到map中,方便后续使用。
其他的在说下run方法
//通过反射拿到接口对象的class对象 beanName
String itfName = request.getItfName();
//Object bean = applicationContext.getBean(itfName);
Class<?> clazz = Class.forName(itfName);
// Class<?> clazz = Class.forName(itfName);
//再从Spring容器里面拿出其实现类
Object bean = applicationContext.getBean(clazz);
//获取实现类的class对象
Class<?> implClass = bean.getClass();
//获取请求方法的Method对象
Method method = implClass.getMethod(request.getMethodName(), request.getParamClassType());
//通过反射执行其方法
Object retObj = method.invoke(bean, request.getArgs());
//组织返回对象
ResponseImpl response = new ResponseImpl(request.getRequestId(), ResponseStatus.OK);
response.setMessageType(request.getMessageType());
if (retObj == null || retObj instanceof Void) {
response.setResponseType(Void.class);
} else {
response.setContent(JacksonUtil.bean2Json(retObj));
boolean isarray = isArray(retObj);
boolean iscollection = isCollection(retObj);
response.setArrayFlag(isarray);
response.setCollectionFlag(iscollection);
Class<?> retClass = retObj.getClass();
if (response.isArrayFlag()) {
response.setArrayType(retClass);
response.setResponseType(retClass.getComponentType());
} else if (response.isCollectionFlag()) {
Collection collection = (Collection) retObj;
for (Object o : collection) {
response.setResponseType(o.getClass());
break;
}
} else {
response.setResponseType(retClass);
}
;
}
handlerContext.channel().writeAndFlush(response);
} catch (ClassNotFoundException e) {
e.printStackTrace();
ResponseImpl response = new ResponseImpl(request.getRequestId(), ResponseStatus.ERROR);
response.setErrorMsg("server [" + request.getItfName() + "] not found!");
response.setMessageType(MessageType.SERVER);
handlerContext.channel().writeAndFlush(response);
} catch (NoSuchMethodException me) {
me.printStackTrace();
ResponseImpl response = new ResponseImpl(request.getRequestId(), ResponseStatus.ERROR);
response.setErrorMsg("method [" + request.getMethodName() + "] args[" + Arrays.toString(request.getParamClassType()) + "] not found!");
response.setMessageType(MessageType.SERVER);
handlerContext.channel().writeAndFlush(response);
} catch (Exception ee) {
ee.printStackTrace();
ResponseImpl response = new ResponseImpl(request.getRequestId(), ResponseStatus.ERROR);
response.setErrorMsg("method [" + request.getMethodName() + "] invoke error!" + ee.toString());
response.setMessageType(MessageType.SERVER);
handlerContext.channel().writeAndFlush(response);
}
因为从客户端发过来的时候 是带上对应的服务端的全限定类名的。我们在redis中,存储了对应的全限定类名。然后从ioc容器中取出对应的处理类,根据从客户端发过来的方法名,去调用对应的方法,更具是否有返回值,返回值类型,和参数类型,做对应的封装。数组的方法需要单独处理,还有对应的 list map。。。java类型,后续优化,可以自己实现,这个我留个接口在这里。
本人所有代码上传码云还有csdn
地址:https://download.csdn.net/download/drsbbbl/19774196
码云:https://gitee.com/mibaowei/mbb-rpc.git