一:什么是RPC

远程过程调用(Remote Procedure Call)。就是调用其他业务方的方法的时候,就像是调用自己本地的方法一样。

二:java rpc实现简介

服务端(使用反射)

(1)服务端写一个接口和一个接口的实现。

(2)服务端维护一个map,key为接口的类名,value为接口的实现类。

(3)服务端通过 ServerSocket 接收客户端发送过来的数据(接口的类名,方法名,请求参数)

(4)服务端根据接口的类名和方法名得到对应实现类的方法,通过反射调用服务

(5)把处理之后的结果通过 ServerSocket  返回给客户端

客户端(动态代理)

(1)把服务端的接口copy过来

(2)写一个代理类,调用服务端方法的时候,通过代理类的 invoke 方法把服务需要的 接口类名,方法名,请求参数 通过 Socket 发送给服务端;接收服务端处理的结果

三:具体实现的代码

(1)服务端的接口声明

public interface IHello {
String sayHello(String string);
}

(2)服务端接口的实现(真正干活的类)

public class HelloServiceImpl implements IHello{

	@Override
public String sayHello(String string) {
// TODO Auto-generated method stub
return "你好:" + string;
}
}

(3)服务端的服务类

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap; public class RpcServer { private static final HashMap<String, Class<?>> serviceRegistry = new HashMap<>();
private int port; public RpcServer(int port) {
this.port =port;
} public RpcServer register(Class serviceInterface, Class impl) {
serviceRegistry.put(serviceInterface.getName(), impl);
return this;
} public void run() throws IOException { ServerSocket server = new ServerSocket();
server.bind(new InetSocketAddress (port));
System.out.println("start server");
ObjectInputStream input =null;
ObjectOutputStream output =null;
Socket socket=null;
try {
while(true){
socket = server.accept ();
input =new ObjectInputStream(socket.getInputStream());
String serviceName = input.readUTF();
String methodName = input.readUTF();
System.out.println ("客户端调用了 " + methodName + "方法");
Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
Object[] arguments = (Object[]) input.readObject();
Class serviceClass = serviceRegistry.get(serviceName);
if (serviceClass == null) {
throw new ClassNotFoundException(serviceName + " not found");
}
Method method = serviceClass.getMethod(methodName, parameterTypes);
Object result = method.invoke(serviceClass.newInstance(), arguments);
output = new ObjectOutputStream (socket.getOutputStream());
output.writeObject(result);
}
} catch (Exception e){
e.printStackTrace();
}
} public static void main(String[] args) throws IOException {
new RpcServer (8888).register(IHello.class,HelloServiceImpl.class).run();
} }

(4)客户端的实现

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket; import com.cs.rpc.nativ.server.IHello; public class RpcClientProxy<T> implements InvocationHandler { private Class<T> serviceInterface;//被调用者的接口声明
private InetSocketAddress addr;//被调用者的地址(ip+port) public RpcClientProxy(Class<T> serviceInterface, String ip,String port) {
this.serviceInterface = serviceInterface;
this.addr = new InetSocketAddress(ip, Integer.parseInt ( port ));
} public T getClientIntance(){
return (T) Proxy.newProxyInstance (serviceInterface.getClassLoader(),new Class<?>[]{serviceInterface},this);
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Socket socket = null;
ObjectOutputStream output = null;
ObjectInputStream input = null; try {
// 2.创建Socket客户端,根据指定地址连接远程服务提供者
socket = new Socket();
socket.connect(addr); // 3.将远程服务调用所需的接口类、方法名、参数列表等编码后发送给服务提供者
output = new ObjectOutputStream(socket.getOutputStream());
output.writeUTF(serviceInterface.getName());
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(args); // 4.同步阻塞等待服务器返回应答,获取应答后返回
input = new ObjectInputStream(socket.getInputStream());
return input.readObject();
} finally {
if (socket != null) socket.close();
if (output != null) output.close();
if (input != null) input.close();
}
} public static void main(String[] args) {
RpcClientProxy client = new RpcClientProxy<>(IHello.class,"localhost","8888");
IHello hello = (IHello) client.getClientIntance ();
System.out.println (hello.sayHello ( "socket rpc" ));
}
}

  

RPC-基于原生java实现的更多相关文章

  1. github上的golang双向rpc,基于原生“net/rpc”库实现,可以注册回调

    github上的golang双向rpc,基于原生“net/rpc”库实现,可以注册回调.仅支持一个server和一个client交互. 地址:https://github.com/rocket049/ ...

  2. 分布式架构的基石.简单的 RPC 框架实现(JAVA)

    前言 RPC 的全称是 Remote Procedure Call,它是一种进程间通信方式.允许像调用本地服务一样调用远程服务. 学习来源:<分布式系统架构:原理与实践> - 李林锋 1. ...

  3. 原生java调用webservice的方法,不用生成客户端代码

    原生java调用webservice的方法,不用生成客户端代码 2015年10月29日 16:46:59 阅读数:1455 <span style="font-family: Aria ...

  4. 用jQuery基于原生js封装的轮播

    我发现轮播在很多网站里面都用到过,一个绚丽的轮播可以为网页增色不少,最近闲来无事,也用原生js封装了一个轮播,可能不像网上的插件那么炫,但是也有用心去做.主要用了闭包的思想.需要传递的参数有:图片地址 ...

  5. 基于纯Java代码的Spring容器和Web容器零配置的思考和实现(3) - 使用配置

    经过<基于纯Java代码的Spring容器和Web容器零配置的思考和实现(1) - 数据源与事务管理>和<基于纯Java代码的Spring容器和Web容器零配置的思考和实现(2) - ...

  6. 基于原生js的图片延迟加载

    当页面图片比较多的时候,我们通常会做一个延迟加载,避免页面打开时一下子的请求数太多,加载过慢影响用户体验. 如果项目用了jquery框架,则可以直接用 jquery.lazyload.可在jquery ...

  7. 基于原生js的返回顶部组件,兼容主流浏览器

    基于原生js的返回顶部插件,兼容IE8及以上.FF.chrome等主流浏览器. js文件中封装了getScrollTop()和changeScrollTop()函数分别用于获取滚动条滚动的高度和修改滚 ...

  8. 原生Java代码拷贝目录

    拷贝.移动文件(夹),有三方包commons-io可以用,但是有时候有自己的需求,只能使用原生java代码,这时可以用以下几种方式进行拷贝: 1.使用系统命令(Linux)调用 此种方式对操作系统有要 ...

  9. 一个基于原生JavaScript开发的、轻量的验证码生成插件

    Vcode.js 一个基于原生JavaScript开发的.轻量的验证码生成插件 V: 1.0.0 DEMO:https://jofunliang.github.io/Vcode.js/example. ...

  10. RPC基于http协议通过netty支持文件上传下载

    本人在中间件研发组(主要开发RPC),近期遇到一个需求:RPC基于http协议通过netty支持文件上传下载 经过一系列的资料查找学习,终于实现了该功能 通过netty实现文件上传下载,主要在编解码时 ...

随机推荐

  1. 题解 P1587 【[NOI2016]循环之美】

    知识点:莫比乌斯反演 积性函数 杜教筛 废话前言: 我是古明地恋,写这篇题解的人已经被我 请各位读者自行无视搞事的恋恋带有删除线的内容,谢谢茄子. 这道题目本身并不难,但是公式推导/代码过程中具有迷惑 ...

  2. Qt 打包release发布问题

    除了使用depens查看exe依赖的dll,本文使用qt5.13自带的打包工具windeployqt.exe tips: demo.exe(x86) :C:\Qt\Qt5.12.3\5.12.3\ms ...

  3. A1002

    多项式相加,按非零项个数,指数,系数输入两个n项多项式,合并同类项之后输出. 用数组编号保存指数,编号对应的数组值保存系数,相加之后用count记录非零项的个数,最后输出. 数组最大1000个,如果有 ...

  4. HTML和CSS实现的透明登录框效果

    实现代码 HTML部分 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&qu ...

  5. telnet ip 端口

    telnet ip 端口 1.关闭防火墙, 2.配置防火墙,出入端规则

  6. Python_020(几个经典内置方法)

    一.内置方法 1.内置方法表示:__名字__ 几种名称: 1)双下方法 2)魔术方法 3)类中的特殊方法/内置方法 类中的每一个双下方法都有它自己的特殊意义;所有的双下方法没有 需要你在外部直接调用的 ...

  7. (18)C++项目练习一(功能会不断扩展)--------【聊天工具】

    1.准备使用Qt和C++做一个远程(基于互联网的)聊天工具,需要实现以下功能 (1)多对多聊天功能 (2)文件传输功能 (3)注册.登录功能 (4)加好友.同意好友功能 (5)好友列表.黑名单功能(分 ...

  8. ionic使用自定义icon

    参考文档:https://www.jianshu.com/p/5346fee9fd80  angular+ionic 自定义图标 注意: 这里不用name 用class类名显示出来 最后出来图标是个小 ...

  9. Redis实现存取数据+数据存取

    添加依赖: <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId> ...

  10. InfluxDB安装使用

    influxdb简介   启动步骤 服务启停:sudo service influxdb start/stop/restart 安装过程: 1.增加yum源 cat <<EOF | sud ...