实践Golang语言N层应用架构

本文涉及的产品
Serverless 应用引擎 SAE,800核*时 1600GiB*时
可观测链路 OpenTelemetry 版,每月50GB免费额度
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 【10月更文挑战第2天】本文介绍了如何在Go语言中使用Gin框架实现N层体系结构,借鉴了J2EE平台的多层分布式应用程序模型。文章首先概述了N层体系结构的基本概念,接着详细列出了Go语言中对应的构件名称,包括前端框架(如Vue.js、React)、Gin的处理函数和中间件、依赖注入和配置管理、会话管理和ORM库(如gorm或ent)。最后,提供了具体的代码示例,展示了如何实现HTTP请求处理、会话管理和数据库操作。

1 简介:

本文首先概述了N层体系结构的基本概念,说明了不同逻辑功能的应用程序如何被封装到不同的构件中。

接着,详细列出了在Go语言中对应的构件名称,包括前端框架(如Vue.js、React)替代Applet,Gin的处理函数和中间件对应Servlet,依赖注入和配置管理替代EJB容器,使用中间件管理会话对应SessionBean,以及使用ORM库(如gorm或ent)进行数据库交互对应EntityBean。

最后,提供了具体的代码示例,展示了如何实现HTTP请求处理、会话管理和数据库操作。

  • 回望N层体系结构

很多平台采用了多层分布式作为应用程序模型,比如J2EE平台实现不同逻辑功能的应用程序被封装到不同的构件中,处于不同层次的构件可被分别部署到不同的机器中。

question_ans.png

如下图是已完成N层体系结构,

客户端         Web               (EJB容器)       DB

浏览器      Web服务器         Simple 
                               Bean   -->      (EntityBean)  -->  ----
HTML        <--  JSP              --->              <---   |  |
(Applet)  -->   (Servlet) <---    (SessionBean)   <--          ----

    J2EE 的N层体系结构示意图。

在该平台的N层体系结构应用程序模型中,实现不同逻辑功能的应用程序被封装到不同的构件中,
比如浏览器 HTML对应构件Applet,JSP对应构件Servlet,EJB容器对应 Bean(包括Simple Bean,SessionBean),与数据库交互有EntityBean,这样对应用程序就有较好的整体一致性的开发体验,

2 实现N层体系结构

假如我们要在golang语言中利用Gin框架的构件实现N层结构,或者借助其他web开发框架对应构件,以下为go语言中对应的构件名称

  • Applet

在现代 web 开发中,Applet 已不再常用。
一般使用前端框架如 Vue.js、React 或 Angular 来实现类似的功能。
它们与服务器通过 RESTful API 进行交互。

  • Servlet

在go语言框架中,比如Gin 的处理函数(Handler)和中间件(Middleware)可以看作是 Servlet 的对应物。它们负责处理 HTTP 请求和生成响应。

  • EJB 容器。

Go 语言中没有直接对应的 EJB 容器,但 Gin 框架及其生态系统中有很多库和工具可以实现类似的功能。
例如:

Dependency Injection (DI):可以使用依赖注入库如 google/wire 或 uber/dig。
Configuration Management:可以使用 viper 来管理配置。
  • SessionBean

在 Gin 框架中,可以使用中间件来管理会话。
常用的会话中间件包括 gin-contrib/sessions,它可以处理会话数据的存储和管理。

  • EntityBean

对应于与数据库交互的 EntityBean,在 Go 语言中,常用 ORM 库如 gorm 或 ent 可以实现类似的功能。
它们提供了与数据库进行 CRUD 操作的方便方法。

3 go 语言N层体系实例:

实现 Handler(Servlet)

package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

实现 Session Management(SessionBean)

package main

import (
    "github.com/gin-gonic/gin"
    "github.com/gin-contrib/sessions"
    "github.com/gin-contrib/sessions/cookie"
)

func main() {
    r := gin.Default()
    store := cookie.NewStore([]byte("secret"))
    r.Use(sessions.Sessions("mysession", store))

    r.GET("/session", func(c *gin.Context) {
        session := sessions.Default(c)
        var count int
        v := session.Get("count")
        if v == nil {
            count = 0
        } else {
            count = v.(int)
            count++
        }
        session.Set("count", count)
        session.Save()
        c.JSON(200, gin.H{"count": count})
    })

    r.Run(":8080")
}

4 实现数据库ORM操作实例 Database Interaction(EntityBean)

  • 使用gorm

          package main
    
          import (
              "github.com/gin-gonic/gin"
              "gorm.io/driver/sqlite"
              "gorm.io/gorm"
          )
    
          type User struct {
              ID   uint   `json:"id" gorm:"primaryKey"`
              Name string `json:"name"`
          }
    

创建ORM对象映射并操作数据

        func main() {
            db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
            if err != nil {
                panic("failed to connect database")
            }

    db.AutoMigrate(&User{})

    r := gin.Default()

    r.POST("/users", func(c *gin.Context) {
        var user User
        if err := c.ShouldBindJSON(&user); err == nil {
            db.Create(&user)
            c.JSON(200, user)
        } else {
            c.JSON(400, gin.H{"error": err.Error()})
        }
    })

    r.GET("/users/:id", func(c *gin.Context) {
        var user User
        if err := db.First(&user, c.Param("id")).Error; err != nil {
            c.JSON(404, gin.H{"error": "User not found"})
        } else {
            c.JSON(200, user)
        }
    })

    r.Run(":8080")
}

5 使用ent的ORM数据操作

  • 使用 ent

结合 ent 作为 ORM 的完整示例,包括处理 HTTP 请求(Servlet)、会话管理(SessionBean)和数据库交互(EntityBean)。

项目结构

myapp/
├── main.go
├── ent/
│   ├── generate.go
│   ├── schema/
│   │   └── user.go
go.mod

首先,初始化一个新的 Go 模块并添加所需的依赖:

go mod init myapp
go get github.com/gin-gonic/gin
go get github.com/gin-contrib/sessions
go get github.com/gin-contrib/sessions/cookie
go get entgo.io/ent
go get entgo.io/ent/entc
go get entgo.io/ent/dialect/sqlite
ent/schema/user.go

创建 User 数据模型:

package schema

import (
    "entgo.io/ent"
    "entgo.io/ent/schema/field"
)

type User struct {
    ent.Schema
}

func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("name").NotEmpty(),
    }
}

func (User) Edges() []ent.Edge {
    return nil
}
ent/generate.go

生成 ent 代码:

package ent

//go:generate go run entgo.io/ent/cmd/ent generate ./schema

然后运行 go generate ./ent 生成代码。

main.go

在 main.go 中,设置 Gin 框架,配置会话管理,并实现 CRUD 操作:

package main

import (
    "context"
    "log"
    "net/http"

    "github.com/gin-contrib/sessions"
    "github.com/gin-contrib/sessions/cookie"
    "github.com/gin-gonic/gin"
    "entgo.io/ent/dialect/sqlite"
    "entgo.io/ent/dialect"
    "myapp/ent"
    _ "github.com/mattn/go-sqlite3"
)

func main() {

使用ent实例化数据库操作

    // Initialize ent client
    client, err := ent.Open(dialect.SQLite, "file:ent?mode=memory&cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("failed opening connection to sqlite: %v", err)
    }
    defer client.Close()
    // Run the auto migration tool.
    if err := client.Schema.Create(context.Background()); err != nil {
        log.Fatalf("failed creating schema resources: %v", err)
    }

    r := gin.Default()

设置会话存储和数据库操作

    // Set up session store
    store := cookie.NewStore([]byte("secret"))
    r.Use(sessions.Sessions("mysession", store))

    // Routes
    r.POST("/users", func(c *gin.Context) {
        var user struct {
            Name string `json:"name"`
        }
        if err := c.ShouldBindJSON(&user); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }

        u, err := client.User.Create().SetName(user.Name).Save(context.Background())
        if err != nil {
            c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
            return
        }
        c.JSON(http.StatusOK, u)
    })

    r.GET("/users/:id", func(c *gin.Context) {
        id := c.Param("id")
        u, err := client.User.Get(context.Background(), id)
        if err != nil {
            c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
            return
        }
        c.JSON(http.StatusOK, u)
    })

会话接口中实现了一个访问计数器,每次GET调用该接口将增加计数并保存到会话数据中。

    r.GET("/session", func(c *gin.Context) {
        session := sessions.Default(c)
        var count int
        v := session.Get("count")
        if v == nil {
            count = 0
        } else {
            count = v.(int)
            count++
        }
        session.Set("count", count)
        session.Save()
        c.JSON(http.StatusOK, gin.H{"count": count})
    })

    r.Run(":8080")
}

6 运行项目

确保所有依赖和生成的代码都已经准备好,然后运行应用:

go run main.go

现在,应用将在 http://localhost:8080 运行,可以使用 curl 或 Postman 等工具测试以下接口:

创建用户:POST /users
获取用户:GET /users/:id
会话管理:GET /session

7 小结

本文介绍了如何在Go语言中使用Gin框架实现N层体系结构,借鉴了J2EE平台的多层分布式应用程序模型。

示例中使用了gorm和ent作为ORM工具,展示了如何创建用户、获取用户信息以及管理会话数据。最后,强调了运行项目的步骤,并总结了如何在Gin框架中结合ent实现N层体系结构的基本功能。

这两个示例展示了如何在 Gin 框架中使用ORM来处理数据库交互,同时实现会话管理和基本的 HTTP 请求处理,实际的项目开发中可能需要更深入更自由地使用SQL编写复杂的查询,相比ORM工具此时使用查询生成器更具有优势。

目录
相关文章
|
4天前
|
编解码 Java 程序员
写代码还有专业的编程显示器?
写代码已经十个年头了, 一直都是习惯直接用一台Mac电脑写代码 偶尔接一个显示器, 但是可能因为公司配的显示器不怎么样, 还要接转接头 搞得桌面杂乱无章,分辨率也低,感觉屏幕还是Mac自带的看着舒服
|
6天前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1551 7
|
1月前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
9天前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
639 25
|
6天前
|
存储 SQL 关系型数据库
彻底搞懂InnoDB的MVCC多版本并发控制
本文详细介绍了InnoDB存储引擎中的两种并发控制方法:MVCC(多版本并发控制)和LBCC(基于锁的并发控制)。MVCC通过记录版本信息和使用快照读取机制,实现了高并发下的读写操作,而LBCC则通过加锁机制控制并发访问。文章深入探讨了MVCC的工作原理,包括插入、删除、修改流程及查询过程中的快照读取机制。通过多个案例演示了不同隔离级别下MVCC的具体表现,并解释了事务ID的分配和管理方式。最后,对比了四种隔离级别的性能特点,帮助读者理解如何根据具体需求选择合适的隔离级别以优化数据库性能。
209 3
|
1天前
|
Java 开发者
【编程进阶知识】《Java 文件复制魔法:FileReader/FileWriter 的奇妙之旅》
本文深入探讨了如何使用 Java 中的 FileReader 和 FileWriter 进行文件复制操作,包括按字符和字符数组复制。通过详细讲解、代码示例和流程图,帮助读者掌握这一重要技能,提升 Java 编程能力。适合初学者和进阶开发者阅读。
100 60
|
13天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
615 5
|
12天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
2天前
vue3+Ts 二次封装ElementUI form表单
【10月更文挑战第8天】
107 56
|
25天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。