GO接口走私扩展API对中间件的影响

2020-07-11 15:34:12

最近,Go博客“保持你的模块兼容”(Keep Your Modules Compatible)就是在你添加功能并想要扩展你模块的API时这样做的。当模块的API涉及接口时,他们建议的方法之一是我所说的接口混杂和其他人所说的接口升级。让我引述这篇文章:

当您遇到想要向现有接口添加方法的情况时,或许可以遵循此策略。从使用新方法创建新接口开始,或使用新方法标识现有接口。接下来,确定需要支持它的相关功能,为第二个接口键入check,并添加使用它的代码。

这是一种相当流行的方法,Go&#s标准库和第三方软件包中的许多软件包都使用这种方法。然而,它也有阴暗面,那就是它对中间件的不幸影响。

最常见的一种中间件最能说明中间件的问题,它是插在http处理程序链中以修改结果的东西。很多中间件希望查看或处理HTTP回复的某些方面,例如根据结果代码收集度量,这意味着它必须修改和代理传递给子http.Handler的http.ResponseWriter。随着时间的推移,http包已经在ResponseWriters上获得了一整个走私接口集合,例如http.CloseNotifier(已弃用)、http.Flusher、http.Hijacker和http.Pusher。在未来,可能会有更多。

如果您是一个中间件,则您重新传递的ResponseWriter可能支持一些、许多或所有这些附加API。但是,Go并不提供通过proxyResponseWriter传递此支持的好方法,您将把它传递给子处理程序。不管怎样,普罗米修斯人都很努力地去做,结果是相当混乱的,涉及到API的可能组合的组合爆炸。如io.ReaderFrom所示,这些额外的APIsdon甚至不一定来自http包。可能需要支持来自任何地方的自鸣得意的接口。

对此问题的一个答案是,您只是不在您的中间件中支持这些额外的API,或者您只支持其中的几个。这样做的问题是,人们试图很好地使用您的中间件的ResponseWriter和客户端代码都是在使用这些扩展的API的环境中开发、测试和正常使用的,而不是被切断。我们都知道,如果你不测试它,它是不会起作用的。您的中间件可能是第一个尝试使用真正狭窄的API传递下一跳ResponseWriter的代码,因为这种狭窄的API可能大多来自中间件。当然,如果结果中有任何错误,人们会指责您的中间件。

所有这些都不是不可逾越的。但是,撇开问题和麻烦不谈,这意味着如果人们使用中间件来扩展API,那么通过走私接口来扩展API肯定是不透明的。这是一个实际问题,您的新API在一定时间内将无法使用,直到中间件被扩展以应对它(如果它曾经可以使用的话)。

另一个问题是,在您的新API本身普及之前,这种为应对新API而进行的中间件扩展不会发生。GO目前不支持基于其他包的版本或其API的状态进行条件构建,因此中间件不能包含对新API接口的任何使用,除非它必须针对早于新API接口的包版本进行构建。

(人们可以在HTTP中间件上解决这个问题,因为他们只能在Go的特定最低版本上构建文件。您的软件包没有这种神奇的功能;它只适用于Go标准库中的新API。)。

因为这并不是什么新鲜事,这个问题早在2014年围棋的界面升级中就被注意到了,它是最早将这种模式称为界面升级的地方之一。本文注意到了代理问题,并以一个Call结尾,该调用要求节约使用接口升级。在我看来,这是一个很好的建议,但与通常使用界面升级来扩展API的想法非常不同。