文档章节

我是怎么写出eos的(RPC印象)

ulyn
 ulyn
发布于 2015/12/01 02:20
字数 2315
阅读 534
收藏 8

1 引言

在eos中,一般我们都有client和server两个应用,server端写了服务PersonService,在client中直接可以调用,上代码:

PersonService service = com.sunsharing.eos.client.ServiceContext.getBean(PersonService.class);
Map map = service.exportData(batchNo,key));

这时想起类似的两个场景:

1)随着页面的复杂,服务越来越多,部署在不同的机器上,如何简单方便的让远程服务的调用如同本地服务调用一般?

2)甚至可以联想到前端ajax的调用,浏览器端如何调用后端所写的服务?

上述有几点表现:

  • 客户端与服务端分属不同空间内存区域

  • 透明调用机制让使用者不必显式的区分本地调用和远程调用

说了这么多,其实,我只是想聊聊RPC...

2 何为RPC

RPC(Remote Procedure Call Protocol),即远程过程调用。通俗的讲,两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。RPC让构建分布式计算(应用)更容易,在提供强大的远程调用能力时不损失本地调用的语义简洁性。

一个简单RPC的过程是这样的:

(图片来源:https://www.cs.rutgers.edu/~pxk/417/notes/03-rpc.html

这其中包含了以下流程步骤:

  1. client调用以本地调用方式调用服务

  2. client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体

  3. client stub找到服务地址,通过网络将消息发送到服务端

  4. server stub收到消息后进行解码

  5. server stub根据解码结果调用本地的服务

  6. 本地服务执行并将结果返回给server stub

  7. server stub将返回结果打包成消息并返回给client

  8. client stub接收到消息,并进行解码

  9. client得到最终结果

RPC的目标就是要2~8这些步骤都封装起来,让用户对这些细节透明,以适应引言中提出的场景。RPC 在整个过程中,体现了逐层抽象,将复杂的协议编解码和数据传输封装到了一个函数中。

3 RPC过程分析

说起来其实挺简单的,但是做起来,还有很多的细节!

3.1 如何做到本地服务存根调用

对于流程步骤1,client中是没有该服务实现的,我们需要让client stub接收到调用时候,能够执行步骤2的事情。对于java来说就是使用代理。java代理有两种方式:1) jdk 动态代理;2)字节码生成。尽管字节码生成方式实现的代理更为强大和高效,但代码不易维护,大部分公司实现RPC框架时还是选择动态代理方式。

public <T> T getProxy(final Class<T> clazz,final String implClassName) throws RpcException {
        InvocationHandler handler = new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                Object result = new Object();
                //dosomething...
                return result;
            }
        };
        T t = (T) Proxy.newProxyInstance(RPCClient.class.getClassLoader(), new Class[]{clazz}, handler);
        return t;
    }

流程步骤5,server端中解码后需要执行调用实际服务实现。java 中实现代码的动态接口调用除了原生的 jdk 自带的反射,一些第三方库也提供了性能更优的反射调用。

 Method m = obj.getClass().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
 Object o = m.invoke(obj, invocation.getArguments());

 3.2 怎么对入参和结果进行编码和解码

对于流程步骤2、6、4、8,在client和server之间,我们需要有一个两方都能认识的消息作为交互通讯的媒介。(此部分属于摘抄)

3.2.1 确定消息数据结构

客户端的请求消息结构一般需要包括以下内容:

  1. 服务接口名称

  2. 方法名

  3. 参数类型&参数值

  4. 超时时间

  5. requestID,标识唯一请求id

同理服务端返回的消息结构一般包括以下内容

  1. 返回值

  2. 状态code

  3. requestID

3.2.2 序列化

一旦确定了消息的数据结构后,下一步就是要考虑序列化与反序列化了。

什么是序列化?序列化就是将数据结构或对象转换成二进制串的过程,也就是编码的过程。

什么是反序列化?将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。

为什么需要序列化?转换为二进制串后才好进行网络传输嘛!为什么需要反序列化?将二进制转换为对象才好进行后续处理!

由于Java提供了良好的默认支持,实现基本的对象序列化是件比较简单的事。待序列化的Java类只需要实现Serializable接口即可。Serializable仅是一个标记接口,并不包含任何需要实现的具体方法。实现该接口只是为了声明该Java类的对象是可以被序列化的。实际的序列化和反序列化工作是通过ObjectOuputStream和ObjectInputStream来完成的。(链接阅读:Java对象序列化与RMI

现如今序列化的方案越来越多,每种序列化方案都有优点和缺点,它们在设计之初有自己独特的应用场景,那到底选择哪种呢?从RPC的角度上看,主要看三点:

  1. 通用性,比如是否能支持Map等复杂的数据结构;

  2. 性能,包括时间复杂度和空间复杂度,由于RPC框架将会被公司几乎所有服务使用,如果序列化上能节约一点时间,对整个公司的收益都将非常可观,同理如果序列化上能节约一点内存,网络带宽也能省下不少;

  3. 可扩展性,对互联网公司而言,业务变化快,如果序列化协议具有良好的可扩展性,支持自动增加新的业务字段,删除老的字段,而不影响老的服务,这将大大提供系统的健壮性。

目前国内各大互联网公司广泛使用hessian、protobuf、thrift、avro等成熟的序列化解决方案来搭建RPC框架,这些都是久经考验的解决方案。甚至json的方式也未尝不是一种方式。

3.3 网络通信传输

协议编码之后,自然就是需要将编码后的 RPC 请求消息传输到服务方,服务方执行后返回结果消息或确认消息给客户方。RPC 的应用场景实质是一种可靠的请求应答消息流,和 HTTP 类似。当然如果你愿意,没有否决你用HTTP的方式。

对于性能的考虑上,需要了解目前有两种IO通信模型:1)BIO;2)NIO。(链接阅读:一个故事讲清楚 NIO)。连接可以是按需连接,调用结束后就断掉,也可以是长连接,多个远程过程调用共享同一个连接。

现在很多RPC框架都直接基于netty,比如阿里巴巴的HSF、dubbo等。

3.4 服务发现

一个RPC过程中,简单的话,可能不需要服务发现的动作,配置好服务的IP以及端口就可以了。但是在分布式的应用中,就是一个很重要也很必要的过程。

比如说,当部分服务迁移到另一台机器B上,这时候就需要告诉调用者,部分服务要走哪个地址。如果你是手动的被动去修改,那么当又新增一台机器C挂载一部分服务。这时候调用者又需要手动修改;更复杂的情况下,A和B机器上都同时部署相同的服务做分压,那么我们肯定会想去实现负载均衡,那问题来了,手动的方式需要轮询机器服务是否存活,否则哪天机器A挂了,你会发现服务时好时坏。

分布式的一个很重要的点就是能实现服务发现,即调用者不用事先知道服务在哪,不再需要写死服务提供方地址。机器的增添、剔除对调用方透明。其中,zookeeper是一个很好的选择,它被广泛用于实现服务自动注册与发现功能,相对也比较成熟。

4 finally

思考:回到前言所讲的前端ajax的调用,浏览器端如何调用后端所写的服务(某个java类)?

提示:这区别于前面所提的client stub的存根调用,更像动态调用服务的方式。

  1. client调用:JS调用方式,可以提供统一的封装接口

  2. 服务消息的格式数据确定:服务名,调用方法名,入参......

  3. 数据序列化、反序列化:json

  4. 网络通信:ajax、http

  5. 服务执行:java反射

编码:

out.close();



© 著作权归作者所有

ulyn
粉丝 56
博文 16
码字总数 18070
作品 1
厦门
程序员
私信 提问
技术分享:EOS区块链PHP开发包

1. 开发包概述 EosTool的目的是消除使用PHP开发EOS区块链应用的痛苦,例如: 通过Nodeos和Keosd的RPC接口调用其功能 离线生成EOS格式的私钥和公钥 使用本地私钥生成符合EOS要求的交易签名 将...

geek12345
2018/12/10
111
0
EOS RPC API官方文档中文版【1.5版】

EOS RPC API是应用访问EOS区块链上智能合约的必备开发接口,根据所实现插件的不同,EOS RPC API被归入不同的分组: CHAIN:由chain_api_plugin实现,主要提供区块链数据的访问功能 HISTORY:...

汇智网教程
2018/12/17
28
0
EosTool - PHP版的EOS区块链应用开发包

EosTool的目的是消除使用PHP开发EOS区块链应用的痛苦,例如: 通过Nodeos和Keosd的RPC接口调用其功能 离线生成EOS格式的私钥和公钥 使用本地私钥生成符合EOS要求的交易签名 将交易对象序列化...

汇智网教程
2018/12/06
31
0
EOS应用接口(RPC API)官方文档中文版

EOS RPC API是应用访问EOS区块链上智能合约的必备开发接口,中文文档由汇智网翻译整理,访问地址:EOS RPC API手册 - 汇智网。 根据所实现插件的不同,EOS RPC API被归入不同的分组: CHAIN:...

geek12345
2018/12/18
89
0
eosjs调用getactions

使用eosjs时,如何调用history模块的get_actions接口获取用户的历史交易动作? 如果要深入系统地学习Eos上的应用开发,推荐这个教程:Eos智能合约与Dapp开发入门 。 eosjs的封装与eos的rpc a...

汇智网教程
2018/12/24
107
0

没有更多内容

加载失败,请刷新页面

加载更多

一、docker 入坑(win10和Ubuntu 安装)

前言 终究还是绕不过去了,要学的知识真的是太多了,好在我们还有时间,docker 之前只闻其声,不曾真正的接触过,现在docker 越来越火,很多公司也都开始使用了。所以对于我们程序员而言,又...

quellanan2
3分钟前
2
0
AutoCompleteTextView

小技巧按菜单键 当菜单打开之前会调用onMenuOpened(int featereId,Menu menu),可以重写这个方法,弹出对话框或者Popmenu 再布局中添加控件AutoCompleteTextView. <AutoCompleteTextVie...

逆天游云
6分钟前
2
0
谷歌软件商店:推出5美元会员 可用数百个软件

腾讯科技讯,谷歌和苹果是全球两大智能手机操作系统的运营者,两家公司旗下分别拥有占据行业垄断地位的谷歌软件商店和苹果软件商店。据外媒最新消息,手机软件商店的商业模式正在发生一些变化...

linuxCool
28分钟前
1
0
RocketMQ 多副本前置篇:初探raft协议

Raft协议是分布式领域解决一致性的又一著名协议,主要包含Leader选举、日志复制两个部分。 温馨提示: 本文根据raft官方给出的raft动画进行学习,其动画展示地址:http://thesecretlivesofda...

中间件兴趣圈
28分钟前
1
0
elasticsearch 6.8.0 添加认证

1. 修改elasticsearch-6.8.0/config/elasticsearch.yml 最后添加一行:xpack.security.enabled: true 2. 初始化用户和密码 ./bin/elasticsearch-setup-passwords interactive 我这里初始化为......

coord
30分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部