从Nodejs迁移到Golang:如何入门?
从Nodejs迁移到Golang:如何入门? 我对学习Go感到非常兴奋,并且已经阅读了许多关于在生产环境中使用Go的成功案例。
然而,当你拥有一个庞大的NodeJS单体代码库时,该如何开始呢?
我相信每个微服务都能从使用Go中受益,但替换一个微服务可能需要数月的工作,因为我们为实现与Kafka、MySQL和MongoDB等交互而编写的工具库(“包装器”)。
我曾考虑从GoLang调用这些用NodeJS编写的库——并在我有时间时替换它们,但我不确定这是否可行。
谢谢。
感谢您的回复,我将逐一回应。
或者,进行这次变更的主要原因可能是为了您/您的团队学习一项新技术
是为了改善CPU/内存消耗。例如:在Node.js中,GQL解析操作耗时较长。
在审视架构时,您能否识别出一个小的部分
说得好,我可以将“一个函数”隔离到一个新的微服务中,并逐步迁移其余代码。
Node和Go的生态系统不同,与各种技术交互的方式也不同
正确。
例如,如果您能为您的应用程序采用“端口与适配器”风格,您就可以解耦处理,例如Kafka的处理
我们目前正是这样做的,但适配器做了大量工作来抽象底层技术并简化其使用。这就是为什么我考虑将适配器保留在NodeJS中,并从Golang调用它,然后逐步替换适配器。如果Golang可以调用NodeJS,并让Kafka和MongoDB的TCP/IP连接在nodejs进程中保持活跃,对我来说可能会更容易。用Golang重写所有适配器可能需要一些时间 😊
更多关于从Nodejs迁移到Golang:如何入门?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
首先,我认为你需要明确为什么想要更换技术。是否是因为你现在遇到了某个问题,而你认为Node是其中的瓶颈(例如,也许你的应用程序——或其一部分——需要执行CPU密集型操作,而Node无法跟上)?或者,更换的主要原因可能是你或你的团队想要学习一门新技术。这也是一个很好的理由。
接下来,我会仔细审视当前Node应用程序的架构。你是否可以先在那里进行一些调整,以解决上述问题?先把火扑灭,这样你就可以在没有压力的情况下规划向Go的迁移。在审查架构时,你能识别出应用程序中一个可以解耦成独立服务的小部分吗?——也许是应用程序关键路径之外的某个功能。这将是开始迁移的绝佳候选——你可以引入一个用Go编写的新组件,它与当前的Node应用程序协作,为用户提供相同的功能。可能的例子包括:从外部世界导入数据;生成报告;与用户进行异步通信——当然,这些可能不适用于你的应用程序,但我希望你能明白我想表达的观点。接下来,你可以逐步剥离应用程序的其他部分——如此反复。
关于你提到的你或你的团队围绕不同技术构建的包装器——我不一定认为这些是迁移的障碍。Node和Go的生态系统是不同的,与各种技术交互的方式也不同。我敢说,你可能不需要完全按照Node应用程序的方式原样迁移。回到你的应用程序架构,也许你可以重新设计与外部世界交互的方式——例如,如果你能为你的应用程序采用“端口和适配器”风格,你就可以解耦处理,比如与Kafka的交互。这样,你就可以编写一个符合Go语言习惯的适配器来与Kafka交互,这与你的Node应用程序的做法毫无关系。
如果你能够或愿意分享你当前应用程序的详细信息以及你进行变更的动机,我很乐意进行更深入的探讨。
从Node.js迁移到Go是一个明智的决策,尤其是在需要更高性能和并发处理的微服务场景中。针对你提到的庞大Node.js单体代码库和现有工具库的迁移问题,以下是一些具体的实现方案和示例代码。
1. 渐进式迁移策略
不要一次性重写整个单体应用,而是采用逐步迁移的方式。你可以从边缘服务或新功能开始,逐步用Go替换Node.js模块。
示例:使用Go编写新的微服务
假设你有一个用户管理模块,可以先将其拆分为独立的Go微服务。
package main
import (
"encoding/json"
"net/http"
"github.com/gorilla/mux"
)
type User struct {
ID string `json:"id"`
Name string `json:"name"`
}
func GetUser(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
user := User{ID: vars["id"], Name: "John Doe"}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/users/{id}", GetUser).Methods("GET")
http.ListenAndServe(":8080", r)
}
2. 通过RPC或REST调用现有Node.js库
在迁移过程中,你可以让Go服务通过RPC或REST API调用现有的Node.js工具库,直到这些库被完全重写。
示例:Go服务调用Node.js包装器
假设你有一个Node.js的Kafka生产者包装器运行在http://localhost:3000/produce,Go服务可以通过HTTP客户端调用它。
package main
import (
"bytes"
"encoding/json"
"net/http"
)
func callNodeJSKafkaProducer(message string) error {
data := map[string]string{"message": message}
jsonData, _ := json.Marshal(data)
resp, err := http.Post("http://localhost:3000/produce", "application/json", bytes.NewBuffer(jsonData))
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
func main() {
// 调用Node.js的Kafka生产者
callNodeJSKafkaProducer("Hello from Go service")
}
3. 逐步重写工具库
针对Kafka、MySQL和MongoDB的包装器,可以逐个用Go重写。Go有成熟的驱动库,如github.com/segmentio/kafka-go、github.com/go-sql-driver/mysql和go.mongodb.org/mongo-driver。
示例:用Go重写Kafka生产者
package main
import (
"context"
"github.com/segmentio/kafka-go"
)
func produceKafkaMessage(broker, topic, message string) error {
writer := kafka.NewWriter(kafka.WriterConfig{
Brokers: []string{broker},
Topic: topic,
})
defer writer.Close()
msg := kafka.Message{
Value: []byte(message),
}
return writer.WriteMessages(context.Background(), msg)
}
func main() {
produceKafkaMessage("localhost:9092", "test-topic", "Message from Go")
}
示例:用Go重写MySQL查询
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
type Product struct {
ID int
Name string
}
func queryMySQL() {
db, err := sql.Open("mysql", "user:password@/dbname")
if err != nil {
panic(err)
}
defer db.Close()
rows, err := db.Query("SELECT id, name FROM products")
if err != nil {
panic(err)
}
defer rows.Close()
for rows.Next() {
var p Product
rows.Scan(&p.ID, &p.Name)
fmt.Printf("Product: %+v\n", p)
}
}
4. 使用API网关进行路由
在迁移期间,可以使用API网关(如Kong或Traefik)将请求路由到Node.js或Go服务,实现平滑过渡。
总结
迁移过程可以这样进行:
- 保持现有Node.js单体运行
- 新功能用Go实现
- 通过API调用现有Node.js工具库
- 逐步用Go重写关键工具库
- 最终将流量完全切换到Go微服务
这种方法最小化了迁移风险,并允许团队在保持系统运行的同时逐步积累Go经验。

