You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This approach is cloned from msb (NodeJS) and is very flexible. However it leads to the following issues:
A lot boilerplate code that needs to be copy-pasted:
Initialization of MsbContext
Creation and hooking up of ResponderServer
Manual parsing of input message (request.getQueryAs(RequestQuery.class);)
Explicit starting of DefaultChannelMonitorAgent
A lot of static methods and constructors are used which complicates unit testing.
Lifecycle steps (initialization/shutdown) need to be manually triggered by microservice developer. In the example above we don't have to worry about that too much but imagine a microservice that needs to gracefully close database connection upon shutdown.
Microservice developer has to use too many MSB classes which may complicate further msb-java development (for compatibility reasons).
Possible solution: inversion of control
With inversion of control and dependency injection we could solve all the problems described in the previous section. The general idea is to free microservice developer from implementing own main method. Instead he/she has to implement some simple interface with business logic and have the lifecycle methods of that interface invoked externally by msb-java "infrastructure" (whatever it is).
The idea is taken from Java Servlet specification because each microservice is in some way resembles good-old javax.servlet.http.HttpServlet.
However different microservices might have different lifecycles so let's discuss various types of them in detail.
Common microservices
Microservice driven by MSB messages
This type of microservice listens to a single request topic, does something and produces acks and responses to the corresponding response topic. DateExtractor considered above is an example of such microservice. It obtains a query string, tries to parse a date from it and sends the result back.
Optionally during request processing such microservice can consult with another microservice by sending request to it. For this reason we need to inject an instance of Requester during initialization. An important point here is that with such approach we have to use single instance of Requester inside the microservice as opposed to creating a new one for each request (as currently implemented).
Here's the proposed interface for such type of microservice:
publicinterfaceMsbMicroservlet {
/** * Invoked during initialization of microservice * @param msbContext holds initial configuration and core MSB objects * @param requester can be used to send requests to (and process responses from) other microservices via bus */voidinit(MsbContextmsbContext, Requesterrequester);
/** * Processes incoming request * @param request the payload of the incoming request * @param responder allows to send responses and acks back */voidprocess(Payloadrequest, Responderresponder);
/** * Shuts down this microservice * @param shouldInterrupt whether active tasks should be interrupted */voidshutdown(booleanshouldInterrupt);
}
Of course something has to instantiate the object that implements such interface and subscribe it to the given namespace. That something is going to be instance of library class MsbMicroserviceRunner which has main method implemented. It creates instances of MsbMicroservlet and invokes their lifecycle methods.
So each microservice is launched from command line as a separate OS process like this:
Also MsbMicroserviceRunner has to know the actual class and namespace name. To achieve this microservice author specifies this information in the config file:
Microservice driven by messages coming from external systems
Another common type of microservice doesn't explicitly subscribe to serve requests from bus but rather only puts them there.
Examples are:
http2bus listens to HTTP requests, converts them into bus messages and publishes into the configured topics. After that MSB responses are collected, converted back to HTTP and sent back.
monitor and logger have internal timer that periodically triggers heartbeat requests into a _channels:heartbeat topic to get stats from other microservices. Actually they also subscribe to _channels:announce so they are hybrid (driven both by internal timer and by incoming MSB bus messages).
In this case interface MsbMicroservlet given in the previous section also works and:
Service setup (starting of heartbeater or HTTP server) should be done in init
process may be no-op if the microservice doesn't subscribe to any topic.
An interesting caveat is that we need to reserve a value to specify that MsbMicroservlet shouldn't listen to any topic:
Now let's consider a microservice that subscribes to multiple topics (possibly dynamically). An example of such microservice would be logger that dynamically subscribes to every topic that it gets from other microservices.
To streamline development of such microservices we need to introduce further changes in interfaces:
First of all we need to add another parameter to MsbMicroservlet.init:
/** * Invoked during initialization of microservice * @param msbContext holds initial configuration and core MSB objects * @param requester can be used to send requests to (and process responses from) other microservices via bus * @param msbMicroservletManager allows to dynamically instantiate new services in the same JVM */voidinit(MsbContextmsbContext, Requesterrequester, MsbMicroservletManagermsbMicroservletManager);
And here's MsbMicroservletManager:
publicinterfaceMsbMicroservletManager {
/** * Initializes a given microservice by its class name and subscribes it to the given namespace. All initialization lifecycle steps are executed. * @param namespace defines a topic to subscribe to * @param microserviceClass defines a class name to instantiate microservice from * @return initialized microservice instance */MsbMicroservletinitMicroservice(Stringnamespace, Class<MsbMicroservlet> microserviceClass);
/** * Shuts down the microservice by executing its shutdown lifecycle steps. The microservice is also unsubscribed from its namespace * @param namespace defines a topic that the microservice is subscribed to */voidshutdownMicroservice(Stringnamespace);
/** * Shuts down the microservice by executing its shutdown lifecycle steps. The microservice is also unsubscribed from its namespace * @param microservice the microservice to shut down */voidshutdownMicroservice(MsbMicroservletmicroservice);
}
Another point is that probably those instances of MsbMicroservlet need to share some state so we need to enrich MsbContext as well:
The current flexible approach is not going away. For ninjas that need to implement other patterns not covered above we still expose ChannelManager, Requester, ResponderServer through MsbContext. That is going to allow doing any low-level stuff one may need.
The text was updated successfully, but these errors were encountered:
After discussion with Simon and Benny we concluded that:
while the proposed approach is useful for development of microservices from scratch it might not be applicable for embedding msb-java into existing application. Because the proposed approach assumes that some class from msb-java imposes lifecycle on "microservlets" which might be tricky if existing application already have established lifecycle.
in any case we need to expose lower-level API (like ResponderServer and Requester) to microservice author. Moreover, we need to improve the current API to "code against interfaces" to make the API easily mockable.
Issues with the current API
Here’s a part of
DateExtractor
implemented using msb-java (you can find full source here):This approach is cloned from msb (NodeJS) and is very flexible. However it leads to the following issues:
MsbContext
ResponderServer
request.getQueryAs(RequestQuery.class);
)DefaultChannelMonitorAgent
Possible solution: inversion of control
With inversion of control and dependency injection we could solve all the problems described in the previous section. The general idea is to free microservice developer from implementing own
main
method. Instead he/she has to implement some simple interface with business logic and have the lifecycle methods of that interface invoked externally by msb-java "infrastructure" (whatever it is).The idea is taken from Java Servlet specification because each microservice is in some way resembles good-old
javax.servlet.http.HttpServlet
.However different microservices might have different lifecycles so let's discuss various types of them in detail.
Common microservices
Microservice driven by MSB messages
This type of microservice listens to a single request topic, does something and produces acks and responses to the corresponding response topic.
DateExtractor
considered above is an example of such microservice. It obtains a query string, tries to parse a date from it and sends the result back.Optionally during request processing such microservice can consult with another microservice by sending request to it. For this reason we need to inject an instance of
Requester
during initialization. An important point here is that with such approach we have to use single instance ofRequester
inside the microservice as opposed to creating a new one for each request (as currently implemented).Here's the proposed interface for such type of microservice:
Of course something has to instantiate the object that implements such interface and subscribe it to the given namespace. That something is going to be instance of library class
MsbMicroserviceRunner
which hasmain
method implemented. It creates instances ofMsbMicroservlet
and invokes their lifecycle methods.So each microservice is launched from command line as a separate OS process like this:
Also
MsbMicroserviceRunner
has to know the actual class and namespace name. To achieve this microservice author specifies this information in the config file:Microservice driven by messages coming from external systems
Another common type of microservice doesn't explicitly subscribe to serve requests from bus but rather only puts them there.
Examples are:
http2bus
listens to HTTP requests, converts them into bus messages and publishes into the configured topics. After that MSB responses are collected, converted back to HTTP and sent back.monitor
andlogger
have internal timer that periodically triggers heartbeat requests into a_channels:heartbeat
topic to get stats from other microservices. Actually they also subscribe to_channels:announce
so they are hybrid (driven both by internal timer and by incoming MSB bus messages).In this case interface
MsbMicroservlet
given in the previous section also works and:init
process
may be no-op if the microservice doesn't subscribe to any topic.An interesting caveat is that we need to reserve a value to specify that
MsbMicroservlet
shouldn't listen to any topic:Less-common microservices
Microservice that listens to multiple topics
Now let's consider a microservice that subscribes to multiple topics (possibly dynamically). An example of such microservice would be
logger
that dynamically subscribes to every topic that it gets from other microservices.To streamline development of such microservices we need to introduce further changes in interfaces:
First of all we need to add another parameter to
MsbMicroservlet.init
:And here's
MsbMicroservletManager
:Another point is that probably those instances of
MsbMicroservlet
need to share some state so we need to enrichMsbContext
as well:NOTE: The actual
logger
uses raw broker consumers to react on messages because in that particular case there's no need to send the replies back.Even more!!!
The current flexible approach is not going away. For ninjas that need to implement other patterns not covered above we still expose
ChannelManager
,Requester
,ResponderServer
throughMsbContext
. That is going to allow doing any low-level stuff one may need.The text was updated successfully, but these errors were encountered: