使用 Service Weaver,您可以将应用程序编写成模块化单体,使用组件将应用程序模块化。Service Weaver 中的组件以 Go 接口为模型,您可以为业务逻辑提供具体实现,而无需与网络或序列化代码耦合。组件是一种代表计算实体的 Actor。这些围绕核心业务逻辑构建的模块化组件可以像本地方法调用一样调用其他组件的方法,无论这些组件是以模块化二进制形式运行还是以微服务形式运行,都无需使用 HTTP 或 RPC。
当这些组件作为微服务或在不同机器上运行时,Service Weaver 会使用 RPC 机制进行方法调用。否则,将是本地方法调用。您不必担心您的组件如何调用其他组件的方法。您可以在本地测试 Service Weaver 应用程序,然后将其部署到云中。Service Weaver 可让您专注于代码的工作,而不必担心代码在哪里运行。您可以在同一进程中以单体二进制形式运行组件,也可以在不同机器上以微服务形式运行组件,还可以轻松地扩大或缩小规模,以应对可扩展性挑战。在构建现代应用程序时,添加可观察性组件非常重要。Service Weaver 集成了可观察性,并拥有日志、度量和跟踪库。
go install github.com/ServiceWeaver/weaver/cmd/weaver@latestService Weaver Components
type Service interface { MakeInterview(ctx context.Context, golang model.Golang) error } type implementation struct { weaver.Implements[Service] } func (s *implementation) MakeInterview(ctx context.Context, golang model.Golang) error { defer s.Logger(ctx).Info( "talk about golang", "channel: ", golang.Channel, "goroutine: ", golang.Goroutine, ) return nil }在前面的代码块中,我们定义了一个 Service 接口,将其作为 interview 组件的模型,并通过嵌入通用类型 weaver.Implements[T],在 implementation 结构中提供了具体的实现。与 interview 服务类似,我们实现一个 working 的服务。
type Service interface { Working(ctx context.Context, golang model.Golang) error } type implementation struct { weaver.Implements[Service] } func (s *implementation) Working(ctx context.Context, golang model.Golang) error { defer s.Logger(ctx).Info( "working, fixing golang bug", "channel bug: ", golang.Channel, "goroutines bug: ", golang.Goroutine, ) return nil }通过嵌入 weaver.Implements[T],我们可以在 Service Weaver 中创建组件。模块化二进制组件将由实现 weaver.Implements[weaver.Main] 类型的主组件创建。让我们编写一个组件,在 studyService 组件中嵌入 weaver.Implements[weaver.Main] 类型,该组件用于运行 HTTP 服务器进行学习。
type Server struct { weaver.Implements[weaver.Main] handler http.Handler interviewService weaver.Ref[interview.Service] working weaver.Ref[working.Service] studyapi weaver.Listener `weaver:"studyapi"` }与其他组件比较
interviewService weaver.Ref[interview.Service] working weaver.Ref[working.Service]study 组件通过公开 HTTP 服务器来允许网络流量,因此我们保留了 http.Handler 类型的属性处理程序来创建 HTTP 服务器。
handler http.Handler网络监听
studyapi weaver.Listener `weaver:"studyapi"`weaver.Listener 字段可以从 .toml 配置文件中读取监听器地址。结构标记 weaver: "studyapi" 从名为 weaver.toml 的配置文件中读取监听器地址。Weaver Services 使用 .toml 文件进行配置。
[serviceweaver] binary = "./studyapp" [single] listeners.studyapi = {address = "localhost:13000"} [multi] listeners.studyapi = {address = "localhost:3000"}初始化组件
func (s *Server) Init(ctx context.Context) error { s.Logger(ctx).Info("Init") r := chi.NewRouter() r.Route("/api/study", func(r chi.Router) { r.Post("/", s.Study) r.Get("/{type}", s.GetKnowledges) }) s.handler = r return nil }为 Service Weaver 应用程序提供服务
func Serve(ctx context.Context, s *Server) error { s.Logger(ctx).Info("StudyApi listener available.", "addr:", s.studyapi) httpServer := &http.Server{ Handler: s.handler, } httpServer.Serve(s.studyapi) return nil }在 Serve 函数中,我们创建了一个 HTTP 服务器,并使用 weaver.Listener 属性为其提供服务。
type Server struct { weaver.Implements[weaver.Main] handler http.Handler interviewService weaver.Ref[interview.Service] working weaver.Ref[working.Service] studyapi weaver.Listener `weaver:"studyapi"` } func (s *Server) Init(ctx context.Context) error { s.Logger(ctx).Info("Init") r := chi.NewRouter() r.Route("/api/study", func(r chi.Router) { r.Post("/", s.Study) r.Get("/{type}", s.GetKnowledges) }) s.handler = r return nil } // 堆代码 duidaima.com func Serve(ctx context.Context, s *Server) error { s.Logger(ctx).Info("StudyApi listener available.", "addr:", s.studyapi) httpServer := &http.Server{ Handler: s.handler, } httpServer.Serve(s.studyapi) return nil }调用引用组件的方法
interviewService weaver.Ref[interview.Service] working weaver.Ref[working.Service]当 Service Weaver 创建组件实例时,它也会自动创建引用的组件(weaver.Ref[T] 字段)。为了从 weaver.Ref[T] 字段中获取组件实例,调用组件的方法,只需调用 weaver.Ref[T] 字段中的 Get 方法即可。例如,下面的代码块获取了 interview 组件:
interview := s.interviewService.Get()在本示例演示中,学习时,interview 组件用于面试,working 用于工作。这是在 HTTP 处理程序代码中实现的,该代码用于处理 study 的 HTTP Post 请求。
var ctx = context.Background() func (s *Server) Study(writer http.ResponseWriter, request *http.Request) { var golang model.Golang err := json.NewDecoder(request.Body).Decode(&golang) if err != nil { http.Error(writer, "Invalid Course Data", 500) return } golang.Goroutine = "GMP" golang.Channel = "blocking" if err := s.interviewService.Get().MakeInterview(ctx, golang); err != nil { s.Logger(ctx).Error( "interview failed", "error:", err, ) http.Error(writer, "interview failed", http.StatusInternalServerError) return } golang.Goroutine = "fixing goroutine bug" golang.Channel = "non-blocking" // send notification using notificationService component if err := s.working.Get().Working(ctx, golang); err != nil { s.Logger(ctx).Error( "fixing bug failed", "error:", err, ) } s.Logger(ctx).Info("those bugs has been fixed") writer.Header().Set("Content-Type", "application/json") writer.WriteHeader(http.StatusOK) }在前面的代码块中,通过调用 weaver.Ref[T] 字段的 Get 方法和调用组件的方法,创建了引用组件。
if err := s.interviewService.Get().MakeInterview(ctx, golang); err != nil {} if err := s.working.Get().Working(ctx, golang); err != nil {}使用 weaver generate 命令生成代码
weaver generate .下面的命令为当前目录及其所有子目录生成代码:
weaver generate ./...基于上面的代码,当我们执行上面语句的时候,会提示我们没有序列化:
interview\interview.go:10:2: Method `MakeInterview(ctx context.Context, golang model.Golang) (error)` of Service Weaver component "Service" has incorrect argument types. Argument 1 has type model.Golang, which is not serializable. All arguments, besides the initial context.Context, must be serializable. model.Golang: named structs are not serializable by default. Consider using weaver.AutoMarshal. working\working.go:10:2: Method `Working(ctx context.Context, golang model.Golang) (error)` of Service Weaver component "Service" has incorrect argument types. Argument 1 has type model.Golang, which is not serializable. All arguments, besides the initial context.Context, must be serializable. model.Golang: named structs are not serializable by default. Consider using weaver.AutoMarshal. -: # github.com/timliudream/go-test/serviceWeaverDemo/study study\study.go:30:22: s.GetKnowledges undefined (type *Server has no field or method GetKnowledges) D:\gopath\src\github.com\timliudream\go-test\serviceWeaverDemo\study\study.go:30:22: s.GetKnowledges undefined (type *Server has no field or method GetKnowledges)下面我们来讲讲序列化的事情。
type Golang struct { weaver.AutoMarshal Channel string Goroutine string }运行 Service Weaver 应用程序
func main() { if err := weaver.Run(context.Background(), study.Serve); err != nil { log.Fatal(err) } }在前面的代码块中,study.Serve 是一个函数,其签名为 func(context.Context, *T) error,其中 T 是主组件的结构实现。函数 weaver.Run 会自动创建一个实现 weaver.Main 的主组件实例。如果组件实现中提供了任何 Init 方法,则将用于创建实例。
go run .服务运行在“127.0.0.1:50076”,输出如下:
╭───────────────────────────────────────────────────╮ │ app : serviceWeaverDemo.exe │ │ deployment : 615d74c6-0e33-4f18-ae0c-f380b49c8e91 │ ╰───────────────────────────────────────────────────╯ I0914 14:01:50.363086 weaver.Main 85b8796b study.go:26 │ Init I0914 14:01:50.363086 weaver.Main 85b8796b study.go:76 │ StudyApi listener available. addr:="[::]:50076"下面的命令显示 Service Weaver 应用程序的状态:
weaver single status如下图所示,状态显示了每个部署、组件和监听器:
╭──────────────────────────────────────────────────────────────────────╮ │ DEPLOYMENTS │ ├───────────────────────┬──────────────────────────────────────┬───────┤ │ APP │ DEPLOYMENT │ AGE │ ├───────────────────────┼──────────────────────────────────────┼───────┤ │ serviceWeaverDemo.exe │ 615d74c6-0e33-4f18-ae0c-f380b49c8e91 │ 2m33s │ ╰───────────────────────┴──────────────────────────────────────┴───────╯ ╭───────────────────────────────────────────────────────────────────────╮ │ COMPONENTS │ ├───────────────────────┬────────────┬───────────────────┬──────────────┤ │ APP │ DEPLOYMENT │ COMPONENT │ REPLICA PIDS │ ├───────────────────────┼────────────┼───────────────────┼──────────────┤ │ serviceWeaverDemo.exe │ 615d74c6 │ weaver.Main │ 4356 │ │ serviceWeaverDemo.exe │ 615d74c6 │ interview.Service │ 4356 │ │ serviceWeaverDemo.exe │ 615d74c6 │ working.Service │ 4356 │ ╰───────────────────────┴────────────┴───────────────────┴──────────────╯ ╭────────────────────────────────────────────────────────────╮ │ LISTENERS │ ├───────────────────────┬────────────┬──────────┬────────────┤ │ APP │ DEPLOYMENT │ LISTENER │ ADDRESS │ ├───────────────────────┼────────────┼──────────┼────────────┤ │ serviceWeaverDemo.exe │ 615d74c6 │ studyapi │ [::]:50076 │ ╰───────────────────────┴────────────┴──────────┴────────────╯你也可以运行 weaver single dashboard 来打开仪表盘,在浏览器中打开仪表盘。
weaver multi deploy weaver.toml
weaver multi status ╭──────────────────────────────────────────────────────────────────────╮ │ DEPLOYMENTS │ ├───────────────────────┬──────────────────────────────────────┬───────┤ │ APP │ DEPLOYMENT │ AGE │ ├───────────────────────┼──────────────────────────────────────┼───────┤ │ serviceWeaverDemo.exe │ 615d74c6-0e33-4f18-ae0c-f380b49c8e91 │ 2m33s │ ╰───────────────────────┴──────────────────────────────────────┴───────╯ ╭───────────────────────────────────────────────────────────────────────╮ │ COMPONENTS │ ├───────────────────────┬────────────┬───────────────────┬──────────────┤ │ APP │ DEPLOYMENT │ COMPONENT │ REPLICA PIDS │ ├───────────────────────┼────────────┼───────────────────┼──────────────┤ │ serviceWeaverDemo.exe │ 615d74c6 │ weaver.Main │ 4356 │ │ serviceWeaverDemo.exe │ 615d74c6 │ interview.Service │ 4356 │ │ serviceWeaverDemo.exe │ 615d74c6 │ working.Service │ 4356 │ ╰───────────────────────┴────────────┴───────────────────┴──────────────╯ ╭────────────────────────────────────────────────────────────╮ │ LISTENERS │ ├──────────────┼──────────────────────────────────────┼──────┤ │ studyapp.exe │ 0f15e18b-a336-44b6-ac92-b7c9436ad8ce │ 1m4s │ ╰──────────────┴──────────────────────────────────────┴──────╯ ╭──────────────────────────────────────────────────────────────╮ │ COMPONENTS │ ├──────────────┬────────────┬───────────────────┬──────────────┤ │ APP │ DEPLOYMENT │ COMPONENT │ REPLICA PIDS │ ├──────────────┼────────────┼───────────────────┼──────────────┤ │ studyapp.exe │ 0f15e18b │ weaver.Main │ 21500, 23724 │ │ studyapp.exe │ 0f15e18b │ interview.Service │ 23908, 24372 │ │ studyapp.exe │ 0f15e18b │ working.Service │ 22936, 26616 │ ╰──────────────┴────────────┴───────────────────┴──────────────╯ ╭───────────────────────────────────────────────────────╮ │ LISTENERS │ ├──────────────┬────────────┬──────────┬────────────────┤ │ APP │ DEPLOYMENT │ LISTENER │ ADDRESS │ ├──────────────┼────────────┼──────────┼────────────────┤ │ studyapp.exe │ 0f15e18b │ studyapi │ 127.0.0.1:3000 │ ╰──────────────┴────────────┴──────────┴────────────────╯