Github源码下载地址:https://github.com/chenxingxing6/myrpc

一、前言

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外的为这个交互作用编程,如果涉及的软件采用面向对象编程(java),那么远程过程调用亦可称作远程调用或远程方法调用。只要支持网络传输的协议就是RPC协议,RPC是一种框架。简单的说就是远程调用,以API的方式调用远程的服务器上的方法,像调本地方法一样!


RPC要解决的两个问题

1.解决分布式系统中,服务之间的调用问题。
2.远程调用时,要能够像本地调用一样方便,让调用者感知不到远程调用的逻辑。

完整RPC调用过程:

以左边的Client端为例,Application就是rpc的调用方,Client Stub就是我们上面说到的代理对象,其实内部是通过rpc方
式来进行远程调用的代理对象,至于Client Run-time Library,则是实现远程调用的工具包,比如jdk的Socket,最后通过底
层网络实现实现数据的传输。

这个过程中最重要的就是序列化和反序列化了,因为数据传输的数据包必须是二进制的,你直接丢一个Java对象过去,人家可不
认识,你必须把Java对象序列化为二进制格式,传给Server端,Server端接收到之后,再反序列化为Java对象。


二、项目结构

要实现一个RPC不算难,难的是实现一个高性能高可靠的RPC框架。

实现功能

1.支持多种协议传输socket,http,dubbo
2.支持多种注册方式file,redis,zookeepre
3.可以动态切换协议和注册方式,只需要更改配置文件
4.Netty实现 (更新2019.10.1)
5.注解方式发布服务@Rpc (更新2019.10.1)


服务端:提供API,启动的时候要注册服务
消费端:从注册中心获取服务,调用子服务
注册中心:保存服务配置
RPC协议:基于Tomcat的HttpProtocol,基于Netty的DubboProtocol,Socket
注册中心:本地文件,Redis,Zookeeper方式进行注册



三、核心代码

如果要补充协议,只需要实现IProtocolClient,IProtocolService接口

package com.mydubbo.rpc.protocol;import com.mydubbo.rpc.framework.Invocation;
import com.mydubbo.rpc.framework.URL;/*** User: lanxinghua* Date: 2019/9/30 17:24* Desc:*/
public interface IProtocolClient {/*** 客户端发送请求* @param url* @param invocation* @return*/public Object send(URL url, Invocation invocation);
}

package com.mydubbo.rpc.protocol;import com.mydubbo.registry.AbstractRegistryDiscovery;
import com.mydubbo.rpc.framework.URL;/*** User: lanxinghua* Date: 2019/9/30 17:24* Desc:*/
public interface IProtocolServer {/*** 启动服务* @param url* @param charset* @param registryDiscovery*/public void start(URL url, String charset, AbstractRegistryDiscovery registryDiscovery);
}

如果要实现多种注册中心,只需继承AbstractRegistryDiscovery

package com.mydubbo.registry;import com.mydubbo.rpc.framework.URL;import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;/*** User: lanxinghua* Date: 2019/9/30 16:09* Desc: 服务注册,发现接口*/
public abstract class AbstractRegistryDiscovery {protected static Map<String/*接口名*/, Map<URL, Class>> REGISTER = new HashMap<String, Map<URL, Class>>();/*** 服务注册* @param url* @param interfaceName* @param implClass*/public void register(URL url, String interfaceName, Class implClass){Map<URL, Class> map = new HashMap<URL, Class>();map.put(url, implClass);REGISTER.put(interfaceName, map);save();}/*** 服务发现* @param url* @param interfaceName* @return*/public Class discovery(final URL url, String interfaceName){REGISTER = get();return Optional.ofNullable(REGISTER.get(interfaceName)).map(r -> r.get(url)).orElseThrow(() -> new RuntimeException("service not found!"));}/*** 负载均衡,获取可用服务地址* @param interfaceName* @return*/public URL randomServer(String interfaceName){REGISTER = get();Set<URL> urls = Optional.ofNullable(REGISTER.get(interfaceName)).map(r -> r.keySet()).orElseThrow(() -> new RuntimeException("service not found!"));if (urls == null || urls.isEmpty()){throw new RuntimeException("service not found!");}// 这里就返回第一个return urls.iterator().next();}/*** 保存到注册中心*/public abstract void save();/*** 从注册中心获取*/public abstract Map<String, Map<URL, Class>> get();
}

四、内置tomca

<Server port="8005" shutdown="SHUTDOWN"><Service name="Catalina"><Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" URIEncoding="UTF-8"/><Engine name="Catalina" defaultHost="localhost"><Host name="localhost"  appBase="webapps"unpackWARs="true" autoDeploy="true"><Context path="" doBase="WORKDIR" reloadable="true"/></Host></Engine></Service>
</Server>

package com.mydubbo.rpc.protocol.http;import com.mydubbo.registry.AbstractRegistryDiscovery;
import com.mydubbo.rpc.framework.URL;
import com.mydubbo.rpc.protocol.IProtocolServer;
import org.apache.catalina.*;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.startup.Tomcat;/*** User: lanxinghua* Date: 2019/9/30 11:59* Desc:*/
public class HttpServer implements IProtocolServer {private static final String TOMCAT = "Tomcat";@Overridepublic void start(URL url, String charset, AbstractRegistryDiscovery registryDiscovery){// 实例一个TomcatTomcat tomcat = new Tomcat();// 构建ServerServer server = tomcat.getServer();// 获取ServiceService service = server.findService(TOMCAT);// 构建ConnectorConnector connector = new Connector();connector.setPort(url.getPort());connector.setURIEncoding(charset);// 构建引擎Engine engine = new StandardEngine();engine.setDefaultHost(url.getHostName());// 构建HostHost host = new StandardHost();host.setName(url.getHostName());// 构建ContextString contextPath = "";Context context = new StandardContext();context.setPath(contextPath);context.addLifecycleListener(new Tomcat.FixContextListener());// 按照server.xml进行配置host.addChild(context);engine.addChild(host);service.setContainer(engine);service.addConnector(connector);// tomcat是一个servlet,设置路径与映射String servletName = "dispatcher";tomcat.addServlet(contextPath, servletName, new DispatcherServlet(url, registryDiscovery));context.addServletMappingDecoded("/client/*", servletName);// 启动服务,接受请求try {System.out.println("Tomcat..服务启动成功.....");tomcat.start();tomcat.getServer().await();}catch (Exception e){e.printStackTrace();}}
}

五、注册中心

1.本地文件
2.zk
3.redis



六、测试

## 协议配置信息
## 支持socket,http,dubbo
config.protocol.name=socket
config.protocol.host=localhost
config.protocol.port=8080
config.protocol.charset=UTF-8## 服务注册 支持localfile,redis,zookeeper
service.registry.type=redis
#service.registry.type=localfile
#service.registry.type=zookeeper

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:property-placeholder location="classpath:spring.properties"/><!-- 协议配置信息 --><bean id="protocolConfig" class="com.mydubbo.config.ProtocolConfig"><constructor-arg name="name" value="${config.protocol.name}"/><constructor-arg name="host" value="${config.protocol.host}"/><constructor-arg name="port" value="${config.protocol.port}"/><constructor-arg name="charset" value="${config.protocol.charset}"/></bean><!-- 服务注册方式 --><bean id="registryDiscoveryFactory" class="com.mydubbo.registry.RegistryDiscoveryFactory"><constructor-arg name="registryType" value="${service.registry.type}"/></bean><bean id="rpcClient" class="com.mydubbo.rpc.RpcClient"><constructor-arg name="config" ref="protocolConfig"/><constructor-arg name="registryDiscoveryFactory" ref="registryDiscoveryFactory"/></bean><bean id="rpcServer" class="com.mydubbo.rpc.RpcServer"><constructor-arg name="config" ref="protocolConfig"/><constructor-arg name="registryDiscoveryFactory" ref="registryDiscoveryFactory"/></bean>
</beans>

package com.demo.consumer;import com.demo.provider.api.IHelloService;
import com.mydubbo.rpc.RpcClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** User: lanxinghua* Date: 2019/9/30 11:56* Desc: 服务启动*/
public class ConsumerStart {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");RpcClient rpcClient = (RpcClient) context.getBean("rpcClient");IHelloService helloService = rpcClient.getProxy(IHelloService.class);System.out.println(helloService.sayHello("lanxinghua"));System.out.println(helloService.getUser());System.out.println(helloService.saveUser(helloService.getUser()));}
}

package com.demo.provider;import com.demo.provider.api.IHelloService;
import com.demo.provider.impl.HelloService;
import com.mydubbo.rpc.RpcServer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** User: lanxinghua* Date: 2019/9/30 11:55* Desc: 服务提供*/
public class ProviderStart {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");RpcServer rpcServer = (RpcServer) context.getBean("rpcServer");// 服务注册rpcServer.register(IHelloService.class.getName(), HelloService.class);// 启动服务rpcServer.start();}
}

注解方式发布服务

package com.demo.provider.impl;import com.demo.provider.api.ILogService;
import com.mydubbo.config.Rpc;/*** @Author: cxx* @Date: 2019/10/1 20:03* 通过注解方式发布dubbo服务*/
@Rpc
public class LogService implements ILogService {@Overridepublic void log(String msg) {System.out.println("【日志】" + msg);}
}

总结

希望路过的童鞋们,自己手写RPC后,可以更加清楚知道服务远程间是怎么调用的,后期还要许多东西需要完善,比如:负载均衡,熔断机制,Mock数据等等…

第四篇 - 手写RPC框架相关推荐

  1. 手写篇:如何手写RPC框架?

    手写篇:如何手写RPC框架? 首先我们讲下什么是RPC? RPC(Remote Procedure Call)远程过程调用协议,他是一种通过网络从远程计算机程序请求服务.简单的来说,就是通过网络进行远 ...

  2. MyRPCDemo netty+jdk动态代理+反射+序列化,反序列化手写rpc框架

    RPC RPC(remote procedure call)远程过程调用 RPC是为了在分布式应用中,两台主机的Java进程进行通信,当A主机调用B主机的方法时,过程简洁,就像是调用自己进程里的方法一 ...

  3. 第一篇 - 手写SpringMvc框架

    Github源码下载地址:https://github.com/chenxingxing6/springmvc CSDN源码下载地址:https://download.csdn.net/downloa ...

  4. 第02篇:手写JavaRPC框架之设计思路

    作者: 西魏陶渊明 博客: https://blog.springlearn.cn/ 天下代码一大抄, 抄来抄去有提高, 看你会抄不会抄! 一.前言 隔壁老李又在喷我了: "完犊子了,小编这 ...

  5. 手写RPC框架(六)

    v1.4 小更新 更新事项 暂定目标对启动类进行修改 直接集合 这个就直接看代码吧 不是特别难 难的地方我会点出来 启动引导类直接进行修改 可以传参 可以这样 当然 我想到了可以注解传参 注解构造 注 ...

  6. 手写RPC框架(十六)

    v2.7 更新:实现CGLIB动态代理 实现CGLIB动态代理 实现一下统一调用代理类,创建总调用类,和对应模板接口,调用注解,同时在每个consumerbootstrap进行修改 对应模板接口 pa ...

  7. 手写RPC框架(五)

    v1.3 (启动器依旧使用1.2 1.3版本在启动服务版本上尚未做出大变动 主要是增加了方便学习的功能) 更新事项 以下更新均在非阻塞模块进行更新,阻塞模块可供读者自己尝试 使用注解方式 改造一下启动 ...

  8. 手写RPC框架(八)

    v1.6 热补丁,nio目前来看最后的完善,使用Curator简化zookeeper的操作,优化调用体验 使用Curator创建服务注册和服务发现类(是看快速开始速成的) 服务注册类实现代码 pack ...

  9. Marco's Java【Dubbo 之手写Dubbo框架实现远程调用】

    前言 关于Dubbo入门的网上教程也特别多,因此我没有专门出关于Dubbo的系列博文(主要呢- 也是在忙些工作上的事儿),用Dubbo特别简单,但是想要把Dubbo学好,学精还得花费不少时间的,特别是 ...

最新文章

  1. Python时间转换函数:时间转化为时间戳、时间戳转化为时间、当前日期、当前时间、星期几、前面或者后面多少天、年、月、日等
  2. OS- -调度(一)
  3. Linux 下禅道和 SVN、GIT 集成插件发布
  4. 干货 | 双目摄像头实现手势识别,完美还原人体运动手势
  5. 垃圾代码还能出圈?手把手教你写垃圾代码,从入门到精通!
  6. java多线程中的死锁、活锁、饥饿、无锁都是什么鬼?
  7. 教你开发Jquery插件-Jquery插件开发教程
  8. wamp无法访问php,wamp无法访问phpmyadmin怎么办
  9. POJ NOI MATH-7647 余数相同问题
  10. 本地离线语音识别芯片厂家盘点,哪一家实力更强
  11. matlab傅里叶光学仿真,关于微透镜阵列的傅里叶光学分析
  12. 如何合理的拆分微服务
  13. 暖风熏的游人醉 直把杭州作汴州 题临安邸--林升
  14. php 2038,php处理大于2038年以后日期的一种方法
  15. Ubuntu下安装 rust和urdf-viz
  16. halcon改变图像大小
  17. 3D建模一个月的真实收入,当作副业在家就能月入过万?
  18. 阿姆斯特朗数python
  19. 回归和分类模型性能评估指标MSE,MAE,PR,ROC,AUC
  20. 头皮发麻之win10宽带拨号错误797

热门文章

  1. 美和易思 MOOT去鼠标检测,快进,倍速,自动下一章
  2. confluent【kafka企业版】安装配置————附带详细信息
  3. 使用组策略实现统一企业形象
  4. HUAWEI HiAI常见FAQ小贴士
  5. 其实华为手机长按2秒屏幕,就能一键翻译全文!你不会不知道吧
  6. 宝塔7Linux去掉文件夹的锁
  7. vscode安装使用,开发python项目环境配置
  8. 文化中国 系列十:未归类系列
  9. chrome浏览器官方(稳定版)下载以及历史版本下载
  10. 人像3D模型互动展示 | 做个逼真的人像模型,get潮玩的新玩法