Go 简单实现多租户设计

简介: 在 SaaS 系统中多租户是一个很重要的架构,在服务上仅需运行一个软件实例就能够支持每个租户的功能,它们之间的数据互相隔离。就比如 Gitee ,每个用户或组织都拥有自己的空间。它们自己的数据仅在自己的空间内流通,彼此之间互不影响。多租户其实就是一种软件设计结构。所以。租户模型其实就是分离数据的方式:• 单租户:一个应用一个数据库。其实就是应用分离,数据分离。• 多租户:一个应用多个数据库。就是同一个应用不同数据库。

多租户

在 SaaS 系统中多租户是一个很重要的架构,在服务上仅需运行一个软件实例就能够支持每个租户的功能,它们之间的数据互相隔离。就比如 Gitee ,每个用户或组织都拥有自己的空间。它们自己的数据仅在自己的空间内流通,彼此之间互不影响。多租户其实就是一种软件设计结构。

所以。租户模型其实就是分离数据的方式:

  • 单租户:一个应用一个数据库。其实就是应用分离,数据分离。
  • 多租户:一个应用多个数据库。就是同一个应用不同数据库。

为什么需要多租户?

刚毕业第一家公司,接触的第一个项目的内容主要帮助客户提高生产力。每次有一个很大的问题就是,合作一个客户,项目复制一份出来,然后改成该客户定制的功能需求。这样就导致相同功能要更新多份代码,还要注意可能会出现因不同客户的不同需求问题。整个过程就导致了一个很大的维护成本的支出。

多租户应用程序的维护更容易,只需根据每个租户专属标志,然后提供不同的功能,且公共的功能可以集中更新升级。

数据隔离方式

在 SaaS 系统中一般分离租户的数据有两种模型:

  • 数据的逻辑分离:所有租户只使用一个数据库。它们的数据通过为每个租户使用一个唯一标识符来分隔。
  • 数据的物理分离:一个租户分配一个数据库。就是一个应用对应多个数据库。每个数据库就是一个租户。这种方式在客户的增长时扩展了应用的功能,也方便扩展数据库

物理分离详细实现

目标:每个请求过来,应用程序能够识别租户,并从该租户数据库中进行提供数据。

  • 公共数据库:存储所有租户相关全局配置和所有租户数据库信息。
  • 租户数据库:每个租户独立的的数据库,根据租户需要保存数据。

启动服务

创建 main 文件,并使用 Gin 框架。初始化一个简单的应用程序并创建基本的路由。

func main() {

r := gin.Default()

r.GET("/ping", func(c *gin.Context) {

c.JSON(http.StatusOK, gin.H{

  "message": "pong",

})

})

r.Run()

}


数据库配置

在公共数据库创建一个保存租户数据库信息表并添加上租户的数据库。

Create Table db_tenant (

id int primary key,

db_name varchar(100) unique not null,

db_domain  varchar(100) unique not null,

conn_str varchar(200) not null,

remark varchar(1000) ,

)

在配置文件中加入一个公共数据库的连接配置,这样就可以在服务启动时第一次连接的是公共数据库。接着需要实现根据请求连接到正确的租户数据库。主要使用 gorm 的 DBResolver ,多个数据库的连接支持

func allDbConnect(db *db.gorm) (allDbs map[string]) {

 db.Model(&db_tenant).Find(&allDbs)


   var gconn []gorm.Dialector

  for _,connStr := range allDbs {

      db :=mysql.Open(connStr)

  gconn = append(gconn,db)

   }

db.Use(dbresolver.Register(dbresolver.Config{

 Sources:  gconn,

 // sources/replicas 负载均衡策略

 Policy: dbresolver.RandomPolicy{},

})

  return allDbs

}

通过使用中间件来解析每个请求连接,从而确定整个请求是由哪个租户数据库来提供数据读写。中间件中主要的处理方式是得到租户对应的数据库连接名,然后从连接池中拿到该连接并设置在全局变量中。在业务逻辑处理中从全局拿到数据库连接,就可以进行数据库读写。

r.Use(middlerware.dbResolve)


func dbResolve(ctx *gin.Context) {

dbName := ctx.Query("dbName")


setGlobalDb(dbName)

 

}


func dbOperater(){

db := getDb()

 var users []User

 db.Clauses(dbresolver.Use(dbName)).First(&users)

}


这样就可以启动项目,然后访问 localhost:8003?dbName=db1 ,这样就可以正常进行数据读写。

其实可以从域名上解析对应的数据库连接。这里最重要就是对数据库的解析。这里的例子只是实验使用,其中还有很多可优化的点。比如可以考虑使用连接池,这样就不用在项目启动时都全部创建数据库连接,造成浪费。

相关文章
|
消息中间件 Go
rabbitMQ go语言简单实现
rabbitMQ go语言简单实现
64 0
|
Go
go 简单实现分布式锁
单一执行,通过分布式锁实现
559 0
|
Go
go语言简单实现加载ini文件
go语言简单实现加载ini文件
136 0
go语言简单实现加载ini文件
|
2月前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
85 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
2月前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
47 7
|
2月前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
2月前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
116 71
|
2月前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
117 67
|
9天前
|
存储 监控 算法
内网监控系统之 Go 语言布隆过滤器算法深度剖析
在数字化时代,内网监控系统对企业和组织的信息安全至关重要。布隆过滤器(Bloom Filter)作为一种高效的数据结构,能够快速判断元素是否存在于集合中,适用于内网监控中的恶意IP和违规域名筛选。本文介绍其原理、优势及Go语言实现,提升系统性能与响应速度,保障信息安全。
22 5
|
18天前
|
算法 安全 Go
Go语言中的加密和解密是如何实现的?
Go语言通过标准库中的`crypto`包提供丰富的加密和解密功能,包括对称加密(如AES)、非对称加密(如RSA、ECDSA)及散列函数(如SHA256)。`encoding/base64`包则用于Base64编码与解码。开发者可根据需求选择合适的算法和密钥,使用这些包进行加密操作。示例代码展示了如何使用`crypto/aes`包实现对称加密。加密和解密操作涉及敏感数据处理,需格外注意安全性。
38 14
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等