btcwallet对外服务

btcwallet除了像btcd对外提供rpc服务以外,还提供了grpc服务,同时grpc采用的是protobuf来实现.

这方便与不同语言进行交互,降低客户端代码编写量.

阅读这个模块,顺便了解一下proto的使用,更详细的细节问题.

Service分类

总共有三种Service,分别是VersionService,WalletService和WalletLoaderService,

从中可以看出

VersionService

只是提供版本查询服务,为什么会做成一个独立的服务,设计者是出于什么考虑的呢?

这里重点考察grpc服务的启动过程

  1. walletMain函数中传递wallet.Loader调用startRPCServers
  2. 配置grpc所需参数,包括证书
  3. 创建grpcServer
  4. 通过rpcserver.StartVersionService注册VersionService
  5. 通过pb.RegisterVersionServiceServer 注册versionServer

    这里的RegisterVersionServiceServer是自动生成,
  6. versionServer实现了Version接口,对外提供服务

下面是proto自动生成的Service Description ,其中HandlerType为空,需要我们自己实现.

var _VersionService_serviceDesc = grpc.ServiceDesc{
ServiceName: "walletrpc.VersionService",
HandlerType: (*VersionServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Version",
Handler: _VersionService_Version_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api.proto",
}

VersionService client的实现

同时proto自动生成了客户端访问代码,

  1. 通过NewVersionServiceClient创建VersionServiceClient
  2. 通过VersionServiceClient的Version来访问

相关参数

grpc调用的所有参数都是通过Message来定义,

可以看出,虽然VersionRequest什么都没偶,还是要定义

message VersionRequest {}
message VersionResponse {
string version_string = 1;
uint32 major = 2;
uint32 minor = 3;
uint32 patch = 4;
string prerelease = 5;
string build_metadata = 6;
}

客户端和服务端的实现

客户端,由proto自动生成, 完全不用管理

type VersionServiceClient interface {
Version(ctx context.Context, in *VersionRequest, opts ...grpc.CallOption) (*VersionResponse, error)
} func (c *versionServiceClient) Version(ctx context.Context, in *VersionRequest, opts ...grpc.CallOption) (*VersionResponse, error) {
out := new(VersionResponse)
err := grpc.Invoke(ctx, "/walletrpc.VersionService/Version", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}

服务端

type VersionServiceServer interface {
Version(context.Context, *VersionRequest) (*VersionResponse, error)
} func (*versionServer) Version(ctx context.Context, req *pb.VersionRequest) (*pb.VersionResponse, error) {
return &pb.VersionResponse{
VersionString: semverString,
Major: semverMajor,
Minor: semverMinor,
Patch: semverPatch,
}, nil
}

这里给的例子比较特殊,就是输入参数根本没用,不过看得出如何使用proto以及grpc了.

WalletLoaderService

此服务主要用于打开关闭钱包,

StartConsensusRpc是在btcwallet启动的时候没有指定btcd的情形下,可以连接指定的btcd.

service WalletLoaderService {
rpc WalletExists (WalletExistsRequest) returns (WalletExistsResponse);
rpc CreateWallet (CreateWalletRequest) returns (CreateWalletResponse);
rpc OpenWallet (OpenWalletRequest) returns (OpenWalletResponse);
rpc CloseWallet (CloseWalletRequest) returns (CloseWalletResponse);
rpc StartConsensusRpc (StartConsensusRpcRequest) returns (StartConsensusRpcResponse);
}

WalletLoaderService启动方式和VersionService完全一致.

我的问题:

钱包不存在的时候只能通过--create创建完成以后再启动,是否这个服务目前根本没用?

核心服务WalletService

接口

service WalletService {
// Queries
rpc Ping (PingRequest) returns (PingResponse);
rpc Network (NetworkRequest) returns (NetworkResponse);
rpc AccountNumber (AccountNumberRequest) returns (AccountNumberResponse);
rpc Accounts (AccountsRequest) returns (AccountsResponse);
rpc Balance (BalanceRequest) returns (BalanceResponse);
rpc GetTransactions (GetTransactionsRequest) returns (GetTransactionsResponse); // Notifications
rpc TransactionNotifications (TransactionNotificationsRequest) returns (stream TransactionNotificationsResponse);
rpc SpentnessNotifications (SpentnessNotificationsRequest) returns (stream SpentnessNotificationsResponse);
rpc AccountNotifications (AccountNotificationsRequest) returns (stream AccountNotificationsResponse); // Control
rpc ChangePassphrase (ChangePassphraseRequest) returns (ChangePassphraseResponse);
rpc RenameAccount (RenameAccountRequest) returns (RenameAccountResponse);
rpc NextAccount (NextAccountRequest) returns (NextAccountResponse);
rpc NextAddress (NextAddressRequest) returns (NextAddressResponse);
rpc ImportPrivateKey (ImportPrivateKeyRequest) returns (ImportPrivateKeyResponse);
rpc FundTransaction (FundTransactionRequest) returns (FundTransactionResponse);
rpc SignTransaction (SignTransactionRequest) returns (SignTransactionResponse);
rpc PublishTransaction (PublishTransactionRequest) returns (PublishTransactionResponse);
}

启动过程

  1. walletMain中等待钱包打开以后获取到钱包句柄,然后调用startWalletRPCServices
  2. 注意startWalletRPCServices传递进去三个参数,一个是钱包句柄,一个是grpc server,另一个是普通的http rpc server
  3. rpcserver.StartWalletService启动grpc WalletService
  4. legacyServer.RegisterWallet 注册http rpc服务
  5. pb.RegisterWalletServiceServer注册rpc.walletServer
  6. rpc.walletServer实现了接口
type WalletServiceServer interface {
// Queries
Ping(context.Context, *PingRequest) (*PingResponse, error)
Network(context.Context, *NetworkRequest) (*NetworkResponse, error)
AccountNumber(context.Context, *AccountNumberRequest) (*AccountNumberResponse, error)
Accounts(context.Context, *AccountsRequest) (*AccountsResponse, error)
Balance(context.Context, *BalanceRequest) (*BalanceResponse, error)
GetTransactions(context.Context, *GetTransactionsRequest) (*GetTransactionsResponse, error)
// Notifications
TransactionNotifications(*TransactionNotificationsRequest, WalletService_TransactionNotificationsServer) error
SpentnessNotifications(*SpentnessNotificationsRequest, WalletService_SpentnessNotificationsServer) error
AccountNotifications(*AccountNotificationsRequest, WalletService_AccountNotificationsServer) error
// Control
ChangePassphrase(context.Context, *ChangePassphraseRequest) (*ChangePassphraseResponse, error)
RenameAccount(context.Context, *RenameAccountRequest) (*RenameAccountResponse, error)
NextAccount(context.Context, *NextAccountRequest) (*NextAccountResponse, error)
NextAddress(context.Context, *NextAddressRequest) (*NextAddressResponse, error)
ImportPrivateKey(context.Context, *ImportPrivateKeyRequest) (*ImportPrivateKeyResponse, error)
FundTransaction(context.Context, *FundTransactionRequest) (*FundTransactionResponse, error)
SignTransaction(context.Context, *SignTransactionRequest) (*SignTransactionResponse, error)
PublishTransaction(context.Context, *PublishTransactionRequest) (*PublishTransactionResponse, error)
}

stream返回的实现

stream就是持续不断的有返回的意思吧.

rpc TransactionNotifications (TransactionNotificationsRequest) returns (stream TransactionNotificationsResponse); proto中的接口被转换成了 TransactionNotifications(*TransactionNotificationsRequest, WalletService_TransactionNotificationsServer) error

其中TransactionNotificationsResponse被转换成了

type WalletService_TransactionNotificationsServer interface {
Send(*TransactionNotificationsResponse) error
grpc.ServerStream
}

服务端TransactionNotifications实现

func (s *walletServer) TransactionNotifications(req *pb.TransactionNotificationsRequest,
svr pb.WalletService_TransactionNotificationsServer) error { n := s.wallet.NtfnServer.TransactionNotifications()
defer n.Done() ctxDone := svr.Context().Done()
for {
select {
case v := <-n.C:
resp := pb.TransactionNotificationsResponse{
AttachedBlocks: marshalBlocks(v.AttachedBlocks),
DetachedBlocks: marshalHashes(v.DetachedBlocks),
UnminedTransactions: marshalTransactionDetails(v.UnminedTransactions),
UnminedTransactionHashes: marshalHashes(v.UnminedTransactionHashes),
}
err := svr.Send(&resp)
if err != nil {
return translateError(err)
} case <-ctxDone:
return nil
}
}
}

其他: 与http rpc服务的简单比较

通过代码实现对比就可以发现http rpc服务实现起来比较繁琐,各种客户端编解码需要自己处理,

不过从代码完善度来说,http接口明显更胜一筹,无论是注释还是测试case,包括api文档.

如果生产中使用,还是使用http rpc更好,如果熟悉代码的话,使用grpc更清晰.

btcWallet系列之一-grpc模块的更多相关文章

  1. openresty开发系列21--lua的模块

    openresty开发系列21--lua的模块 从lua5.1开始,Lua 加入了标准的模块管理机制,Lua 的模块是由变量.函数等已知元素组成的 table, 因此创建一个模块很简单,就是创建一个 ...

  2. 跟我一起学 Go 系列:gRPC 拦截器

    Go gRPC 学习系列: 跟我一起学Go系列:gRPC 入门必备 第一篇内容我们已经基本了解到 gRPC 如何使用 .对应的三种流模式.现在已经可以让服务端和客户端互相发送消息.本篇仍然讲解功能性的 ...

  3. JBOSS EAP 6 系列六 公共模块的jar配置到jboss的modules详细配置

    公司项目中遇到并要解决的问题 1:原则上除了自己写的代码之外,公共的jar不应该都在打包的时候打包到ear里面,这样的话包太大,也不符合的分层的逻辑,在jboss容器内部,每个ear的包重复jar都会 ...

  4. Python基础笔记系列十:模块

    本系列教程供个人学习笔记使用,如果您要浏览可能需要其它编程语言基础(如C语言),why?因为我写得烂啊,只有我自己看得懂!! 模块 #1.类比于java中的jar包,模块能让你能够有逻辑地组织你的Py ...

  5. [系列] Go gRPC 调试工具

    目录 概述 写一个 gRPC API grpcui 使用 go-gin-api 系列文章 概述 最近这段时间工作挺忙的,发现已经 3 周没更文了... 感谢你们还在,今天给大家分享一款 gRPC 的调 ...

  6. [开源]OSharpNS 步步为营系列 - 1. 业务模块设计

    什么是OSharp OSharpNS全称OSharp Framework with .NetStandard2.0,是一个基于.NetStandard2.0开发的一个.NetCore快速开发框架.这个 ...

  7. go微服务系列(四) - gRPC入门

    1. 前言 2. gRPC与Protobuf简介 3. 安装 4. 中间文件演示 4.1 编写中间文件 4.2 运行protoc命令编译成go中间文件 5. 创建gRPC服务端 5.1 新建Produ ...

  8. 嵌入Python系列 | 调用Python模块中无参数函数

    开发环境 Python版本:3.6.4 (32-bit) 编辑器:Visual Studio Code C++环境:Visual Studio 2013 需求说明 在用VS2013编写的Win32程序 ...

  9. ansible学习系列2-ansible常用模块使用

    1. 查看支持的模块 [root@localhost ~]# ansible-doc -l 这里我们看下ansible的支持的模块个数 [root@localhost ~]# ansible-doc ...

随机推荐

  1. oracle--查询速度慢

    查询速度慢的原因很多,常见如下几种: 1,  没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2, I/O吞吐量小,形成了瓶颈效应. 3, 没有创建计算列导致查询不优化. 4, ...

  2. 面试官:”准备用HashMap存1w条数据,构造时传10000会触发扩容吗?“

    通常在初始化 HashMap 时,初始容量都是根据业务来的,而不会是一个固定值,为此我们需要有一个特殊处理的方式,就是将预期的初始容量,再除以 HashMap 的装载因子,默认时就是除以 0.75. ...

  3. 动手学深度学习7-从零开始完成softmax分类

    获取和读取数据 初始化模型参数 实现softmax运算 定义模型 定义损失函数 计算分类准确率 训练模型 小结 import torch import torchvision import numpy ...

  4. Econ 493 A1 - Fall 2019

    Econ 493 A1 - Fall 2019Homework 4Assignment InformationThis assignment is due on Monday November 18 ...

  5. 利用mysql的LOAD DATA INFILE的功能读取客户端文件

    前言:今天在浏览某知论坛时,看到某大佬在渗透过程中使用伪造的MySQL服务端读取客户端文件,此大佬在利用过程中描述得不是很详细,作为小白的我看不懂啊,由此产生了此篇文章. 某大佬文章:https:// ...

  6. CSS属性相关知识

    Css选择器 选择器的权重 在css中,哪个选择器的权重高,就走谁的样式. 标签选择器的权重是 1 Class选择器的权重是10 Id选择器的权重是100 行间样式的权重是1000 带有关键字 !im ...

  7. ant-design-pro引用css

    ant-design-pro中默认只能引用less文件,引用了css文件也是无效的.所以需要在配置文件config.js中找到  cssLoaderOptions,在 getLocalIdent中加入 ...

  8. 转 Pytorch 教学资料

    本文收集了大量PyTorch项目(备查) 转自:https://blog.csdn.net/fuckliuwenl/article/details/80554182 目录: 入门系列教程 入门实例 图 ...

  9. 【01】Saltstack:从零开始 Saltstack

    写在前面的话 最近一直都在整理以前的乱七八糟的笔记,所以会有很多老旧的东西都会被拉出来重新遛遛.算是再度系统的进行学习. 关于 Saltstack 的一些概念 Saltstack 是基于 Python ...

  10. 【IntelliJ IDEA】idea部署服务到Tomcat的工作原理

    参考地址: https://blog.csdn.net/qq_41116058/article/details/81435084 为什么idea部署服务到tomcat时候,一定要修改Applicati ...