本教程以go-zero分布式go微服务框架为基础
在此框架之上去实现获取整个链路的请求时长
一、创建middleware
1、如何在go-zero创建局部中间件
这里稍微对新使用go-zero用户说明一下
如果需要再中间件中使用其他服务的rpc,就需要在svc中注册时,对中间件进行传值
1.1 svc注册如下
这里加上了其他rpc服务供middleware中调用
type ServiceContext struct {
AdminAuthRpc adminauthrpc.Adminauthrpc
AminRbacRpc adminrbacrpc.AdminrbacRpc
// 中间件
RbacMiddle rest.Middleware
}
func NewServiceContext(c config.Config) *ServiceContext {
redisCLient := c.Cache[0].NewRedis()
aminRbacRpc := adminrbacrpc.NewAdminrbacRpc(zrpc.MustNewClient(c.AdminRbac))
aminAuthRpc := adminauthrpc.NewAdminauthrpc(zrpc.MustNewClient(c.AdminAuthRpc))
return &ServiceContext{
Config: c,
RedisClient: redisCLient,
RbacMiddle: middleware.NewRbacMiddleMiddleware(redisCLient, aminRbacRpc, aminAuthRpc).Handle,
}
}
1.2 middleware中使用
package middleware
type RbacMiddleMiddleware struct {
RedisClient *redis.Redis
AminRbacRpc adminrbacrpc.AdminrbacRpc
AminAuthRpc adminauthrpc.Adminauthrpc
}
func NewRbacMiddleMiddleware(redisClient *redis.Redis, adminRbacRpc adminrbacrpc.AdminrbacRpc, aminAuthRpc adminauthrpc.Adminauthrpc) *RbacMiddleMiddleware { //, aminAuthRpc adminauthrpc.Adminauthrpc
return &RbacMiddleMiddleware{
RedisClient: redisClient,
AminRbacRpc: adminRbacRpc,
AminAuthRpc: aminAuthRpc,
}
}
func (m *RbacMiddleMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
startTime := timex.Now() // 计算耗时(开始时间)
// 这里是执行api的logic种具体的方法
next(w, r)
// 所有api-rpc请求后,返回结果前
duration := timex.Since(startTime) //整个链路执行时间
//获取所有请求参数
query := r.URL.Query()
jsonStr, _ := json.Marshal(query)
_, err = m.AminAuthRpc.AddSysLog(ctx, &adminauthrpc.SysLog{
Username: strconv.FormatInt(userId, 10),
Operation: r.Method,
Method: route,
Params: string(jsonStr),
Time: duration.String(),
Ip: GetRemoteClientIp(r),
})
if err != nil {
logx.Errorf(err.Error())
}
}
}
// GetRemoteClientIp 获取远程客户端IP
func GetRemoteClientIp(r *http.Request) string {
remoteIp := r.RemoteAddr
if ip := r.Header.Get("X-Real-IP"); ip != "" {
remoteIp = ip
} else if ip = r.Header.Get("X-Forwarded-For"); ip != "" {
remoteIp = ip
} else {
remoteIp, _, _ = net.SplitHostPort(remoteIp)
}
//本地ip
if remoteIp == "::1" {
remoteIp = "127.0.0.1"
}
return remoteIp
}
func handler(w http.ResponseWriter, r *http.Request) {
//获取所有请求参数
query := r.URL.Query()
jsonStr, err := json.Marshal(query)
if err != nil {
log.Fatalf("%v\n", err)
}
fmt.Fprintf(w, "get all param: "+string(jsonStr)+"\n")
//获取指定请求参数
names, ok := query["name"]
if !ok || len(names[0]) < 1 {
log.Println("Url Param 'name' is missing")
return
}
fmt.Fprintf(w, "name="+names[0]+"\n")
}
二、依赖middleware计算链路时间长度
1、如何算请求链路的完整时长计算
在middleware中有一个关联的方法
如下代码:
func (m *RbacMiddleMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 这里是执行api的logic具体的方法
next(w, r)
}
}
方法:next(w,r)
- 在next()方法前,为进入api的logic之前要执行的方法,这里我们还可以做rbac的权限校验
- 在next()方法后,为执行api的logic内所有方法后,在api接口返回前处理,这里我们可以计算出最后的用时
2、go计算方法耗时
startTime := timex.Now() // 计算耗时(开始时间)
duration := timex.Since(startTime) //整个链路执行时间
// 计算最后时长:
str :=duration.String()
// 输出结果如下:
871.498ms
3、go获取获取远程客户端IP
// GetRemoteClientIp 获取远程客户端IP
func GetRemoteClientIp(r *http.Request) string {
remoteIp := r.RemoteAddr
if ip := r.Header.Get("X-Real-IP"); ip != "" {
remoteIp = ip
} else if ip = r.Header.Get("X-Forwarded-For"); ip != "" {
remoteIp = ip
} else {
remoteIp, _, _ = net.SplitHostPort(remoteIp)
}
//本地ip
if remoteIp == "::1" {
remoteIp = "127.0.0.1"
}
return remoteIp
}
三、操作日志
1、通过middleware做切面保存所有请求日志
1.1、日志表
CREATE TABLE `sys_log` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名',
`operation` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户操作',
`method` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '请求方法',
`params` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '请求参数',
`time` varchar(255) COLLATE utf8mb4_general_ci NOT NULL COMMENT '执行时长(毫秒)',
`ip` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'IP地址',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=303 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='系统日志';
1.2、存入日志表
_, err = m.AminAuthRpc.AddSysLog(ctx, &adminauthrpc.SysLog{
Username: strconv.FormatInt(userId, 10),
Operation: r.Method,
Method: route,
Params: string(jsonStr),
Time: duration.String(),
Ip: GetRemoteClientIp(r),
})