网络篇 - rpc协议的应用web3j
最近的公司区块链钱包,用到了以太坊的官方开发库 web3j。web3j 是Java版本的以太坊 rpc-json 接口协议封装实现,如果需要将你的 Java 或安卓应用接入以太坊,或者希望用 Java 开发一个钱包应用,那么 web3j 完全能满足你的需求。
目录:
- rpc 简介
- rpc 和 http 对比
- json-rpc 简介
- web3j 调用
- web3j json-rpc 实现
1. rpc 简介
rpc ( Remote Procedure Call) — 远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。rpc 协议假定某些传输协议的存在,如 tcp 或 udp,为通信程序之间传输信息数据。在 OSI 网络通信模型中,rpc 跨越了传输层和应用层。rpc 使得开发包括网络分布式多程序在内的应用程序更加容易。
我从 google 上找了一张 rpc 模型图,帮助大家理解下:
2. rpc 和 http 对比
目前有很多 Java 的 rpc 框架,有基于 Json 的,有基于 XML,也有基于二进制对象的。rpc 和 平常用的 http/https 到底有什么区别,不都是写一个服务然后在客户端调用吗?
在 http 和 rpc 的选择上可能有些人是迷惑的,主要是因为,有些 rpc 框架配置复杂,如果走 http 也能完成同样的功能,那么为什么要选择 rpc,而不是更容易上手的 http 来实现?
从以下几个点来分析:
(1) 传输协议
- rpc 可以基于 tcp 协议 (可以省去了 http 报头等一系列东西,rpc 并没有规定数据传输格式,这个格式可以任意指定,不同的 rpc 协议,数据格式不一定相同),也可以基于 http 协议。
- http 只能基于 http 协议。
(2) 传输效率
- rpc 使用自定义的 tcp 协议,可以让请求报文体积更小,或者使用 http 2.0协议,也可以很好的减少报文的体积,提高传输效率。
- http 如果是基于 http 1.1的协议,请求中会包含很多无用的内容,如果是基于 http 2.0,那么简单的封装以下是可以作为一个 rpc 来使用的,这时标准 rpc 框架更多的是服务治理。
(3) 性能消耗,主要在于序列化和反序列化的耗时
- rpc 可以基于 thrift 实现高效的二进制传输。
- http 大部分是通过 json 来实现的,字节大小和序列化耗时都比 thrift 要更消耗性能。
(3) 负载均衡
- rpc 基本都自带了负载均衡策略。
- http 需要配置 Nginx,HAProxy来实现。
(3) 服务治理 (下游服务新增,重启,下线时如何不影响上游调用者)
- rpc 能做到自动通知,不影响上游。
- http 需要事先通知,修改 Nginx/HAProxy 配置。
早期的 webservice,现在热门的 dubbo,都是 rpc 的典型。rpc 服务主要是针对大型企业的,而 http 服务主要是针对小企业的,因为 rpc 效率更高,而 http 服务开发迭代会更快。
3. json-rpc 简介
json-rpc 是基于 json 的跨语言远程调用协议,比 xml-rpc、webservice 等基于文本的协议数据传输格小,相对 hessian、java-rpc 等二进制协议便于调试、实现、扩展,是很优秀的一种远程调用协议。眼下主流语言都已有 json-rpc 的实现框架,Java 语言中较好的 json-rpc 实现框架有 jsonrpc4j、jpoxy、json-rpc。
json-rpc 协议很简单,发起远程调用时向服务端数据传输格式例如以下:
{ "method": "helloCoder", "params": ["Hello JSON-RPC"], "id": 1}
参数说明:
- method:调用的方法名。
- params:方法传入的參数,若无參数则传入 []。
- id: 调用标识符,用于标示一次远程调用过程。
服务端收到调用请求,处理方法调用,将方法调用结果返回给调用方,返回数据格式:
{"result": "Hello JSON-RPC","error": null,"id": 1
}
参数说明:
- result: 方法返回值,若无返回值,则返回 null。若调用错误,返回 null。
- error :调用时错误,无错误返回 null。
- id : 调用标识符,与调用方传入的标识符一致。
以上就是 json-rpc 协议规范,很简单,便于各种语言实现。
4. web3j 调用
在 gradle 中添加依赖:
// 以太坊开发库
compile 'org.web3j:core:3.3.1-android'
拿我们公司的区块链钱包为例,获取余额和转币调用了 web3j 的方法,它封装了 json-rpc 的调用过程,先看看获取余额的代码:
@Overridepublic BigInteger getBalance(String address) throws CTXCException {CTXCException.checkNetwork();EthGetBalance ethGetBalance;try {ethGetBalance = getWeb3j().ethGetBalance(address, PENDING).send();if (ethGetBalance.hasError())throw new CTXCException(true, ethGetBalance.getError().getMessage());} catch (Exception e) {throw new CTXCException(true, e);}String response = ethGetBalance.getRawResponse();Web3jResponseProcessor processor = new Web3jResponseProcessor(response).process();if (processor.isSuccess())return TokenBigConverter.toBigInteger(processor.getRawResponse().result);return BigInteger.ZERO;}
再看看转账的调用:
@Overridepublic String sendTransaction(String fromAddress, String password, String toAddress, BigInteger gasPrice,BigInteger gasLimit, BigInteger amount, String payload) throws CTXCException {CTXCException.checkNetwork();try {EthGetTransactionCount ethGetTransactionCount = getWeb3j().ethGetTransactionCount(fromAddress, PENDING).sendAsync().get();BigInteger nonce = ethGetTransactionCount.getTransactionCount();payload = payload == null ? "" : payload;RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, toAddress, amount, payload);Credentials credentials = Credentials.create(WalletManagerService.instance().exportPrivateKey(fromAddress, password));byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);String hexValue = Numeric.toHexString(signedMessage);EthSendTransaction ethSendTransaction = getWeb3j().ethSendRawTransaction(hexValue).send();if (ethSendTransaction.hasError())throw new CTXCException(true, ethSendTransaction.getError().getMessage());Web3jResponseProcessor processor = new Web3jResponseProcessor(ethSendTransaction.getRawResponse()).process();if (processor.isSuccess())return processor.getRawResponse().result;} catch (Exception e) {throw new CTXCException(true, e.getMessage());}return "";}
比如:getWeb3j().ethGetBalance(address, PENDING).send(),这样就能获取到某个 erc20地址的余额。而 web3j 封装了 json-rpc 的调用过程,在以太坊的开发文档上,对获取余额的 rpc 调用是这样的:
可以看到,正是上面说的 json-rpc 协议的格式。
5. web3j json-rpc 实现
以 getWeb3j().ethGetBalance(address, PENDING).send() 作为入口分析。
实现在 JsonRpc2_0Web3j 这个类:
public Request<?, EthGetBalance> ethGetBalance(String address, DefaultBlockParameter defaultBlockParameter) {return new Request("eth_getBalance", Arrays.asList(address, defaultBlockParameter.getValue()), this.web3jService, EthGetBalance.class);}
将 json-rpc 方法名"eth-getBalance"和Array.asList 将参数转换成 List 传入,返回一个 Request 对象:
public class Request<S, T extends Response> {private static AtomicLong nextId = new AtomicLong(0L);// jsonrpc 版本private String jsonrpc = "2.0";// 方法名private String method;// 参数列表private List<S> params;// jsonrpc 参数 idprivate long id;private Web3jService web3jService;private Class<T> responseType;public Request() {}public Request(String method, List<S> params, Web3jService web3jService, Class<T> type) {this.method = method;this.params = params;this.id = nextId.getAndIncrement();this.web3jService = web3jService;this.responseType = type;}public String getJsonrpc() {return this.jsonrpc;}public void setJsonrpc(String jsonrpc) {this.jsonrpc = jsonrpc;}public String getMethod() {return this.method;}public void setMethod(String method) {this.method = method;}public List<S> getParams() {return this.params;}public void setParams(List<S> params) {this.params = params;}public long getId() {return this.id;}public void setId(long id) {this.id = id;}public T send() throws IOException {return this.web3jService.send(this, this.responseType);}public Future<T> sendAsync() {return this.web3jService.sendAsync(this, this.responseType);}public Observable<T> observable() {return (new RemoteCall(new Callable<T>() {public T call() throws Exception {return Request.this.send();}})).observable();}
}
这个类封装了请求参数和行为,接着看里面的 send() 方法:
public T send() throws IOException {return this.web3jService.send(this, this.responseType);}
调用 web3jService的 send() 方法进行 json-rpc 调用,传入 request 对象和返回类型。Web3jService 是一个接口,那这边调的是哪个实现类呢?看看 web3j 对象的构造代码:
web3j = Web3jFactory.build(new HttpService(web3jRpcURL, true));
我这边传入的是 HttpService (实现 Web3jService 接口):
protected abstract InputStream performIO(String var1) throws IOException;public <T extends Response> T send(Request request, Class<T> responseType) throws IOException {String payload = this.objectMapper.writeValueAsString(request);InputStream result = this.performIO(payload);return result != null ? (Response)this.objectMapper.readValue(result, responseType) : null;}
可以看到 send() 方法:
String payload = this.objectMapper.writeValueAsString(request);
先将 request 对象转换成 json,即上面 json-rpc 协议的请求格式:
{ "method": "helloCoder", "params": ["Hello JSON-RPC"], "id": 1}
然后执行 performIO() 进行 rpc 调用,下面是 HttpService 的 performIO() 的实现:
protected InputStream performIO(String request) throws IOException {RequestBody requestBody = RequestBody.create(JSON_MEDIA_TYPE, request);Headers headers = this.buildHeaders();Request httpRequest = (new okhttp3.Request.Builder()).url(this.url).headers(headers).post(requestBody).build();Response response = this.httpClient.newCall(httpRequest).execute();if (response.isSuccessful()) {ResponseBody responseBody = response.body();return responseBody != null ? this.buildInputStream(responseBody) : null;} else {throw new ClientConnectionException("Invalid response received: " + response.body());}}
rpc 的过程用的是 okhttp3,上面说过 rpc 可以基于 http 协议,这边的 httpClient 需要传入一个 http 协议版本,这边用的是默认的:
DEFAULT_PROTOCOLS = Util.immutableList(new Protocol[]{Protocol.HTTP_2, Protocol.HTTP_1_1});
而以太坊官方的 web3j json-rpc 的文档中也写到:
可以看到它们的服务提供的 json-rpc 支持 http 进行消息传递。
网络篇 - rpc协议的应用web3j相关推荐
- 02.iOS开发网络篇—HTTP协议
iOS开发网络篇-HTTP协议 说明:apache tomcat服务器必须占用8080端口 一.URL 1.基本介绍 URL的全称是Uniform Resource Locator(统一资源定位符) ...
- 网络篇 - https协议中的数据是否需要二次加密
随着互联网整体的发展,https 也被越来越多的应用.甚至苹果去年还曾经放言要强制所有的 app 都使用 https,可见在如今的互联网它的重要性.前面的文章说了 OSI 七层模型,https 可以保 ...
- iOS开发网络篇—HTTP协议
说明:apache tomcat服务器必须占用8080端口 一.URL 1.基本介绍 URL的全称是Uniform Resource Locator(统一资源定位符) 通过1个URL,能找到互联网上唯 ...
- ios开发网络篇—HTTP协议 - 转
一.URL 1.基本介绍 URL的全称是Uniform Resource Locator(统一资源定位符) ,通过1个URL,能找到互联网唯一的1个资源 ,URL就是资源的地址,位置,互联网上的每个 ...
- 《每日一记 》网络篇-ARP协议与IP地址MAC地址
一.ARP/RARP协议 1.地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议.主机发送信息时将包含目标IP地址的AR ...
- 网络篇 EIGRP协议-27
目录 一.EIGRP的基本概述 二.EIGRP的特点 三.EIGRP的四种重要技术 四.EIGRP的相关术语 五.EIGRP的三张表 1.路由表 2.邻居表 3.拓扑表 六.EIGRP的五个分组 1. ...
- 网络协议 19 - RPC 协议:远在天边近在眼前
[前五篇]系列文章传送门: 网络协议 14 - 流媒体协议:要说爱你不容易 网络协议 15 - P2P 协议:小种子大学问 网络协议 16 - DNS 协议:网络世界的地址簿 网络协议 17 - HT ...
- 网络协议 22 - RPC 协议(下)- 二进制类 RPC 协议
网络协议 22 - RPC 协议(下)- 二进制类 RPC 协议 原文:网络协议 22 - RPC 协议(下)- 二进制类 RPC 协议 前面我们认识了两个常用文本类的 RPC 协议,对于陌生 ...
- 【网络篇】第十七篇——IP协议详解
IP协议 网络层与数据链路层有什么关系? 基本概念 IP协议格式 分卡与组装 网段划分 IP地址的构成 DHCP协议 IP地址的分类 IP分类的缺点 无分类地址 CIDR 特殊的IP地址 IP地址的数 ...
最新文章
- Dom4j 解析Xml文档及XPath查询 学习笔记
- 《LeetCode力扣练习》剑指 Offer 27. 二叉树的镜像 Java
- 钽电容正负极_固态电容怎么看正负极,固态电容正负极区分方法
- mysql数据库导出最大值_4.6 MySQL数据库导入与导出攻略
- python 选择排序算法
- 网络安全公司奇安信集团是如何基于 Flink 构建 CEP 引擎实时检测网络攻击【未来不可忽视的网络安全】
- Windows 中自定义Error Codes
- scala重载无参构造方法_Scala中的无参数方法
- [转载] sklearn FutureWarning: numpy not_equal will not check..., The comparison did not return the sam
- win10开启文件共享服务器,墨涩网 - Windows10开启局域网文件共享功能——墨涩网...
- python语言设置_Python语言脚本的安装和配置
- 使用fiddler4进行微信小程序抓包
- 北京理工大学计算机学院放假时间,北理珠这个老师厉害了,竟然利用假期干了这么件事…...
- ioncube php encode,ioncube 加密
- 重大噩耗:苹果账号无法付款!(11-20更新:账单地址和卡地址一样,信用卡名字和开发者名字一致,都无法付款)
- 京东联盟API - 万能转链接口 - 高效转链接口 - 接口定制
- 威斯康星麦迪逊计算机专业排名,恭喜L同学获得威斯康星大学麦迪逊分校计算机专业(专业排名TOP10)EA录取...
- Chapter2:时域分析法(上)
- 计算机网络:数据链路层:有线和无线网络
- 简易性格测试题--你适合什么职业?