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. Scala Symbol解释

    Symbol This class provides a simple way to get unique objects for equal strings. Since symbols are i ...

  2. Can't locate Math/Round.pm in @INC

    遭遇报错: Can't locate Math/Round.pm in @INC 经过亲自测试,下面的命令解决了我的问题. yum install perl-Math-Round 参考资料 ===== ...

  3. [转帖]分布式事务之解决方案(XA和2PC)

    分布式事务之解决方案(XA和2PC) https://zhuanlan.zhihu.com/p/93459200 3. 分布式事务解决方案之2PC(两阶段提交) 针对不同的分布式场景业界常见的解决方案 ...

  4. Centos 7.6搭建Skywalking6.5+es6.2.4

    软件包版本1.elasticsearch-6.2.4.tar.gz,下载地址:https://artifacts.elastic.co/downloads/elasticsearch/elastics ...

  5. mybatis 报Invalid bound statement(not found) 和 Property 'mapperLocations' was not specified or not matching resources found

    排除问题的步骤: 1.首先检查mapper文件和mapper接口的文件名是否相等. 2.pom.xml是否把xml排除了,这样写就会src/main/java下所有的Mybatis的xml文件都删除, ...

  6. Turbo码基本框架

    1. 基本原理 1.1 定义 1.2 分量码设计 2. 编码方法 2.1 交织器的设计 2.2 分量编码器 2.3 删余矩阵及复用 3. 译码方法和算法 3.1 迭代译码 3.2 MAP类算法 3.3 ...

  7. 如何解决RIP的问题

    如何解决RIP的问题 RIP的问题  优化或解决的方式  收敛慢,故障恢复时间长  触发更新 缺少对全局网络拓扑的了解 路由器基于拓扑信息,独立计算路由 存在选择次优路径的风险 将链路带宽作为选路参考 ...

  8. WPF 精修篇 WPF嵌入Winfrom控件

    原文:WPF 精修篇 WPF嵌入Winfrom控件 先增加DLL 支持 使用  WindowsFormsHost 来加载Forms的控件 引用命名空间 xmlns:forms="clr-na ...

  9. 在ASP.NET Web API 2中使用Owin基于Token令牌的身份验证

    基于令牌的身份验证 基于令牌的身份验证主要区别于以前常用的常用的基于cookie的身份验证,基于cookie的身份验证在B/S架构中使用比较多,但是在Web Api中因其特殊性,基于cookie的身份 ...

  10. Flutter实体与JSON解析的一种方法

    vs code作为编辑器 1. 首先,json对象与字符串的转换是使用json.encode和json.decode的,需要导入import 'dart:convert'; 这里主要的自然不是这个,而 ...