一:什么是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. #pragma 的使用(转)

    尽管 C 和 C++ 都已经有标准,但是几乎每个编译器 (广义,包含连接器等) 扩展一些 C/C++ 关键字. 合理地应用这些关键字,有时候能使我们的工作非常方便.下面随便说说 Visual C++ ...

  2. Centos7没有Ifconfig命令该怎么办?

    centos7没有ifconfig命令该怎么办? linux系统查看ip地址常用命令是[ifconfig],CentOS 7.0最小安装是没有ifconfig命令怎么办?当然可用[ip addr]查看 ...

  3. babel 转换箭头函数

    转换前: const sum = (a,b)=>a+b 转化后: // "use strict"; // var fn = function fn(a, b) { // re ...

  4. JDK7

    https://docs.oracle.com/javase/7/docs/index.html

  5. springboot自定义错误页

    静态错误页放在         动态可以放在freemaker或者thymeleaf         匹配规则: 先找动态页面再找静态页面 先找精确错误页面再找模糊页面     注:精确错误页面=50 ...

  6. Quick BI的宝藏工具——交叉表

    对于普通的表格展示数据,相信大家都非常熟悉了,今天给大家介绍的是BI领域的分析利器-交叉表,这个在BI分析场景中使用占比最多的分析利器.通过交叉表对数据的承载和管理,用户可以一目了然地分析出各种场景指 ...

  7. eclipse配置Maven——菜鸟篇

    首先解释关于webservice: Web service是一个平台独立的,低耦合的,自包含的.基于可编程的web的应用程序, 可使用开放的XML(标准通用标记语言下的一个子集)标准来描述.发布.发现 ...

  8. 运行job检验单元测试覆盖率

    http://ns.jenkins.baidu.com/user/anyixing/my-views/view/Map_ut/job/poi-zhunru/ 1在http://ns.jenkins.b ...

  9. VS2015中添加QT5.9.0插件

    https://blog.csdn.net/hhhuang1991/article/details/79768595 VS2015里使用QTDIR路径查找QT开发包目录 路径配置操作系统环境变量里添加 ...

  10. 手把手教您在 Windows Server 2019 上使用 Docker

    配置 Windows 功能 要运行容器,您还需要启用容器功能 Install-WindowsFeature -Name Containers 在 Window Server 2019 上安装 Dock ...