我已经玩了一段时间了,我一直在使用Service Weaver,并且很好奇我们应该如何用它来设置多个听众。我的意图是当我们部署应用程序时,web
的处理程序和api
的处理程序分别运行(例如)。我的代码目前如下:
package main
import (
"context"
"log"
"sync"
"github.com/ServiceWeaver/weaver"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
type Server struct {
weaver.Implements[weaver.Main]
apiServer weaver.Ref[APIServer]
webServer weaver.Ref[WebServer]
}
type APIServer interface {
Serve(context.Context) error
}
type apiServer struct {
weaver.Implements[APIServer]
api weaver.Listener
}
func (a apiServer) Serve(ctx context.Context) error {
logger := a.Logger(ctx)
e := echo.New()
e.Listener = a.api
e.Use(middleware.RequestID())
e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
LogStatus: true,
LogURI: true,
LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
logger.Info("Request", "id", v.RequestID, "uri", v.URI, "status", v.Status, "size", v.ResponseSize)
return nil
},
}))
return e.Start("")
}
type WebServer interface {
Serve(context.Context) error
}
type webServer struct {
weaver.Implements[WebServer]
web weaver.Listener
}
func (w webServer) Serve(ctx context.Context) error {
logger := w.Logger(ctx)
e := echo.New()
e.Listener = w.web
e.Use(middleware.RequestID())
e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
LogStatus: true,
LogURI: true,
LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
logger.Info("Request", "id", v.RequestID, "uri", v.URI, "status", v.Status, "size", v.ResponseSize)
return nil
},
}))
return e.Start("")
}
func main() {
if err := weaver.Run(context.Background(), serve); err != nil {
log.Fatal(err)
}
}
func serve(ctx context.Context, server *Server) error {
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
server.apiServer.Get().Serve(context.Background())
}()
go func() {
defer wg.Done()
server.webServer.Get().Serve(context.Background())
}()
wg.Wait()
return nil
}
基本上,我只是设置两个回声服务器,一个用于每个组件。
对于它的value,这是我的配置:
[serviceweaver]
binary = "./platform"
[multi]
listeners.api = { address = "localhost:12345" }
listeners.web = { address = "localhost:54321" }
[single]
listeners.api = { address = "localhost:12345" }
listeners.web = { address = "localhost:54321" }
当我在一个过程中运行它时,一切似乎都在起作用as-expected。我看到Web请求的日志条目,表明事情正常工作。当我以部署模式运行它(即实际上执行多个过程魔术)时,我只能在看到如下的日志条目之前提出1-2请求,然后响应不经常工作:
2023/09/14 21:35:51 http: proxy error: context canceled
看来我在做错事,但似乎这是一种用例以某种方式支持的用例,所以我想知道是否有适当的方法可以解决这个问题。
谢谢!
TL; DR Service Weaver当前没有一个从non-main组件运行HTTP服务器的好方法。我建议您将两个侦听器移至Server
结构,并在serve
功能中运行两个HTTP服务器。 HTTP服务器可以在其他组件上调用方法。
type Server struct {
weaver.Implements[weaver.Main]
api weaver.Listener
web weaver.Listener
}
细节
weaver multi
Deployer将每个组件复制两次,每个副本在其自己的OS进程中运行。考虑当APIServer
组件在端口12345上请求网络侦听器时会发生什么。只有一个操作系统可以在端口上收听。为了解决这个问题,这两个复制品在随机选择的端口上聆听,例如8000和9000。然后,weaver multi
部署者在端口12345上运行HTTP代理,该代理将请求转发到端口8000和9000。
在您的应用程序中,主Server
组件被复制两次,并且serve
功能运行两次,一次在每个副本上一次。在serve
内部,当您调用apiServer.Get().Serve(context.Background())
时,随机选择APIServer
的复制品以执行Serve
方法。如果幸运的是,两个方法调用将发送到两个不同的副本,那么一切都应该顺利运行。但是,如果两个方法调用都发送到同一副本,则APIServer
的一个副本正在运行HTTP服务器,而另一个则没有。
在这种情况下,代理将所有请求的一半转发到运行的HTTP服务器,而另一半请求将其转发给不聆听的听众。这导致您看到的代理错误。
最后,请注意,当您使用go run
或weaver single deploy
运行应用程序时,事情可以正常工作,因为没有代理和组件未复制。