Recently in learning the code of the open source project Grafana, I found that the author has implemented an event bus mechanism, which is used in a large number of projects, the effect is also very good, the code is also relatively simple.
1. Registration and invocation
This writeup is seen everywhere inside this project.
The key is the code
bus.Dispatch(&query), which takes as an argument a structure
GetAlertByIdQuery with the following content.
Based on the name you can see that this method is to query Alert by Id, where the
Alert structure is the result object, which will not be posted here.
By looking at the source code we can learn that behind Dispatch is a call to the method
GetAlertById, and then the result is assigned to the Result of the query parameter and returned.
The question arises, how is this achieved? What exactly does Dispatch do? What are the benefits of doing this?
Let me answer them one by one.
First, before Dispatch, you need to register this method, that is, call
AddHandler, you can see a lot of such code inside the init function in this project.
In fact, the logic of this method is also very simple, the so-called registration is also through a map of the function name and the corresponding function to do a mapping relationship to save, when we Dispatch is actually through the parameter name to find the previously registered function, and then through reflection to call the function.
There are several map members in the Bus structure, and in this project the author has defined three different types of handlers, one is a normal handler, the kind shown earlier, the second is a contextual handler, and the other is a handler used for event subscription, where we register multiple listeners to an event. When the event is triggered, multiple listeners will be called in turn, which is actually an observer pattern.
Here’s a look at the source code, the
AddHandler method is as follows.
The source code for the Dispatch method is as follows.
DispatchCtx these two methods are basically the same, but with an additional context parameter that can be used for timeout control or other purposes.
2. Subscribe and Publish
Besides that, there are 2 methods
Publish, which are the subscription and publication of events.
Check the source code to know that you can register multiple handler functions to an event, and Publish is to call the registered functions in turn, and the logic is not complicated.
There is a bad point here, all subscription functions are called sequentially and do not use Goroutine, so this is not very efficient if many functions are registered.
Some people may wonder why it is so complicated to make a detour when you can just call the function directly.
Moreover, every time you call it, you have to use the reflection mechanism, and the performance is not good.
I think the main points are as follows.
- This way to write a clear logic, decoupling
- Convenient unit testing
- performance is not the biggest consideration, although it is said that reflection will reduce performance