首页
留言
友情链接
壁纸
更多
关于
Search
1
dockerfile怎么执行shell脚本(利用tail -f /dev/null命令防止container启动后退出)
5,016 阅读
2
channel常见的异常总结
4,276 阅读
3
支付宝支付配置开发流程
1,430 阅读
4
HTTP 协议中的Content-Encoding
1,286 阅读
5
Laravel底层原理(二) —— 契约(Contracts)
950 阅读
PHP
composer
laravel
swoole
Docker
Linux
Go
随笔
mysql
nginx
Search
标签搜索
kratos
golang
微服务
gopher
PHP
Docker
dockerfile
通道
go
defer
alipay
支付
git
phpstorm
IDEA
web安全
漏洞
socket
设计原则
依赖注入
Royal
累计撰写
51
篇文章
累计收到
0
条评论
首页
栏目
PHP
composer
laravel
swoole
Docker
Linux
Go
随笔
mysql
nginx
页面
留言
友情链接
壁纸
关于
搜索到
22
篇与
Go
的结果
2025-02-05
(五)kratos的依赖注入库- Wire
理解依赖注入 什么是依赖注入?为什么要依赖注入? 依赖注入 (Dependency Injection,缩写为 DI),可以理解为一种代码的构造模式(就是写法),按照这样的方式来写,能够让你的代码更加容易维护。对于很多软件设计模式和架构的理念,我们都无法理解他们要绕好大一圈做复杂的体操、用奇怪的方式进行实现的意义。他们通常都只是丢出来一段样例,说这样写就很好很优雅,由于省略掉了这种模式是如何发展出来的推导过程,我们只看到了结果,导致理解起来很困难。那么接下来我们来尝试推导还原一下整个过程,看看代码是如何和为什么演进到依赖注入模式的,以便能够更好理解使用依赖注入的意义。依赖是什么? 这里的依赖是个名词,不是指软件包的依赖(比如那坨塞在 node_modules 里面的东西),而是指软件中某一个模块(对象/实例)所依赖的其它外部模块(对象/实例)。注入到哪里? 被依赖的模块,在创建模块时,被注入到(即当作参数传入)模块的里面。Wire 依赖注入 Wire 是一个灵活的依赖注入工具,通过自动生成代码的方式在编译期完成依赖注入。在各个组件之间的依赖关系中,通常鼓励显式初始化,而不是全局变量传递。所以通过 Wire 进行初始化代码,可以很好地解决组件之间的耦合,以及提高代码维护性。安装工具# 导入到项目中 go get -u github.com/google/wire # 安装命令 go install github.com/google/wire/cmd/wire工作原理 Wire 具有两个基本概念:Provider 和 Injector。Provider 是一个普通的 Go Func ,这个方法也可以接收其它 Provider 的返回值,从而形成了依赖注入;// 提供一个配置文件(也可能是配置文件) func NewConfig() *conf.Data {...} // 提供数据组件,依赖了数据配置(初始化 Database、Cache 等) func NewData(c *conf.Data) (*Data, error) {...} // 提供持久化组件,依赖数据组件(实现 CURD 持久化层) func NewUserRepo(d *data.Data) (*UserRepo, error) {...}使用方式 在 Kratos 中,主要分为 server、service、biz、data 服务模块,会通过 Wire 进行模块顺序的初始化;在每个模块中,只需要一个 ProviderSet 提供者集合,就可以在 wire 中进行依赖注入;并且我们在每个组件提供入口即可,不需要其它依赖,例如:-data --data.go // var ProviderSet = wire.NewSet(NewData, NewGreeterRepo) --greeter.go // func NewGreeterRepo(data *Data, logger log.Logger) biz.GreeterRepo {...}然后通过 wire.go 中定义所有 ProviderSet 可以完成依赖注入配置。初始化组件 通过 wire 初始化组件,需要定义对应的 wire.go,以及 kratos application 用于启动管理。// 应用程序入口 cmd -main.go -wire.go -wire_gen.go // main.go 创建 kratos 应用生命周期管理 func newApp(logger log.Logger, hs *http.Server, gs *grpc.Server, greeter *service.GreeterService) *kratos.App { pb.RegisterGreeterServer(gs, greeter) pb.RegisterGreeterHTTPServer(hs, greeter) return kratos.New( kratos.Name(Name), kratos.Version(Version), kratos.Logger(logger), kratos.Server( hs, gs, ), ) } // wire.go 初始化模块 func initApp(*conf.Server, *conf.Data, log.Logger) (*kratos.App, error) { // 构建所有模块中的 ProviderSet,用于生成 wire_gen.go 自动依赖注入文件 panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp)) }在项目的 main 目录中,运行 wire 进行生成编译期依赖注入代码:wire总结 重新总结一下用 wire 做依赖注入的过程。 1. 定义 Injector 创建wire.go文件,定义下你最终想用的实例初始化函数例如initApp(即 Injector),定好它返回的东西*App,在方法里用panic(wire.Build(NewRedis, SomeProviderSet, NewApp))罗列出它依赖哪些实例的初始化方法(即 Provider)/或者哪些组初始化方法(ProviderSet)2. 定义 ProviderSet(如果有的话) ProviderSet 就是一组初始化函数,是为了少写一些代码,能够更清晰的组织各个模块的依赖才出现的。也可以不用,但 Injector 里面的东西就需要写一堆。 像这样 var SomeProviderSet = wire.NewSet(NewES,NewDB)定义 ProviderSet 里面包含哪些 Provider3. 实现各个 Provider Provider 就是初始化方法,你需要自己实现,比如 NewApp,NewRedis,NewMySQL,GetConfig 等,注意他们们各自的输入输出 4. 生成代码 执行 wire 命令生成代码,工具会扫描你的代码,依照你的 Injector 定义来组织各个 Provider 的执行顺序,并自动按照 Provider 们的类型需求来按照顺序执行和安排参数传递,如果有哪些 Provider 的要求没有满足,会在终端报出来,持续修复执行 wire,直到成功生成wire_gen.go文件。接下来就可以正常使用initApp来写你后续的代码了。如果需要替换实现,对 Injector 进行相应的修改,实现必须的 Provider,重新生成即可。
2025年02月05日
16 阅读
0 评论
0 点赞
2025-02-04
(四)基于 Kratos 的微服务架构的领域驱动设计(Domain-Driven Design, DDD)
领域驱动设计 (Domain-Driven Design, DDD)是一种软件开发方法论,它强调将业务领域知识与软件实现紧密结合,以提高软件对于复杂业务需求的适应性和可维护性。DDD由埃里克·埃文斯(Eric Evans)在其2003年出版的同名书籍《领域驱动设计》中首次提出,并逐渐成为处理复杂业务逻辑的一种重要指导思想。核心概念领域(Domain) :指的是业务活动或专业知识的特定范围。在软件开发中,领域通常是指应用程序所要解决的问题空间。模型(Model): 是对领域的抽象表示,用于捕捉领域中的核心概念及其相互关系。模型是领域专家和开发者之间沟通的基础。领域模型(Domain Model) :是一个概念性的模型,用来描述领域内的实体、它们之间的关系以及它们的行为。领域模型不仅仅是一组类图,而是对领域深层次理解的体现。通用语言(Ubiquitous Language) :是在领域专家和开发团队之间共享的一套术语和概念体系,旨在减少误解并促进有效沟通。上下文映射(Context Map) :用于明确不同子系统或模块之间的边界,以及它们如何交互。这有助于避免不同部分的模型发生混淆或冲突。限界上下文(Bounded Context) :定义了模型适用的具体边界,确保每个模型只在自己的上下文中保持一致性和完整性。聚合(Aggregate) :是一组相关对象的集合,被视为一个整体进行管理和操作。聚合根(Aggregate Root)是这个集合的入口点。实体(Entity) :具有唯一标识的对象,即使其属性完全相同,不同的实体也被视为不同的对象。值对象(Value Object) :没有独立的身份,其价值在于其属性的组合。两个具有相同属性的值对象被认为是等价的。服务(Service) :当一个操作不属于任何特定的实体或值对象时,可以将其设计为服务。服务通常是无状态的。仓库(Repository) :提供了一种访问机制,用于持久化存储中的聚合管理,使得客户端代码能够像操作内存中的集合一样操作这些聚合。实践原则聚焦于核心领域 :识别并优先考虑项目中最关键的部分,即核心领域,投入最多的资源和精力来优化这部分的设计和实现。深入探索领域 :通过持续地与领域专家合作,不断深化对领域的理解,确保模型能够准确反映现实世界的复杂性。 迭代式开发 :采用敏捷方法,快速迭代开发过程,及时调整设计方案以应对变化。持续重构 :随着对领域的理解加深,不断地对现有模型进行重构,以保持其清晰度和准确性。总之,领域驱动设计鼓励开发团队与业务领域紧密合作,通过构建精确反映业务需求的软件模型来提高软件的质量和业务价值。
2025年02月04日
13 阅读
0 评论
0 点赞
2025-02-03
(三)微服务用户管理系统开发实例:使用 Kratos 构建用户模块
一. 项目初始化 创建项目模板# 使用默认模板创建项目 kratos new user # 如在国内环境拉取失败, 可 -r 指定源 kratos new user -r https://gitee.com/go-kratos/kratos-layout.git cd user 二. 拉取项目依赖go mod tidy 三. 获取一个纯原生,无添加的代码框架删除api/helloworld/,使api文件为空删除internal/biz/greeter.go打开internal/biz/biz.go,将报红内容删除删除internal/data/greeter.go打开internal/data/data.go,将报红内容删除删除internal/service/greeter.go打开internal/service/service.go,将报红内容删除四. 连接数据库 安装gorm# 进入项目目录 cd user # 安装GORM go get -u gorm.io/gorm # 安装GORM的mysql驱动 go get -u gorm.io/driver/mysql .使用gorm服务内部目录中的data负责业务数据访问,包含 cache、db 等封装,实现了 biz 的 repo 接口。我们所有的处理数据逻辑全部写在internal/data打开/internal/data.go五.模型定义 自己定义一个用户的数据模型,用于对数据库进行操作。新建:在/biz目录下创建一个名为user.go的文件在internal/biz/user.go文件里添加如下内容为了让GORM知道我们定义了一个数据模型我们需要对internal/data/data.go文件进行修改到这里,我们就完成了用户数据模型的定义,并且让GORM知道我们定义了这个数据模型六. repo接口的定义 在repo接口中,我们需要定义一些基础操作数据库修改数据的函数回到internal/biz/user.go文件,继续敲代码我们创建一个私有的结构体去实现/biz/user.go中的biz.UserRepo接口七.编写一个接口 生成proto模板打开终端,执行以下指令# 进入"user"项目目录 cd user # 在"api/user/v1"目录下,生成名为"user"的proto模板 kratos proto add api/user/v1/user.proto 编写 proto 模板引入google/api/annotations.proto因为我们只写了用户添加模块,所以保留CreateUser,将生成的user.proto文件中其他接口删除给CreateUser添加路由,设置访问方法为POST,在添加一个body消息主体生成proto源码打开终端,执行以下指令# 在"api/user/v1"目录下,生成"user"的proto源码 kratos proto client api/user/v1/user.proto 八.生成service模板 在终端中执行下面的命令kratos proto server api/user/v1/user.proto -t internal/service 我们在用户服务结构体UserService中添加上一节中封装好的用户数据操作biz.UserUseCase。// UserService 用户服务 type UserService struct { pb.UnimplementedUserServer // uc 用户操作的封装,在“/biz/user.go”中 uc *biz.UserUseCase log *log.Helper }修改NewUserService函数的内容,给创建的UserService结构体初始化func NewUserService(uc *biz.UserUseCase, logger log.Logger) *UserService { return &UserService{ uc: uc, log: log.NewHelper(logger), } } 编写用户服务中的CreateUser服务接口,使得它能够处理前端发送的请求// CreateUser 创建用户服务接口 // 这里是一个给前端调用的接口 // 参数中的 req *pb.CreateUserRequest 为前端发送给后端的“创建用户请求参数” // 如果成功返回一个 *pb.CreateUserReply 它是前端需要得到的回复“创建用户返回结果” func (s *UserService) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.CreateUserReply, error) { // 获取前端发送的Body数据 body := req.GetCreatBody() // 创建用户 id, err := s.uc.CreateUser(ctx, body.GetName(), body.GetPasswd()) if err != nil { return nil, err } // 给前端返回数据 return &pb.CreateUserReply{ Id: uint64(id), }, nil } 按照惯例,让我们吧NewUserService函数添加到依赖提供者集中修改/service/service.go文件中的wire.NewSet()函数,在NewSet函数里添加NewUserService// ProviderSet is service providers. var ProviderSet = wire.NewSet(NewUserService) 注册HTTP服务器在HTTP服务器中注册我们写好的用户服务打开“/server/http.go”引入kratos 使用proto生成的源码和我们写好的用户服务import ( ...... user "user/api/user/v1" "user/internal/service" ) 在“NewHTTPServer”函数的参数中加入我们的用户服务“userService *service.UserService”,以便wire可以正确的注入依赖// NewHTTPServer new a HTTP server. func NewHTTPServer(c *conf.Server, userService *service.UserService, logger log.Logger) *http.Server { ...... } 注册用户服务HTTP服务器在“NewHTTPServer”函数的末尾加入“user.RegisterUserHTTPServer(srv, userService)”// NewHTTPServer new a HTTP server. func NewHTTPServer(c *conf.Server, userService *service.UserService, logger log.Logger) *http.Server { ...... srv := http.NewServer(opts...) user.RegisterUserHTTPServer(srv, userService) return srv } wire依赖注入我们“按照惯例”的将所有创建结构体的函数都加入到wire的依赖提供者集中,但这是为啥呢?为什么要将创建结构体的函数都加入到wire的依赖提供者集中呢?在进行接下来的操作前,请务必确定你的wire依赖提供者集依照上文和前两节中的添加正确在“/biz/biz.go”中// ProviderSet is biz providers. var ProviderSet = wire.NewSet(NewUserUseCase) 在“/data/data.go”中// ProviderSet is data providers. var ProviderSet = wire.NewSet(NewData, NewDataBase, NewUseRepo) 在“/service/service.go”中// ProviderSet is service providers. var ProviderSet = wire.NewSet(NewUserService) 在终端中输入命令# 进入项目目录 cd user # 生成所有proto源码、wire等等 go generate ./... 九.测试 根据上面的步骤来到这里,我们的用户添加操作就完成了。接下来我们要对自己写的接口进行测试,看看是否能跑通在终端中输入命令# 运行项目 kratos run
2025年02月03日
20 阅读
0 评论
0 点赞
2025-02-02
(二) Kratos 框架快速开始:CLI工具
一. 安装 三种安装方式任选其一:go get 安装go get -u github.com/go-kratos/kratos/cmd/kratos/v2@latestgo install 安装go install github.com/go-kratos/kratos/cmd/kratos/v2 # go 1.16版本以上需要指定版本号或使用最新版 go install github.com/go-kratos/kratos/cmd/kratos/v2@latest源码编译安装git clone https://github.com/go-kratos/kratos cd kratos make install二. 创建项目 通过 kratos 命令创建项目模板:kratos new helloworld(项目名称)使用 -r 指定源# 国内拉取失败可使用gitee源 kratos new helloworld -r https://gitee.com/go-kratos/kratos-layout.git # 亦可使用自定义的模板 kratos new helloworld -r xxx-layout.git # 同时也可以通过环境变量指定源 KRATOS_LAYOUT_REPO=xxx-layout.git kratos new helloworld使用 -b 指定分支kratos new helloworld -b main三.添加 Proto 文件 kratos-layout 项目中对 proto 文件进行了版本划分,放在了 v1 子目录下kratos proto add api/helloworld/demo.proto输出:api/helloworld/demo.proto四.生成 Proto 代码# 可以直接通过 make 命令生成 make api # 或使用 kratos cli 进行生成 kratos proto client api/helloworld/demo.proto会在proto文件同目录下生成:api/helloworld/demo.pb.go api/helloworld/demo_grpc.pb.go # 注意 http 代码只会在 proto 文件中声明了 http 时才会生成(如下图所示) api/helloworld/demo_http.pb.go五.生成 Service 代码 通过 proto文件,可以直接生成对应的 Service 实现代码:使用 -t 指定生成目录kratos proto server api/helloworld/demo.proto -t internal/service输出:internal/service/demo.go六. 运行项目 如子目录下有多个项目则出现选择菜单kratos run 七.查看版本kratos -v八.工具升级kratos upgrade九.更新日志# 等同于打印 https://github.com/go-kratos/kratos/releases/latest 的版本更新日志 kratos changelog # 打印指定版本更新日志 kratos changelog v2.1.4 # 查看自上次版本发布后的更新日志 kratos changelog dev十.查看帮助 kratos -h kratos new -h
2025年02月02日
24 阅读
0 评论
0 点赞
2025-02-01
(一)初识 Kratos 框架:Go 微服务开发的利器
一. Kratos 一套轻量级 Go 微服务框架,包含大量微服务相关功能及工具。名字来源于:《战神》游戏以希腊神话为背景,讲述奎托斯(Kratos)由凡人成为战神并展开弑神屠杀的冒险经历。 二. kratos遵循以下基本原则:简单:不过度设计,代码平实简单;通用:通用业务开发所需要的基础库的功能;高效:提高业务迭代的效率;稳定:基础库可测试性高,覆盖率高,有线上实践安全可靠;健壮:通过良好的基础库设计,减少错用;高性能:性能高,但不特定为了性能做 hack 优化,引入 unsafe ;扩展性:良好的接口设计,来扩展实现,或者通过新增基础库目录来扩展功能;容错性:为失败设计,大量引入对 SRE 的理解,鲁棒性高;工具链:包含大量工具链,比如 cache 代码生成,lint 工具等等; 三. kratos具有以下特性:APIs :协议通信以 HTTP/gRPC 为基础,通过 Protobuf 进行定义;Errors :通过 Protobuf 的 Enum 作为错误码定义,以及工具生成判定接口;Metadata :在协议通信 HTTP/gRPC 中,通过 Middleware 规范化服务元信息传递;Config :支持多数据源方式,进行配置合并铺平,通过 Atomic 方式支持动态配置;Logger :标准日志接口,可方便集成三方 log 库,并可通过 fluentd 收集日志;Metrics :统一指标接口,可以实现各种指标系统,默认集成 Prometheus;Tracing :遵循 OpenTelemetry 规范定义,以实现微服务链路追踪;Encoding :支持 Accept 和 Content-Type 进行自动选择内容编码;Transport :通用的 HTTP /gRPC 传输层,实现统一的 Middleware 插件支持;Registry :实现统一注册中心接口,可插件化对接各种注册中心;Validation: 通过Protobuf统一定义校验规则,并同时适用于HTTP/gRPC服务.SwaggerAPI: 通过集成第三方Swagger插件 能够自动生成Swagger API json并启动一个内置的Swagger UI服务. 四. kratos目录结构 . ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── api // 下面维护了微服务使用的proto文件以及根据它们所生成的go文件 │ └── helloworld │ └── v1 │ ├── error_reason.pb.go │ ├── error_reason.proto │ ├── error_reason.swagger.json │ ├── greeter.pb.go │ ├── greeter.proto │ ├── greeter.swagger.json │ ├── greeter_grpc.pb.go │ └── greeter_http.pb.go ├── cmd // 整个项目启动的入口文件 │ └── server │ ├── main.go │ ├── wire.go // 我们使用wire来维护依赖注入 │ └── wire_gen.go ├── configs // 这里通常维护一些本地调试用的样例配置文件 │ └── config.yaml ├── generate.go ├── go.mod ├── go.sum ├── internal // 该服务所有不对外暴露的代码,通常的业务逻辑都在这下面,使用internal避免错误引用 │ ├── biz // 业务逻辑的组装层,类似 DDD 的 domain 层,data 类似 DDD 的 repo,而 repo 接口在这里定义,使用依赖倒置的原则。 │ │ ├── README.md │ │ ├── biz.go │ │ └── greeter.go │ ├── conf // 内部使用的config的结构定义,使用proto格式生成 │ │ ├── conf.pb.go │ │ └── conf.proto │ ├── data // 业务数据访问,包含 cache、db 等封装,实现了 biz 的 repo 接口。我们可能会把 data 与 dao 混淆在一起,data 偏重业务的含义,它所要做的是将领域对象重新拿出来,我们去掉了 DDD 的 infra层。 │ │ ├── README.md │ │ ├── data.go │ │ └── greeter.go │ ├── server // http和grpc实例的创建和配置 │ │ ├── grpc.go │ │ ├── http.go │ │ └── server.go │ └── service // 实现了 api 定义的服务层,类似 DDD 的 application 层,处理 DTO 到 biz 领域实体的转换(DTO -> DO),同时协同各类 biz 交互,但是不应处理复杂逻辑 │ ├── README.md │ ├── greeter.go │ └── service.go └── third_party // api 依赖的第三方proto ├── README.md ├── google │ └── api │ ├── annotations.proto │ ├── http.proto │ └── httpbody.proto └── validate ├── README.md └── validate.proto 五. Kratos 项目初始化 环境准备: - go - protoc - protoc-gen-go首先要确保已经安装以上三个工具,而且go版本建议≥go1.23.6最新版本,不然后面安装依赖时会提示unknown directive: toolchain 建议开启GO111MODULEgo env -w GO111MODULE=on 六.创建项目# 使用默认模板创建项目 kratos new helloworld # 如在国内环境拉取失败, 可 -r 指定源 kratos new helloworld -r https://gitee.com/go-kratos/kratos-layout.git # 进入项目目录 cd helloworld # 拉取项目依赖 go mod download 七. 代码生成与运行# 安装依赖 go get github.com/google/wire/cmd/wire@latest # 生成所有proto源码、wire等等 go generate ./... # 运行项目 kratos run # 输出 INFO msg=config loaded: config.yaml format: yaml # 默认载入 configs/config.yaml 配置文件 INFO msg=[gRPC] server listening on: [::]:9000 # gRPC服务监听 9000 端口 INFO msg=[HTTP] server listening on: [::]:8000 # HTTP服务监听 8000 端口 八.测试HTTP接口相关逻辑代码位于 internal/service/greeter.go
2025年02月01日
19 阅读
0 评论
0 点赞
1
2
...
5