在本章节中,我们将之前介绍HTTP Client&Server的示例修改为GRPC微服务,并演示如何使用GoFrame框架开发一个简单的GRPC服务端和客户端,并且为GRPC微服务增加链路跟踪特性。OK, Let's 撸!

目录结构

Protocol

syntax = "proto3";

package user;

option go_package = "protobuf/user";

import "github.com/gogo/protobuf/gogoproto/gogo.proto";

// User service for tracing demo.

service User {

rpc Insert(InsertReq) returns (InsertRes) {}

rpc Query(QueryReq) returns (QueryRes) {}

rpc Delete(DeleteReq) returns (DeleteRes) {}

}

message InsertReq {

string Name = 1 [(gogoproto.moretags) = 'v:"required#Please input user name."'];

}

message InsertRes {

int32 Id = 1;

}

message QueryReq {

int32 Id = 1 [(gogoproto.moretags) = 'v:"min:1#User id is required for querying."'];

}

message QueryRes {

int32 Id = 1;

string Name = 2;

}

message DeleteReq {

int32 Id = 1 [(gogoproto.moretags) = 'v:"min:1#User id is required for deleting."'];

}

message DeleteRes {}

这里使用到了第三方的 github.com/gogo/protobuf 开源项目,用于注入自定义的Golang struct标签。这里不详细介绍,感兴趣的小伙伴可以自行了解。未来Katyusha微服务框架的官网文档也会做对这块详细介绍,包括GRPC工程目录、开发规范、开发工具、拦截器、注册发现、负载均衡等设计话题。

GRPC Server

package main

import (

"context"

"fmt"

"gftracing/examples/grpc+db+redis+log/protobuf/user"

"gftracing/tracing"

"github.com/gogf/gcache-adapter/adapter"

"github.com/gogf/gf/frame/g"

"github.com/gogf/katyusha/krpc"

"google.golang.org/grpc"

"net"

"time"

)

type server struct{}

const (

ServiceName = "tracing-grpc-server"

JaegerUdpEndpoint = "localhost:6831"

)

func main() {

flush, err := tracing.InitJaeger(ServiceName, JaegerUdpEndpoint)

if err != nil {

g.Log().Fatal(err)

}

defer flush()

g.DB().GetCache().SetAdapter(adapter.NewRedis(g.Redis()))

address := ":8000"

listen, err := net.Listen("tcp", address)

if err != nil {

g.Log().Fatalf("failed to listen: %v", err)

}

s := grpc.NewServer(

grpc.ChainUnaryInterceptor(

krpc.Server.UnaryError,

krpc.Server.UnaryRecover,

krpc.Server.UnaryTracing,

krpc.Server.UnaryValidate,

),

)

user.RegisterUserServer(s, &server{})

g.Log().Printf("grpc server starts listening on %s", address)

if err := s.Serve(listen); err != nil {

g.Log().Fatalf("failed to serve: %v", err)

}

}

// Insert is a route handler for inserting user info into dtabase.

func (s *server) Insert(ctx context.Context, req *user.InsertReq) (*user.InsertRes, error) {

res := user.InsertRes{}

result, err := g.Table("user").Ctx(ctx).Insert(g.Map{

"name": req.Name,

})

if err != nil {

return nil, err

}

id, _ := result.LastInsertId()

res.Id = int32(id)

return &res, nil

}

// Query is a route handler for querying user info. It firstly retrieves the info from redis,

// if there's nothing in the redis, it then does db select.

func (s *server) Query(ctx context.Context, req *user.QueryReq) (*user.QueryRes, error) {

res := user.QueryRes{}

err := g.Table("user").

Ctx(ctx).

Cache(5*time.Second, s.userCacheKey(req.Id)).

WherePri(req.Id).

Scan(&res)

if err != nil {

return nil, err

}

return &res, nil

}

// Delete is a route handler for deleting specified user info.

func (s *server) Delete(ctx context.Context, req *user.DeleteReq) (*user.DeleteRes, error) {

res := user.DeleteRes{}

_, err := g.Table("user").

Ctx(ctx).

Cache(-1, s.userCacheKey(req.Id)).

WherePri(req.Id).

Delete()

if err != nil {

return nil, err

}

return &res, nil

}

func (s *server) userCacheKey(id int32) string {

return fmt.Sprintf(`userInfo:%d`, id)

}

服务端代码简要说明:

1、首先,客户端也是需要通过initTracer方法初始化Jaeger。

2、可以看到,业务逻辑和之前HTTP示例项目完全一致,只是接入层修改为了GRPC协议。

3、我们仍然通过缓存适配器的方式注入Redis缓存:

g.DB().GetCache().SetAdapter(adapter.NewRedis(g.Redis()))

4、关键技术点来了,拦截器。这里我们添加了若干个拦截器,并且都由Katyusha微服务框架提供,由于这里使用的GRPC原生的服务端创建方式,因此只是复用了Katyusha的一些拦截器,所有拦截器便需要显式设置:

Unary Interceptor说明

krpc.Server.UnaryError错误处理,增加了对gerror组件的支持,支持业务方便地定义错误码。

krpc.Server.UnaryRecover异常捕获,防止业务处理逻辑抛一个panic整个进程就崩溃。

krpc.Server.UnaryTracing链路跟踪,通过该拦截器启用服务端的链路跟踪特性。

krpc.Server.UnaryValidate数据校验,通过拦截器的形式自动对输入对象调用gvalid组件执行数据校验,校验失败直接返回。如果对象struct定义中不包含校验标签时,该拦截器什么都不会做,快速返回,不影响性能。

5、这里也是通过Cache方法启用ORM的缓存特性,之前已经做过介绍,这里不再赘述。

GRPC Client

package main

import (

"context"

"gftracing/examples/grpc+db+redis+log/protobuf/user"

"gftracing/tracing"

"github.com/gogf/gf/frame/g"

"github.com/gogf/gf/net/gtrace"

"github.com/gogf/katyusha/krpc"

"google.golang.org/grpc"

)

const (

ServiceName = "tracing-grpc-client"

JaegerUdpEndpoint = "localhost:6831"

)

func main() {

flush, err := tracing.InitJaeger(ServiceName, JaegerUdpEndpoint)

if err != nil {

g.Log().Fatal(err)

}

defer flush()

StartRequests()

}

func StartRequests() {

ctx, span := gtrace.NewSpan(context.Background(), "StartRequests")

defer span.End()

grpcClientOptions := make([]grpc.DialOption, 0)

grpcClientOptions = append(

grpcClientOptions,

grpc.WithInsecure(),

grpc.WithBlock(),

grpc.WithChainUnaryInterceptor(

krpc.Client.UnaryError,

krpc.Client.UnaryTracing,

),

)

conn, err := grpc.Dial(":8000", grpcClientOptions...)

if err != nil {

g.Log().Fatalf("did not connect: %v", err)

}

defer conn.Close()

client := user.NewUserClient(conn)

// Baggage.

ctx = gtrace.SetBaggageValue(ctx, "uid", 100)

// Insert.

insertRes, err := client.Insert(ctx, &user.InsertReq{

Name: "john",

})

if err != nil {

g.Log().Ctx(ctx).Fatalf(`%+v`, err)

}

g.Log().Ctx(ctx).Println("insert:", insertRes.Id)

// Query.

queryRes, err := client.Query(ctx, &user.QueryReq{

Id: insertRes.Id,

})

if err != nil {

g.Log().Ctx(ctx).Printf(`%+v`, err)

return

}

g.Log().Ctx(ctx).Println("query:", queryRes)

// Delete.

_, err = client.Delete(ctx, &user.DeleteReq{

Id: insertRes.Id,

})

if err != nil {

g.Log().Ctx(ctx).Printf(`%+v`, err)

return

}

g.Log().Ctx(ctx).Println("delete:", insertRes.Id)

// Delete with error.

_, err = client.Delete(ctx, &user.DeleteReq{

Id: -1,

})

if err != nil {

g.Log().Ctx(ctx).Printf(`%+v`, err)

return

}

g.Log().Ctx(ctx).Println("delete:", -1)

}

客户端代码简要说明:

1、首先,客户端也是需要通过initTracer方法初始化Jaeger。

2、主要的也是UnaryError和UnaryTracing两个拦截器的使用,作用同上服务端介绍。

效果查看

启动服务端:

启动客户端:

这里客户端的执行最后报了一个错误,那是我们故意为之,目的是演示GRPC报错时的链路信息展示。我们打开jaeger查看一下链路跟踪信息:

可以看到本次请求涉及到两个服务:tracing-grpc-client和tracing-grpc-server,即客户端和服务端。整个请求链路涉及到17个span,客户端5个span,服务端12个span,并且产生了2个错误。我们点击查看详情:

我们点击查看一下最后接口调用错误的span情况:

看起来像个参数校验错误,点击查看Events/Logs中的请求参数:

查看Process中的Log信息可以看到,是由于传递的参数为-1,不满足校验规则,因此在数据校验的时候报错返回了。

GRPC Client

由于orm、redis、logging组件在之前的章节中已经介绍过链路信息,因此我们这里主要介绍GRPC Client&Server的链路信息。

Attributes

Attribute/Tag说明

net.peer.ip请求的目标IP。

net.peer.port请求的目标端口。

rpc.grpc.status_codeGRPC的内部状态码,0表示成功,非0表示失败。

rpc.serviceRPC的服务名称,注意这里是RPC而不是GRPC,因为这里是通用定义,客户端支持多种RPC通信协议,GRPC只是其中一种。

rpc.methodRPC的方法名称。

rpc.systemRPC协议类型,如:grpc, thrift等。

Events/Logs

Event/Log说明

grpc.metadata.outgoingGRPC客户端请求提交的Metadata信息,可能会比较大。

grpc.request.baggageGRPC客户端请求提交的Baggage信息,用于服务间链路信息传递。

grpc.request.messageGRPC客户端请求提交的Message数据,可能会比较大,最大只记录512KB,如果超过该大小则忽略。仅对Unary请求类型有效。

grpc.response.messageGRPC客户端请求接收返回的的Message信息,可能会比较大。仅对Unary请求类型有效。

GRPC Server

Attributes

GRPC Server端的Attributes含义同GRPC Client,在同一请求中,打印的数据基本一致。

Events

GRPC Server端的Events与GRPC Client不同的是,在同一请求中,服务端接收到的metadata为grpc.metadata.incoming,其他同GRPC Client。

gogoclient java_链路跟踪-GRPC请求 - GoFrame官网 - 类似PHP-Laravel, Java-SpringBoot的Go企业级开发框架...相关推荐

  1. php dao类设计,DAO数据访问对象设计 - GoFrame官网 - 类似PHP-Laravel, Java-SpringBoot的Go企业级开发框架...

    关于DAO数据访问对象设计其实是关于GoFrame框架工程化实践中比较重要一块设计. DAO设计结合GoFrame的ORM组件性能和易用性都很强,可以极大提高开发和维护效率.看完本章节内容之后,小伙伴 ...

  2. 开发企业官网就用这个基于SpringBoot的CMS系统,真香

    前言 推荐这个项目是因为使用手册部署手册非常完善,项目也有开发教程视频对小白非常贴心,接私活可以直接拿去二开非常舒服. 开源说明 系统100%开源 模块化开发模式,铭飞所开发的模块都发布到了maven ...

  3. 几个比较好的IT站和开发库官网

    几个比较好的IT站和开发库官网 1.IT技术.项目类网站 (1)首推CodeProject,一个国外的IT网站,官网地址为:http://www.codeproject.com,这个网站为程序开发者提 ...

  4. html网页表格相同行自动合并,威尼斯人官网-官网首页

    1.jar包 添加jar包依赖,注意和Jmeter的版本相同: ApacheJMeter_core.jar,ApacheJMeter_java.jar 2.写Java请求 先继承 AbstractJa ...

  5. 接入腾讯广告 -按照官网视频步步来

    1.在官网注册一个账号 https://developers.e.qq.com/oauth/authorize?client_id=123456&redirect_uri=https%3a%2 ...

  6. 【java基础】java的官网和jdk安装和下载

    本篇文章主要讲解java初级知识,java的jdk安装和下载. 作者:任聪聪 java 官网介绍 java只有一个唯一官网,下载jdk千万不要通过资源站下载,很有可能会存在隐患. 官网地址:https ...

  7. SpringCloud学习笔记(十二)Sleuth 分布式请求链路跟踪

    目录 一.Sleuth介绍 1.为什么会出现这个技术?需要解决哪些问题? 2.是什么 二.搭建链路监控步骤 1.zipkin搭建安装 1)下载 2)运行jar 3)运行控制台 4)术语 2.服务提供者 ...

  8. SkyWalking java单体和dubbo微服务请求链路跟踪,SkyWalking钉钉告警

    一. 基于docker-compose或二进制部署skywalking  skywalking-ui: 前端服务,端口号8080.  skywalking-oap(Observability An ...

  9. 使用OpenTelemetry搭配Zipkin构建NetCore分布式链路跟踪 | WebAPI + gRPC

    OpenTelemetry介绍 OpenTelemetry是一组标准和工具的集合,旨在管理观测类数据,如 trace.metrics.logs 等. 通过标准化不同的应用程序和框架如何收集和发出可观察 ...

最新文章

  1. 常用软件架构模式分类
  2. 名词解释CPC、CPM、CPA.......[来源于网络]
  3. v-pre的指令|| v-cloak 的指令
  4. 2招按摩轻松解除黑眼圈 - 生活至上,美容至尚!
  5. python数据库有什么特点_python进阶十——mysql初识
  6. python 单例模式内存泄露_彻底搞懂Java内存泄露
  7. 网易云信为你的互联网应用快速接入直播功能
  8. IDEA导入多层父子maven项目
  9. CygWin / 获取 root 权限的方法
  10. 线程本地ThreadLocal的介绍与使用!
  11. 一些奇葩的元素节点object,video
  12. 关于springMVC传参问题
  13. grafana 安装- 曲线图展示每秒新增数据量
  14. php正则表达式 什么,php正则表达式是什么?(代码实例)
  15. 如何自学python-作为一个Python自学者,怎样学好Python?
  16. 最新PHP编程零基础入门项目实战教程(完整)
  17. PADA: Example-based Prompt Learning for on-the-fly Adaptation to Unseen Domains
  18. arctanx麦克劳林公式推导过程_三角函数的求导过程
  19. tpc ds mysql_TPC-DS 生成数据
  20. linux 音频文件切割_Linux 对音频万能处理的命令——SOX

热门文章

  1. JSP房屋租赁系统设计与实现答辩PPT免费下载
  2. 微信语音麦克风静音_智能语音专题(二):语音信号处理
  3. qnap raid5升级raid6_QNAP TS-419P组建RAID5后重建Transmission!
  4. erdas遥感图像几何校正_【答疑】为什么要进行遥感图像处理?
  5. 后台cs代码控制html控件,cshtml中正则表达式使用后台代码
  6. c语言实现全排列并存储,C语言实现全排列和回溯法总结
  7. aptio2018设置u盘启动_华硕笔记本重装系统时,BIOS内无法设置u盘启动怎么办?
  8. 启动mysql55命令_mysql服务的启动和停止登陆mysql增加新用命令和方法实例教程
  9. QTableView的表格项中加入图标的方法
  10. Qt Creator 设置默认编码格式为 UTF-8