广告
返回顶部
首页 > 资讯 > 后端开发 > GO >一文教你如何封装安全的go
  • 759
分享到

一文教你如何封装安全的go

2024-04-02 19:04:59 759人浏览 薄情痞子
摘要

目录前言封装SafeGoSafeGoAndWait实现说明首先是接口设计方面其次是日志兼容hade最后是打印panic的trace记录总结前言 在业务代码开发过程中,我们会有很大概率

前言

在业务代码开发过程中,我们会有很大概率使用go语言的goroutine来开启一个新的goroutine执行另外一段业务,或者开启多个goroutine来并行执行多个业务逻辑。所以我为hade框架增加了两个方法goroutine.SafeGo 和 goroutine.SafeGoAndWait。

封装

SafeGo

SafeGo 这个函数,提供了一种goroutine安全的函数调用方式。主要适用于业务中需要进行开启异步goroutine业务逻辑调用的场景。

// SafeGo 进行安全的goroutine调用
// 第一个参数是context接口,如果还实现了Container接口,且绑定了日志服务,则使用日志服务
// 第二个参数是匿名函数handler, 进行最终的业务逻辑
// SafeGo 函数并不会返回error,panic都会进入hade的日志服务
func SafeGo(ctx context.Context, handler func())

调用方式参照如下的单元测试用例:

func TestSafeGo(t *testing.T) {
    container := tests.InitBaseContainer()
    container.Bind(&log.HadeTestingLogProvider{})

    ctx, _ := gin.CreateTestContext(Httptest.NewRecorder())
    goroutine.SafeGo(ctx, func() {
        time.Sleep(1 * time.Second)
        return
    })
    t.Log("safe go main start")
    time.Sleep(2 * time.Second)
    t.Log("safe go main end")

    goroutine.SafeGo(ctx, func() {
        time.Sleep(1 * time.Second)
        panic("safe go test panic")
    })
    t.Log("safe go2 main start")
    time.Sleep(2 * time.Second)
    t.Log("safe go2 main end")

}

SafeGoAndWait

SafeGoAndWait 这个函数,提供安全的多并发调用方式。该函数等待所有函数都结束后才返回。

// SafeGoAndWait 进行并发安全并行调用
// 第一个参数是context接口,如果还实现了Container接口,且绑定了日志服务,则使用日志服务
// 第二个参数是匿名函数handlers数组, 进行最终的业务逻辑
// 返回handlers中任何一个错误(如果handlers中有业务逻辑返回错误)
func SafeGoAndWait(ctx context.Context, handlers ...func() error) error

调用方式参照如下的单元测试用例:

func TestSafeGoAndWait(t *testing.T) {
    container := tests.InitBaseContainer()
    container.Bind(&log.HadeTestingLogProvider{})

    errStr := "safe go test error"
    t.Log("safe go and wait start", time.Now().String())
    ctx, _ := gin.CreateTestContext(httptest.NewRecorder())

    err := goroutine.SafeGoAndWait(ctx, func() error {
        time.Sleep(1 * time.Second)
        return errors.New(errStr)
    }, func() error {
        time.Sleep(2 * time.Second)
        return nil
    }, func() error {
        time.Sleep(3 * time.Second)
        return nil
    })
    t.Log("safe go and wait end", time.Now().String())

    if err == nil {
        t.Error("err not be nil")
    } else if err.Error() != errStr {
        t.Error("err content not same")
    }

    // panic error
    err = goroutine.SafeGoAndWait(ctx, func() error {
        time.Sleep(1 * time.Second)
        return errors.New(errStr)
    }, func() error {
        time.Sleep(2 * time.Second)
        panic("test2")
    }, func() error {
        time.Sleep(3 * time.Second)
        return nil
    })
    if err == nil {
        t.Error("err not be nil")
    } else if err.Error() != errStr {
        t.Error("err content not same")
    }
}

实现说明

实现方面,有几个难点记录下。

首先是接口设计方面

可以看到handler函数在两个接口中是不一样的。在SafeGo接口中,handler定义为func() 而在SafeGoAndWait中,定义为func() error

两者的区别就在于SafeGo这个接口是没有能力处理error的,因为它go出去一个goroutine就直接进行接下来的操作了。而SafeGoAndWait是必须等到所有的请求结束,所以它是有能力接收到error的。

所以SafeGo的handler没有必要设置error返回值,而SafeGoAndWait是可以设置error的。

其次是日志兼容hade

如果出现了panic,如何将panic的日志打印出来。

整个框架我们并不希望有任何的全局变量,包括全局的Log,所以我这里做了一个兼容逻辑。

如果只是传递一个context,我们就使用官方的log包进行打印。

如果传递的是一个即实现了context,又实现了container接口的结构,我们就从container中获取日志服务,来进行日志打印。这样框架的所有日志就能统一在日志打印里面。

				if logger != nil {
						logger.Error(ctx, "safe go handler panic", map[string]interface{}{
							"stack": string(buf),
							"err":   e,
						})
				} else {
						log.Printf("panic\t%v\t%s", e, buf)
				}

由于我们修改了gin的context,让它支持了我们的container容器结构,所以我们可以直接将gin.Context传递进来。具体使用起来就像这样了:

// DemoGoroutine goroutine 的使用示例
func (api *DemoApi) DemoGoroutine(c *gin.Context) {
    logger := c.MustMakeLog()
    logger.Info(c, "request start", nil)

    // 初始化一个ORM.DB
    gormService := c.MustMake(contract.ORMKey).(contract.ORMService)
    db, err := gormService.GetDB(orm.WithConfigPath("database.default"))
    if err != nil {
        logger.Error(c, err.Error(), nil)
        c.AbortWithError(50001, err)
        return
    }
    db.WithContext(c)

    err = goroutine.SafeGoAndWait(c, func() error {
        // 查询一条数据
        queryUser := &User{ID: 1}

        err = db.First(queryUser).Error
        logger.Info(c, "query user1", map[string]interface{}{
            "err":  err,
            "name": queryUser.Name,
        })
        return err
    }, func() error {
        // 查询一条数据
        queryUser := &User{ID: 2}

        err = db.First(queryUser).Error
        logger.Info(c, "query user2", map[string]interface{}{
            "err":  err,
            "name": queryUser.Name,
        })
        return err
    })

    if err != nil {
        c.AbortWithError(50001, err)
        return
    }
    c.JSON(200, "ok")
}

最后是打印panic的trace记录

官方的panic其实打印的是所有goroutine的堆栈信息。但是这里我们希望打印的是出panic的那个堆栈信息。所以我们会使用

debug.Stack()

来打印出问题的goroutine的堆栈信息。

为了打印美观,这里将换行符统一替换为\n 来进行展示。

具体的实现代码可以参考GitHub地址:https://github.com/gohade/hade/blob/main/framework/util/goroutine/goroutine.go

说明文档:https://github.com/gohade/hade/blob/main/docs/guide/util.md

总结

为hade封装了两个SafeGo方法。特别是第二个SafeGoAndWait,在实际工作中确实是非常有用的。

到此这篇关于如何封装安全的go的文章就介绍到这了,更多相关封装安全的go内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

您可能感兴趣的文档:

--结束END--

本文标题: 一文教你如何封装安全的go

本文链接: https://www.lsjlt.com/news/140128.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 一文教你如何封装安全的go
    目录前言封装SafeGoSafeGoAndWait实现说明首先是接口设计方面其次是日志兼容hade最后是打印panic的trace记录总结前言 在业务代码开发过程中,我们会有很大概率...
    99+
    2022-11-13
  • 一文教你如何正确封装ECharts
    本篇文章给大家带来了关于ECharts的相关知识,其中主要跟大家聊一聊怎么封装ECharts,感兴趣的朋友下面一起来看一下吧,希望对大家有帮助。文章的开头总是很难水的,就不多说了本文涉及 : TypeScript、Vue3、 echarts...
    99+
    2023-05-14
    前端 ECharts Vue.js
  • 教你win8一键装机如何安装
    如今也有部分网友在使用了win10系统后想换行win8系统,但是又不清楚怎么装回去,下面就和大家分享一个方法吧。1、我们可以使用小白一键重装系统来帮助我们安装win8即可。打开浏览器,搜索小白官网,进入官网点击下载小白三步装机版软件,选择w...
    99+
    2023-07-15
  • 一步步教你如何安装Arch Linux
    这篇文章主要介绍了一步步教你如何安装Arch Linux,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Arch Linux 是一个 x86-64 通用发行版,它流行...
    99+
    2023-06-16
  • 一文教你如何快速学会Go的struct数据类型
    目录什么是结构体创建结构体创建匿名结构体访问结构体字段结构体零值结构体指针匿名字段结构体嵌套字段升级结构体导出结构体比较什么是结构体 结构是表示字段集合的用户定义类型。它可以用于将数...
    99+
    2023-03-07
    Go struct数据类型 Go struct
  • 一文教你如何优雅的控制全局loading的显示
    在很多后台管理系统中,发送请求的时候,需要打开一个loading,收到响应后,需要关闭这个loading,对于这种通用的逻辑,我一般是在axios拦截器中做这种处理,因为不是每个请求...
    99+
    2022-11-12
  • 一文教你如何优雅的使用WebSocket
    目录简介特性使用场景具体实现一、首先是服务端代码,这里我用的是nodejs。二、然后是客户端代码总结简介 websocket是基于TCP的一种双向通信协议。在此之前,一直是采用轮询的...
    99+
    2022-11-13
  • 一文教你如何使用原生的Feign
    目录什么是Feign为什么使用Feign为什么要使用HTTP client为什么要使用Feign如何使用Feign项目环境说明引入依赖入门例子个性化配置更换为Spring的注解自定义...
    99+
    2022-11-12
  • Go语言对象文件存储:如何保护你的数据安全?
    随着互联网的发展,数据安全问题越来越受到关注。在现代化的应用程序中,数据存储是一个必不可少的部分。因此,保护数据的安全性变得越来越重要。在本文中,我们将介绍如何使用Go语言的对象文件存储来保护你的数据安全。 什么是对象文件存储? 对象...
    99+
    2023-09-30
    对象 文件 存储
  • 一文教你如何快速学会Go的切片和数组数据类型
    目录什么是数组声明数组数组是值类型数组的长度迭代数组多维数组切片创建切片修改切片切片长度和容量使用make创建切片切片追加切片作为函数参数多维切片什么是数组 数组是属于同一类型的元素...
    99+
    2023-03-07
    Go切片 数组 Go切片 Go 数组
  • 教你win7如何释放C盘安全删除休眠文件的方法
      随着Win 7系统的问世,大部分用户已经把系统换成了Win 7,但很多朋友在使用时都发现了一个问题。电脑刚被买回来时,我们往往都把C盘(系统盘)预留50G左右的空间,而且什么东西都从不装到C盘,过了一段时间,发现C盘...
    99+
    2023-06-04
    释放C盘 删除休眠文件 文件 C盘 休眠 方法
  • 一文带你安装opencv和常用库(保姆级教程少走80%的弯路)
    0.导语 离上一个opencv安装保姆级教程发布已经过去了快一年了,这一年来我收到了来自很多C友的鼓励。打算学opencv的各位朋友都会在安装opencv和各种库过程中浪费掉60%的时间和精力;博主在...
    99+
    2023-08-31
    opencv python pycharm
  • 一文教你如何实现localStorage的过期机制
    目录前言cookie过期机制expires和max-age的区别localStorage 数据过期需求分析:动手实践参考文章总结前言 我们都知道cookie存储的数据是可以添加过期时...
    99+
    2022-11-13
  • 一文教你MySQL如何优化无索引的join
    目录前言遍历循环查询join 查询join buffer (Block Nested Loop)附:mysql  join查询没有走索引的原因总结前言 MySQL Join...
    99+
    2022-11-13
  • ASP框架教程:如何让你的网站更安全?
    ASP框架是一种广泛使用的Web开发框架,它可以帮助开发人员快速构建动态网站和Web应用程序。虽然ASP框架提供了很多便利,但它也存在一些安全风险。本文将介绍一些ASP框架的安全问题,以及如何通过代码演示来解决这些问题,以确保你的网站更加...
    99+
    2023-08-05
    框架 教程 学习笔记
  • 教你如何安装iso格式的win7系统镜像
    win7系统是微软经典的操作系统,也是目前比较稳定的操作系统之一,很多网友现在还在下载安装win7系统使用。最近有网友说自己下载了win7系统iso镜像,不知道如何安装win7系统镜像,想请教小编安装win7系统iso镜像文件的方法。那今天...
    99+
    2023-07-10
  • 一文教你如何优雅处理Golang中的异常
    我们在使用Golang时,不可避免会遇到异常情况的处理,与Java、Python等语言不同的是,Go中并没有try...catch...这样的语句块,我们知道在Java中使用try....
    99+
    2022-11-11
  • 一文教会你如何在npm上传自己的包
    目录1、官网注册2、查询是否存在包名3、初始化项目4、切换自己的npm源5、添加用户6、查看是否成功7、上传自己的包8、下载自己的包9、补充总结1、官网注册 首先要在npm官网注册自...
    99+
    2023-03-19
    npm上传包 npm 包 npm 包上传
  • 一文教会你如何在Linux系统中使用Docker安装Mysql 5.7版本 【详细过程+图解】
    文章目录 1、安装指定版本的mysql2、创建实例并且启动3、可视化工具Navicat连接测试4、docker容器文件挂载与端口映射5、进入已运行mysql容器中6、查看安装的位置7、Mysq...
    99+
    2023-10-03
    docker linux mysql
  • 一文教你Python如何创建属于自己的IP池
    目录开发环境模块使用如果安装python第三方模块如何配置pycharm里面的python解释器pycharm如何安装插件代理ip结构思路一. 数据来源分析二. 代码实现步骤代码开发...
    99+
    2022-11-10
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作