0%

缘起

今年是Go语言的10年生日,准确的说应该是Go开放于2009年,当时虽然公开了,但是其实真正的可用性并不是很好。甚至Windows的支持都没有。当时我的主要环境还是在Windows平台,因此没有太关注Go语言。在2012年Go语言正式发布1.0版本之后,我当时工作环境转移到了Linux/macOS/iOS相关的产品上,因此重新学习了一下Go语言。

阅读全文 »

在升级到Python 3.5+版本之后,最大的项目管理优化来自于PEP-484 Type Hint的引入。借助Type Hint,我们可以进一步提升Python代码的类型标注,保障在重构过程中避免出现一些低级失误。

我们可以通过高版本Python新加的新语法启用这项特性,然后通过mypy等工具检查:

1
2
def greeting(name: str) -> str:
return 'Hello ' + name

然而,在实际实践过程中,也往往存在一些问题,这些问题来自于很多方面:

阅读全文 »

今天早些时候,golang/x/exp中默默的更新了一个名曰xerrors的包,这个包和同样处于golang/x/exp下的另一个名叫errors的包名字十分相似,就连介绍也都一致:

1
2
3
 Package errors implements functions to manipulate errors.

This package implements the Go 2 draft designs for error inspection and printing

从目前的情况来看,基本上错误的处理形式基本已经定型,处理方式则是类似于之前的另一个github.com/pkg/errors包,但是具体细节不尽相同。

如何处理error?

在之前介绍文章中提到过github.com/pkg/errors包的设计思路,那么在Go团队的实现中,这种思路也得到了继承。先从一个小例子开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"fmt"

"golang.org/x/exp/xerrors"
)

func raiseError() error {
return xerrors.New("a new error")
}

func main() {
err := xerrors.Errorf("raiseError: %w", raiseError())
fmt.Println(err)
}

输出结果:

1
raiseError: a new error

看起来非常类似于之前github.com/pkg/errors的显示内容。而其中xerrors.Errorf则充当了之前errors.Wrap的功能。 其中值得一提的是%w,这个用于包装错误,后续验证错误中也会用到其中的值。

同时,这个包中也包含了几个非常有用的辅助函数,分别是:验证错误类型方法Is、错误类型转换方法As、错误关系链解除方法Opaque和提取内层错误方法Unwrap。我们可以用一个简单的演示来说明这种关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
var (
ErrBase = xerrors.New("a new error")
)

func main() {
err := xerrors.Errorf("raiseError: %w", ErrBase)
fmt.Println(ErrBase == ErrBase) // 地址相同
fmt.Println(err == ErrBase) // 基于ErrBase包装之后不同
fmt.Println(xerrors.Is(err, ErrBase)) // 验证是否为基于ErrBase
fmt.Println(xerrors.Opaque(err) == err) // 解除关系链之后不为相同地址
fmt.Println(xerrors.Is(xerrors.Opaque(err), ErrBase)) // 解除关系链之后无法确定关系
fmt.Println(xerrors.Unwrap(err) == ErrBase) // 获取内层错误为原错误,地址相同
}

输出结果为:

1
2
3
4
5
6
true
false
true
false
false
true

即便是在包裹多层之后,错误类型也能通过Is方法正确识别:

1
2
3
4
5
6
7
func main() {
err := xerrors.Errorf("raiseError: %w", ErrBase)
err2 := xerrors.Errorf("wrap#01: %w", err)
err3 := xerrors.Errorf("wrap#02: %w", err2)
fmt.Println(xerrors.Is(err, ErrBase))
fmt.Println(xerrors.Is(err3, ErrBase)) // 能够正确识别关系,打印为true
}

如果需要打印详细的调用链路,则可以通过标准库fmt相关功能进行输出,比如Printf或者Sprintf等。

1
2
3
4
5
6
func main() {
err := xerrors.Errorf("raiseError: %w", ErrBase)
err2 := xerrors.Errorf("wrap#01: %w", err)
err3 := xerrors.Errorf("wrap#02: %w", err2)
fmt.Printf("%+v\n", err3)
}

输出链路如下:

1
2
3
4
5
6
7
8
9
10
11
12
wrap#02:
main.main
/.../error.go:16
- wrap#01:
main.main
/.../error.go:15
- raiseError:
main.main
/.../error.go:14
- a new error:
main.init
/.../error.go:10

但是注意,使用%w进行包装时,仅能单次包装单个错误,比如下面这种两个错误同时包装时,是会无法识别的:

1
2
3
4
5
6
7
8
9
10
var (
ErrBase = xerrors.New("a new error")
ErrBase2 = xerrors.New("another new error")
)

func main() {
err := xerrors.Errorf("raiseError: %w, %w", ErrBase2, ErrBase2)
fmt.Println(xerrors.Is(err, ErrBase))
fmt.Println(xerrors.Is(err, ErrBase2))
}

落地使用

在具体项目实现中,如对外提供的库文件中,我建议可以参考其他语言的实现方式,定义一个特殊的基础错误类型ErrBase,所有其他错误类型均由此类型派生,则后续外部在使用过程中,只需通过xerros.Is判定错误类型,则可以快速判定错误是否来自于本包,并且可以借助此功能统一单独处理来自于此第三方包的异常。

如果你有其他的建议和疑问,也欢迎在留言区中留言讨论。另外,根据实际实现代码来看,其中还有一些边缘情况未考虑或者未做处理,由于是试验性库,本库在使用过程中需自行承担所带来的风险。

没想到今年突然有动力还能写一篇。可能来自于两个方面,有些东西需要整理,另外有一个事情我是感觉到很诧异的。今天技术圈最火的事情当属AntDesign彩蛋事件了(以下简称AntD),很不幸的是,本司也是本次受害者之一。

考虑到AntD是一个使用十分广泛并且关注度非常高的国内项目,并且有阿里支付宝的背书效应,这一次我想影响的会在两个方面:一个是阿里开源本身的信用,一个是开源项目在国内的推广。当然,我们确实在引入AntD方面确实太过草率:我其实关注AntD了大概一年时间,考虑到issue情况和项目开发活跃度情况,要求团队引入此框架希望可以提升开发效率,没想到相关的项目1-2个月就出现了这种事情,真是令人实在痛心。

这件事情也暴露出来,我们以前在使用开源项目时,太过信任

另外,作为最后的一个建议,我们希望如果大家以后开源项目,如果做不到,请不要说自己是企业级项目,因为一般也就是放在自己企业里面用,这跟企业级项目是两个概念。至少我们具体到AntD这个项目上,这个项目无论代码开发和管控上,都是算不上的。

最近我们在把线上系统升级至Go 1.10版本时发现,在我们实现的某些接口中,出现了客户反馈调用失败提示参数缺失的情况。这种情况我们在测试过程中未能复线,后来经过了解,发现了故障原因为Go 1.10版本升级过程中更改了部分程序代码与验证逻辑导致的。

阅读全文 »

今天正好看到一篇关于敏感信息过滤的文章,这算做一个interface实际应用的一些举例和应用。

例子中介绍了一种比较常见的使用场景:使用JSON保存数据时的对诸如用户密码等信息进行保护时候应该做的事情。作者以使用JSON格式保存用户账户和密码为例,讲解了使用json.Unmarshaler接口类型过滤敏感信息。

阅读全文 »

我们从 Consul 0.6.x 版本开始使用,中间也遇到的一些各种各样的问题,比较常见的操作问题就是 consul 的升级问题(比如解决 BUG,早期 Consul 的 BUG 也遇到了好几个)。

平滑升级时,我们常见的方式一般为替换 consul 可执行文件,然后执行 Graceful 重启

常见 Consul 的 Graceful leave 的方法有以下两种:

  1. 发送 SIGINT 信号至 Consul;
  2. 连接要升级的 consul,使用命令 consul leave 发送离开命令;

这两种方式都会让该节点主动退出集群并结束进程,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[INFO] agent: Caught signal:  terminated
[INFO] agent: Graceful shutdown disabled. Exiting
[INFO] agent: Requesting shutdown
[INFO] consul: shutting down server
[WARN] serf: Shutdown without a Leave
[ERR] agent: Coordinate update error: No cluster leader
[ERR] agent: failed to sync remote state: No cluster leader
[WARN] serf: Shutdown without a Leave
[INFO] manager: shutting down
[INFO] agent: consul server down
[INFO] agent: shutdown complete
[INFO] agent: Stopping DNS server 10.135.218.10:53 (tcp)
[INFO] agent: Stopping DNS server 10.135.218.10:53 (udp)
[INFO] agent: Stopping HTTP server 10.135.218.10:8500 (tcp)
[INFO] agent: Waiting for endpoints to shut down
[INFO] agent: Endpoints down
[INFO] agent: Exit code: 1

之后重新启动 consul 进程即可。