如何加速golang写业务的开发速度

如何加速golang写业务的开发速度

不要忌讳panic

golang写业务代码经常会被吐槽,写业务太慢了,其中最大的吐槽点就是,处理各种error太麻烦了。一个项目中,会有30%或者更多的是在处理error。

对于golang的error这个事情,golang的官方也说的很详细,建议函数返回error,并且让上层调用处理。

error和panic实际上就是以前写PHP业务的时候争论的使用errno还是exception的争论。实际上,后续在PHP世界里面,大家都倾向于会使用exception来做错误处理。不知道为何,在golang这个环境中,好像网络上更倾向于使用error的机制。

我思考,理由是处理的问题语境不同。在服务端工具世界里面,我们并不希望跑出来一个程序告诉我coredump了,而希望能准确告诉我为什么coredump,这样才是一个更健壮的工具。而在业务,特别是web业务中,我们更希望的是快速实现主流程功能,一些错误机制,我们希望能后续逐步加入。基本上,在web业务中,速度决定市场,从有一个idea,到具体的实现,整个过程的实现路径越短越好,但是到实现上线后,还可以有后续的迭代版本。有充足的时间来增加健壮性。但是这个第一步上线的速度应该是第一位的。

所以我觉得,如果大多数gopher都是觉得要立即处理error,我倒并不这么看。panic的意思更像是一种延后处理的TODO机制:“TODO:我不知道这个错误现在怎么处理,后续有时间再完善。”。这种逻辑,在业务代码中是非常必要的,而且会让写业务的思维更多的聚焦在正确的实现路径上。就像一棵树,先用最快的步骤实现了树干,再实现具体的枝枝叶叶。所以我强烈建议,在写业务代码的时候,对于不确定,或者基本不想实现的error,直接panic出去,在需要捕获的地方recover这个panic,这才是写业务代码,golang应该有的姿态。

即使golang2.0出来的error handler机制,也就在一个函数范围内做错误的handle,我认为还是不够,我们希望能在goroutine有一个统一的地方进行recover。

所以,不要忌讳panic,在写业务代码的时候。

让人发愁的slice & map

写过PHP的人自然就知道,php中的array是个多么强大的存在,如果你写过laravel,laravel中的collection更是神一般的存在。但是在golang中,slice和map是基础的结构,但是由于强类型的关系,对一个slice进行查找,都是有一定思维负担的工作。

这里说的思维负担,是说我的思维逻辑正处在“实现这个业务,我第一步需要从A数据库拿取a数据,从B数据库中拿取b数据,然后这两个数据进行合并去重”这种逻辑中抽离出来,思考如何进行“合并去重”。如果我们在写业务逻辑过程中,屡屡被这种“从array中获取一个最大值”、“从map中获取所有key”等等的逻辑,这个无意对于业务代码的速度是个不小的拖累。

所以基于这个逻辑,我使用的方法是自己实现了一个IArray和IMap的接口的实现。这两个接口的实现尽量能完成对于golang的Slice和Map的一些更为通用的操作,但是同时又能保持一定的扩展性。collection包

这个包所有的错误“类型不对”,“结构不对”等,均使用panic的方式来返回错误,这样,把这个包强制处理成可以链式模式处理的逻辑。增加代码可读性。

比如下面这个需求:

1 我需要从一个inoutLinks的Slice对象中,获取所有的LogicLinkID的的值,排重,得到一个数组。
2 我需要从一个inoutLinks的Slice对象中,获取其中最大的ID值。
// 需求一:
// 从junctionMap表批量根据junction_id批量获取
objArray := collection.NewObjArray(reflect.ValueOf(inoutLinks))
logicLinkIds := objArray.Column("LogicLinkID").Unique().ToString()

// 需求二:
// 获取最大的一个元素作为start
startId = objArray.Column("ID").Max().ToInt64()

上面的两个代码看起来确实清爽不少,并且意思很清晰。

当然也有副作用:

错误

一旦其中有错误,或者有“未想到的传入错误”,就会在链条的任何一个地方panic出错误,需要及时recover。

这个在上一节“不要忌讳panic”就思考过,我现在的代码是为了正常的业务逻辑,比如这里的其他异常的业务逻辑,比如“如果这个inoutLinks没有一个字段叫做ID”这种错误,就直接panic出错误了。

所以强烈建议在统一的一个地方进行recover,比如:

func() {
            defer func() {
                if err := recover(); err != nil{
                    log.Println("painc error:", err, string(debug.Stack()))
                }
            }()
      doSomeThings()
      ...
}

这里建议使用debug.Stack来将错误堆栈打印出来,以便于调试。

其实try...catch...的本质就是在于代码逻辑的中断和goto。在golang中,只有使用defer+recover才能进行这个处理。

这里还是要强调一下,我建议错误直接panic,是在web业务处理场景下。

性能

看到上面我的例子,一定有人会诟病,这里的Column方法,本质里面是不是使用了反射啊?那么性能是不是没有保证?

对于golang的反射,我的态度也是,并不要惧怕使用。golang是一个工具,它的出现本质是为了解决问题,而不是要求所有代码的性能。换而言之,如果我代码中大量使用反射,增加了我代码的灵活度,减少了开发周期,更早的占据了市场的份额,那么这个工具在这个事情上的使用,就是成功的。不要被性能所绑架。在准确评估市场,项目,访问量的情况下,大部分的业务项目应该来说,都可以牺牲一定的性能来满足业务的实现速度的。

所以,我这里的ObjectArray中的Column方法等,使用了反射等原理。

当然有人会质疑,我并不是所有的业务接口都不追求性能。当我需要追求性能的时候,难道让我重写一遍?

所以这里的Collection包使用的是接口和继承设计,换而言之,如果你对某个ObjectArray的性能确实需要非常追求的话,当你对某个Object获取ID字段的值是非常需要性能的话,你完全可以自定义一个继承ObjectArray的结构,并且覆盖实现其中的Column方法,直接写

func (arr *ObjArray) Column(key string) IArray {
  ...

  if key == "ID" {
    for _, obj := arr {
      result = append(result, obj.ID)
    }
    return result
  }
}

所以这个Collection包的基本思想是“提供加速golang业务代码的能力,同时提供足够扩展追求性能的能力”。

Collection包实现了两个通用数据类型,希望能在追求业务代码速度的场景中替换golang中的slice和map:


type IArray interface {
    // 放入一个元素到数组中,对所有Array生效
    Append(obj interface{})

    // 查找数据中是否包含,-1不包含,>=0 返回数组中元素下标,对所有Array生效
    Search(obj interface{}) int
    // 返回数组中对象的某个key组成的数组,仅对ObjectArray生效
    Column(key string) IArray
    // 过滤数组中重复的元素,仅对基础Array生效
    Unique() IArray

    // 将数组中对象某个key作为map的key,整个对象作为value,作为map返回,如果key有重复会进行覆盖,仅对ObjectArray生效
    KeyBy(key string) *Map

    // 数组中最大的元素,仅对基础Array生效
    Max() *Mix
    // 数组中最小的元素,仅对基础Array生效
    Min() *Mix

    // 获取数组片段,对所有Array生效
    Slice(start, end int) IArray
    // 获取某个下标,对所有Array生效
    Index(i int) *Mix
    // 获取数组长度,对所有Array生效
    Len() int
    // 判断是否包含某个元素,(并不进行定位),对基础Array生效
    Has(obj interface{}) bool
    // 将两个数组进行合并,参数的数据挂在当前数组中,返回当前数组,对所有Array生效
    Merge(arr IArray) IArray

    // 转化为golang原生的字符数组,仅对StrArray生效
    ToString() []string
    // 转化为golang原生的Int64数组,仅对Int64Array生效
    ToInt64() []int64
    // 转化为golang原生的Int数组,仅对IntArray生效
    ToInt() []int
}
type IMap interface {

    // 设置一个Map的key和value,如果key存在,则覆盖
    Set(key interface{}, value interface{})
    // 删除一个Map的key
    Remove(key interface{})
    // 根据key获取一个Map的value
    Get(key interface{}) *Mix
    // 获取一个Map的长度
    Len() int

    // 获取Map的所有key组成的集合
    Keys() IArray
    // 获取Map的所有value组成的集合
    Values() IArray
}

其中有些地方不确定的单个元素的类型,使用的是*Mix结构,这个结构提供一系列的ToXxx接口,使用方需要对这个Mix对象所代表的数据结构负责。

原文地址:https://www.cnblogs.com/yjf512/p/10368032.html

时间: 2024-08-11 07:36:27

如何加速golang写业务的开发速度的相关文章

golang写业务代码,用全局函数还是成员函数

在golang中,函数划分为全局函数和成员函数,在使用的时候,有种情况,会产生一些疑惑的,就是在写业务代码的时候,使用全局函数好像会比较方便,一般业务代码,都不会复用,都是针对特定的业务进行编程,要复用的代码都会封装为功能函数了.在写业务代码的时候,使用包+全局函数的划分方式,可以将业务代码写成单例,把receive也省略掉了,简单清晰. 使用包+全局函数的方式来划分模块,很多项目在写业务代码的时候,都是这样操作的,但这样会增加目录的层次,看起来会比较啰嗦. 因为使用包划分代码,业务代码使用的变

加速Java应用开发速度3——单元/集成测试+CI

大家可能对如下情景比较熟悉: 如果开发过SSH的web项目,启动服务器可能会比较慢,有的项目甚至需要1分多钟,甚至更多,这个启动时间的等待一般就浪费了: 在开发项目时,有些功能比较复杂,当时觉得思路特清晰,但是过了一段时间后,自己也忘了,完善功能时频繁出现bug,降低开发速度: 在维护项目时,不知道自己修改的对还是不对,是否存在隐患:维护速度降下来了: 如果开发一个很多人都使用的接口,典型的如用户系统,要保证比如升级时向下兼容: 在团队间协作时,有时候只定义好接口,对方还没有给实现,如何进行同步

加速Java应用开发速度2——加速项目调试启动速度

上一篇Spring/Hibernate提升速度的文章主要是通过一些技巧来提升启动速度,还是做不到如类的热部署/热替换.因此再写一篇关于热部署/热替换的文章.之前也有很多人介绍过这些知识,不过比较分散,我写此篇的目的是聚合它们.本文以HotSpot虚拟机为例. 首先让我们来看两个概念:热部署.热替换 热部署 即在容器运行过程中,重新加载类或重新加载整个项目.常见的解决方案就是使用自定义ClassLoader: 部分加载的示例:如JSP.Play框架: 重新加载整个项目的示例:如Tomcat.Jet

加速Java应用开发速度1——加速spring/hibernate应用调试时启动速度

在调试spring应用时,动辄几十秒,甚至有的应用上分钟的启动速度,会让整个调试速度慢下来了.等待时间让人抓狂.不知道大家是如何加速spring应用调试速度的,在此分享下我的一次加速过程.欢迎补充指正. 环境 配置: thinkpad t410 内存:4G内存 CPU:Intel P8700 双核2.53GHZ 系统:WIN XP 开发工具:Intellij IDEA 12.0.4 Maven + spring3.2.3 + hibernate4.2.2+Spring data jpa 1.3.

如何加快Vue项目的开发速度

如何加快Vue项目的开发速度 本文摘自奇舞周刊,侵权删. 现如今的开发,比如内部使用的管理平台这种项目大都时间比较仓促.实际上来说,在使用了webpack + vue 这一套来开发的话已经大大了提高了效率.但是对于我们的开发层面.还是有很多地方可以再次提高我们的项目开发效率,让我们更加专注于业务,毕竟时间就是生命.下面我们挨个来探讨. 巧用Webpack Webpack是实现我们前端项目工程化的基础,但其实她的用处远不仅仅如此,我们可以通过Webpack来帮我们做一些自动化的事情.首先我们要了解

CSDN日报20170413 ——《天天写业务代码的那些年,我们是如何成长过来的》

[程序人生]天天写业务代码的那些年,我们是如何成长过来的 作者:Phodal 比起写业务代码更不幸的是,主要工作是修 Bug , bug , buG , bUg. [Java 编程]Springboot实战:我们的第一款开源软件 作者:纯洁的微笑 在信息爆炸时代,如何避免持续性信息过剩,使自己变得专注而不是被纷繁的信息所累?每天会看到各种各样的新闻,各种新潮的技术层出不穷,如何筛选出自己所关心的? [物联网]Android Things:外设I/O接口-I2C 作者:1024工场 内部集成电路(

天天写业务代码,如何成为技术大牛

前序 在工作之余浏览公司的技术网站,看到了以下这篇文章,细细读来真心觉得不错,写得有价值很实在.于是想联系下作者,问一下是否可以转载.打开钉钉一搜,作者是资深技术专家,差不多就是技术总监级别啊,这也从侧面旁征了,以下的内容是有其亲身经历,切实体会的,而不是鸡汤口号之流.相较与作者的级别,自己确实惭愧汗颜,所以没好直接聊天询问而是在文章底下留言.在得到了作者的同意后将文章的内容贴到这里,作为分享也作为自己的鞭策和提醒.在这里谢谢我的大牛同事了^_^. ....................以下内

【Android 系统开发】 编译 Android 系统 u-boot 内核 源码 并烧写到 OK-6410A 开发板上

博客地址 : http://blog.csdn.net/shulianghan/article/details/40299813  本篇文章中用到的工具源码下载 : -- ok-6410A 附带的 Android 光盘 下载地址 : http://pan.baidu.com/share/link?shareid=3662728609&uk=2754759285 ; -- 光盘所含内容 : Android 引导 u-boot 源码, Android 内核 源码, Android 系统源码, 交叉编

嵌入式Linux如何加快物联网方案的开发速度

很明显,无论是对现有设备进行扩展还是从零开始设计新型设备或系统,使用 嵌入式Linux 都能够带来最理想的物联网生态系统构建速度.嵌入式Linux 与桌面 Linux 共享同一套源代码库,但其同时匹配不同的用户界面工具及其它高层组件. 下面,我们将共同探讨几类常见情况. 通常,极低功率应用( 例如传感器 ) 能够凭借一次性电池甚至自身供能机制运行数个月之久.这意味着任何 Linux 在其上运行时都将带来无法承载的负荷.其低功率特性意味着我们无法直接使用 IP 连接,必须配合互联网网关.网关能够通