btcWallet系列之一-grpc模块
btcwallet对外服务
btcwallet除了像btcd对外提供rpc服务以外,还提供了grpc服务,同时grpc采用的是protobuf来实现.
这方便与不同语言进行交互,降低客户端代码编写量.
阅读这个模块,顺便了解一下proto的使用,更详细的细节问题.
Service分类
总共有三种Service,分别是VersionService,WalletService和WalletLoaderService,
从中可以看出
VersionService
只是提供版本查询服务,为什么会做成一个独立的服务,设计者是出于什么考虑的呢?
这里重点考察grpc服务的启动过程
- walletMain函数中传递wallet.Loader调用startRPCServers
- 配置grpc所需参数,包括证书
- 创建grpcServer
- 通过rpcserver.StartVersionService注册VersionService
- 通过pb.RegisterVersionServiceServer 注册versionServer
这里的RegisterVersionServiceServer是自动生成, - 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自动生成了客户端访问代码,
- 通过NewVersionServiceClient创建VersionServiceClient
- 通过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);
}
启动过程
- walletMain中等待钱包打开以后获取到钱包句柄,然后调用startWalletRPCServices
- 注意startWalletRPCServices传递进去三个参数,一个是钱包句柄,一个是grpc server,另一个是普通的http rpc server
- rpcserver.StartWalletService启动grpc WalletService
- legacyServer.RegisterWallet 注册http rpc服务
- pb.RegisterWalletServiceServer注册rpc.walletServer
- 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更清晰.
最新文章
- 从零开始学 Java - CentOS 安装 JDK
- [ACM_动态规划] 数字三角形(数塔)_递推_记忆化搜索
- union与union all的区别
- win7打开或关闭windows功能 提示“出现错误,并非所有的功能被更改”,管理员权限惹的祸
- python django 自定义 装饰器
- CodeForces 689A -Mike and Cellphone
- 32位linux(centos)下mongoDB的安装
- vs code调试console程序报错--preLaunchTask“build”
- linux云计算(keystone swift cinder配置)
- 6.2 PowerPC处理器如何处理MSI中断请求
- jQuery对于动态生成的元素绑定无效的问题~~
- 【Unity Shaders】Transparency —— 透明的cutoff shader
- PHP判断点是否在多边形区域内外
- Kotlin入门(32)网络接口访问
- Python Selenium系列学习
- AOP切面实现原理以及多个切面切同一个地方时的优先级讲解
- ERROR 000732:Output Geodatabase:Dataset Database Connections\Connection to localhost.sde\SDE.Dataset does not exist or is not supported
- centos6 pyenv和virtualenv搭建python虚拟环境
- Java 的抽象特性:抽象类与接口深度解析
- CentOS安装使用git
热门文章
- Qt 调试信息、打印信息、输出到文本
- ros脚本断点调试-编写过程可以把过程变量输出到log查看的方法
- [转载]3.1 UiPath鼠标操作元素的介绍和使用
- 第23课 优先选用make系列函数
- [bash-shell]构建WebAPI项目并且发布到本地
- ReentrantLock的实现原理及AQS和CAS
- DHT11温湿度传感器模块使用方法和驱动代码实现
- Java中接口和Sala中的特质的区别?
- maven的pom.xml用<;exclusion>;解决版本问题
- PIE SDK内存矢量数据的创建