需求与思路
- 基于Netty
- 客户端与服务端交互需要使用JSON
- 客户端调用具体类中的方法需要使用代理模式
- 客户端发送的数据为一个请求实体,实体中包括
请求id
,需要调用的类名
,需要调用的方法名
,需要传入方法的参数类型
,需要传入方法的参数值
- 服务端使用@SpringBootApplication来标注启动类(主要是为了引入spring容器)
- 服务端用@Service标注具体的service层实现类(客户端需要调用的类)
代码
-
首先创建工程, 有三个模块, 分别为rpc-common,rpc-provider,rpc-consumer
rpc-common模块, 主要是定义通用依赖,消息编码解密类,请求实体类,service接口类 rpc-provider模块, pom依赖于rpc-common,为服务端 rpc-consumer模块, pom依赖于rpc-common,为客户端
-
编写common模块
2.1 在common模块中首先需要引入fastjson的pom依赖
<dependencies> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.6.Final</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.41</version> </dependency> </dependencies>
2.2 定义一个序列话的接口以及对应的实现类
Serializer.javapublic interface Serializer { /** * java对象转换为二进制 */ byte[] serialize(Object object) throws IOException; /** * 二进制转换成java对象 */ <T> T deserialize(Class<T> clazz, byte[] bytes) throws IOException; }
JsonSerializer.java
public class JsonSerializer implements Serializer{ public byte[] serialize(Object object) throws IOException { return JSON.toJSONBytes(object); } public <T> T deserialize(Class<T> clazz, byte[] bytes) throws IOException { return JSON.parseObject(bytes, clazz); } }
2.3 定义RpcEncode,RpcDecode,给客户端和服务端启动的时候,添加到pipeline上用,主要是解决服务端接收到客户端的数据,把json字符串的字节自动解码对象,客服端发送给服务端的数据,自动把实体转为json字符类型的二进制。
RpcDecoder.javapublic class RpcDecoder extends MessageToMessageDecoder<ByteBuf> { private Class<?> clazz; private Serializer serializer; public RpcDecoder(Class<?> clazz, Serializer serializer) { this.serializer = serializer; this.clazz = clazz; } @Override protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception { // readInt 读索引加4 int length = byteBuf.readableBytes() >=4 ? byteBuf.readInt() : 0; byte[] bytes = new byte[length]; byteBuf.readBytes(bytes); Object deserialize = serializer.deserialize(clazz, bytes); System.out.println(deserialize); list.add(deserialize); } }
RpcEncoder.java
public class RpcEncoder extends MessageToByteEncoder { private Class<?> clazz; private Serializer serializer; public RpcEncoder(Class<?> clazz, Serializer serializer) { this.clazz = clazz; this.serializer = serializer; } @Override protected void encode(ChannelHandlerContext channelHandlerContext, Object msg, ByteBuf byteBuf) throws Exception { if (clazz != null && clazz.isInstance(msg)) { byte[] bytes = serializer.serialize(msg); byteBuf.writeInt(bytes.length); byteBuf.writeBytes(bytes); } } }
2.4 编写请求实体
RpcRequest.java
public class RpcRequest { /** * 请求对象的ID */ private String requestId; /** * 类名 */ private String className; /** * 方法名 */ private String methodName; /** * 参数类型 */ private Class<?>[] parameterTypes; /** * 入参 */ private Object[] parameters; public String getRequestId() { return requestId; } public void setRequestId(String requestId) { this.requestId = requestId; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } 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 Object[] getParameters() { return parameters; } public void setParameters(Object[] parameters) { this.parameters = parameters; } }
2.5 编写业务需要使用的接口类
IUserService.javapublic interface IUserService { public String sayHello(String msg); }
-
编写provider模块
3.1 编写标注@SpringBootApplication的启动类,使得依赖注入
RpcHomeworkProviderBoot.java@SpringBootApplication(scanBasePackages={"com.sherlock"}) public class RpcHomeworkProviderBoot { public static void main(String[] args) { SpringApplication.run(RpcHomeworkProviderBoot.class, args); } }
3.2 编写rpc的启动类
ServerBoot.java@Component public class ServerBoot { @Bean // 让springboot启动的时候,运行此方法 public void initServer() { System.out.println("init server..."); try { startServer("127.0.0.1", 8999); // 启动rpc的服务端, 绑定地址和端口 } catch (InterruptedException e) { e.printStackTrace(); } } public void startServer(String ip, int port) throws InterruptedException { // 创建线程池 NioEventLoopGroup boosGroup = new NioEventLoopGroup(); NioEventLoopGroup workGroup = new NioEventLoopGroup(); // 创建启动类 ServerBootstrap serverBootstrap = new ServerBootstrap(); // 配置启动类参数 serverBootstrap.group(boosGroup, workGroup) // 使用nio通道 .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<NioSocketChannel>() { protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception { nioSocketChannel .pipeline() .addLast(new StringEncoder()) // 把接收的参数按RpcDecoder规则解码 .addLast(new RpcDecoder(RpcRequest.class, new JsonSerializer())) // 处理类 .addLast(new ServiceHandler()); } }); // 绑定端口 serverBootstrap.bind(ip, port).sync(); } }
3.3 编写服务处理类
ServiceHandler.java@Component // 加入容器,让可以访问ApplicationContext public class ServiceHandler extends ChannelInboundHandlerAdapter implements ApplicationContextAware { private static ApplicationContext applicationContext; // 获取容器上下文, 从而获取bean @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("setApplicationContext...."); this.applicationContext = applicationContext; } // 客户端连接服务器时, 服务器读取数据方法 @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { RpcRequest request = (RpcRequest) msg; // 在容器中获取bean 客户端传过来的时候, 用的是@Service的name,所以可以直接读取到 Object bean = this.applicationContext.getBean(request.getClassName()); Class<?> beanClass = bean.getClass(); // 通过反射调用方法 Method method = beanClass.getMethod(request.getMethodName(), request.getParameterTypes()); method.invoke(bean, request.getParameters()); // 返回数据 ctx.writeAndFlush("success"); } }
3.4 具体业务类
IUserServiceImpl.java@Service("IUserService") public class IUserServiceImpl implements IUserService { public String sayHello(String msg) { String s = "Say Hello " + msg; System.out.println(s); return s; } }
4 编写consumer模块
4.1 编写客户端启动类
RPCConsumer.javapublic class RPCConsumer { // 定义线程池 获取当前cpu可以使用的线程数 private static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); // 自定义一个处理类 private static CommonServiceHandler serviceHandler; // 初始话客户端 public static void init() { serviceHandler = new CommonServiceHandler(); // 定义一个线程 EventLoopGroup eventExecutors = new NioEventLoopGroup(); // 创建启动类 Bootstrap bootstrap = new Bootstrap(); // 配置参数 bootstrap.group(eventExecutors) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline() // 客户端发送数据,自动把实体变为json数据二进制 .addLast(new RpcEncoder(RpcRequest.class, new JsonSerializer())) .addLast(new StringDecoder()) // 处理方法 .addLast(serviceHandler); } }) // 设置tcp协议 .option(ChannelOption.TCP_NODELAY, true); try { bootstrap.connect("127.0.0.1", 8999).sync(); } catch (InterruptedException e) { e.printStackTrace(); } } public static Object creatObject(Class<?> serviceClass) { return Proxy.newProxyInstance(RPCConsumer.class.getClassLoader(), new Class[]{serviceClass}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (serviceHandler == null) { init(); } // 设置请求实体 RpcRequest rpcRequest = new RpcRequest(); rpcRequest.setRequestId(UUID.randomUUID().toString()); // 设置类名, 这里设置的不是全限定类名, 而是简单的类名, // 跟服务端的@service定义的名称一致 rpcRequest.setClassName(method.getDeclaringClass().getSimpleName()); rpcRequest.setMethodName(method.getName()); rpcRequest.setParameterTypes(method.getParameterTypes()); rpcRequest.setParameters(args); serviceHandler.setRpcRequest(rpcRequest); // 提交给线程处理 Object result = executorService.submit(serviceHandler).get(); return result; } }); } }
4.2 CommonServiceHandler线程处理类
CommonServiceHandler.javapublic class CommonServiceHandler extends ChannelInboundHandlerAdapter implements Callable { // 保存channel上下文信息 private ChannelHandlerContext ctx; // 保存结果 private Object result; private RpcRequest rpcRequest; public void setRpcRequest(RpcRequest rpcRequest) { this.rpcRequest = rpcRequest; } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { // 客户端与服务器连接创建得时候会触发 System.out.println("channelActive"); this.ctx = ctx; } @Override public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // 读取服务端返回得数据 result = msg.toString(); // 通知call方法 notify(); } @Override public synchronized Object call() throws Exception { // 向服务器写数据 ctx.writeAndFlush(rpcRequest); // 等待,被唤醒 wait(); return result; } }
4.3 客户端启动类
ConsumerBoot.javapublic class ConsumerBoot { public static void main(String[] args) { IUserService object = (IUserService) RPCConsumer.creatObject(IUserService.class); while (true) { String result = object.sayHello("Sherlock"); System.out.println(result); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
- 贴一下项目目录
common模块
provider模块
consumer模块
- 贴一下项目目录