扩展 ======== 配置文件打包到可执行文件 ------------------------ .. code-block:: go cfg, err := juice.NewXMLConfiguration("config.xml") 这是我们最开始用来加载配置文件的方式。 这种方式有一个问题,当我们的go程序编制之后,需要依赖这个配置文件来运行,它们不是一个整体。 在go1.16标准库新增了embed这个库,它允许开发者将静态文件打包到go的可执行文件里面去。 juice也提供了这样的支持。 .. code-block:: go package main import ( "embed" "fmt" "github.com/go-juicedev/juice" ) //go:embed config.xml var fs embed.FS func main() { cfg, err := juice.NewXMLConfigurationWithFS(fs, "config.xml") if err != nil { panic(err) } fmt.Println(cfg) } 注意:当你的mappers里面引用了别的mapper文件的时候,你的所有的被引用的mapper文件也需要被打包进来。 如: .. code-block:: xml 这时候我们最好的做法是将这些配置文件放在一个文件夹下面。 .. code-block:: shell ├── config ├── config.xml └── mappers.xml 如上所示,我们的文件布局是这样的。 这时候我们只需要把代码改一下 .. code-block:: go package main import ( "embed" "fmt" "github.com/go-juicedev/juice" ) //go:embed config var fs embed.FS func main() { cfg, err := juice.NewXMLConfigurationWithFS(fs, "config/config.xml") if err != nil { panic(err) } fmt.Println(cfg) } 这样就解决上面的问题了。 读写分离 -------- Juice 提供了一个强大的读写分离实现,用于优化数据库性能和可扩展性。 配置多数据源 ~~~~~~~~~~~~ 首先,在配置文件中配置多个数据源: .. code-block:: xml root:qwe123@tcp(localhost:3306)/database mysql root:qwe123@tcp(localhost:3307)/database mysql root:qwe123@tcp(localhost:3308)/database mysql 默认情况下,Juice 只会连接 ``environments`` 中 ``default`` 属性指定的数据源。 建议将 ``master`` 设置为默认数据源以处理写操作。 启用读写分离 ~~~~~~~~~~~~ 要启用读写分离功能,需要使用 ``TxSensitiveDataSourceSwitchMiddleware`` 中间件: .. code-block:: go var engine *juice.Engine ... // 初始化 engine.Use(&juice.TxSensitiveDataSourceSwitchMiddleware{}) 路由策略 ~~~~~~~~ Juice 支持多种读操作路由策略,可以在语句级别或全局级别配置: 全局配置 ^^^^^^^^ 在项目的 ``settings`` 中配置默认的路由策略: .. code-block:: xml 语句级别配置 ^^^^^^^^^^^^ 1. **指定数据源** 明确指定从库进行读取: .. code-block:: xml 2. **随机路由** 使用 ``?`` 从所有可用数据源中随机选择(包括主库): .. code-block:: xml 3. **仅从库随机路由** 使用 ``?!`` 从从库中随机选择(排除主库): .. code-block:: xml 注意:语句级别的配置优先级高于全局配置。如果语句没有配置 ``dataSource`` 属性,则使用全局配置中的 ``selectDataSource`` 值。 事务安全 ~~~~~~~~ 中间件具有事务感知能力,当检测到当前操作在事务中时,将不会进行数据源切换,直接使用当前事务的数据源,以保证事务的完整性和数据一致性。 最佳实践 ~~~~~~~~ 1. 所有写操作使用主库 2. 读密集型操作使用 ``?!`` 在从库间分散负载 3. 当需要特定从库特性时,使用显式路由(如 ``slave1``、 ``slave2`` ) 4. 当读一致性要求不高时,可以使用 ``?`` 让主库也参与负载均衡 使用场景 ~~~~~~~~ 读写分离特别适用于: - 读操作密集的场景 - 需要扩展读取能力 - 需要减轻主库负载 - 提升整体应用性能 链路追踪 -------- 跟上面读写分离一个道理,想要对实现代码的无侵入式的增加新功能,我们可以利用中间件来链路追踪。 下面是一个伪代码 .. code-block:: go type TraceMiddleware struct{} func (r TraceMiddleware) QueryContext(_ juice.Statement, _ juice.Configuration, next juice.QueryHandler) juice.QueryHandler { return func(ctx context.Context, query string, args ...any) (sql.Rows, error) { trace.Log(ctx, "query", query) // your own trace return next(ctx, query, args...) } } func (r TraceMiddleware) ExecContext(_ juice.Statement, _ juice.Configuration, next juice.ExecHandler) juice.ExecHandler { return func(ctx context.Context, query string, args ...any) (sql.Result, error) { trace.Log(ctx, "exec", query) // your own trace return next(ctx, query, args...) } } XML文档约束 ----------- DTD ~~~~~ XML 文档约束(XML Document Type Definition,DTD)是一种用于定义 XML 文档结构和规则的文档类型定义语言。通过使用 DTD,我们可以约束一个 XML 文档只能包括哪些元素、元素的属性、元素之间的关系和顺序等信息。 在实际应用中,通常需要将 DTD 文件与 XML 文档关联起来,以便在解析 XML 文档时自动进行验证。在 XML 文档中,可以通过 元素来指定 DTD 文件及其位置。 举例来说,在 juice 的配置文件 config.xml 或者 mapper.xml 中,我们可以通过指定 元素中的 PUBLIC 属性和 URI 来关联 DTD 文件。这样,当我们在编辑器或者其他工具中打开 XML 文件时,就可以根据 DTD 定义的规则检查 XML 文档是否符合规范,并且发现潜在的错误和问题。 config xml .. code-block:: xml mapper xml .. code-block:: xml XSD ~~~~ XML Schema Definition (XSD) 是 DTD 的继任者,提供了更强大和灵活的 XML 文档验证机制。相比 DTD,XSD 具有以下优势: 1. **类型系统** - 支持更丰富的数据类型 - 可以自定义复杂类型 - 支持类型继承和扩展 2. **命名空间支持** - 更好的模块化支持 - 避免命名冲突 - 更容易管理大型 XML 结构 3. **可读性** - 使用 XML 语法编写 - 更容易理解和维护 - 更好的工具支持 在 Juice 中使用 XSD: .. code-block:: xml