Golang实战中遇到适用场景选择的困惑,求开发者经验分享

Golang实战中遇到适用场景选择的困惑,求开发者经验分享 作为一名拥有多年PHP丰富经验的开发者,同时也熟悉Python以及HTML、JavaScript和CSS等其他Web技术,我发现与Kotlin/Java、C#和Node.JS相比,Go是一门极其迷人的语言(例如:大多数用例外部依赖少、跨平台编译、并发能力强)。然而,我仍然在努力寻找Go能真正满足我需求的实用场景。

在Web后端开发方面,PHP长期以来一直是我的首选语言。它不仅驱动着一些最大的CMS系统,而且PHP在7版本,尤其是8.3版本的改进中取得了长足进步,引入了即时编译器等功能。此外,PHP得到了大多数Web主机商的广泛支持,使其集成变得极其容易。无论是在共享虚拟主机、VPS还是专用云基础设施上运行,PHP都能动态扩展,轻松覆盖小型和大型用例。相比之下,部署基于Go的Web应用程序通常需要对服务器有更高级的访问权限,或者至少需要一个你能控制运行时环境的系统。一个简单的PHP脚本通常可以在基础的Web空间上运行,而Go则需要更定制的设置。

这并不是说我没有尝试过在不同的场景中使用Go。最近,我尝试用Go开发一个桌面应用程序,用于从PDF文件中提取文本,使用AI处理数据并进行分类。然而,我在Go提取PDF文本的能力方面遇到了重大挑战。尽管测试了几乎所有可用的库(例如GitHub - dslipak/pdf: PDF readerGitHub - ledongthuc/pdf: PDF reader),但没有一个能处理我遇到的各种PDF文件。最终,我不得不依赖将Apache Tika Java应用程序与Go集成,这增加了不必要的复杂性,包括更大的文件大小以及需要在系统上安装Java。虽然这个解决方案可行,但我更希望有一个更直接、纯Go的方法。

从积极的一面来看,使用Go的Fyne框架构建桌面UI相对顺利且实用,所以那里肯定有潜力。然而,我最初的目标是构建一个功能齐全的、具有本地文件处理能力的Go桌面应用程序(为了最终用户的数据隐私,并且我可以免费向最终用户提供此程序),但这并不像我期望的那样无缝。

我发现Go不太实用的另一个领域是处理JSON结构。使用PHP和JavaScript处理JSON数据——尤其是在结构并非总是100%可预测的情况下,比如处理API响应时——感觉要容易得多。这两种语言在处理动态或部分定义的结构时都非常灵活。相比之下,在Go中处理JSON感觉有点更死板和繁琐,特别是当API响应并不总是完美匹配你预定义的结构时。我还注意到,当OpenAI引入新的结构化JSON格式时,Python库在Go库赶上之前几周就实现了对它的支持。这突显了在某些情况下,Go的生态系统可能会落后于Python或JavaScript等更成熟的语言。

在系统管理任务方面,我通常选择Python或Shell脚本。原因很简单:Python脚本通常内置了我所需的大部分功能,不需要额外的项目文件或复杂的设置。你只需编写脚本,运行它,它就能工作。此外,Python易于编辑且不需要编译,这对于日志文件分析、更新暂存系统等任务来说是完美的。在这里,Go的编译特性感觉不那么自然。我宁愿要一个简单、可编辑的脚本,不需要多个文件或复杂的构建过程。Go虽然强大,但对于这些快速、迭代的任务,它无法提供同样水平的简单性。

到目前为止,我已经在Web后端、桌面应用程序和系统管理脚本方面探索了Go,但我还没有找到一个案例能证明Go是更优越的选择。 无论是由于某些库的不成熟,还是语言的范式与某些任务不太匹配,我总是遇到限制。尽管我很兴奋地想更多地使用Go,但我经常发现对于我的大多数项目来说,PHP或Python最终是更实用的选择。

这就引出了我的问题:对于那些使用Go的人,你们主要构建的是什么样的应用程序? 如果你的用例与我的有重叠,我很想知道你为什么选择Go而不是PHP或Python。你的见解真的可以帮助我弄清楚Go在我的工作流程中最适合的位置,以及哪些项目可能会从中受益。

感谢分享你的想法!


更多关于Golang实战中遇到适用场景选择的困惑,求开发者经验分享的实战教程也可以访问 https://www.itying.com/category-94-b0.html

10 回复

你可能会欣赏这些经验丰富的PHP开发者关于从PHP迁移到Go的(主要是)优点和(一些)缺点的看法。

更多关于Golang实战中遇到适用场景选择的困惑,求开发者经验分享的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


您知道Kubernetes是用Go语言开发的吗? 我认为Go语言非常适合后端开发,尤其是在微服务架构中。只需一个编译后的可执行文件结合Docker,就能非常轻松地实现持续集成/持续部署、自动扩缩容等操作。

几乎是为构建 RESTful API 量身定做的

在数据库之上构建 RESTful API,您会推荐什么框架呢?我认为 Go 几乎是编写服务器代码的最佳语言,只是我还没有找到任何 Go 框架能与 Python 的 FastAPI 相媲美。也许这个比较并不公平。

使用 FastAPI,你可以获得自动数据验证和强大的依赖注入系统。我确实发现了一些 Go 包,它们试图在 Go 中实现类似的功能(例如 GitHub - sashabaranov/go-fastapi: 创建 API 并免费获取 Swagger 定义),所以也许我需要在未来的项目中研究一下这些包。

为什么你选择 Go 而不是 PHP 或 Python

体积、依赖、速度。简而言之,它在各个方面都更经济。你可以使用 //embed 将网站构建为单个可执行文件。

虽然 Go 基本上不是为网页开发而设计的,但我发现 Go 的 HTML 模板非常容易创建我的第一个网站。尽管今天我应该用不同的方式来做这件事。

而且我还创建了一些 API 和 Cron 服务器,这些正是 Go 更擅长的领域。

你会推荐什么框架来在数据库之上构建 RESTful API?我认为 Go 几乎是编写服务器代码的最佳语言,只是我还没有找到任何能与 Python 的 FastAPI 相媲美的 Go 框架。也许这个比较不太公平。

实际上,你可以完全使用标准库来构建整个 REST API:json、net/http、sql/db 等。试试看。之后,看看 ‘Gin 框架’。

对于构建RESTful API,我尚未找到比Go更让我青睐的语言或生态系统。它几乎是为构建RESTful API量身定做的(标准库在这方面非常强大,无需任何外部依赖即可证明这一点)。并发处理简单且合乎逻辑。它有自己的设计哲学。交叉编译到不同目标平台比我用过的任何其他工具都更容易。其生态系统和工具链非常出色。

关于交叉编译,可以看看这个:

https://www.reddit.com/r/golang/comments/13bfvqd/comment/jjb6xx7/?utm_source=share&utm_medium=web2x&context=3

并听听Rob Pike在链接中的演讲。关于这一点,这里还有一些其他讨论:

https://www.reddit.com/r/golang/comments/1dqf2ng/whats_the_secret_of_the_go_compiler_and_why_dont/

根据我部署生产应用程序的实际经验,与其他语言构建的API相比,基于Go的API性能更好,也更容易理解。这对我所针对的云平台(主要是Google Cloud、AWS和Azure)很重要,因为这些平台的内存/CPU成本仍然较高,占用空间小至关重要。并且不止我一人这么认为

除了能够快速迁移到更可靠的平台之外,我们在此期间还成功将基础设施成本削减了一半。

除此之外,我还用它来自动化重复性任务,因为我通常编写Go代码比编写bash脚本更快。另外,我团队中的一些开发人员不使用Linux,而bash脚本不具备跨平台性。

还有一点:在我使用过的所有Excel库中,excelize是最好的。因此,任何需要生成Excel报告的项目,如果我能做主,我都会使用Go。

目前我主要使用标准库,感觉非常高效。我的新项目大多使用Postgres作为数据库,并且我使用jackc/pgx,它在database/sql的基础上增加了批量导入、扫描到切片/结构体/结构体切片等功能。它还增加了一些Postgres独有的特性。

我不熟悉FastAPI,但你具体怀念它的哪些功能呢?我查看了他们的示例:

from typing import Union

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def read_root():
    return {"Hello": "World"}


@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
    return {"item_id": item_id, "q": q}

……在我看来,这与Go的代码并没有太大的不同:

package main

import (
	"fmt"
	"log"
	"net/http"
)

func root(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, `{"Hello": "World"}`)
}

func readItem(w http.ResponseWriter, r *http.Request) {
	itemID := r.PathValue("item_id")
	q := r.URL.Query().Get("q")
	fmt.Fprintf(w, `{"item_id": %v, "q": "%v"}`, itemID, q)
}

func main() {
	http.HandleFunc("GET /", root)
	http.HandleFunc("GET /items/{item_id}", readItem)
	log.Fatal(http.ListenAndServe(":8089", nil))
}

请注意,在实际应用中,你可能需要做一些清理或检查工作。但这两个例子都极其简化。slight_smile

我尝试过使用Go开发桌面应用、Web服务、网络转发、监控服务、移动应用(混合)、OpenGL(2D)、树莓派4B开发等等。 我想我可以发表一些看法。

  1. 桌面应用 非常糟糕,目前没有太好的解决方案。我使用过Fyne并编写过应用程序,但其破坏性的API接口和奇怪的内存调度让我不喜欢这个开发框架,尽管它目前是纯Go中最好的GUI框架。 我也用过Wails,但它仍然缺少一些GUI功能,例如无法多窗口。

  2. Web服务/网络 简单来说,Go在这方面有太多成功案例了,如果你对此有疑问,我认为你还没有深入了解。 最初,Golang在网络方面(Goroutine、Web库、CGO等)也具有巨大优势,这使得这门语言脱颖而出,其效率目前是毋庸置疑的。

  3. 工具库支持 由于成功案例和从业者比例的原因,Golang的非网络工具库相对薄弱,但这也没办法。 我也遇到过对PDF的支持,它是有缺陷的,因为大多数人并不关注这部分。

  4. C语言 Golang的一大优势是非常接近C语言,当你需要引用一些C库时相对容易。 由于类似的原因,有很多C绑定库,你通常可以通过查看C文档来编写相应的Go代码,例如OpenGL、Qt、VLC等。

  5. 嵌入式 不可否认,用它来写嵌入式项目确实有用,但相对小众,所以我这里不做评论。

  6. 总结 没有哪种开发语言是完美的,Golang也不例外。但它高效且易用,如果你想知道能用Golang做什么,你应该看看别人在做什么(比如GitHub上知名的Go项目)。

  7. 题外话 由于Golang的简单设计,开发者很容易写出奇怪的代码,至少在我的观察中是这样。 也可能是因为这个原因,他们更喜欢语法糖更高或库集成度更高的语言。

Go在并发密集型、网络服务和基础设施工具方面确实表现出色。以下是一些具体的应用场景和示例:

1. 高并发Web服务 Go的goroutine和channel机制非常适合处理大量并发连接,比如API网关、实时聊天服务:

package main

import (
    "net/http"
    "time"
)

func handler(w http.ResponseWriter, r *http.Request) {
    time.Sleep(100 * time.Millisecond) // 模拟处理耗时
    w.Write([]byte("请求处理完成"))
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

2. 微服务架构 Go编译为单个二进制文件,部署简单,适合微服务:

// user-service/main.go
package main

import (
    "encoding/json"
    "net/http"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func main() {
    http.HandleFunc("/users/1", func(w http.ResponseWriter, r *http.Request) {
        user := User{ID: 1, Name: "张三"}
        json.NewEncoder(w).Encode(user)
    })
    http.ListenAndServe(":3001", nil)
}

3. CLI工具 Go的交叉编译能力使其成为CLI工具的理想选择:

package main

import (
    "flag"
    "fmt"
    "os"
)

func main() {
    file := flag.String("f", "", "要处理的文件")
    flag.Parse()
    
    if *file == "" {
        fmt.Println("请指定文件路径")
        os.Exit(1)
    }
    
    content, err := os.ReadFile(*file)
    if err != nil {
        fmt.Printf("读取文件失败: %v\n", err)
        os.Exit(1)
    }
    
    fmt.Printf("文件大小: %d 字节\n", len(content))
}

4. 数据处理管道 利用channel构建高效的数据处理流水线:

package main

import (
    "fmt"
    "time"
)

func producer(ch chan<- int) {
    for i := 0; i < 10; i++ {
        ch <- i
        time.Sleep(50 * time.Millisecond)
    }
    close(ch)
}

func worker(id int, ch <-chan int, results chan<- string) {
    for num := range ch {
        results <- fmt.Sprintf("Worker %d 处理了: %d", id, num*2)
        time.Sleep(100 * time.Millisecond)
    }
}

func main() {
    dataCh := make(chan int, 5)
    resultsCh := make(chan string, 10)
    
    go producer(dataCh)
    
    // 启动3个worker
    for i := 1; i <= 3; i++ {
        go worker(i, dataCh, resultsCh)
    }
    
    for i := 0; i < 10; i++ {
        fmt.Println(<-resultsCh)
    }
}

5. JSON处理示例 Go的JSON处理虽然需要结构体,但可以通过灵活的方式处理动态数据:

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    // 动态JSON处理
    jsonData := `{"name": "John", "age": 30, "extra": {"city": "NYC"}}`
    
    var data map[string]interface{}
    json.Unmarshal([]byte(jsonData), &data)
    
    // 安全访问嵌套字段
    if extra, ok := data["extra"].(map[string]interface{}); ok {
        if city, ok := extra["city"].(string); ok {
            fmt.Printf("城市: %s\n", city)
        }
    }
    
    // 使用结构体标签处理不同字段名
    type User struct {
        Name string `json:"name"`
        Age  int    `json:"age"`
    }
    
    var user User
    json.Unmarshal([]byte(jsonData), &user)
    fmt.Printf("用户: %s, 年龄: %d\n", user.Name, user.Age)
}

6. HTTP客户端并发请求

package main

import (
    "fmt"
    "io"
    "net/http"
    "sync"
)

func fetchURL(url string, wg *sync.WaitGroup) {
    defer wg.Done()
    
    resp, err := http.Get(url)
    if err != nil {
        fmt.Printf("获取 %s 失败: %v\n", url, err)
        return
    }
    defer resp.Body.Close()
    
    body, _ := io.ReadAll(resp.Body)
    fmt.Printf("%s: %d 字节\n", url, len(body))
}

func main() {
    urls := []string{
        "https://api.github.com",
        "https://httpbin.org/get",
        "https://jsonplaceholder.typicode.com/posts/1",
    }
    
    var wg sync.WaitGroup
    for _, url := range urls {
        wg.Add(1)
        go fetchURL(url, &wg)
    }
    wg.Wait()
}

Go在以下场景中特别有优势:

  • 需要高并发处理的网络服务
  • 需要快速启动和低内存占用的服务
  • 需要部署简单的CLI工具
  • 微服务架构中的各个服务组件
  • 需要精确控制并发和资源使用的场景

对于PDF处理这类特定领域,Go的生态确实可能不如Python成熟。但在网络编程、并发处理和系统工具开发方面,Go提供了PHP和Python难以比拟的性能和部署优势。

回到顶部