在学习RPC的时候发现了一个很不错的项目,以下是我在学习这个项目的过程中遇到的疑问和问题的答案。
目录
原文
项目地址:
原项目的地址:
rpc: 轻量级分布式 RPC 框架http://git.oschina.net/huangyong/rpc加了注释的项目地址:
项目概括:
本文通过 Spring + Netty + Protostuff + ZooKeeper 实现了一个轻量级 RPC 框架,使用 Spring 提供依赖注入与参数配置,使用 Netty 实现 NIO 方式的数据传输,使用 Protostuff 实现对象序列化,使用 ZooKeeper 实现服务注册与发现。使用该框架,可将服务部署到分布式环境中的任意节点上,客户端通过远程接口来调用服务端的具体实现,让服务端与客户端的开发完全分离,为实现大规模分布式应用提供了基础支持。
如何把这个项目跑起来?
首先下载和学习ZooKeeper ,参考这篇文章
Zookeeper基本原理+通俗理解+安装使用_trigger的博客-CSDN博客
1.启动ZooKeeper 的服务器,这样服务注册和发现中心就启动了,如果要使用该服务,只需要访问127.0.0.1:2181即可。
2.启动红字所标识的 rpc-sample-server,命令行窗口会出现:
start server
connect zookeeper
create address node: /registry/com.xxx.rpc.sample.api.HelloService/address-0000000001
register service: com.xxx.rpc.sample.api.HelloService => 127.0.0.1:8000
create address node: /registry/com.xxx.rpc.sample.api.HelloService-sample.hello2/address-0000000001
register service: com.xxx.rpc.sample.api.HelloService-sample.hello2 => 127.0.0.1:8000
server started on port 8000
3.启动rpc-sample-client中的第一个测试类即可。
会出现:
connect zookeeper
get only address node: address-0000000001
discover service: com.xxx.rpc.sample.api.HelloService => 127.0.0.1:8000
time: 653ms
Hello! World
connect zookeeper
get only address node: address-0000000001
discover service: com.xxx.rpc.sample.api.HelloService-sample.hello2 => 127.0.0.1:8000
time: 17ms
你好! 世界
这样就启动了这个项目。
一个RPC请求之后会发生什么?
这个项目总的流程是:
ZooKeeper启动,rpcServer启动,把特定目录下的远程服务加入到handlerMap(本地缓存)中,同时利用zk客户端向zk中注册这些服务,之后监听对应的端口8000。
客户端发起请求之前,首先需要获取动态代理对象,这个对象里面有zk客户端(可以通过它获得服务的地址)。
在调用这个动态代理对象的某一个方法时,会先利用zk客户端去zk中查找是否有这个服务,如果有,得到其地址端口比如127.0.0.1:8000,新建一个rpcClient,绑定对应的地址端口,连接rpcServer,发送相应的信息请求服务。
rpc-sample-client包下的HelloClient类是一个客户端类,用来测试。
具体测试代码为
public class HelloClient {
public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
RpcProxy rpcProxy = context.getBean(RpcProxy.class);
HelloService helloService = rpcProxy.create(HelloService.class);
String result = helloService.hello("World");
System.out.println(result);
}
}
前两行代码获得了rpcProxy,debug如下,运行完create对象后,知道了服务的地址在127.0.0.1:8000端口。
String result = helloService.hello(“World”);
这句代码调用了invoke函数,
函数中生成了一个rpcClient,首先把要访问的函数和输入参数等信息包装成RpcRequest“请求消息”对象,序列化之后发送给rpcServer并等待结果。
rpcserver始终在监听着8000端口,有消息过来,先进行解码(反序列化),得到RpcRequest“请求消息”对象,将其中的信息(函数,输入参数等)取出,进行反射调用得到结果,再把这个结果包装成RpcResponse“响应消息”对象,发回给客户端。
项目的先修知识
这个项目的技术选型是Spring + Netty + Protostuff + ZooKeeper,所以这四个是需要掌握的,其中Protostuff 不需要很多时间,大概了解一下即可。
spring如何去实现依赖注入?
Spring5之IOC详解_trigger的博客-CSDN博客
代码中主要的依赖注入有两处,一处是测试类获得rpcClient,另一处是rpcServer启动的时候,在启动时采用rpcClient、rpcServer中的属性都是通过xml文件配置的方式注入的。
<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:rpc.properties"/>
<!--一个zk的服务发现对象serviceDiscovery,该对象是ZooKeeperServiceDiscovery的实例化,可以实现 服务发现功能
构造函数需要传入 zk的地址和端口 用字符串传入 也就是127.0.0.1:2181
这个值是在 rpc.properties配置好了 -->
<bean id="serviceDiscovery" class="com.xxx.rpc.registry.zookeeper.ZooKeeperServiceDiscovery">
<constructor-arg name="zkAddress" value="${rpc.registry_address}"/>
</bean>
<!-- 生成rpcProxy,用来远程调用对应的服务,构造函数的输入参数是 一个zk的服务发现对象serviceDiscovery
调用serviceDiscovery的discover方法就能远程获取相应的服务-->
<bean id="rpcProxy" class="com.xxx.rpc.client.RpcProxy">
<constructor-arg name="serviceDiscovery" ref="serviceDiscovery"/>
</bean>
</beans>
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.xxx.rpc.sample.server"/> <context:property-placeholder location="classpath:rpc.properties"/><!--导入 一个serviceRegistry进行服务注册 构造函数的输入参数是 zk的ip + port --> <bean id="serviceRegistry" class="com.xxx.rpc.registry.zookeeper.ZooKeeperServiceRegistry"> <constructor-arg name="zkAddress" value="${rpc.registry_address}"/> </bean><!--根据serviceAddress 8000 serviceRegistry 2181 构建RpcServer如果客户端有请求 只需要访问 ip+8000 就能获得服务--> <bean id="rpcServer" class="com.xxx.rpc.server.RpcServer"> <constructor-arg name="serviceAddress" value="${rpc.service_address}"/> <constructor-arg name="serviceRegistry" ref="serviceRegistry"/> </bean></beans>
spring的注解如何发挥作用,如何自定义注解?
java注解详解和自定义注解_不积跬步,无以至千里-CSDN博客https://blog.csdn.net/u010902721/article/details/52576624/在这里定义了RpcService 注解。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface RpcService {
/**
* 服务接口类
*/
Class<?> value();
/**
* 服务版本号
*/
String version() default "";
}
在服务器启动之后就会扫描这样的注解进行注册。debug如下。
Netty 学习
参考 B站黑马的视频
Protostuff
ZooKeeper
其他问题
编码和序列化的区别?
序列化是编码过程的一个示例;它将复杂的数据结构转换为单个文本字符序列。 然后可以将该字符序列进一步编码成某种二进制表示,以实现更紧凑的存储或更快的传输。
参见
编码和序列化_小豆角的博客-CSDN博客_序列化和编码的区别https://blog.csdn.net/u013755520/article/details/99938337
这个项目和之前写的RPC有什么区别?
之前写的RPC:
手写RPC框架(文末附代码)_trigger的博客-CSDN博客https://blog.csdn.net/weixin_40757930/article/details/122908918
之前的项目只能算作一个RPC调用,不能称之为RPC框架,因为服务注册、服务发现是基于一个map实现的。
改进点
一个轻量级分布式RPC框架–NettyRpc – 阿凡卢 – 博客园https://www.cnblogs.com/luxiaoxun/p/5272384.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/92874.html