From 35e92380403fb70ee0f17c024ca8dfbe23151bfb Mon Sep 17 00:00:00 2001 From: "miancai.li" Date: Sun, 15 Nov 2020 16:58:32 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E7=BF=BB=E8=AF=91=20Go:=20Fan-out=20Patter?= =?UTF-8?q?n=20in=20Our=20ETL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20190703-Go-Fan-out-Pattern-in-Our-ET.md | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 translated/tech/20190703-Go-Fan-out-Pattern-in-Our-ET.md diff --git a/translated/tech/20190703-Go-Fan-out-Pattern-in-Our-ET.md b/translated/tech/20190703-Go-Fan-out-Pattern-in-Our-ET.md new file mode 100644 index 000000000..ad2f2bb9a --- /dev/null +++ b/translated/tech/20190703-Go-Fan-out-Pattern-in-Our-ET.md @@ -0,0 +1,67 @@ +# Go: 在我们的 ETL 中使用扇出模式(Go: Fan-out Pattern in Our ETL) + +![](https://miro.medium.com/max/1400/1*r7OWMQAx6G2AGoKTXmHM6A.png) + +Go 语言在构建微服务、特别是有使用 gRPC 的应用中,非常地流行,其实在构建命令行程序时也是特别地好用。为了学习扇出模式,我会基于我们公司使用 ETL 的例子,来介绍这个模式。 + +## ETL + +ETL(提取(Extract),转换(Transform),加载(Load))通常都需要处理大量的数据。在这样的场景下,有一个好的并发策略对于 ETL 来说,能够带来巨大的性能提升。 + +ETL 中有两个最重要的部分是提取(extracting)和加载(Load),通常它们都跟数据库有关,瓶颈通常也属于老生常谈的话题:网络带宽,查询性能等等。基于我们要处理的数据以及瓶颈所在,两种模式对于处理数据或者处理输入流的编码和解码过程中,非常有用。 + +## 扇入扇出模式(Fan-in, fan-out pattern) + +扇入和扇出模式在并发场景中能得到较大的好处。这里将对它们逐个做专门的介绍(review): + +扇出,在 GO 博客中这样定义: + +多个函数能够同时从相同的 channel 中读数据,直到 channel 关闭。 + +这种模式在快速输入流到分布式数据处理中,有一定的优势: + +![fan-out pattern with distributed work](https://miro.medium.com/max/754/1*pIc5IqD0qd6uPRVlrdzk5g.png) + +扇入,在 Google 这样定义: + +一个函数可以从多个输入中读取,并继续操作,直到所有 channel 所关联的输入端,都已经关闭。 + +这种模式,在有多个输入源,且需要快速地数据处理中,有一定的优势: + +![fan-in pattern with multiple inputs](https://miro.medium.com/max/754/1*WAoD1zJQfzvGENSJPmd6SA.png) + +## 在实际中使用扇出模式(Fan-out in action) + +在我们的项目中,我们需要处理存储在 CSV 文件的大量数据,它们加载后,将在 elastic 中被检索。输入的处理必须快,否则(阻塞加载)加载就会变得很慢。因此,我们需要比输入生成器更多的数据处理器。扇出模式在这个例子中,看起来非常适合: + +![](https://miro.medium.com/max/734/1*8K93LJ5t-4z8nEXoZWQdJg.png) + +下面是我们的伪代码: + +```bash +Variables: +data chan +Start: +// a goroutine will parse the CSV and will send it to the channel +ParseCSV(data<-) +// a goroutine is started for each workers, defined as command line arguments +For each worker in workers + Start goroutine + For each value in <-data + Insert the value in database by chunk of 50 +Wait for the workers +Stop +``` + +输入和加载程序是并发执行的,我们不需要等到解析完成后再开始启动数据处理程序。 + +这种模式让我们可以单独考虑业务逻辑的同时,还可以使用(Go)并发的特性。几个工作器之间原生的分布式负载能力,有助于我们解决此类过程中的峰值负载问题。 + +--- +via: https://medium.com/a-journey-with-go/go-fan-out-pattern-in-our-etl-9357a1731257 + +作者:[Vincent Blanchon](https://medium.com/@blanchon.vincent) +译者:[译者ID](https://github.com/gogeof) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [GCTT](https://github.com/studygolang/GCTT) 原创编译,[Go 中文网](https://studygolang.com/) 荣誉推出 From 512ff5b5930f93655f40d5daf6e389c66ffaf241 Mon Sep 17 00:00:00 2001 From: "miancai.li" Date: Tue, 1 Dec 2020 16:17:08 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E6=84=8F=E8=A7=81=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- translated/tech/20190703-Go-Fan-out-Pattern-in-Our-ET.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/translated/tech/20190703-Go-Fan-out-Pattern-in-Our-ET.md b/translated/tech/20190703-Go-Fan-out-Pattern-in-Our-ET.md index ad2f2bb9a..11fc4c206 100644 --- a/translated/tech/20190703-Go-Fan-out-Pattern-in-Our-ET.md +++ b/translated/tech/20190703-Go-Fan-out-Pattern-in-Our-ET.md @@ -1,4 +1,4 @@ -# Go: 在我们的 ETL 中使用扇出模式(Go: Fan-out Pattern in Our ETL) +# Go: 在我们的 ETL 中使用扇出模式 ![](https://miro.medium.com/max/1400/1*r7OWMQAx6G2AGoKTXmHM6A.png) @@ -61,7 +61,7 @@ Stop via: https://medium.com/a-journey-with-go/go-fan-out-pattern-in-our-etl-9357a1731257 作者:[Vincent Blanchon](https://medium.com/@blanchon.vincent) -译者:[译者ID](https://github.com/gogeof) -校对:[校对者ID](https://github.com/校对者ID) +译者:[gogeof](https://github.com/gogeof) +校对:[Xiaobin.Liu](https://github.com/lxbwolf) 本文由 [GCTT](https://github.com/studygolang/GCTT) 原创编译,[Go 中文网](https://studygolang.com/) 荣誉推出 From bff29119f92214ff4a465a987a2f149ad006c21e Mon Sep 17 00:00:00 2001 From: Administrator Date: Sun, 22 Aug 2021 20:00:13 +0800 Subject: [PATCH 3/4] add 20200528-Sorting-in-Go-from-JavaScript.md --- .../20200528-Sorting-in-Go-from-JavaScript.md | 205 ++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 translated/tech/20200528-Sorting-in-Go-from-JavaScript.md diff --git a/translated/tech/20200528-Sorting-in-Go-from-JavaScript.md b/translated/tech/20200528-Sorting-in-Go-from-JavaScript.md new file mode 100644 index 000000000..44bcff5fb --- /dev/null +++ b/translated/tech/20200528-Sorting-in-Go-from-JavaScript.md @@ -0,0 +1,205 @@ +# 从 JavaScript 中学习到的 Go 排序算法 + +在计算机科学中,排序的意思是获取一个数组,然后重新对他们进行排列,使他们遵循指定的顺序,例如按字母顺序对字符串进行排序、按最小到最大的顺序对数字进行排序,或按结构中的一个字段对结构数组进行排序。您可以使用它(排序)来提高算法的工作效率,或按特定顺序显示数据(例如时间上的从最近到最远)。 + +对于 Go 中的排序,标准库提供了 sort 包,有意思的是,它使用了 Go 接口来定义对数据进行排序的规则。如果您使用过 JavaScript 的 Array.prototype.sort 方法,(那么,您对此)会很熟悉! + +## 字符串排序 + +让我们从按字母顺序排列一组字符串开始: + +```golang +var languages = []string{"Go", "C", "Ruby", "JavaScript", "XML"} +``` + +在 JavaScript 中,对它们进行排序(代码)就像这样: + +```javascript +let languages = ["Go", "C", "Ruby", "JavaScript", "XML"]; + +languages.sort(); +console.log(languages); // ["C", "Go", "JavaScript", "Ruby", "XML"] +``` + +由于 ```languages``` 是一个数组,我们可以使用 Array.prototype.sort, 对它们进行排序。 + +由于与 JS 数组不同,Go 切片没有开箱即用的方法,我们没办法直接使用已有的排序算法,我们需要导入 sort 包并使用其 Sort 函数来对数组重新排列。让我们试试吧!(首先,)将此代码放在一个名为 sort-strings.go 的文件中: + +```golang +package main + +import ( + "fmt" + "sort" +) + +func main() { + languages := []string{"Go", "C", "Ruby", "JavaScript", "XML"} + sort.Sort(languages) + + fmt.Println(languages) +} +``` + +然后,运行 ```go run sort-strings.go```,你应该会得到(如下错误): + +```bash +./sort-strings.go:10:14: cannot use languages (type []string) as type sort.Interface in argument to sort.Sort: + []string does not implement sort.Interface (missing Len method) +``` + +编译器错误?之所以会这样,是因为 ```sort.Sort``` 不接受切片类型,它无法对切片类型进行自动转换。它的函数签名实际上是这样的: + +```golang +func Sort(data Interface) +``` + +sort.Interface(带有一个大 I)是一个 Go 接口,表示可以排序的数据集合,如字符串、数字抑或是结构体列表。由于对字符串和整数的切片进行排序很常见,所以 sort 包也提供了一些内置方法,使 sort.Sort 方法与字符串或整数切片可以兼容. 试试这个! + +```golang + func main() { + languages := []string{"Go", "C", "Ruby", "JavaScript", "XML"} +- sort.Sort(languages) ++ sort.Sort(sort.StringSlice(languages)) + + fmt.Println(languages) + } +``` + +sort.StringSlice 是一个字符串切片方法,但它实现了 sort.Interface 接口. 因此,通过将一个 []string 类型转换为 StringSlice 类型,就可以使用 sort.Sort! 现在,如果您(再)执行 ```go run sort-strings.go``` 命令,您应该会看到按字母顺序排列的编程语言列表! + +为什么我们需要使用一个特殊的接口来对数据进行排序,而不是让 Go 语言的 sort.Sort 方法直接接受(字符串或整型)切片?原因是因为,我们传入的是一个元素集合,Go 语言需要通过某种方法来知道元素的顺序。为了编写这些规则来对切片进行排序,您需要实现 sort.Interface 方法。正如您看到的,Interface 使我们可以灵活地以任何您喜欢的方式来定义元素的顺序! + +## 实现自定义排序类型 + +假设我们的 languages 切片包含 "fish"(一种 shell 脚本语言)。如果您按字母顺序对 "编程工具" 进行排序,那么像这样有意义的排序是: + +```golang +[C, fish, Go, JavaScript, Ruby, XML] +``` + +但是,即使有 XML,"fish" 也排在最后!(这是因为)使用 sort.StringSlice, 与使用 JS 中的字符串列表排序算法 Array.prototype.sort 相同,默认按照字典顺序排序,而不是字母顺序。在字典顺序中,小写字母(如fish 中的 f)在大写字母(如 XML 中的 X)之后。如果我们想不区大小写,就按照字母的顺序排序,我们需要实现一些自定义行为。那会是什么样子呢? + +在实现自定义排序规则之前,我们需要想想排序的作用。在本教程中,我们不会研究不同排序算法(如快速排序、归并排序和冒泡排序)的细节,虽然学习它们在编程中很重要。关于在 Go 和 JS 中编写自定义排序算法,您需要了解的是,它们需具备: + +- 查看集合中的元素 +- 比较它们,看看哪些元素应该排在前面 +- 根据这些比较将元素按顺序排列 + +在 JavaScript 中,您将传入一个自定义函数来告诉 sort 如何对数组中的元素进行比较,如下所示: + +```golang +languages.sort((langA, langB) => { + langA = langA.toLowerCase(); + langB = langB.toLowerCase(); + if (langA < langB) { + return -1; // return -1 if langA should go before langB in the array + } else if (langB > langA) { + return 1; // return 1 if langB should go before langA in the array + } + return 0; // return 0 if they can go in either order +}) +``` + +因为我们在比较之前已使用 toLowerCase 方法,(这样可以)使得 fish 语言排在 Go、JavaScript、Ruby 和 XML 语言之前,但在 C 语言之后! + +如果我们查看 Go sort.Interface,我们可以看到需要实现的方法如下: + +```golang +type Interface interface { + Len() int + Less(i, j int) bool + Swap(i, j int) +} +``` + +所以要创建一个可以排序的类型,我们需要实现 sort.Interface 接口: + +- 告诉 Go sort 包集合的长度 +- 取集合中的任意两个元素(元素 i 和 j),并将他们进行交换 +- 查看集合中的任意两个元素,看看 Less 方法在对集合进行排序时哪个应该排在前面 + +让我们以 Len 和 Swap 的实现方法开始。 + +```golang +type alphabeticalStrings []string + +func (a alphabeticalStrings) Len() int { return len(a) } + +func (a alphabeticalStrings) Swap(i, j int) { + placeholder := a[j] + a[j] = a[i] + a[i] = placeholder +} +``` + +首先,我们对字符串切片进行封装,定义一个新的类型,alphabeticalStrings。在 Go 语言中,我们通过定义自己的类型,我们可以为它编写方法。 + +对于 Len 方法,我们只是使用 Go 的内置 len 函数来获取切片的长度,对于 Swap,我们交换切片中的两个元素。目前为止一切顺利。现在让我们实现 Less 方法。先导入 strings 包,并添加这个函数: + +```golang +func (a alphabeticalStrings) Less(i, j int) bool { + return strings.ToLower(a[i]) < strings.ToLower(a[j]) +} +``` + +注意到关于 Less 方法了吗?它看起来非常像我们在 Array.prototype.sort 函数中定义的比较方法,除了它返回一个 bool 类型而不是 int 类型,并接受的是切片索引而不是元素本身! + +现在,让我们来试试!编辑 main 函数,让它像这样: + +```golang + func main() { + languages := []string{"Go", "C", "fish", "Ruby", "JavaScript", "XML"} +- sort.Sort(sort.StringSlice(languages)) ++ sort.Sort(alphabeticalStrings(languages)) + + fmt.Println(languages) + } +``` + +如果您执行 ```go run sort-strings.go``` 命令,现在您应该可以看到按预期排序的列表! + +```golang +[C, fish, Go, JavaScript, Ruby, XML] +``` + +你知道 Go 有什么好玩的 sort.Interface 接口吗?我们编写的字母字符串类型和 Go 团队编写的 StringSlice 类型都建立在一个普通的旧类型之上,[]string 并且都可以传递到 sort.Sort. 我们可以通过选择我们将字符串切片转换为哪种类型来选择我们想要的字符串顺序! + +## 使用 sort.Slice 简化我们的排序! + +JS 和 Go 语言不同版本的 sort 方法之间的一个重要区别是,对 Go 切片进行排序时,除了比较函数之外,我们还需要编写 Len 和 Swap 方法。对于不同的切片类型,Len 和 Swap 看起来都差不多。故,定义一个新的(元素)排序,都必须实现这三种方法感觉有点麻烦。 + +需要实现这三种方法的原因是,你实现 sort.Interface 接口时,对应的数据,并不一定是数组或切片。我只是使用了切片的 sort 包,但您可以使用其他数据类型实现 sort.Interface 接口,例如链表。 + +对于切片,在 Len 和 Swap 方法中,我们经常使用的是相同的逻辑,我们能不能只实现 Less,就像在 JavaScript 中一样?(其实)sort 包就有这样的方法,sort.Slice! + +```golang +func Slice( + slice interface{}, + less func(i, j int) bool, +) +``` + +我们传入我们想要排序的数据切片作为第一个参数,以及一个函数来比较切片的元素作为第二个参数。甚至无需创建新类型,现在我们就可以对数据进行排序!让我们再一次重构我们的 main 函数来试试: + +```golang + func main() { + languages := []string{"Go", "C", "fish", "Ruby", "JavaScript", "XML"} +- sort.Sort(alphabeticalStrings(languages)) ++ sort.Slice(languages, func(i, j int) bool { ++ return strings.ToLower(languages[i]) < strings.ToLower(languages[j]) ++ }) + + fmt.Println(languages) + } +``` + +完成了!我们已经获取到我们要的排序切片了! + +sort 包还有一些很酷的地方,除了我们能够选择排序所依据的顺序外,注意在 sort.Sort 和 sort.Slice 中,我们不需要知道我们正在使用哪种排序算法。sort.Sort 已包含了具体的实现算法,所有需要从我们这边获取到的信息是,如何比较元素,如何交换它们,以及我们有多少元素。这就是接口的作用! + +顺便说一下,熟悉排序算法的工作原理仍然是绝对值得的,这会让您知道如何让你的计算机做更少的工作来排列数据,而其(原理)的应用场景非常多。因此,如果您想了解它们是如何工作,sort.Sort 和我们编写的这些函数在背后都做了什么,下面是一些关于算法本身的材料(可供学习)。 + +- [Free Code Camp - 解释排序算法](https://www.freecodecamp.org/news/sorting-algorithms-explained/) +- [Toptal - 排序算法动画](https://www.toptal.com/developers/sorting-algorithms) +- [在 JavaScript 中实现排序算法](https://medium.com/@rwillt/implementing-sorting-algorithms-in-javascript-b08504cdf4a9) \ No newline at end of file From c23e0fd418808437c8841245d24140334c338e98 Mon Sep 17 00:00:00 2001 From: limc001 Date: Sun, 28 May 2023 23:50:41 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E7=BF=BB=E8=AF=91=E6=96=87=E6=A1=A3?= =?UTF-8?q?=EF=BC=9A=D0=9Cessy=20Route=20Handling=20in=20Golang=20and=20ho?= =?UTF-8?q?w=20to=20avoid=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...Handling-in-Golang-and-how-to-avoid-it.md" | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 "translated/tech/20220331-\320\234essy-Route-Handling-in-Golang-and-how-to-avoid-it.md" diff --git "a/translated/tech/20220331-\320\234essy-Route-Handling-in-Golang-and-how-to-avoid-it.md" "b/translated/tech/20220331-\320\234essy-Route-Handling-in-Golang-and-how-to-avoid-it.md" new file mode 100644 index 000000000..79c217cf1 --- /dev/null +++ "b/translated/tech/20220331-\320\234essy-Route-Handling-in-Golang-and-how-to-avoid-it.md" @@ -0,0 +1,177 @@ +# Golang 中混乱的路由处理及如何避免 + +以下例子是用 Go 语言编写的,但是同样的原则也适用于多种编程语言。 + +在处理路由方面,Go 采用了非常简单易懂的方法。开发者可以使用 http 包中提供的默认处理程序,因此我们可以立即开始响应请求。 + +```golang +func (w http.ResponseWriter, r *http.Request) { + _, err := w.Write(w, []byte("hello there!")) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + } +} +``` + +我见过很多这样构建的API,但是迟早都会遇到同样的问题——不一致性、责任纠缠、不当行为和脆弱的代码。 + +## 使用默认处理程序的有效情况 + +著名的中间件串联模式是处理多种情况的有用方式,因此默认处理程序是执行该任务的绝佳选择。 + +```golang +func (w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + if something { + w.WriteHeader(http.StatusUnauthorized) + return + } + next(w, r) +} +``` + +中间件方法链规则要求处理程序应该将结果写入响应对象或调用链中的下一个处理程序。因此,路由处理程序负责向响应对象写入内容。这是可以的,但在某些情况下可能会出现问题。 + +## 出现问题的原因 +使用路由处理程序编写响应内容应该是一个额外任务,(糅合在一起会)带来以下让人不愉快的后果: + +### 代码重复 +每个路由处理程序都必须复制设置正确的标头、对数据进行编码以及将内容写入响应对象的逻辑。 + +### 一致性无法保证 +API 应该在所有端点上始终保持一致。有时返回纯文本,有时返回 JSON 对象,有时根本什么也没有,这是不自然的。 + +如果同时使用 NoContent 状态和将 JSON 对象放置在响应正文中作为响应,则会发生什么? + +虽然使用默认处理程序可以创建几乎任何组合,但维护起来肯定很麻烦。 + +### 关注分离 +一旦路由处理程序完成操作,它需要选择适当的方式将结果写入响应。这不是一个好的,符合 "关注分离" 的习惯。结果已经准备好返回,这应该已经足够了。 + +### 脆弱的代码 +实际上,整个代码库中创建新路由的人都必须熟悉必须完成的所有工作,并应该行使该规范。否则,我们将违反前面提到的任何内容,这是一个不能总是及时捕捉的错误。代码库越大,发现此类错误就越困难。 + +### 简单的“修复” +如果涉及代码重复和标准化,那么自然应该考虑使用子函数来协助。因此,我们可能会添加一个名为 httputil 的包到代码库中,并开始描述有效响应的样子。 + +```golang +func WriteCreated(w http.ResponseWriter, payload interface{}) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + if err := json.NewEncoder(w).Encode(payload); err != nil { + w.WriteHeader(http.StatusInternalServerError) + } +} +``` + +虽然这是向前迈出的一步,但问题仍然存在 - 没有人强制路由处理程序使用此或任何其他子函数。他们可以决定独立设置并自己编写响应内容。 + +## 了解真正的问题 +如果我们期望从函数中得到一个结果,我们自然会使用 return 函数。这样我们就知道我们将得到什么,而且我们可以确定它总是以我们要求的格式呈现。 + +但是,void 函数没有强制返回内容。它们不必返回任何内容。它们不能被迫执行与将结果返回给调用者有关的任何操作。 + +## 使用 void 函数返回值 +根据定义,Go 中的默认路由处理程序是 void 函数。因此,它们没有束缚力,不能期望或要求它以任何方式运行。它们只会做它们认为最好的事情。 + +## 如何修复此问题 +路由处理程序的责任是接受和验证输入,调用业务逻辑,然后返回正确的结果。 + +路由处理程序需要完成其工作的全部内容只有请求对象。编写器对象只是一个需要管理的痛苦额外项。 + +可以使用简单的结构对象描述响应。那么,为什么不将路由处理程序变成返回此类结构的返回函数呢? + +## 标准化响应 +首先,我们创建一个响应包并在其中定义以下内容: + +response.go + +```golang +type PayloadType int +var( + PayloadEmpty PayloadType = iota + PayloadText + PayloadJSON +) +type HttpResponse struct { + statusCode int + payload interface{} + payloadType PayloadType + еrrMessage string +} +``` + +我们有意将字段保持私有,因此只能使用标准化响应构造函数之一来创建对象。 + +```golang +func Created(payload interface{}) HttpResponse { + return HttpResponse{ + statusCode: http.StatusCreated, + payload: payload, + } +} +// all needed response types... +``` + +最后一步是实现一个写入函数。这将是代码库中用于编写所有响应的单一故障点。 + +```golang +func Write(w http.ResponseWriter, response HttpResponse) error { + // write the response +} +``` + +## 符合路由处理程序 + +可以使用我们创建的HttpResponse结构来描述响应。我们所要做的就是修改路由处理程序,使其能够开始返回标准化的值。 + +```golang +func(a *api) DoSomething (r *http.Request) response.HttpResponse { + id := getParam(r, "id") + if isValidID(id) == false { + return response.BadRequest(ErrMsgInvalidID) + } + result, err := a.service.DoSomething(id) + if err != nil { + msg := "do something was unable to complete" + return response.InternalServerError(msg) + } + return response.OK(payload) +} +``` + +现在,我们可以将其封装在标准处理程序中,并在任何路由器和中间件中使用。 + +```golang +type RouteHandler func (*http.Request) response.HttpResponse +func Wrap (handler RouteHandler) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + resp := handler(r) + + if err := response.Write(w, resp); err != nil { + logger.Error(err) + } + } +} +``` + +## 结论 + +本文章的想法远非革命性,但它们解决了在所有代码库中仍然存在的问题。我已经看到其中许多。 + +随着时间的推移,代码库往往会增长。参与开发的人数也会增加。如果不强制实施标准化行为和一致性,那么混乱将悄悄占据优势。 + +虽然在开始的时候快速完成并避免过度复杂化事情很诱人,但响应是每个 API 中至关重要的一部分,我认为它们应该做得正确。 + +我希望本文中的信息能够帮助人们提出自己的想法,这样我们就可以共同创建令人愉快的 API。 + +欢迎提出评论和问题。 + +--- +via: https://medium.com/@antonyjekov/messy-route-handling-in-golang-and-how-to-avoid-it-736e7d5e216b + +作者:[Antony Jekov](https://medium.com/@antonyjekov) +译者:[gogeof](https://github.com/gogeof) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [GCTT](https://github.com/studygolang/GCTT) 原创编译,[Go 中文网](https://studygolang.com/) 荣誉推出 \ No newline at end of file