最近的公司区块链钱包,用到了以太坊的官方开发库 web3j。web3j 是Java版本的以太坊 rpc-json 接口协议封装实现,如果需要将你的 Java 或安卓应用接入以太坊,或者希望用 Java 开发一个钱包应用,那么 web3j 完全能满足你的需求。

目录:

  1. rpc 简介
  2. rpc 和 http 对比
  3. json-rpc 简介
  4. web3j 调用
  5. 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相关推荐

  1. 02.iOS开发网络篇—HTTP协议

    iOS开发网络篇-HTTP协议 说明:apache tomcat服务器必须占用8080端口 一.URL 1.基本介绍 URL的全称是Uniform Resource Locator(统一资源定位符) ...

  2. 网络篇 - https协议中的数据是否需要二次加密

    随着互联网整体的发展,https 也被越来越多的应用.甚至苹果去年还曾经放言要强制所有的 app 都使用 https,可见在如今的互联网它的重要性.前面的文章说了 OSI 七层模型,https 可以保 ...

  3. iOS开发网络篇—HTTP协议

    说明:apache tomcat服务器必须占用8080端口 一.URL 1.基本介绍 URL的全称是Uniform Resource Locator(统一资源定位符) 通过1个URL,能找到互联网上唯 ...

  4. ios开发网络篇—HTTP协议 - 转

    一.URL 1.基本介绍  URL的全称是Uniform Resource Locator(统一资源定位符) ,通过1个URL,能找到互联网唯一的1个资源 ,URL就是资源的地址,位置,互联网上的每个 ...

  5. 《每日一记 》网络篇-ARP协议与IP地址MAC地址

    一.ARP/RARP协议 1.地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议.主机发送信息时将包含目标IP地址的AR ...

  6. 网络篇 EIGRP协议-27

    目录 一.EIGRP的基本概述 二.EIGRP的特点 三.EIGRP的四种重要技术 四.EIGRP的相关术语 五.EIGRP的三张表 1.路由表 2.邻居表 3.拓扑表 六.EIGRP的五个分组 1. ...

  7. 网络协议 19 - RPC 协议:远在天边近在眼前

    [前五篇]系列文章传送门: 网络协议 14 - 流媒体协议:要说爱你不容易 网络协议 15 - P2P 协议:小种子大学问 网络协议 16 - DNS 协议:网络世界的地址簿 网络协议 17 - HT ...

  8. 网络协议 22 - RPC 协议(下)- 二进制类 RPC 协议

    网络协议 22 - RPC 协议(下)- 二进制类 RPC 协议 原文:网络协议 22 - RPC 协议(下)- 二进制类 RPC 协议     前面我们认识了两个常用文本类的 RPC 协议,对于陌生 ...

  9. 【网络篇】第十七篇——IP协议详解

    IP协议 网络层与数据链路层有什么关系? 基本概念 IP协议格式 分卡与组装 网段划分 IP地址的构成 DHCP协议 IP地址的分类 IP分类的缺点 无分类地址 CIDR 特殊的IP地址 IP地址的数 ...

最新文章

  1. Dom4j 解析Xml文档及XPath查询 学习笔记
  2. 《LeetCode力扣练习》剑指 Offer 27. 二叉树的镜像 Java
  3. 钽电容正负极_固态电容怎么看正负极,固态电容正负极区分方法
  4. mysql数据库导出最大值_4.6 MySQL数据库导入与导出攻略
  5. python 选择排序算法
  6. 网络安全公司奇安信集团是如何基于 Flink 构建 CEP 引擎实时检测网络攻击【未来不可忽视的网络安全】
  7. Windows 中自定义Error Codes
  8. scala重载无参构造方法_Scala中的无参数方法
  9. [转载] sklearn FutureWarning: numpy not_equal will not check..., The comparison did not return the sam
  10. win10开启文件共享服务器,墨涩网 - Windows10开启局域网文件共享功能——墨涩网...
  11. python语言设置_Python语言脚本的安装和配置
  12. 使用fiddler4进行微信小程序抓包
  13. 北京理工大学计算机学院放假时间,北理珠这个老师厉害了,竟然利用假期干了这么件事…...
  14. ioncube php encode,ioncube 加密
  15. 重大噩耗:苹果账号无法付款!(11-20更新:账单地址和卡地址一样,信用卡名字和开发者名字一致,都无法付款)
  16. 京东联盟API - 万能转链接口 - 高效转链接口 - 接口定制
  17. 威斯康星麦迪逊计算机专业排名,恭喜L同学获得威斯康星大学麦迪逊分校计算机专业(专业排名TOP10)EA录取...
  18. Chapter2:时域分析法(上)
  19. 计算机网络:数据链路层:有线和无线网络
  20. 简易性格测试题--你适合什么职业?

热门文章

  1. OTB官方评估代码matlab版本
  2. fastclick 解决移动端click事件300ms延迟
  3. 机器学习----聚类算法
  4. Scrap入门之基础命令
  5. POST、DELETE、PUT、GET的含义及区别
  6. osgQTWidget
  7. MySQL | 数据库基础理论、六大设计范式详解
  8. 废品回收微信小程序开发方案
  9. 【区块链】走进web3的世界-对于前端来说,web2与web3的区别
  10. daphile的dsd设置_玩Daphile Digital Mus(2)