如何加速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对象所代表的数据结构负责。
如何加速golang写业务的开发速度的更多相关文章
- golang写业务代码,用全局函数还是成员函数
在golang中,函数划分为全局函数和成员函数,在使用的时候,有种情况,会产生一些疑惑的,就是在写业务代码的时候,使用全局函数好像会比较方便,一般业务代码,都不会复用,都是针对特定的业务进行编程,要复 ...
- 如何加快Vue项目的开发速度
如何加快Vue项目的开发速度 本文摘自奇舞周刊,侵权删. 现如今的开发,比如内部使用的管理平台这种项目大都时间比较仓促.实际上来说,在使用了webpack + vue 这一套来开发的话已经大大了提高了 ...
- Python写业务逻辑的几个编码原则
作为一个写业务逻辑的boy,我需要专注的就是把业务逻辑写好.写业务逻辑并不复杂,就是把编程最基础的东西使用好,有变量.循环.流程控制.函数.数据库等. 但是写出的逻辑要通俗易懂.易于理解,避免炫技.晦 ...
- golang中的RPC开发-2
RPC简介 远程过程调用(Remote Procedure Call,RPC)是一个计算机通信协议 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程 如果 ...
- golang中的rpc开发
golang中实现RPC非常简单,官方提供了封装好的库,还有一些第三方的库 golang官方的net/rpc库使用encoding/gob进行编解码,支持tcp和http数据传输方式,由于其他语言不支 ...
- [原创].NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇)
原文:[原创].NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇) .NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇) 前言:接着上篇来. 系列文章链接: [ ...
- [原创].NET 业务框架开发实战之九 Mapping属性原理和验证规则的实现策略
原文:[原创].NET 业务框架开发实战之九 Mapping属性原理和验证规则的实现策略 .NET 业务框架开发实战之九 Mapping属性原理和验证规则的实现策略 前言:之前的讨论一直关注在怎么从D ...
- [原创].NET 业务框架开发实战之八 业务层Mapping的选择策略
原文:[原创].NET 业务框架开发实战之八 业务层Mapping的选择策略 .NET 业务框架开发实战之八 业务层Mapping的选择策略 前言:在上一篇文章中提到了mapping,感觉很像在重新实 ...
- [原创].NET 业务框架开发实战之七 业务层初步构想
原文:[原创].NET 业务框架开发实战之七 业务层初步构想 .NET 业务框架开发实战之七 业务层初步构想 前言:本篇主要讲述如何把DAL和BLL衔接起来. 本篇议题如下: 1. DAL ...
随机推荐
- 13.app后端为什么要用到消息队列
很多没有实际项目经验的小伙伴,对消息队列系统非常陌生,看着很多架构的介绍中,都提到消息队列.但是,不知道为什么要用消息队列?什么是消息队列?常见的消息队列产品有哪些? 通过阅读本文,帮你解开以上的疑惑 ...
- python抓取数据构建词云
1.词云图 词云图,也叫文字云,是对文本中出现频率较高的"关键词"予以视觉化的展现,词云图过滤掉大量的低频低质的文本信息,使得浏览者只要一眼扫过文本就可领略文本的主旨. 先看几个词 ...
- Tornado框架实现图形验证码功能
图形验证码是项目开发过程中经常遇到的一个功能,在很多语言中都有对应的不同形式的图形验证码功能的封装,python 中同样也有类似的封装操作,通过绘制生成一个指定的图形数据,让前端HTML页面通过链接获 ...
- http.go
) } if name != cfgName { continue } return val.FieldByNa ...
- Java 线程池(ThreadPoolExecutor)原理分析与使用
在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 使用线程池的好处 1.降低资源消耗 可以重复利用 ...
- Ubuntu 18 安装chrome
1.下载chrome文件 32位使用如下命令 wget https://dl.google.com/linux/direct/google-chrome-stable_current_i386.deb ...
- 如何在ST官网下载STM32固件库
1.首先要注册一个ST账号,下载的时候需要. 2.找到工具与软件 3.进去之后选"产品列表" 4.在产品列表里选STM32微控制器软件,直接点下图标号2,不要点左边的加号 5.进去 ...
- java泛型中使用的排序算法——归并排序及分析
一.引言 我们知道,java中泛型排序使用归并排序或TimSort.归并排序以O(NlogN)最坏时间运行,下面我们分析归并排序过程及分析证明时间复杂度:也会简述为什么java选择归并排序作为泛型的排 ...
- 闲聊js中的apply、call和arguments
JavaScript提供了apply和call两种调用方式来确定函数中的this的指向,在现实编码中,我确实 很少接触到这两个方法.但很无奈,很多面试题都要考这两种方法,我又没怎么用到,所以我们先来 ...
- LDA && NCA: 降维与度量学习
已迁移到我新博客,阅读体验更佳LDA && NCA: 降维与度量学习 代码实现放在我的github上:click me 一.Linear Discriminant Analysis(L ...