在生产环境用了一个月Go语言,我有4点体会

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最近,我获得了一份DevOps工作,工作内容主要涉及用Go完全从头开始编写一个新的后端系统。此前,我从未在生产环境使用过Go,从个人项目中有过了解。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"你(可能)应该使用一个Web框架"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一开始,我们决定只使用Go的http库和一个简单的路由库——"},{"type":"codeinline","content":[{"type":"text","text":"mux"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而,我很快就遇到了现实生活中的生产问题:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"恢复中间件"},{"type":"text","text":"——用来日志打印和静默处理程序代码中的死机。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"日志"},{"type":"text","text":"——我想要某个方案,可以打印每个请求的信息,包含body params、auth tokens等等(用于调试目的)。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"更好的错误处理"},{"type":"text","text":"——我希望错误仍然是带有错误信息和代码的JSON响应。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"其它常用的中间件"},{"type":"text","text":"——包含JWT验证和CORS。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我有两个选择:自己实现上述问题的解决方案,针对每个问题使用不同的第三方库,或者选择一个Web框架,基本上已经做了大部分(如果不是全部)这些事情。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我最终决定使用Echo这个Web框架。据悉,它在GitHub上有近2万个点赞,有一个非常活跃的社区,还有很棒的文档。我认为它是完成这份工作的一个很棒的工具。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/23\/cc\/23ce53db89926a4ec71c7e0cfea8cccc.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我还发现,用"},{"type":"codeinline","content":[{"type":"text","text":"echo"}]},{"type":"text","text":"编写应用程序时有一些比较精简的样板(主要是解析json body、编写errors以及手动设置headers等),让代码的可读性有所提高。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/d7\/ae\/d738630299bde59f7f54e84e5799faae.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而,当你有一些比较复杂的端点时,你就会注意到生产率的真正差异。你经常会遇到需要验证某些JSON字段的情况,并且需要有意义的错误信息来描述错误。如果你想要在不使用任何库的情况下完成这些,你的代码将很快变得很难阅读:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/48\/61\/486024d41cff85ab4f78d0fb23416a61.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"你需要一个好的代码结构"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Go的Web框架(或者一般的go项目)不强制任何特定的文件结构。如果你使用过ASP.NET\/ASP.NET Core之类的东西,当我说一些框架是紧密结构的,而且很多事情都是通过约定而不是显式指定来完成的时,你就会知道我在说什么。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"关于Go的问题是,你很容易跳过关于构建代码结构的学习,使得代码很难阅读和维护。如果你还不知道我在说什么,下面是我不久前写的一个("},{"type":"text","marks":[{"type":"strong"}],"text":"糟糕的"},{"type":"text","text":")Go端点例子:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/b9\/86\/b98603e588e67a3d936ab02f4fab3a86.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你明白我的意思吗?在添加了所有的"},{"type":"codeinline","content":[{"type":"text","text":"CreateUser"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"CreateAgency"}]},{"type":"text","text":"方法后,“更好的”方法很可能会包含更多的行,但是...它以后会非常容易理解、重用、调试和修改,因为每个方法都有单独的用途。如果你还没有明白,我强烈建议你看一看下面关于良好代码结构的资源:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/github.com\/ribice\/gorsk","title":"","type":null},"content":[{"type":"text","text":"https:\/\/github.com\/ribice\/gorsk"}]},{"type":"text","text":"- 基础REST API的良好例子"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/github.com\/bxcodec\/go-clean-arch","title":"","type":null},"content":[{"type":"text","text":"https:\/\/github.com\/bxcodec\/go-clean-arch"}]},{"type":"text","text":"- 也是一个REST API例子,但是更严格地遵循“Clean Architecture”理念"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般来说,这个理念很简单。你应该将与数据库通信的代码与实际的应用程序逻辑本身分开,而且应用逻辑也应该与传输\/端点逻辑(在本例中是HTTP端点)分开。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"明智地选择你的SQL driver"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当我第一次用Go开始编程时,我希望尽可能使用最新的库,因此我选择使用"},{"type":"codeinline","content":[{"type":"text","text":"database\/sql"}]},{"type":"text","text":"包(使用Postgres)。虽然这个体验还可以,但在查询数据时,我遇到很多样本,特别是不得不使用Scan语法。这导致我有下面2个选项:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"sqlx"}]},{"type":"text","text":"- 一个基于"},{"type":"codeinline","content":[{"type":"text","text":"database\/sql"}]},{"type":"text","text":"的轻量包装器,做了一些扩展,使得做查询更容易。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"gorm"}]},{"type":"text","text":"- 一个针对Go的ORM(Object-Relational Mapping,对象-关系映射)库,根据你的Go models生成SQL models和查询。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我不认为有一个明确的“更好的”库,最终取决于使用场景和个人偏好。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"gorm"}]},{"type":"text","text":"可能会让你轻松一些,特别是如果你经常在修改数据库之后忘记在查询中增加字段的话(因为在gorm中,你根本不需要做这些)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另一方面,"},{"type":"codeinline","content":[{"type":"text","text":"sqlx"}]},{"type":"text","text":"更以SQL为中心,它更像是写Go代码来调用SQL接口,而不是gorm方案那样根据Go代码生成SQL。如果你喜欢完全掌控SQL并且不必学习GORM的新语法,那么这是一个不错的方案。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Docker"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我遇到的一个挑战是配置这个项目的生产环境。开发环境和生产环境总会有一些差别,例如这个应用程序在哪个端口上运行、数据库的主机和凭证,等等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我见过有人通过JSON、YAML甚至git忽略的.go文件来配置应用程序变量。我个人发现env文件最好用,特别是配合docker-compose使用:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/70\/73\/708b9f588yyc8e83ff9645f380174d73.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/96\/3a\/96386e0a7212141a4bcb5fccb59af63a.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我通常将这些与以下实用的函数结合使用:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/47\/41\/47613e382533632c1039c66fa4983f41.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用Go构建Docker镜像也超级简单:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/62\/11\/62b7f3cb7f7b35b972d58b0b82504e11.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"其它?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我还想到了其它一些东西,但我不认为它们值得单独用章节来讨论:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/tour.golang.org\/basics\/7","title":"","type":null},"content":[{"type":"text","text":"Named returns in Go"}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/golang.org\/doc\/effective_go.html","title":"","type":null},"content":[{"type":"text","text":"Effective Go"}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/blog.golang.org\/using-go-modules","title":"","type":null},"content":[{"type":"text","text":"Go Modules"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文链接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https:\/\/tdom.dev\/go-in-production","title":"","type":null},"content":[{"type":"text","text":"https:\/\/tdom.dev\/go-in-production"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章