rpc简易实现-zookeeper
一、RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

1、这张图是dubbo官网的模型图
2、过程:服务——>注册地址到zk
客户端——>通过zk获取对应服务地址
代理——>通过接口代理,实现服务调用
调用方式——>这个不一定了,一般tcp方式比较直接
四、简易版rpc实现,注册中心用的zk,可以使用其他注册中心
1)目录结构
2)依赖的jar
- <dependency>
- <groupId>org.apache.zookeeper</groupId>
- <artifactId>Zookeeper</artifactId>
- <version>3.4.0</version>
- </dependency>
3)zookeeper的连接工具
- import org.apache.zookeeper.*;
- import java.io.IOException;
- import java.util.concurrent.CountDownLatch;
- public class ZookeeperUtils {
- private static ZooKeeper zooKeeper = null;
- public static ZooKeeper connect() throws IOException, InterruptedException {
- CountDownLatch latch = new CountDownLatch(1);
- //连接zk
- zooKeeper = new ZooKeeper("localhost:2182", 60000, watchedEvent -> {
- if (watchedEvent.getState() == Watcher.Event.KeeperState.SyncConnected) {
- latch.countDown();
- }
- });
- //无连接阻塞
- latch.await();
- return zooKeeper;
- }
- }
4)数据传输的实体
- import java.io.Serializable;
- /**
- * 请求需要的数据
- */
- public class RpcRequest implements Serializable{
- //接口名称,用于反射
- private String interfaceName;
- //调用方法
- private String method;
- //参数类型
- private Class<?>[] parameterTypes;
- //参数
- private Object[] params;
- public String getInterfaceName() {
- return interfaceName;
- }
- public void setInterfaceName(String interfaceName) {
- this.interfaceName = interfaceName;
- }
- public String getMethod() {
- return method;
- }
- public void setMethod(String method) {
- this.method = method;
- }
- public Class<?>[] getParameterTypes() {
- return parameterTypes;
- }
- public void setParameterTypes(Class<?>[] parameterTypes) {
- this.parameterTypes = parameterTypes;
- }
- public Object[] getParams() {
- return params;
- }
- public void setParams(Object[] params) {
- this.params = params;
- }
- }
- import java.io.Serializable;
- /**
- * 相应对象
- */
- public class RpcResponse implements Serializable {
- //返回状态,当然这里可以,加入对应的异常等(这里简化)
- private String status;
- //返回的数据
- private Object data;
- public String getStatus() {
- return status;
- }
- public void setStatus(String status) {
- this.status = status;
- }
- public Object getData() {
- return data;
- }
- public void setData(Object data) {
- this.data = data;
- }
- }
5)测试的服务以及实现
- import com.pinnet.zookeeper.service.ITestService;
- public class TestServiceImpl implements ITestService{
- public String test(String name) {
- System.out.println(name);
- return "return" + name;
- }
- }
6)提供方
- import com.pinnet.zookeeper.bean.RpcBeanFactory;
- import com.pinnet.zookeeper.data.RpcRequest;
- import com.pinnet.zookeeper.data.RpcResponse;
- import com.pinnet.zookeeper.service.ITestService;
- import com.pinnet.zookeeper.service.impl.TestServiceImpl;
- import com.pinnet.zookeeper.zookeeper.ZookeeperUtils;
- import org.apache.zookeeper.*;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.net.ServerSocket;
- import java.net.Socket;
- /**
- * rpc服务端
- */
- public class Server {
- //服务器设定的目录
- private String registryPath = "/registry";
- //接口,这里方便测试用
- private String serviceName = ITestService.class.getName();
- //地址目录
- private static String addressName = "address";
- //本地地址
- private static String ip = "localhost";
- //监听接口
- public static Integer port = 8000;
- //链接zk
- public ZooKeeper connect() throws Exception {
- ZooKeeper zooKeeper = ZookeeperUtils.connect();
- return zooKeeper;
- }
- //创建节点,也就是访问的,目录
- public void createNode(ZooKeeper zooKeeper) throws Exception {
- if (zooKeeper.exists(registryPath, false) == null) {
- //创建永久目录,接口服务,可以创建永久目录
- zooKeeper.create(registryPath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
- }
- String servicePath = registryPath + "/" +serviceName;
- if (zooKeeper.exists(servicePath, false) == null) {
- //接口目录
- zooKeeper.create(servicePath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
- }
- String addressPath = servicePath + "/" +addressName;
- //地址目录,这里ip就是本地的地址,用于tcp链接使用
- //这里创建的是临时目录,当zk服务断连过后,自动删除临时节点
- zooKeeper.create(addressPath, (ip + ":"+ port).getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
- }
- //监听过程
- private void accept() throws Exception {
- //当然这里也可以使用netty来进行监听和其他过程
- //这里简化
- ServerSocket serverSocket = new ServerSocket(port);
- while (true) {
- System.out.println("监听中。。。。。。。");
- Socket socket = serverSocket.accept();
- resultData(socket);
- }
- }
- //执行并返回数据
- private void resultData(Socket socket) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
- ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
- //读取请求的参数
- RpcRequest rpcRequest = (RpcRequest) objectInputStream.readObject();
- //这里从容器中获取bean,当然这里的bean可以自己缓存,独立spring容器之外
- Object bean = RpcBeanFactory.getBean(rpcRequest.getInterfaceName());
- //方法调用
- Method method = bean.getClass().getMethod(rpcRequest.getMethod(), rpcRequest.getParameterTypes());
- Object data = method.invoke(bean, rpcRequest.getParams());
- //返回数据
- ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
- RpcResponse rpcResponse = new RpcResponse();
- rpcResponse.setStatus("success");
- rpcResponse.setData(data);
- objectOutputStream.writeObject(rpcResponse);
- }
- public static void main(String[] args) throws Exception {
- //模拟spring容器的加载过程
- RpcBeanFactory.putBean(ITestService.class.getName(), new TestServiceImpl());
- Server server = new Server();
- ZooKeeper zooKeeper = server.connect();
- //创建节点,用于地址访问
- server.createNode(zooKeeper);
- //监听,当然多线程更加理想,这里只显示效果
- server.accept();
- }
- }
7)调用方
- import com.pinnet.zookeeper.bean.RpcBeanFactory;
- import com.pinnet.zookeeper.data.RpcRequest;
- import com.pinnet.zookeeper.data.RpcResponse;
- import com.pinnet.zookeeper.service.ITestService;
- import com.pinnet.zookeeper.zookeeper.ZookeeperUtils;
- import org.apache.zookeeper.ZooKeeper;
- import java.io.IOException;
- 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 java.util.ArrayList;
- import java.util.List;
- import java.util.Map;
- import java.util.concurrent.ConcurrentHashMap;
- /**
- * rpc客户端
- */
- public class Client {
- //缓存地址
- private Map<String, List<String>> adressMap = new ConcurrentHashMap<>();
- //链接zk
- public ZooKeeper connect() throws Exception {
- return ZookeeperUtils.connect();
- }
- /**
- * 这里主要是创建代理,利用代理接口实现来达到调用的目的
- * @param interfaceName
- * @return
- * @throws ClassNotFoundException
- */
- public Object createProxy(final String interfaceName) throws ClassNotFoundException {
- //使用线程实例化,主要考虑处理性
- final Class clazz = Thread.currentThread().getContextClassLoader().loadClass(interfaceName);
- //创建代理
- return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
- //用重连计数
- private int num = 5;
- @Override
- public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
- //发送请求需要的请求参数
- RpcRequest rpcRequest = new RpcRequest();
- //接口名称
- rpcRequest.setInterfaceName(interfaceName);
- //调用方法名
- rpcRequest.setMethod(method.getName());
- //对应参数类型
- rpcRequest.setParameterTypes(method.getParameterTypes());
- //对应参数
- rpcRequest.setParams(params);
- //返回响应结果
- return sendData(rpcRequest);
- }
- //tcp方式调用
- private Object sendData(RpcRequest rpcRequest) throws Exception {
- //当访问地址存在时
- if (adressMap.containsKey(interfaceName)) {
- List<String> adresses = adressMap.get(interfaceName);
- if (adresses != null && !adresses.isEmpty()) {
- //如果存在多个地址,使用可以调通的一个
- for (String adress:adresses) {
- //这个是注册zk的时候设定的数据
- String[] strs = adress.split(":");
- //这里简易版的实现,所以直接使用的socket.
- //实际上可以采用netty框架编写,保存channel访问就可以了,也可以对数据进行加解密
- Socket socket = new Socket();
- try {
- //连接可以访问zk,如果连接失败直接抛出异常,循环下一个地址
- socket.connect(new InetSocketAddress(strs[0], Integer.valueOf(strs[1])));
- ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
- objectOutputStream.writeObject(rpcRequest);
- ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
- //这里是响应数据,也就是执行过后的数据。远程执行结果
- RpcResponse rpcResponse = (RpcResponse) objectInputStream.readObject();
- return rpcResponse.getData();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- //设置重连机制,跳出循环
- if (num == 0) {
- throw new RuntimeException("server connect fail");
- }
- num--;
- //如果多个地址还是不能访问,则从zk上面更新地址
- getAddress(interfaceName);
- //如果多个地址还是不能访问,则重新访问
- sendData(rpcRequest);
- }
- throw new RuntimeException("not found service");
- }else {
- //如果没有地址存储时的访问,主要是请求链接问题
- if (num == 0) {
- throw new RuntimeException("not found server");
- }
- num--;
- getAddress(interfaceName);
- return sendData(rpcRequest);
- }
- }
- });
- }
- //从zk获取最新的地址
- private void getAddress(String interfaceName) throws Exception {
- //链接zk
- ZooKeeper zooKeeper = connect();
- //设定的地址目录
- String interfacePath = "/registry/" + interfaceName;
- //获取地址目录
- List<String> addresses = zooKeeper.getChildren(interfacePath, false);
- if (addresses != null && !addresses.isEmpty()) {
- List<String> datas = new ArrayList<>();
- for (String address:addresses) {
- //获取数据,也就是配置对应的访问地址
- byte[] bytes = zooKeeper.getData(interfacePath + "/" + address, false, null);
- if (bytes.length > 0) {
- //放入数组
- datas.add(new String(bytes));
- }
- }
- //加入缓存
- adressMap.put(interfaceName, datas);
- }
- }
- public static void main(String[] args) throws ClassNotFoundException, InterruptedException {
- Client client = new Client();
- //这一步是模拟spring容器放入bean的过程,实际用spring容器可自定义标签实现
- RpcBeanFactory.putBean("testService", client.createProxy(ITestService.class.getName()));
- //获取bean的过程,实际上实现是代理接口的实现
- ITestService testService = (ITestService) RpcBeanFactory.getBean("testService");
- //调用方法,也就是调用代理的过程
- String test = testService.test("test");
- System.out.println(test);
- }
- }
五、代码部分就这么多,看一下测试结果
服务端:
客户端:
zookeeper的展示部分
六、源码分享
rpc-zk代码:https://github.com/lilin409546297/rpc-zk
zkui代码:https://github.com/lilin409546297/zkui(这个是别人做的,我放在github的)
zookeeper下载地址:http://apache.org/dist/zookeeper/
rpc简易实现-zookeeper的更多相关文章
- 基于Netty的RPC简易实现
代码地址如下:http://www.demodashi.com/demo/13448.html 可以给你提供思路 也可以让你学到Netty相关的知识 当然,这只是一种实现方式 需求 看下图,其实这个项 ...
- 手写RPC框架(netty+zookeeper)
RPC是什么?远程过程调用,过程就是业务处理.计算任务,像调用本地方法一样调用远程的过程. RMI和RPC的区别是什么?RMI是远程方法调用,是oop领域中RPC的一种实现,我们熟悉的restfull ...
- 老王讲自制RPC框架.(三.ZOOKEEPER)
(#)定义Zookeeper 分布式服务框架是 Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务.状态同步服务.集群管理.分布式应用配 ...
- RPC简易学习
0.RPC简介 RPC, 英文全称:Remote Process Call. 中文全称:远程过程调用. 客户端通过网络请求调用远程服务端对外暴露服务.常用的两种RPC协议:TCP.HTTP. ...
- 手撕RPC框架
手撕RPC 使用Netty+Zookeeper+Spring实现简易的RPC框架.阅读本文需要有一些Netty使用基础. 服务信息在网络传输,需要讲服务类进行序列化,服务端使用Spring作为容器.服 ...
- RPC远程过程调用学习之路(一):用最原始代码还原PRC框架
RPC: Remote Procedure Call 远程过程调用,即业务的具体实现不是在自己系统中,需要从其他系统中进行调用实现,所以在系统间进行数据交互时经常使用. rpc的实现方式有很多,可以通 ...
- zookeeper在dubbo中干什么
本文旨在表述出自己对于zookeeper在dubbo的作用的初步理解 在对dubbo进行了初步的探索后,对于zookeeper在其中的作用不甚了解,因为本身对zookeeper就没有一个特别具体的概念 ...
- zookeeper介绍以及安装配置
Zookeeper启动时默认将Zookeeper.out输出到当前目录,不友好.改变位置有两种方法: 1:在当前用户下~/.bash_profile或在/etc/profile,添加ZOO_LOG_D ...
- RabbitMQ、RPC、SaltStack "贡"具的使用
消息队列 使用队列的场景 在程序系统中,例如外卖系统,订单系统,库存系统,优先级较高 发红包,发邮件,发短信,app消息推送等任务优先级很低,很适合交给消息队列去处理,以便于程序系统更快的处理其他请求 ...
随机推荐
- LA 4253 箭术(二分枚举)
https://vjudge.net/problem/UVALive-4253 题意: 有n个平行于x轴的线段,每条线段代表一个靶子.判断是否可以站在x轴上[0,W]区间内的某个位置射箭. 思路:二分 ...
- TimerPickerDialog 中 onTimeSet 执行两次的问题
开发android小闹钟的程序时,在添加闹钟时闹钟列表中总是出现两个相同的闹钟. btnAddAlarm.setOnClickListener(new View.OnClickListener() { ...
- poj3734矩阵快速幂
挑战上面的题目,感觉脑洞很大 分别找红蓝个数全为偶,全为奇,一奇一偶的个数ai,bi,ci 转移矩阵是| 2 1 0 |,是一个对称矩阵(会不会有什么联系.) | 2 2 2 | | 0 1 2 | ...
- Xcode8出现问题总结
上点干货,目前得知的一些bug解决汇总:iOS10相册相机闪退bughttp://www.jianshu.com/p/5085430b029fiOS 10 因苹果健康导致闪退 crashhttp:// ...
- iis6手工创建网站后无法运行php脚本
给人搬了十几个网站,老站用西部数码建站助手创建的,现在过期了无法继续创建,只能在Internet 信息服务(IIS)管理器创建网站,创建下来都没问题,但是就是无法打开网站. 测试打开txt文档.静态页 ...
- 在Google Maps中导出KML文件
教你一招:在Google Maps中导出KML文件 2011-07-11 15:24:49 4819 人阅读 作者:上方文Q 编辑:上方文Q [复制链接] [爆料] Google Earth的一大乐趣 ...
- 使用Array类处理基本数组对象
java里面的Arrays类有个asList方法,参数是1或多个Object对象,如果传入一个Object数组,则可以将该数组转化为List,但如果传入的是一个基本类型的数据(int,long,sho ...
- 【LeetCode 1_数组_哈希表】Two Sum
解法一:O(N) vector<int> twoSum(vector<int>& nums, int target) { unordered_map<int, i ...
- 关于apply、call和bind的总结
基础知识不是你看了一遍书或者两篇文章就能掌握的. 之前看书看文章时,感觉自己看懂了就掌握了.呵呵!too young!too naive! 以前的坑还是要一铲一铲的填上的. 高程上面关于apply和c ...
- L173
Technical problems temporarily blocked some US and European users having access to their accounts an ...