1. 如何使用 Etherscan 的 API
虽然以太坊提供了 Web3 和 Json Rpc 这 2 种 API,geth 也额外提供了一些 API ,但是对于开发以太坊应用来说还是显得有些不足,比如说获取交易记录的时间,需要先通过交易的 hash 找到该交易对应的区块 id,然后才能找到对应的时间,查询起来相当不方便。
好在 Etherscan 对外提供了一些公共的 API,给我们提供了额外的能力来处理更多的业务场景。
为了方便开发人员更好地使用 ethersacn.io ,网站提供了 一系列 API 供开发人员使用。
API 的使用非常简单,基本上都是 get 方法,通过 http 请求就可以直接调用,在每个 Api 的说明文档都有对应的例子可以查看。
API 主要包含以下模块:账号、智能合约、交易、区块、事件日志、代币及工具等。
账号相关的 API,有获取账号金额,获取交易记录等,该模块提供的 API 最多。
API 示例
https://api.etherscan.io/api?mole)=account&action=balance&address=&tag=latest&apikey=YourApiKeyToken
参数说明
其中 mole、action、apikey 是每个 API 都有的参数,其他的参数则因不同 API 而不同。
返回结果
API 示例
https://api.etherscan.io/api?mole=account&action=balancemulti&address=,,&tag=latest&apikey=YourApiKeyToken
参数说明
(前面有讲过的参数就不讲了,下同)
与单个账号金额 API 相比,参数 address 用 , 号分隔多个账号,最多可支持 20 个账号的金额查询。
返回结果
API 示例
https://api.etherscan.io/api?mole=account&action=txlist&address=&startblock=0&endblock=99999999&page=1&offset=10&sort=asc&apikey=YourApiKeyToken
参数说明
返回结果
API 示例
https://api.etherscan.io/api?mole=account&action=txlistinternal&address=&startblock=0&endblock=2702578&page=1&offset=10&sort=asc&apikey=YourApiKeyToken
参数说明
参数与上一个 API 基本相同,只有 action 是 txlistinternal 这一点不同,这 2 种交易的区别是什么呢?简单的理解就是“正常”的交易是会记录到区块链上的,而“内部”交易是指不会记录到区块链上的记录,比如交易失败的记录。
另外这个 API 还可以通过交易 hash 查看交易的详情。
https://api.etherscan.io/api?mole=account&action=txlistinternal&txhash=&apikey=YourApiKeyToken
返回结果
API 示例
参数说明
返回结果
API 示例
参数说明
返回结果
智能合约相关的 API,其实只有一个获取智能合约接口的 API,但是这个 API 非常有用。
API 示例
参数说明
智能合约的 abi 就是一个 json 对象,通过这个对象我们可以调用其接口方法,后面会写一篇文章介绍如何操作 abi 对象,敬请期待。
返回结果
返回结果内容比较长,这里省略,就是一个 json 对象,感兴趣的可以自行调用该 API 看结果。
账号和智能合约的 API 已经能满足大部分的业务需求了,其他模块的 API 感觉没什么太大的作用,这里就不介绍了,感兴趣的读者可以自行查阅。
这里再说下 API 的使用限制,刚才提到每个 API 都有一个 apikey 参数,如果 API 没加上这个参数的话,每个 API 的请求次数不能超过 5 次每秒。
Etherscan 提供的这些 API 有些是和以太坊提供的 API 有重复的,比如说获取账号金额,获取事件日志记录等,但有一些 API 给我们带来了很大的便利性,比如获取账号交易记录,有了这个 API 就不用使用几个原生 API 进行各种数据拼接了。
另外 Etherscan 的这套 API 在 Rinkeby 测试网络也有一套一模一样的,区别只是前面的 url 不同,Rinkeby 的是: api-rinkeby.etherscan.io ,感兴趣的同学可以去试试。
2. json-rpc璋冪敤閿欒寮傚父锛孭arse error,閿欒鐮侊細-32700
閿欒璇锋眰濡備笅锛
姝g‘浼犲弬鏂瑰紡鏄灏唈son鍙樻垚瀛楃︿覆浼犵粰data
3. DApp开发入门
本文仅介绍以太坊系列的DApp开发,其他链原理差不太多。
MetaMask安装完成并运行后,可以在Chrome控制台打印 MetaMask注入的window.ethereum对象
关于ethereum对象,我们只需要关心 ethereum.request 就足够了,MetaMask 使用 ethereum.request(args) 方法 来包装 RPC API。这些 API 基于所有以太坊客户端公开的接口。 简单来说钱包交互的大部分操作都是由 request() 方法实现,通过传入不同的方法名来区分。
⚠️ 即使ethereum对象中提供了chainId,isMetaMask,selectAddress属性,我们也不能完全相信这些属性,他们是不稳定或不标准,不建议使用。我们可以通过上面说的request方法,拿到可靠的数据 。
钱包通过method方法名,进行对应的实现 以获取钱包地址为例
调用 ethereum.request({ method: "eth_requestAccounts" }) ,钱包实现了该方法,那么就可以拿到钱包的地址了。
MetaMask注入的 window.ethereum 就是一个Provider,一个RPC节点也是一个Provider,通过Provider,我们有了访问区块链的能力。 在连接到钱包的情况下,通常使用钱包的Provider就可以了, ethers.providers.Web3Provider(ethereum)
如果只需要查询一些区块链数据,可以使用EtherscanProvider 和 InfuraProvider 连接公开的 第三方节点服务提供商 。JsonRpcProvider 和 IpcProvider 允许连接到我们控制或可以访问的以太坊节点。
获取当前账户余额
获取最新区块号
其他RPC操作,可以通过 JSON-RPC 查看。
通过 ethers.js 可以连接ERC20的合约,合约编译后会生成ABI,合约部署后,会生成合约地址,开发者通过 ABI和合约地址 ,对合约发送消息。
合约中的方法大致分为两种: 视图方法(免费),非视图方法(消耗Gas) ,可以通过ABI查看方法类型。
⚠️ ERC20需要多加关注的是 Approve() 方法以及 transfer() 和 transferFrom() 的区别 ,授权过的代币,被授权的那一方,可以通过调用 transferFrom() 方法,转走你授权数量内的代币,所以授权是一个很危险的操作,假设你授权了一个不良的合约,那你会面临授权的token被转走的风险,即使你没有泄露私钥助记词。
便利三方库: web3-react use-wallet
文档: doc.metamask.io ethers
4. 以太坊stratum协议原理
参照比特币的 stratum协议 和 NiceHash的stratum协议规范 编写了一版以太坊版本的stratum协议说明.
stratum协议是目前最常用的矿机和矿池之间的TCP通讯协议。
以太坊是一个去中心化的网络架构,通过安装Mist客户端的节点来转发新交易和新区块。而矿机、矿池也同时形成了另一个网络,我们称之为矿工网络。
矿工网络分成矿机、矿池、钱包等几个主要部分,有时矿池软件与钱包安装在一起,可合称为矿池。
矿机与矿池软件之间的通讯协议是 stratum ,而矿池软件与钱包之间的通讯是 bitcoinrpc 接口。
stratum是 JSON 为数据格式.
矿机启动,首先以 mining.subscribe 方法向矿池连接,用来订阅工作。
矿池以 mining.notify 返回订阅号、ExtraNonce1和ExtraNonce2_size。
Client:
Server:
其中:
是 订阅号 ;
080c是 extranonce ,Extranonce可能最大3字节;
矿机以 mining.authorize 方法,用某个帐号和密码登录到矿池,密码可空,矿池返回 true 登录成功。该方法必须是在初始化连接之后马上进行,否则矿机得不到矿池任务。
Client:
Server:
难度调整由矿池下发给矿机,以 mining.set_difficulty 方法调整难度, params 中是难度值。
Server:
矿机会在下一个任务时采用新难度,矿池有时会马上下发一个新任务并且把清理任务设为true,以便矿机马上以新难度工作。
该命令由矿池定期发给矿机,当矿机以 mining.subscribe 方法登记后,矿池应该马上以 mining.notify 返回该任务。
Server:
任务ID : bf0488aa ;
seedhash : 。每一个任务都发送一个seedhash来支持尽可能多的矿池,这可能会很快地在货币之间交换。
headerhash : 。
boolean cleanjobs : true 。如果设为true,那么矿工需要清理任务队列,并立即开始从事新提供的任务,因为所有旧的任务分享都将导致陈旧的分享错误。如果是 false 则等当前任务结束才开始新任务。
矿工使用seedhash识别DAG,然后带着headerhash,extranonce和自己的minernonce寻找低于目标的share(这是由提供的难度而产生的)。
矿机找到合法share时,就以” mining.submit “方法向矿池提交任务。矿池返回true即提交成功,如果失败则error中有具体原因。
Client:
任务ID : bf0488aa
minernonce : 6a909d9bbc0f 。注意minernonce是6个字节,因为提供的extranonce是2个字节。如果矿池提供3字节的extranonce,那么minernonce必须是5字节
Server:
一般的矿机与矿池通讯过程就如下所示:
5. 以太坊多节点私有链部署
假设两台电脑A和B
要求:
1、两台电脑要在一个网络中,能ping通
2、两个节点使用相同的创世区块文件
3、禁用ipc;同时使用参数--nodiscover
4、networkid要相同,端口号可以不同
1.4 搭建私有链
1.4.1 创建目录和genesis.json文件
创建私有链根目录./testnet
创建数据存储目录./testnet/data0
创建创世区块配置文件./testnet/genesis.json
1.4.2 初始化操作
cd ./eth_test
geth --datadir data0 init genesis.json
1.4.3 启动私有节点
1.4.4 创建账号
personal.newAccount()
1.4.5 查看账号
eth.accounts
1.4.6 查看账号余额
eth.getBalance(eth.accounts[0])
1.4.7 启动&停止挖矿
启动挖矿:
miner.start(1)
其中 start 的参数表示挖矿使用的线程数。第一次启动挖矿会先生成挖矿所需的 DAG 文件,这个过程有点慢,等进度达到 100% 后,就会开始挖矿,此时屏幕会被挖矿信息刷屏。
停止挖矿,在 console 中输入:
miner.stop()
挖到一个区块会奖励5个以太币,挖矿所得的奖励会进入矿工的账户,这个账户叫做 coinbase,默认情况下 coinbase 是本地账户中的第一个账户,可以通过 miner.setEtherbase() 将其他账户设置成 coinbase。
1.4.8 转账
目前,账户 0 已经挖到了 3 个块的奖励,账户 1 的余额还是0:
我们要从账户 0 向账户 1 转账,所以要先解锁账户 0,才能发起交易:
发送交易,账户 0 -> 账户 1:
需要输入密码 123456
此时如果没有挖矿,用 txpool.status 命令可以看到本地交易池中有一个待确认的交易,可以使用 eth.getBlock("pending", true).transactions 查看当前待确认交易。
使用 miner.start() 命令开始挖矿:
miner.start(1);admin.sleepBlocks(1);miner.stop();
新区块挖出后,挖矿结束,查看账户 1 的余额,已经收到了账户 0 的以太币:
web3.fromWei(eth.getBalance(eth.accounts[1]),'ether')
用同样的genesis.json初始化操作
cd ./eth_test
geth --datadir data1 init genesis.json
启动私有节点一,修改 rpcport 和port
可以通过 admin.addPeer() 方法连接到其他节点,两个节点要要指定相同的 chainID。
假设有两个节点:节点一和节点二,chainID 都是 1024,通过下面的步骤就可以从节点二连接到节点一。
首先要知道节点一的 enode 信息,在节点一的 JavaScript console 中执行下面的命令查看 enode 信息:
admin.nodeInfo.enode
" enode://@[::]:30303 "
然后在节点二的 JavaScript console 中执行 admin.addPeer(),就可以连接到节点一:
addPeer() 的参数就是节点一的 enode 信息,注意要把 enode 中的 [::] 替换成节点一的 IP 地址。连接成功后,节点一就会开始同步节点二的区块,同步完成后,任意一个节点开始挖矿,另一个节点会自动同步区块,向任意一个节点发送交易,另一个节点也会收到该笔交易。
通过 admin.peers 可以查看连接到的其他节点信息,通过 net.peerCount 可以查看已连接到的节点数量。
除了上面的方法,也可以在启动节点的时候指定 --bootnodes 选项连接到其他节点。 bootnode 是一个轻量级的引导节点,方便联盟链的搭建 下一节讲 通过 bootnode 自动找到节点
参考: https://cloud.tencent.com/developer/article/1332424
6. 一学就会,手把手教你用Go语言调用智能合约
智能合约调用是实现一个 DApp 的关键,一个完整的 DApp 包括前端、后端、智能合约及区块 链系统,智能合约的调用是连接区块链与前后端的关键。
我们先来了解一下智能合约调用的基础原理。智能合约运行在以太坊节点的 EVM 中。因此要 想调用合约必须要访问某个节点。
以后端程序为例,后端服务若想连接节点有两种可能,一种是双 方在同一主机,此时后端连接节点可以采用 本地 IPC(Inter-Process Communication,进 程间通信)机制,也可以采用 RPC(Remote Procere Call,远程过程调用)机制;另 一种情况是双方不在同一台主机,此时只能采用 RPC 机制进行通信。
提到 RPC, 读者应该对 Geth 启动参数有点印象,Geth 启动时可以选择开启 RPC 服务,对应的 默认服务端口是 8545。。
接着,我们来了解一下智能合约运行的过程。
智能合约的运行过程是后端服务连接某节点,将 智能合约的调用(交易)发送给节点,节点在验证了交易的合法性后进行全网广播,被矿工打包到 区块中代表此交易得到确认,至此交易才算完成。
就像数据库一样,每个区块链平台都会提供主流 开发语言的 SDK(Software Development Kit,软件开发工具包),由于 Geth 本身就是用 Go 语言 编写的,因此若想使用 Go 语言连接节点、发交易,直接在工程内导入 go-ethereum(Geth 源码) 包就可以了,剩下的问题就是流程和 API 的事情了。
总结一下,智能合约被调用的两个关键点是节点和 SDK。
由于 IPC 要求后端与节点必须在同一主机,所以很多时候开发者都会采用 RPC 模式。除了 RPC,以太坊也为开发者提供了 json- rpc 接口,本文就不展开讨论了。
接下来介绍如何使用 Go 语言,借助 go-ethereum 源码库来实现智能合约的调用。这是有固定 步骤的,我们先来说一下总体步骤,以下面的合约为例。
步骤 01:编译合约,获取合约 ABI(Application Binary Interface,应用二进制接口)。 单击【ABI】按钮拷贝合约 ABI 信息,将其粘贴到文件 calldemo.abi 中(可使用 Go 语言IDE 创建该文件,文件名可自定义,后缀最好使用 abi)。
最好能将 calldemo.abi 单独保存在一个目录下,输入“ls”命令只能看到 calldemo.abi 文件,参 考效果如下:
步骤 02:获得合约地址。注意要将合约部署到 Geth 节点。因此 Environment 选择为 Web3 Provider。
在【Environment】选项框中选择“Web3 Provider”,然后单击【Deploy】按钮。
部署后,获得合约地址为:。
步骤 03:利用 abigen 工具(Geth 工具包内的可执行程序)编译智能合约为 Go 代码。abigen 工具的作用是将 abi 文件转换为 Go 代码,命令如下:
其中各参数的含义如下。 (1)abi:是指定传入的 abi 文件。 (2)type:是指定输出文件中的基本结构类型。 (3)pkg:指定输出文件 package 名称。 (4)out:指定输出文件名。 执行后,将在代码目录下看到 funcdemo.go 文件,读者可以打开该文件欣赏一下,注意不要修改它。
步骤 04:创建 main.go,填入如下代码。 注意代码中 HexToAddress 函数内要传入该合约部署后的地址,此地址在步骤 01 中获得。
步骤 04:设置 go mod,以便工程自动识别。
前面有所提及,若要使用 Go 语言调用智能合约,需要下载 go-ethereum 工程,可以使用下面 的指令:
该指令会自动将 go-ethereum 下载到“$GOPATH/src/github.com/ethereum/go-ethereum”,这样还算 不错。不过,Go 语言自 1.11 版本后,增加了 mole 管理工程的模式。只要设置好了 go mod,下载 依赖工程的事情就不必关心了。
接下来设置 mole 生效和 GOPROXY,命令如下:
在项目工程内,执行初始化,calldemo 可以自定义名称。
步骤 05:运行代码。执行代码,将看到下面的效果,以及最终输出的 2020。
上述输出信息中,可以看到 Go 语言会自动下载依赖文件,这就是 go mod 的神奇之处。看到 2020,相信读者也知道运行结果是正确的了。
7. 以太坊event log查询与解析
从 ethereum json-rpc文档 的文档中找到一个同时指定多个事件以 OR 或者 AND 查询的方法.以下是查询 Approval 或 Transfer 事件的方法:
topics 字段中指定查询条件的语法参考上面链接。
通过 getTransactionReceipt 在ropsten测试网上查询到交易号为 的交易详情
这个交易从 "from": "" 发送到合约地址 "to": "" .这个合约为ERC20代币合约.从 topics 的第一个元素可以看出合约中产生了 Transfer 事件(topics第一个元素一定是事件的keccak哈希). topics 的第二个字段是转出代币的地址,第三个字段是接收者地址.ERC20代币 Transfer 事件的签名为
我们注意到 Transfer 事件的第一个和第二个参数被标记为 indexed , 因此他们的值被放在 topics array 中. 由于tokens参数没有标记为 indexed , 所以他的值被放在 data 字段. 如果事件中有多个字段未标记为 indexed , 那么他们的值都会被记录在 data 字段中。
8. 《区块链项目开发指南》读书笔记
ethash
答:在DAPP中,没有一个中心服务器来协调节点,或者决定什么是对,什么是错,因此应对这个挑战确实不容易,一致性协议(concensus protocol)可用于解决这个问题。
补充:共识算法的核心就是解决拜占庭将军问题(分布式网络一致性问题)。
答:修改bug或者更新DAPP很困难。
如果我需要从一个中心化应用抓取数据,如车辆违章信息,怎么保证抓取的数据是真实有效的?
答:为了访问中心化的API,可以使用Oraclize服务可以作为中间人,Oraclize为从中心化服务智能合约中抓取的数据提供TLSNotary验证。
中心化应用的所有者需要有盈利才能长期维护应用的运行,而DAPP虽然没有所有者,但是跟中心化应用一样,DAPP节点需要硬件和网络资源才能维持运行。DAPP节点需要一些有用的回报来维持运行,于是内部货币登场了。大多数DAPP都有内置内部货币,或者可以说最成功的DAPP都有内置内部货币。如以太币
授权的DAPP不对所有人开放。授权的DAPP继承了免权限DAPP的全部属性,但需要权限才能参与到网络中去。授权的DAPP与免权限的DAPP的共识协议是不同的。授权的DAPP没有内部货币。
超级账本(Hyperledger)项目致力于开发创建授权的DAPP技术。
为什么少数国家认定比特币是非法的,大部分国家对此还没有做出决定呢?原因如下:
星际文件存储系统(InterPlanetary File System)是一个去中心化的文件系统。
目标是通过使交易几乎瞬间完成,并隐藏交易账户的信息,还可以防止他人用ISP追踪所有者。
任何人都可以成为以太坊网络中的矿工。每个矿工独自解决问题,第一个解决问题的矿工是胜利者,它得到的回报是5个以太币和该区块中全部交易的交易费。区块链中有多少个区块没有限制,可以生成的以太币总数也没有限制。
网络中的任何节点都可以检查区块链是否合法,首先检查交易在区块链中是否合法以及时间戳的验证情况,然后检查区块的目标值和随机数是否合法、矿工是否得到合法的回报等。
节点是如何发现网络中的其他节点的呢?
以太坊的节点发现协议:Kadelima,在这种协议中,有一种特殊节点Bootstrap节点。它保存了一段时间内与它连接的所有节点列表,但其本身不保存区块链。
当对等节点连接到以太坊网络时,它们首先连接到Bootstrap节点。
可以有多种以太坊实例,也就是说,不同的网络每个都有自己的网络ID。
两种主要的以太坊网络是主网和测试网。以太币在主网上交易,而测试网供开发人员测试。
一个去中心化的通信协议,它支持广播、用户到用户、加密信息等,但不用于传输大数据。
一个去中心化的文件系统。
geth为其他应用提供了与其通信的JSON-RPC API。使用HTTP、WebSocket和其他协议服务于JSON-RPC API。
JSON-RPC API提供的API分成如下类型:
以太坊网络中的节点默认用 30303 端口通信。
--networkid 用于指定网络ID,1代表主网网络ID,缺省默认值为1,2代表测试网络ID
--dev 标记运行一个私有网络
--etherbase 指定挖矿赚取的回报存入的钱包地址
--unlock 解锁一个或者多个账户
以太坊钱包与geth捆绑在一起。运行以太坊时,它会尝试发现一个本地geth实例并与之连接;如果它不能发现geth正在运行,它就启动自己的geth节点。以太坊钱包使用IPC与geth通信。geth支持以文件为基础的IPC。
以太坊下一个主要更新的名字。Serenity把共识协议改为casper,并将整合状态通道和分片。
Casper 实施了一个进程,使得它可以惩罚所有的恶意因素。这就是权益证明在Casper下是如何工作的:
验证者押下一定比例的他们拥有的以太币作为保证金。然后,他们将开始验证区块。也就是说,当他们发现一个可以他们认为可以被加到链上的区块的时候,他们将以通过押下赌注来验证它。
如果该区块被加到链上,然后验证者们将得到一个跟他们的赌注成比例的奖励。但是,如果一个验证者采用一种恶意的方式行动、试图做“无利害关系”的事,他们将立即遭到惩罚,他们所有的权益都会被砍掉。正如你可以看到的,Casper被设计成可以在一个无需信任的系统上工作,并且是更加拜占庭容错的。
支付通道 功能允许将两个以上向另一个账户发送以太币的交易合并成两个交易。其工作原理为:假设X是一个视频网站老板,Y是个用户。X每分钟收费1个以太币。现在X想让Y看视频期间每分钟交一次钱。当然,Y可以每分钟广播交易,但是这里有些问题,例如X不得不等待确认,所以视频就会中断一会。支付通道可以解决这个问题。使用支付通道,Y可以广播一个锁定交易,为X把一些以太币(比如100个以太币)锁定一段时间(比如24小时)。现在每看完一分钟视频,Y将发送一个签名记录表示可以解锁,一个以太币就进入X的账户,其余的进入Y的账户。再过一分钟,Y将发送一个签名记录表示可以解锁,两个以太币就进入X的账户,其余的进入Y的账户。Y观看X网站的视频过程中,该过程将持续。现在假设Y看完了100小时视频或者24小时时间到了,X将向网络广播最后的签名记录,以把钱收到自己的账户里。如果X没有在24小时内提款,全款会返还给Y。所以在区块链中,我们将看到lock和unlock两种交易。
Sybil攻击
51%攻击
补充:不能存储较大数据,目前有Swarm与IPFS等分布式存储方式可供选择
把所有东西都存在内存里,因此,节点一旦重启,将丢失以前的状态。
默认监听端口:8545
9. Dubbo——HTTP 协议 + JSON-RPC
Protocol 还有一个实现分支是 AbstractProxyProtocol,如下图所示:
从图中我们可以看到:gRPC、HTTP、WebService、Hessian、Thrift 等协议对应的 Protocol 实现,都是继承自 AbstractProxyProtocol 抽象类。
目前互联网的技术栈百花齐放,很多公司会使用 Node.js、Python、Rails、Go 等语言来开发 一些 Web 端应用,同时又有很多服务会使用 Java 技术栈实现,这就出现了大量的跨语言调用的需求。Dubbo 作为一个 RPC 框架,自然也希望能实现这种跨语言的调用,目前 Dubbo 中使用“HTTP 协议 + JSON-RPC”的方式来达到这一目的,其中 HTTP 协议和 JSON 都是天然跨语言的标准,在各种语言中都有成熟的类库。
下面就重点来分析 Dubbo 对 HTTP 协议的支持。首先,会介绍 JSON-RPC 的基础,并通过一个示例,快速入门,然后介绍 Dubbo 中 HttpProtocol 的具体实现,也就是如何将 HTTP 协议与 JSON-RPC 结合使用,实现跨语言调用的效果。
Dubbo 中支持的 HTTP 协议实际上使用的是 JSON-RPC 协议。
JSON-RPC 是基于 JSON 的跨语言远程调用协议。Dubbo 中的 bbo-rpc-xml、bbo-rpc-webservice 等模块支持的 XML-RPC、WebService 等协议与 JSON-RPC 一样,都是基于文本的协议,只不过 JSON 的格式比 XML、WebService 等格式更加简洁、紧凑。与 Dubbo 协议、Hessian 协议等二进制协议相比,JSON-RPC 更便于调试和实现,可见 JSON-RPC 协议还是一款非常优秀的远程调用协议。
在 Java 体系中,有很多成熟的 JSON-RPC 框架,例如 jsonrpc4j、jpoxy 等,其中,jsonrpc4j 本身体积小巧,使用方便,既可以独立使用,也可以与 Spring 无缝集合,非常适合基于 Spring 的项目。
下面先来看看 JSON-RPC 协议中请求的基本格式:
JSON-RPC请求中各个字段的含义如下:
在 JSON-RPC 的服务端收到调用请求之后,会查找到相应的方法并进行调用,然后将方法的返回值整理成如下格式,返回给客户端:
JSON-RPC响应中各个字段的含义如下:
Dubbo 使用 jsonrpc4j 库来实现 JSON-RPC 协议,下面使用 jsonrpc4j 编写一个简单的 JSON-RPC 服务端示例程序和客户端示例程序,并通过这两个示例程序说明 jsonrpc4j 最基本的使用方式。
首先,需要创建服务端和客户端都需要的 domain 类以及服务接口。先来创建一个 User 类,作为最基础的数据对象:
接下来创建一个 UserService 接口作为服务接口,其中定义了 5 个方法,分别用来创建 User、查询 User 以及相关信息、删除 User:
UserServiceImpl 是 UserService 接口的实现类,其中使用一个 ArrayList 集合管理 User 对象,具体实现如下:
整个用户管理业务的核心大致如此。下面我们来看服务端如何将 UserService 与 JSON-RPC 关联起来。
首先,创建 RpcServlet 类,它是 HttpServlet 的子类,并覆盖了 HttpServlet 的 service() 方法。我们知道,HttpServlet 在收到 GET 和 POST 请求的时候,最终会调用其 service() 方法进行处理;HttpServlet 还会将 HTTP 请求和响应封装成 HttpServletRequest 和 HttpServletResponse 传入 service() 方法之中。这里的 RpcServlet 实现之中会创建一个 JsonRpcServer,并在 service() 方法中将 HTTP 请求委托给 JsonRpcServer 进行处理:
最后,创建一个 JsonRpcServer 作为服务端的入口类,在其 main() 方法中会启动 Jetty 作为 Web 容器,具体实现如下:
这里使用到的 web.xml 配置文件如下:
完成服务端的编写之后,下面再继续编写 JSON-RPC 的客户端。在 JsonRpcClient 中会创建 JsonRpcHttpClient,并通过 JsonRpcHttpClient 请求服务端:
在 AbstractProxyProtocol 的 export() 方法中,首先会根据 URL 检查 exporterMap 缓存,如果查询失败,则会调用 ProxyFactory.getProxy() 方法将 Invoker 封装成业务接口的代理类,然后通过子类实现的 doExport() 方法启动底层的 ProxyProtocolServer,并初始化 serverMap 集合。具体实现如下:
在 HttpProtocol 的 doExport() 方法中,与前面介绍的 DubboProtocol 的实现类似,也要启动一个 RemotingServer。为了适配各种 HTTP 服务器,例如,Tomcat、Jetty 等,Dubbo 在 Transporter 层抽象出了一个 HttpServer 的接口。
bbo-remoting-http 模块的入口是 HttpBinder 接口,它被 @SPI 注解修饰,是一个扩展接口,有三个扩展实现,默认使用的是 JettyHttpBinder 实现,如下图所示:
HttpBinder 接口中的 bind() 方法被 @Adaptive 注解修饰,会根据 URL 的 server 参数选择相应的 HttpBinder 扩展实现,不同 HttpBinder 实现返回相应的 HttpServer 实现。HttpServer 的继承关系如下图所示:
这里以 JettyHttpServer 为例简单介绍 HttpServer 的实现,在 JettyHttpServer 中会初始化 Jetty Server,其中会配置 Jetty Server 使用到的线程池以及处理请求 Handler:
可以看到 JettyHttpServer 收到的全部请求将委托给 DispatcherServlet 这个 HttpServlet 实现,而 DispatcherServlet 的 service() 方法会把请求委托给对应接端口的 HttpHandler 处理:
了解了 Dubbo 对 HttpServer 的抽象以及 JettyHttpServer 的核心之后,回到 HttpProtocol 中的 doExport() 方法继续分析。
在 HttpProtocol.doExport() 方法中会通过 HttpBinder 创建前面介绍的 HttpServer 对象,并记录到 serverMap 中用来接收 HTTP 请求。这里初始化 HttpServer 以及处理请求用到的 HttpHandler 是 HttpProtocol 中的内部类,在其他使用 HTTP 协议作为基础的 RPC 协议实现中也有类似的 HttpHandler 实现类,如下图所示:
在 HttpProtocol.InternalHandler 中的 handle() 实现中,会将请求委托给 skeletonMap 集合中记录的 JsonRpcServer 对象进行处理:
skeletonMap 集合中的 JsonRpcServer 是与 HttpServer 对象一同在 doExport() 方法中初始化的。最后,我们来看 HttpProtocol.doExport() 方法的实现:
介绍完 HttpProtocol 暴露服务的相关实现之后,下面再来看 HttpProtocol 中引用服务相关的方法实现,即 protocolBindinRefer() 方法实现。该方法首先通过 doRefer() 方法创建业务接口的代理,这里会使用到 jsonrpc4j 库中的 JsonProxyFactoryBean 与 Spring 进行集成,在其 afterPropertiesSet() 方法中会创建 JsonRpcHttpClient 对象:
下面来看 doRefer() 方法的具体实现:
在 AbstractProxyProtocol.protocolBindingRefer() 方法中,会通过 ProxyFactory.getInvoker() 方法将 doRefer() 方法返回的代理对象转换成 Invoker 对象,并记录到 Invokers 集合中,具体实现如下:
本文重点介绍了在 Dubbo 中如何通过“HTTP 协议 + JSON-RPC”的方案实现跨语言调用。首先介绍了 JSON-RPC 中请求和响应的基本格式,以及其实现库 jsonrpc4j 的基本使用;接下来我们还详细介绍了 Dubbo 中 AbstractProxyProtocol、HttpProtocol 等核心类,剖析了 Dubbo 中“HTTP 协议 + JSON-RPC”方案的落地实现。
10. Infura API 获取以太坊当前配置链 ID - 区块链数据开发实战
简介:Infura 是以太坊和 IPFS 的 API 服务提供商。Infura 一开始只是为 ConsenSys 内部项目提供稳定可靠的 RPC 访问,后来随着以太坊生态发展,他们意识到自己可以起到更大作用,于是开始面向开发者提供公共 API 服务。本文整理使用 Infura API 获取以太坊当前配置链 ID 的实现。
Infura 是以太坊和 IPFS 的 API 服务提供商。Infura 一开始只是为 ConsenSys 内部项目提供稳定可靠的 RPC 访问,后来随着以太坊生态发展,他们意识到自己可以起到更大作用,于是开始面向开发者提供公共 API 服务。
本文整理使用 Infura API 获取以太坊当前配置链 ID 的实现。
Infura API 官方文档: https://infura.io/docs
使用 API 需要申请 Project ID ,ID 是免费申请的,申请流程为“注册 - 登录 - 创建新项目”,不需要审核,几分钟就能搞定。
Infura API 标准请求端口格式:
本例中我们使用基于 HTTP 的以太坊主网 JSON-RPC 端口:
Infura API 获取以太坊当前配置链 ID:
Curl 示例:
Node.js 示例:
返回的 JSON 示例:
返回当前链 ID 的大整数。
Infura API 服务思维导图:
我们有一个区块链知识星球,做区块链前沿资料的归纳整理以方便大家检索查询使用,也是国内顶尖区块链技术社区,欢迎感兴趣的朋友加入。如果你对上面内容有疑问,也可以加入知识星球提问我: