@@ -24,6 +24,7 @@ import (
2424 "context"
2525 "errors"
2626 "fmt"
27+ "strconv"
2728 "sync"
2829
2930 "go.uber.org/fx/modules"
@@ -32,6 +33,7 @@ import (
3233 "go.uber.org/fx/ulog"
3334
3435 errs "github.com/pkg/errors"
36+ "go.uber.org/fx/dig"
3537 "go.uber.org/yarpc"
3638 "go.uber.org/yarpc/api/middleware"
3739 "go.uber.org/yarpc/api/transport"
@@ -42,13 +44,17 @@ import (
4244// YARPCModule is an implementation of a core RPC module using YARPC.
4345// All the YARPC modules share the same dispatcher and middleware.
4446// Dispatcher will start when any created module calls Start().
47+ // The YARPC team advised dispatcher to be a 'singleton' to control
48+ // the lifecycle of all of the in/out bound traffic, so we will
49+ // register it in a dig.Graph provided with options/default graph.
4550type YARPCModule struct {
4651 modules.ModuleBase
47- register registerServiceFunc
48- config yarpcConfig
49- log ulog.Log
50- stateMu sync.RWMutex
51- isRunning bool
52+ register registerServiceFunc
53+ config yarpcConfig
54+ log ulog.Log
55+ stateMu sync.RWMutex
56+ isRunning bool
57+ controller * dispatcherController
5258}
5359
5460var (
6167 _starterFn = defaultYARPCStarter
6268
6369 _ service.Module = & YARPCModule {}
64-
65- // Controller represents a collection of all YARPC configs
66- // that are stored together to create a shared dispatcher.
67- // The YARPC team advised it to be a 'singleton' to control
68- // the lifecycle of all of the in/out bound traffic.
69- _controller dispatcherController
7070)
7171
7272type registerServiceFunc func (module * YARPCModule )
@@ -91,6 +91,24 @@ type Inbound struct {
9191 HTTP * Address
9292}
9393
94+ func (i * Inbound ) String () string {
95+ if i == nil {
96+ return ""
97+ }
98+
99+ http := "none"
100+ if i .HTTP != nil {
101+ http = strconv .Itoa (i .HTTP .Port )
102+ }
103+
104+ tchannel := "none"
105+ if i .TChannel != nil {
106+ tchannel = strconv .Itoa (i .TChannel .Port )
107+ }
108+
109+ return fmt .Sprintf ("Inbound:{HTTP: %s; TChannel: %s}" , http , tchannel )
110+ }
111+
94112// Address is a struct that have a required port for tchannel/http transports.
95113// TODO(alsam) make it optional
96114type Address struct {
@@ -111,7 +129,7 @@ type dispatcherController struct {
111129 startError error
112130
113131 configs []* yarpcConfig
114- dispatcher * yarpc.Dispatcher
132+ dispatcher yarpc.Dispatcher
115133}
116134
117135// Adds the config to the controller
@@ -155,12 +173,14 @@ func (c *dispatcherController) Start(host service.Host) error {
155173
156174 _dispatcherMu .Lock ()
157175 defer _dispatcherMu .Unlock ()
158- if c .dispatcher , err = _dispatcherFn (host , cfg ); err != nil {
176+ var d * yarpc.Dispatcher
177+ if d , err = _dispatcherFn (host , cfg ); err != nil {
159178 c .startError = err
160179 return
161180 }
162181
163- c .startError = _starterFn (c .dispatcher )
182+ c .dispatcher = * d
183+ c .startError = _starterFn (& c .dispatcher )
164184 })
165185
166186 return c .startError
@@ -249,7 +269,24 @@ func newYARPCModule(
249269 module .config .inboundMiddleware = inboundMiddlewareFromCreateInfo (mi )
250270 module .config .onewayInboundMiddleware = onewayInboundMiddlewareFromCreateInfo (mi )
251271
252- _controller .addConfig (module .config )
272+ // Try to resolve a controller first
273+ // TODO(alsam) use dig options when available, because we can overwrite the controller in case of multiple
274+ // modules registering a controller.
275+ if err := dig .Resolve (& module .controller ); err != nil {
276+
277+ // Try to register it then
278+ module .controller = & dispatcherController {}
279+ if errCr := dig .Register (module .controller ); errCr != nil {
280+ return nil , errs .Wrap (errCr , "can't register a dispatcher controller" )
281+ }
282+
283+ // Register dispatcher
284+ if err := dig .Register (& module .controller .dispatcher ); err != nil {
285+ return nil , errs .Wrap (err , "unable to register the dispatcher" )
286+ }
287+ }
288+
289+ module .controller .addConfig (module .config )
253290
254291 module .log .Info ("Module successfuly created" , "inbounds" , module .config .Inbounds )
255292
@@ -294,7 +331,7 @@ func (m *YARPCModule) Start(readyCh chan<- struct{}) <-chan error {
294331 defer m .stateMu .Unlock ()
295332
296333 // TODO(alsam) allow services to advertise with a name separate from the host name.
297- if err := _controller .Start (m .Host ()); err != nil {
334+ if err := m . controller .Start (m .Host ()); err != nil {
298335 ret <- errs .Wrap (err , "unable to start dispatcher" )
299336 return ret
300337 }
@@ -316,8 +353,9 @@ func (m *YARPCModule) Stop() error {
316353
317354 m .stateMu .Lock ()
318355 defer m .stateMu .Unlock ()
356+
319357 m .isRunning = false
320- return _controller .Stop ()
358+ return m . controller .Stop ()
321359}
322360
323361// IsRunning returns whether a module is running
@@ -354,9 +392,3 @@ func RegisterStarter(startFn StarterFn) {
354392func defaultYARPCStarter (dispatcher * yarpc.Dispatcher ) error {
355393 return dispatcher .Start ()
356394}
357-
358- // Dispatcher returns a dispatcher that can be used to create clients.
359- // It should be called after at least one module have been started, otherwise it will be nil.
360- func Dispatcher () * yarpc.Dispatcher {
361- return _controller .dispatcher
362- }
0 commit comments