中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Netty與Spring Boot的整合實現

發布時間:2020-08-31 11:45:46 來源:腳本之家 閱讀:339 作者:葉云軒 欄目:編程語言

​ 最近有朋友向我詢問一些Netty與SpringBoot整合的相關問題,這里,我就總結了一下基本整合流程,也就是說,這篇文章 ,默認大家是對netty與Spring,SpringMVC的整合是沒有什么問題的。現在,就進入正題吧。

Server端

總的來說,服務端還是比較簡單的,自己一共寫了三個核心類。分別是

  • NettyServerListener:服務啟動監聽器
  • ServerChannelHandlerAdapter:通道適配器,主要用于多線程共享
  • RequestDispatcher:請求分排器

下面開始集成過程:

在pom.xml中添加以下依賴

<dependency>
 <groupId>io.netty</groupId>
 <artifactId>netty-all</artifactId>
 <version>5.0.0.Alpha2</version>
</dependency>
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-configuration-processor</artifactId>
 <optional>true</optional>
</dependency>

讓SpringBoot的啟動類實現CommandLineRunner接口并重寫run方法,比如我的啟動類是CloudApplication.java

@SpringBootApplication
public class CloudApplication implements CommandLineRunner {

 public static void main(String[] args) {
 SpringApplication.run(CloudApplication.class, args);
 }

 @Override
 public void run(String... strings) {
 }
}

創建類NettyServerListener.java

// 讀取yml的一個配置類
import com.edu.hart.modules.constant.NettyConfig;
// Netty連接信息配置類
import com.edu.hart.modules.constant.NettyConstant;
// 
import com.edu.hart.rpc.util.ObjectCodec;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;

/**
 * 服務啟動監聽器
 *
 * @author 葉云軒
 */
@Component
public class NettyServerListener {
 /**
 * NettyServerListener 日志輸出器
 *
 * @author 葉云軒 create by 2017/10/31 18:05
 */
 private static final Logger LOGGER = LoggerFactory.getLogger(NettyServerListener.class);
 /**
 * 創建bootstrap
 */
 ServerBootstrap serverBootstrap = new ServerBootstrap();
 /**
 * BOSS
 */
 EventLoopGroup boss = new NioEventLoopGroup();
 /**
 * Worker
 */
 EventLoopGroup work = new NioEventLoopGroup();
 /**
 * 通道適配器
 */
 @Resource
 private ServerChannelHandlerAdapter channelHandlerAdapter;
 /**
 * NETT服務器配置類
 */
 @Resource
 private NettyConfig nettyConfig;

 /**
 * 關閉服務器方法
 */
 @PreDestroy
 public void close() {
 LOGGER.info("關閉服務器....");
 //優雅退出
 boss.shutdownGracefully();
 work.shutdownGracefully();
 }

 /**
 * 開啟及服務線程
 */
 public void start() {
 // 從配置文件中(application.yml)獲取服務端監聽端口號
 int port = nettyConfig.getPort();
 serverBootstrap.group(boss, work)
  .channel(NioServerSocketChannel.class)
  .option(ChannelOption.SO_BACKLOG, 100)
  .handler(new LoggingHandler(LogLevel.INFO));
 try {
  //設置事件處理
  serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
  @Override
  protected void initChannel(SocketChannel ch) throws Exception {
   ChannelPipeline pipeline = ch.pipeline();
   pipeline.addLast(new LengthFieldBasedFrameDecoder(nettyConfig.getMaxFrameLength()
    , 0, 2, 0, 2));
   pipeline.addLast(new LengthFieldPrepender(2));
   pipeline.addLast(new ObjectCodec());

   pipeline.addLast(channelHandlerAdapter);
  }
  });
  LOGGER.info("netty服務器在[{}]端口啟動監聽", port);
  ChannelFuture f = serverBootstrap.bind(port).sync();
  f.channel().closeFuture().sync();
 } catch (InterruptedException e) {
  LOGGER.info("[出現異常] 釋放資源");
  boss.shutdownGracefully();
  work.shutdownGracefully();
 }
 }
}

創建類ServerChannelHandlerAdapter.java - 通道適配器

// 記錄調用方法的元信息的類
import com.edu.hart.rpc.entity.MethodInvokeMeta;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 多線程共享
 */
@Component
@Sharable
public class ServerChannelHandlerAdapter extends ChannelHandlerAdapter {
 /**
 * 日志處理
 */
 private Logger logger = LoggerFactory.getLogger(ServerChannelHandlerAdapter.class);
 /**
 * 注入請求分排器
 */
 @Resource
 private RequestDispatcher dispatcher;

 @Override
 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
 cause.printStackTrace();
 ctx.close();
 }

 @Override
 public void channelRead(ChannelHandlerContext ctx, Object msg) {
 MethodInvokeMeta invokeMeta = (MethodInvokeMeta) msg;
 // 屏蔽toString()方法
 if (invokeMeta.getMethodName().endsWith("toString()")
  && !"class java.lang.String".equals(invokeMeta.getReturnType().toString()))
  logger.info("客戶端傳入參數 :{},返回值:{}",
   invokeMeta.getArgs(), invokeMeta.getReturnType());
 dispatcher.dispatcher(ctx, invokeMeta);
 }
}

RequestDispatcher.java

// 封裝的返回信息枚舉類
import com.edu.hart.modules.communicate.ResponseCodeEnum;
// 封裝的返回信息實體類
import com.edu.hart.modules.communicate.ResponseResult;
// 封裝的連接常量類
import com.edu.hart.modules.constant.NettyConstant;
// 記錄元方法信息的實體類
import com.edu.hart.rpc.entity.MethodInvokeMeta;
// 對于返回值為空的一個處理
import com.edu.hart.rpc.entity.NullWritable;
// 封裝的返回信息實體工具類
import com.edu.hart.rpc.util.ResponseResultUtil;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 請求分排器
 */
@Component
public class RequestDispatcher implements ApplicationContextAware {
 private ExecutorService executorService = Executors.newFixedThreadPool(NettyConstant.getMaxThreads());
 private ApplicationContext app;

 /**
 * 發送
 *
 * @param ctx
 * @param invokeMeta
 */
 public void dispatcher(final ChannelHandlerContext ctx, final MethodInvokeMeta invokeMeta) {
 executorService.submit(() -> {
  ChannelFuture f = null;
  try {
  Class<?> interfaceClass = invokeMeta.getInterfaceClass();
  String name = invokeMeta.getMethodName();
  Object[] args = invokeMeta.getArgs();
  Class<?>[] parameterTypes = invokeMeta.getParameterTypes();
  Object targetObject = app.getBean(interfaceClass);
  Method method = targetObject.getClass().getMethod(name, parameterTypes);
  Object obj = method.invoke(targetObject, args);
  if (obj == null) {
   f = ctx.writeAndFlush(NullWritable.nullWritable());
  } else {
   f = ctx.writeAndFlush(obj);
  }
  f.addListener(ChannelFutureListener.CLOSE);
  } catch (Exception e) {
  ResponseResult error = ResponseResultUtil.error(ResponseCodeEnum.SERVER_ERROR);
  f = ctx.writeAndFlush(error);
  } finally {
  f.addListener(ChannelFutureListener.CLOSE);
  }
 });
 }

 /**
 * 加載當前application.xml
 *
 * @param ctx
 * @throws BeansException
 */
 public void setApplicationContext(ApplicationContext ctx) throws BeansException {
 this.app = ctx;
 }
}

application.yml文件中對于netty的一個配置

netty:
 port: 11111

NettyConfig.java

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 讀取yml配置文件中的信息
 * Created by 葉云軒 on 2017/10/31 - 18:38
 * Concat tdg_yyx@foxmail.com
 */
@Component
@ConfigurationProperties(prefix = "netty")
public class NettyConfig {

 private int port;

 public int getPort() {
 return port;
 }

 public void setPort(int port) {
 this.port = port;
 }
}

NettyConstanct.java

import org.springframework.stereotype.Component;

/**
 * Netty服務器常量
 * Created by 葉云軒 on 2017/10/31 - 17:47
 * Concat tdg_yyx@foxmail.com
 */
@Component
public class NettyConstant {

 /**
 * 最大線程量
 */
 private static final int MAX_THREADS = 1024;
 /**
 * 數據包最大長度
 */
 private static final int MAX_FRAME_LENGTH = 65535;

 public static int getMaxFrameLength() {
 return MAX_FRAME_LENGTH;
 }

 public static int getMaxThreads() {
 return MAX_THREADS;
 }
}

至此,netty服務端算是與SpringBoot整合成功。那么看一下啟動情況吧。

Netty與Spring Boot的整合實現

Client端:

Client我感覺要比Server端要麻煩一點。這里還是先給出核心類吧。

  • NettyClient : netty客戶端
  • ClientChannelHandlerAdapter : 客戶端通道適配器
  • CustomChannelInitalizer:自定義通道初始化工具
  • RPCProxyFactoryBean:RPC通信代理工廠

在Client端里。SpringBoot的啟動類要繼承SpringBootServletInitializer這個類,并覆蓋SpringApplicationBuilder方法

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;

@SpringBootApplication
public class OaApplication extends SpringBootServletInitializer {

 public static void main(String[] args) {
 SpringApplication.run(OaApplication.class, args);
 }

 @Override
 protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
 return builder.sources(OaApplication.class);
 }
}

NettyClient.java

// 記錄元方法信息的實體類
import com.edu.hart.rpc.entity.MethodInvokeMeta;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.management.MBeanServer;

/**
 * 客戶端發送類
 * Created by 葉云軒 on 2017/6/16-16:58
 * Concat tdg_yyx@foxmail.com
 */
public class NettyClient {

 private Logger logger = LoggerFactory.getLogger(MBeanServer.class);
 private Bootstrap bootstrap;
 private EventLoopGroup worker;
 private int port;
 private String url;
 private int MAX_RETRY_TIMES = 10;

 public NettyClient(String url, int port) {
 this.url = url;
 this.port = port;
 bootstrap = new Bootstrap();
 worker = new NioEventLoopGroup();
 bootstrap.group(worker);
 bootstrap.channel(NioSocketChannel.class);
 }

 public void close() {
 logger.info("關閉資源");
 worker.shutdownGracefully();
 }

 public Object remoteCall(final MethodInvokeMeta cmd, int retry) {
 try {
  CustomChannelInitializerClient customChannelInitializer = new CustomChannelInitializerClient(cmd);
  bootstrap.handler(customChannelInitializer);
  ChannelFuture sync = bootstrap.connect(url, port).sync();
  sync.channel().closeFuture().sync();
  Object response = customChannelInitializer.getResponse();
  return response;
 } catch (InterruptedException e) {
  retry++;
  if (retry > MAX_RETRY_TIMES) {
  throw new RuntimeException("調用Wrong");
  } else {
  try {
   Thread.sleep(100);
  } catch (InterruptedException e1) {
   e1.printStackTrace();
  }
  logger.info("第{}次嘗試....失敗", retry);
  return remoteCall(cmd, retry);
  }
 }
 }
}

ClientChannelHandlerAdapter.java

import com.edu.hart.rpc.entity.MethodInvokeMeta;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Created by 葉云軒 on 2017/6/16-17:03
 * Concat tdg_yyx@foxmail.com
 */
public class ClientChannelHandlerAdapter extends ChannelHandlerAdapter {
 private Logger logger = LoggerFactory.getLogger(ClientChannelHandlerAdapter.class);
 private MethodInvokeMeta methodInvokeMeta;
 private CustomChannelInitializerClient channelInitializerClient;

 public ClientChannelHandlerAdapter(MethodInvokeMeta methodInvokeMeta, CustomChannelInitializerClient channelInitializerClient) {
 this.methodInvokeMeta = methodInvokeMeta;
 this.channelInitializerClient = channelInitializerClient;
 }

 @Override
 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
 logger.info("客戶端出異常了,異常信息:{}", cause.getMessage());
 cause.printStackTrace();
 ctx.close();
 }

 @Override
 public void channelActive(ChannelHandlerContext ctx) throws Exception {
 if (methodInvokeMeta.getMethodName().endsWith("toString") && !"class java.lang.String".equals(methodInvokeMeta.getReturnType().toString()))
  logger.info("客戶端發送信息參數:{},信息返回值類型:{}", methodInvokeMeta.getArgs(), methodInvokeMeta.getReturnType());
 ctx.writeAndFlush(methodInvokeMeta);

 }

 @Override
 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
 channelInitializerClient.setResponse(msg);
 }
}

CustomChannelInitializerClient.java

import com.edu.hart.rpc.entity.MethodInvokeMeta;
import com.edu.hart.rpc.entity.NullWritable;
import com.edu.hart.rpc.util.ObjectCodec;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/** 
Created by 葉云軒 on 2017/6/16-15:01 
Concat tdg_yyx@foxmail.com
*/
public class CustomChannelInitializerClient extends ChannelInitializer { 
 private Logger logger = LoggerFactory.getLogger(CustomChannelInitializerClient.class);

 private MethodInvokeMeta methodInvokeMeta;

 private Object response;

 public CustomChannelInitializerClient(MethodInvokeMeta methodInvokeMeta) {
 if (!"toString".equals(methodInvokeMeta.getMethodName())) {
  logger.info("[CustomChannelInitializerClient] 調用方法名:{},入參:{},參數類型:{},返回值類型{}"
   , methodInvokeMeta.getMethodName()
   , methodInvokeMeta.getArgs()
   , methodInvokeMeta.getParameterTypes()
   , methodInvokeMeta.getReturnType());
 }
 this.methodInvokeMeta = methodInvokeMeta;
 }

 public Object getResponse() {
 if (response instanceof NullWritable) {
  return null;
 }
 return response;
 }

 public void setResponse(Object response) {
 this.response = response;
 }

 @Override
 protected void initChannel(SocketChannel ch) {
 ChannelPipeline pipeline = ch.pipeline();
 pipeline.addLast(new LengthFieldPrepender(2));
 pipeline.addLast(new LengthFieldBasedFrameDecoder(1024 * 1024, 0, 2, 0, 2));
 pipeline.addLast(new ObjectCodec());
 pipeline.addLast(new ClientChannelHandlerAdapter(methodInvokeMeta, this));
 }}

4. RPCProxyFactoryBean.java

import com.edu.hart.rpc.entity.MethodInvokeMeta;
import com.edu.hart.rpc.util.WrapMethodUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.AbstractFactoryBean;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* Created by 葉云軒 on 2017/6/16-17:16
* Concat tdg_yyx@foxmail.com
*/
public class RPCProxyFactoryBean extends AbstractFactoryBean implements InvocationHandler {
private Logger logger = LoggerFactory.getLogger(RPCProxyFactoryBean.class);
 private Class interfaceClass;

 private NettyClient nettyClient;

 @Override
 public Class<?> getObjectType() {
 return interfaceClass;
 }

 @Override
 protected Object createInstance() throws Exception {
 logger.info("[代理工廠] 初始化代理Bean : {}", interfaceClass);
 return Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, this);
 }

 @Override
 public Object invoke(Object proxy, Method method, Object[] args) {
 final MethodInvokeMeta methodInvokeMeta = WrapMethodUtils.readMethod(interfaceClass, method, args);
 if (!methodInvokeMeta.getMethodName().equals("toString")) {
  logger.info("[invoke] 調用接口{},調用方法名:{},入參:{},參數類型:{},返回值類型{}",
   methodInvokeMeta.getInterfaceClass(), methodInvokeMeta.getMethodName()
   , methodInvokeMeta.getArgs(), methodInvokeMeta.getParameterTypes(), methodInvokeMeta.getReturnType());
 }
 return nettyClient.remoteCall(methodInvokeMeta, 0);
 }

 public void setInterfaceClass(Class interfaceClass) {
 this.interfaceClass = interfaceClass;
 }

 public void setNettyClient(NettyClient nettyClient) {
 this.nettyClient = nettyClient;
 }
}

至此,netty-client與SpringBoot的集成了算完畢了。同樣 ,在netty-client中也要加入相應的依賴

不過上面server與client使用了一些公共的類和工具。下面也給列舉中出來。

MethodInvokeMeta.java

import org.springframework.stereotype.Component;

import java.io.Serializable;

/**
* 記錄調用方法的元信息
* Created by 葉云軒 on 2017/6/7-15:41
* Concat tdg_yyx@foxmail.com
*/
@Component
public class MethodInvokeMeta implements Serializable {

 private static final long serialVersionUID = 8379109667714148890L;
 //接口
 private Class<?> interfaceClass;
 //方法名
 private String methodName;
 //參數
 private Object[] args;
 //返回值類型
 private Class<?> returnType;
 //參數類型
 private Class<?>[] parameterTypes;

 public Object[] getArgs() {
 return args;
 }

 public void setArgs(Object[] args) {
 this.args = args;
 }

 public Class<?> getInterfaceClass() {
 return interfaceClass;
 }

 public void setInterfaceClass(Class<?> interfaceClass) {
 this.interfaceClass = interfaceClass;
 }

 public String getMethodName() {
 return methodName;
 }

 public void setMethodName(String methodName) {
 this.methodName = methodName;
 }

 public Class[] getParameterTypes() {
 return parameterTypes;
 }

 public void setParameterTypes(Class<?>[] parameterTypes) {
 this.parameterTypes = parameterTypes;
 }

 public Class getReturnType() {
 return returnType;
 }

 public void setReturnType(Class returnType) {
 this.returnType = returnType;
 }
}

NullWritable.java

import java.io.Serializable;

/**
* 服務器可能返回空的處理
* Created by 葉云軒 on 2017/6/16-16:46
* Concat tdg_yyx@foxmail.com
*/
public class NullWritable implements Serializable {

 private static final long serialVersionUID = -8191640400484155111L;
 private static NullWritable instance = new NullWritable();

 private NullWritable() {
 }

 public static NullWritable nullWritable() {
 return instance;
 }
}



ObjectCodec.java

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;

import java.util.List;

public class ObjectCodec extends MessageToMessageCodec<ByteBuf, Object> {

 @Override
 protected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) {
 byte[] data = ObjectSerializerUtils.serilizer(msg);
 ByteBuf buf = Unpooled.buffer();
 buf.writeBytes(data);
 out.add(buf);
 }

 @Override
 protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) {
 byte[] bytes = new byte[msg.readableBytes()];
 msg.readBytes(bytes);
 Object deSerilizer = ObjectSerializerUtils.deSerilizer(bytes);
 out.add(deSerilizer);
 }
}

ObjectSerializerUtils.java

package com.edu.hart.rpc.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;

/**
* 對象序列化工具
*/
public class ObjectSerializerUtils {

 private static final Logger logger = LoggerFactory.getLogger(ObjectSerializerUtils.class);

 /**
 * 反序列化
 *
 * @param data
 * @return
 */
 public static Object deSerilizer(byte[] data) {
 if (data != null && data.length > 0) {
  try {
  ByteArrayInputStream bis = new ByteArrayInputStream(data);
  ObjectInputStream ois = new ObjectInputStream(bis);
  return ois.readObject();
  } catch (Exception e) {
  logger.info("[異常信息] {}", e.getMessage());
  e.printStackTrace();
  }
  return null;
 } else {
  logger.info("[反序列化] 入參為空");
  return null;
 }
 }

 /**
 * 序列化對象
 *
 * @param obj
 * @return
 */
 public static byte[] serilizer(Object obj) {
 if (obj != null) {
  try {
  ByteArrayOutputStream bos = new ByteArrayOutputStream();
  ObjectOutputStream oos = new ObjectOutputStream(bos);
  oos.writeObject(obj);
  oos.flush();
  oos.close();
  return bos.toByteArray();
  } catch (IOException e) {
  e.printStackTrace();
  }
  return null;
 } else {
  return null;
 }
 }
}

下面主要是用于Client端的:

NettyBeanSacnner.java

import com.edu.hart.rpc.client.RPCProxyFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

import java.util.List;

/**
* 動態加載代理bean到Spring bean工廠
*/
public class NettyBeanScanner implements BeanFactoryPostProcessor {

 private DefaultListableBeanFactory beanFactory;

 private String basePackage;

 private String clientName;

 public NettyBeanScanner(String basePackage, String clientName) {
 this.basePackage = basePackage;
 this.clientName = clientName;
 }


 /**
 * 注冊Bean到Spring的bean工廠
 */
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
 this.beanFactory = (DefaultListableBeanFactory) beanFactory;
 // 加載遠程服務的接口
 List<String> resolverClass = PackageClassUtils.resolver(basePackage);
 for (String clazz : resolverClass) {
  String simpleName;
  if (clazz.lastIndexOf('.') != -1) {
  simpleName = clazz.substring(clazz.lastIndexOf('.') + 1);
  } else {
  simpleName = clazz;
  }
  BeanDefinitionBuilder gd = BeanDefinitionBuilder.genericBeanDefinition(RPCProxyFactoryBean.class);
  gd.addPropertyValue("interfaceClass", clazz);
  gd.addPropertyReference("nettyClient", clientName);
  this.beanFactory.registerBeanDefinition(simpleName, gd.getRawBeanDefinition());
 }
 }
}

PackageClassUtils.java

這個類要說一下,主要是用來加載Server對應的接口的。因為在Client中RPC接口沒有實現類,所以要自己將這些接口加載到Spring工廠里面。但是現在有個問題就是需要使用**

SpringBoot中application.yml

basePackage: com.edu.hart.rpc.service.login;com.edu.hart.rpc.service.employee;com.edu.hart.rpc.service.authorization;

這樣的方式來加載,使用通配符的時候會加載不到,這個問題我還沒有解決。**

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
* 字節文件加載
*/
public class PackageClassUtils {

 private final static Logger LOGGER = LoggerFactory.getLogger(PackageClassUtils.class);

 /**
 * 解析包參數
 *
 * @param basePackage 包名
 * @return 包名字符串集合
 */
 public static List<String> resolver(String basePackage) {
 //以";"分割開多個包名
 String[] splitFHs = basePackage.split(";");
 List<String> classStrs = new ArrayList<>();
 //s: com.yyx.util.*
 for (String s : splitFHs) {
  LOGGER.info("[加載類目錄] {}", s);
  //路徑中是否存在".*" com.yyx.util.*
  boolean contains = s.contains(".*");
  if (contains) {
  //截斷星號 com.yyx.util
  String filePathStr = s.substring(0, s.lastIndexOf(".*"));
  //組裝路徑 com/yyx/util
  String filePath = filePathStr.replaceAll("\\.", "/");
  //獲取路徑 xxx/classes/com/yyx/util
  File file = new File(PackageClassUtils.class.getResource("/").getPath() + "/" + filePath);
  //獲取目錄下獲取文件
  getAllFile(filePathStr, file, classStrs);
  } else {
  String filePath = s.replaceAll("\\.", "/");
  File file = new File(PackageClassUtils.class.getResource("/").getPath() + "/" + filePath);
  classStrs = getClassReferenceList(classStrs, file, s);
  }
 }
 return classStrs;
 }

 /**
 * 添加全限定類名到集合
 *
 * @param classStrs 集合
 * @return 類名集合
 */
 private static List<String> getClassReferenceList(List<String> classStrs, File file, String s) {
 File[] listFiles = file.listFiles();
 if (listFiles != null && listFiles.length != 0) {
  for (File file2 : listFiles) {
  if (file2.isFile()) {
   String name = file2.getName();
   String fileName = s + "." + name.substring(0, name.lastIndexOf('.'));
   LOGGER.info("[加載完成] 類文件:{}", fileName);
   classStrs.add(fileName);
  }
  }
 }
 return classStrs;
 }


 /**
 * 獲取一個目錄下的所有文件
 *
 * @param s
 * @param file
 * @param classStrs
 */
 private static void getAllFile(String s, File file, List<String> classStrs) {
 if (file.isDirectory()) {
  File[] files = file.listFiles();
  if (files != null)
  for (File file1 : files) {
   getAllFile(s, file1, classStrs);
  }
 } else {
  String path = file.getPath();
  String cleanPath = path.replaceAll("/", ".");
  String fileName = cleanPath.substring(cleanPath.indexOf(s), cleanPath.length());
  LOGGER.info("[加載完成] 類文件:{}", fileName);
  classStrs.add(fileName);
 }
 }
}

RemoteMethodInvokeUtil.java

import com.edu.hart.rpc.entity.MethodInvokeMeta;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* 消息處理類
* Created by 葉云軒 on 2017/6/7-15:49
* Concat tdg_yyx@foxmail.com
*/
public class RemoteMethodInvokeUtil implements ApplicationContextAware {

 private ApplicationContext applicationContext;

 public Object processMethod(MethodInvokeMeta methodInvokeMeta) throws InvocationTargetException, IllegalAccessException {
 Class interfaceClass = methodInvokeMeta.getInterfaceClass();
 Object bean = applicationContext.getBean(interfaceClass);
 Method[] declaredMethods = interfaceClass.getDeclaredMethods();
 Method method = null;
 for (Method declaredMethod : declaredMethods) {
  if (methodInvokeMeta.getMethodName().equals(declaredMethod.getName())) {
  method = declaredMethod;
  }
 }
 Object invoke = method.invoke(bean, methodInvokeMeta.getArgs());
 return invoke;
 }

 @Override
 public void setApplicationContext(ApplicationContext app) throws BeansException {
 applicationContext = app;
 }
}

WrapMethodUtils.java

import com.edu.hart.rpc.entity.MethodInvokeMeta;

import java.lang.reflect.Method;

public class WrapMethodUtils {
/**
* 獲取 method的元數據信息

@param interfaceClass
* @param method
* @param args
* @return
*/
public static MethodInvokeMeta readMethod(Class interfaceClass, Method method, Object[] args) {
MethodInvokeMeta mim = new MethodInvokeMeta();
mim.setInterfaceClass(interfaceClass);
mim.setArgs(args);
mim.setMethodName(method.getName());
mim.setReturnType(method.getReturnType());
Class<?>[] parameterTypes = method.getParameterTypes();
mim.setParameterTypes(parameterTypes);
return mim;
}
}

下面的這些類我也會用在與前臺通信時使用:

ResponseEnum.java

import java.io.Serializable;

/**

響應碼枚舉類
Created by 葉云軒 on 2017/6/13-11:53
Concat tdg_yyx@foxmail.com
*/
public enum ResponseCodeEnum implements Serializable {

// region authentication code
REQUEST_SUCCESS(10000, "請求成功"),
SERVER_ERROR(99999, "服務器內部錯誤"),;

//region 提供對外訪問的方法,無需更改
/**
響應碼
*/
private Integer code;
/**
響應信息
*/
private String msg;
ResponseCodeEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}

public Integer getCode() {
return code;
}

public String getMsg() {
return msg;
}

//endregion
}

ResponseResult.java

import java.io.Serializable;

/**
 * 數據返回實體封裝
 * <p>
 * Created by 葉云軒 on 2017/6/13-11:38
 * Concat tdg_yyx@foxmail.com
 *
 * @param <T> 通用變量
 */
public class ResponseResult<T> implements Serializable {


 private static final long serialVersionUID = -3411174924856108156L;
 /**
 * 服務器響應碼
 */
 private Integer code;
 /**
 * 服務器響應說明
 */
 private String msg;
 /**
 * 服務器響應數據
 */
 private T data;

 public ResponseResult() {

 }

 @Override
 public boolean equals(Object o) {
 if (this == o) return true;
 if (o == null || getClass() != o.getClass()) return false;

 ResponseResult<?> that = (ResponseResult<?>) o;

 return (code != null ? code.equals(that.code) : that.code == null) && (msg != null ? msg.equals(that.msg) : that.msg == null) && (data != null ? data.equals(that.data) : that.data == null);
 }

 public Integer getCode() {

 return code;
 }

 public void setCode(Integer code) {
 this.code = code;
 }

 public T getData() {
 return data;
 }

 public void setData(T data) {
 this.data = data;
 }

 public String getMsg() {
 return msg;
 }

 public void setMsg(String msg) {
 this.msg = msg;
 }

 @Override
 public int hashCode() {
 int result = code != null ? code.hashCode() : 0;
 result = 31 * result
  + (msg != null ? msg.hashCode() : 0);
 result = 31 * result + (data != null ? data.hashCode() : 0);
 return result;
 }

 @Override
 public String toString() {
 return "ResponseResult{"
  + "code="
  + code
  + ", msg='"
  + msg
  + '\''
  + ", data="
  + data
  + '}';
 }
}

ResponseResultUtil.java

import com.edu.hart.modules.communicate.ResponseCodeEnum;
import com.edu.hart.modules.communicate.ResponseResult;

/**
 * 返回結果工具類
 * Created by 葉云軒 on 2017/5/29-10:37
 * Concat tdg_yyx@foxmail.com
 */
public class ResponseResultUtil {

 /**
 * 請求失敗返回的數據結構
 *
 * @param responseCodeEnum 返回信息枚舉類
 * @return 結果集
 */
 public static ResponseResult error(ResponseCodeEnum responseCodeEnum) {
 ResponseResult ResponseResult = new ResponseResult();
 ResponseResult.setMsg(responseCodeEnum.getMsg());
 ResponseResult.setCode(responseCodeEnum.getCode());
 ResponseResult.setData(null);
 return ResponseResult;
 }

 /**
 * 沒有結果集的返回數據結構
 *
 * @return 結果集
 */
 public static ResponseResult success() {
 return success(null);
 }

 /**
 * 成功返回數據結構
 *
 * @param o 返回數據對象
 * @return 返回結果集
 */
 public static ResponseResult success(Object o) {
 ResponseResult responseResult = new ResponseResult();
 responseResult.setMsg(ResponseCodeEnum.REQUEST_SUCCESS.getMsg());
 responseResult.setCode(ResponseCodeEnum.REQUEST_SUCCESS.getCode());
 responseResult.setData(o);
 return responseResult;
 }

 /**
 * 判斷是否成功
 *
 * @param responseResult 請求結果
 * @return 判斷結果
 */
 public static boolean judgementSuccess(ResponseResult responseResult) {
 return responseResult.getCode().equals(ResponseCodeEnum.REQUEST_SUCCESS.getCode());
 }
}

來,我們測試一下遠程通信:

Client調用Server的一個接口。可以看到在hart-oa項目中,RPCEmployeeService沒有任何實現類,控制臺中打印了方法的調用 以及入參信息

Netty與Spring Boot的整合實現

Server斷點監聽到遠程調用,CloudApplication項目為Server端,我們可以看到接收到來自hart-oa的一個請求,參數一致。在CloudApplication中進行相應的處理后,返回到Client(hart-oa)

Netty與Spring Boot的整合實現

返回信息到Client,可以看到我們(hart-oa)收到了來自CloudApplication的響應,結果是我們封裝好的ResponseResult.

Netty與Spring Boot的整合實現

嗯 ~至此整合測試完成。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

柳江县| 黄平县| 长汀县| 大英县| 眉山市| 鄱阳县| 西宁市| 芜湖市| 修文县| 邢台市| 文登市| 万盛区| 桓仁| 衡水市| 龙门县| 清镇市| 苗栗市| 阿拉善左旗| 伊川县| 嘉义市| 五常市| 洪江市| 广饶县| 溧水县| 伊通| 贵定县| 繁昌县| 柳林县| 巩留县| 资源县| 浙江省| 宣威市| 包头市| 龙陵县| 香河县| 绵竹市| 漾濞| 紫金县| 佛坪县| 阳高县| 西峡县|