继续记录一下最近几天折腾的微服务小demo
3 微服务间通信:Protobuf
微服务之间通过传输消息来通信。按照如下语法定义消息:
message example {
string myField = 1;
int64 anotherField = 2;
optional optionalField = 3;
// 接着定义……
}
在上面的例子中:
- 每一个Field都有唯一的编号,这个唯一指的是在一个message的定义里唯一。也就是说,定义两个message,example1和example2,这两个message里都可以有且仅有一个编号为1的字段。
- 每个字段定义的语法是:
<prefix> <type> <name> = <identifier>。具体有哪些type可以看ProtoBuf官网。 - 有些时候爬取得到的数据并不是每个字段都有(比如TuShare……),这时我们可以为这样的字段设置optional关键字。
有时我们需要自己定义一些message里的字段类型。其实刚才定义的example字段就可以看作一种类型。
联系一下具体需求。假设有一个“基金净值查询”功能,查询某一段时间的特定基金的净值。不难想到,后端返回给前端的数据会长这样:
{
"data": [
{
// 日期1的数据
},
{
// 日期2的数据
},
// ...
]
}
写后端逻辑的时候,就是将很多基金净值存储到一个List里,然后再JSON序列化一下。那么如果用ProtoBuf,怎么定义一个消息里的“List”呢?这时就需要用到repeated关键字了。
/**
基金净值
*/
message DomesticFundNavItem {
string tsCode = 1;
int64 annDate = 2;
int64 navDate = 3;
double unitNav = 4;
double accumNav = 5;
double accumDiv = 6;
double netAsset = 7;
double totalNetAsset = 8;
double adjNav = 9;
}
message DomesticFundNavsByTsCodeAndDateRangeResponse {
repeated DomesticFundNavItem items = 1;
}
在上面的例子中:
- 我们定义了“基金单日净值”这一个message。
- 我们定义了查询基金净值的Response message,注意微服务之间需要通过Protobuf序列化后的数据进行交流。
- 在查询基金净值的Response message里,我们创建了一个必填的重复字段items,编号为1,并且items这个字段里可以有很多个DomesticFundNavItem。
定义完消息,我们就需要定义服务的具体输入和输出了,这样服务的调用者才能知道到底有哪些服务可以调用,怎么调用,并且服务的提供者才能知道具体要实现哪些服务。
回想在单体项目中,我们一般创建一个Service子包定义服务接口,然后再创建一个ServiceImpl子包进行具体的服务实现。但在微服务架构中,我们不可能为每个服务全部重复声明一遍所有的服务接口是什么,这样维护起来过于麻烦。
为此,我们可以新建一个包,这里就叫它interface。然后在interface包的main目录下,创建proto子文件夹,在其中编写每个服务所需要的消息和具体实现。
service DomesticFundService {
rpc getDomesticFundInfoByTsCode(DomesticFundInfoByTsCodeRequest) returns (DomesticFundInfoByTsCodeResponse);
rpc getDomesticFundNavsByTsCodeAndDateRange(DomesticFundNavsByTsCodeAndDateRangeRequest) returns (DomesticFundNavsByTsCodeAndDateRangeResponse);
rpc searchFundInfo(DomesticFundSearchRequest) returns (DomesticFundSearchResponse);
}
上面的例子看起来可能让人头皮发麻,毕竟message的名称和服务的名称都太长了。简单来说:
- 一般用rpc就行。
- 服务定义方式为
<协议> <服务方法名称>(<入参>) returns (<出参>)
同时记得改pom.xml。具体怎么改放到下一篇文章吧,其实问问AI就行(