Java event bus library comparison

Introduction

Event based system design is a viable solution for many technical use cases that require decoupling of individual components. Especially in GUI frameworks, components communicate with each other by means of sending and processing(receiving) events. Modeling system interaction in an event driven way decouples the different components and allows to introduce new behaviour into a system at runtime. It is not the goal of this article to explain the pattern in detail, nor will it give references to scenarios where eventing is applicable. Instead, in this article I will try to compare existing eventing solutions with respect to their offered features and performance.

 

My personal motivation to writing this article is that I created an event bus system that I recently released as an open source project. I created the MBassador library since none of the available solutions offered the functionality I needed and I felt that I wanted to give it a try. Since I was interested in how my solution compares to the other available libraries I started writing some test scenarios that I ran with every of the listed libraries using a general adapter for each.

 

The Candidates

Google Guava Event Bus

The Google guava library contains many very useful and powerful general purpose datastructures, most of them with concurrency support built-in. I really like the library a lot and have used the cache implementations frequently. Google Guava was my first choice until I discovered that the event bus uses strong references. Additionally, its feature list is quite small.

http://code.google.com/p/guava-libraries/

Tested version: 14.0-rc1

Simple Bus

As the name suggests, a very simple implementation of an event bus. The good thing is, that it uses weak references to the subscribed listeners such that one can simply forget about what was stuffed in. Event publication is very fast. It is the only bus that supports cancelation of event delivery (vetoing).

http://code.google.com/p/simpleeventbus/

Tested version: 1.2

EventBus

EventBus comes with a lot of features and has been around the longest (I guess). It is used in various projects and I expect it to be quite mature. Due to the number of features it offers it is quite slow.

http://www.eventbus.org

Tested version: 1.4

MBassador

I proudly present you my first open source project. MBassador was designed with performance and ease of use in mind. It works well in concurrent environments and has generally a very high throughput. It uses weak references to the subscribed objects. It does support customzation using custom implementations of subscriptions and different dispatch strategies. Feature wise it roughly compares with EventBus but highly exceeds it in performance.

https://github.com/bennidi/mbassador

Tested version: 1.1.1

 

A brief feature comparison

The following table is a currently incomplete list of features offered by the different libraries. I only included the main features that I consider fundamental for an event bus solution. More exotic features offered by some of the products where excluded since the primary intend of this article is to compare the performance of the different products for the most common use cases.

 

Event Bus

Listener declaration

Synch. dispatch

Asynch dispatch

Filtering

Event type hierarchy

Multimode

Reference Type

Handler Priorities

Google Guava

Via annotations

Yes

Via specialized class only

No

?

No

Strong

No

SimpleBus

Via annotations

No

Yes

No

?

No

Weak

No

EventBus

Via annotations

Yes

Yes

Static?

Configurable

?

Both

Yes

MBassador

Via annotations

Yes

Yes

Static

Yes

Yes

Weak

Yes

Note: I can not guarantee that this feature list is completely correct since I did not inspect all the solutions in much detail. As per the date of writing this article, the documentation for the EventBus was not available online. I am happy to include corrections and additions to this list, so feel free to comment.

 

Listener declaration = How are listeners defined? Is it non-invasive or does it affect the listeners class hierarchy?
Synchronous dispatch = Is synchronous event dispatching supported (publication method blocks until event is received be every handler). This is the most common mode of operation.
Asynchronous dispatch = Is asynchronous event dispatching supported (publication method returns immediately and event delivery is run asynchronously in a different thread).
Filtering = Does a mechanism for event filtering exist, such that handlers are not notified of every event that matches their parameter type?
Event type hierarchy = Is the type hierarchy of the published event considered during event delivery? Implementations that do support type hierarchies for events will deliver the event E to all handlers that accept E or one of its super types
Multimode = Is it possible to use the same bus to dispatch events either synchronously or asynchronously?
Reference Type = What kind of references are used to store subscribed listeners. Strong references require the client code to take care of proper deregistration of subscribed listeners or else a memory leak occurs.
Handler Priorities = Can the execution order of handlers be influenced by an ordering criteria?

The Test scenarios

For the performance tests I created various scenarios that isolate the different functionalities of the tested libraries. Scenarios are implemented as Runnable that take an implementation of the adapter interface. The adapter is the common interface I designed to make the basic functionalities of each library available in a standardized way and is a very thin wrapper around the method invocations of the actual event bus component. All tests are run single threaded and multi threaded – each table shows how many threads are used and the workload per thread. For each single-threaded test a multi-threaded one with the same overall workload is run to evaluate how concurrent access affects the components performance

Note: Since some of the event bus implementations use weak references and others not, a strong reference to each listener will be created before it is subscribed to the bus. Of course this impacts the overall performance but it does so equally for all tested implementations. Furthermore, the SimpleBus needed some extra treatment since it publishes events asynchronously and all tests of SimpleBus therefore need a bit of extra code to wait until event delivery is really finished (all handlers have actually been invoked)

 

The code of the performance comparison can be found here https://github.com/bennidi/eventbus-performance

Test machine: Samsung X22 Laptop (2.2 Ghz Intel Dual Core, 3 Gb ram)

 

Scenario 1: Subscribing/Unsubscribing listeners

This scenario tests the performance of subscribing and unsubscribing new listeners. No messages are published to the listeners. Different classes of listeners with different handler definitions are used. Not every subscribed listener is subsequently unsubscribed such that with each round the number of registered listeners grow. Each loop subscribes three different listeners and unsubscribes under certain conditions.

 

Subscriptions per Thread

6000

Number of Threads

1

Implementation

Duration(ms)

Google Guava

453

SimpleBus

1377

EventBus

1866

MBassador

112

Subscriptions per Thread

18000

Number of Threads

1

Implementation

Duration(ms)

Google Guava

595

SimpleBus

6734

EventBus

12911

MBassador

191

 

Subscriptions per Thread

300

 

 

Number of Threads

20

 

 

Implementation

Avg(ms)

Min(ms)

Max(ms)

Google Guava

360

251

407

SimpleBus

1705

1559

1777

EventBus

2166

1966

2238

MBassador

96

58

118

 

Subscriptions per Thread

900

 

 

Number of Threads

20

 

 

Implementation

Avg(ms)

Min(ms)

Max(ms)

Google Guava

601

480

642

SimpleBus

5789

4772

6140

EventBus

13729

11897

14582

MBassador

189

118

216

 

 

Scenario 2: Publishing events

This scenario tests the performance of event publication. Each test is setup with a number of listeners that are subscribed to the bus (not part of the measured performance). After the test is set up event publications will be issued to the bus. Since the message type hierarchy is not considered by all implementations, only the event class that is highest in the hierarchy will be used.

 

Events per Thread

2000

Number of Threads

1

Number of listeners

6000

Implementation

Duration(ms)

Google Guava

1322

SimpleBus

17603

EventBus

3078

MBassador

228

 

Events per Thread

100

 

 

Number of Threads

20

 

 

Number of listeners

6000

 

 

Implementation

Avg(ms)

Min(ms)

Max(ms)

Google Guava

1038

883

1168

SimpleBus

28334

27772

37753

EventBus

6446

5931

6727

MBassador

261

51

382

 

 

Scenario 3: Mixed usage

This scenario is designed as I would expect a lot of use cases to look like. Listeners are subscribed and/or unsubscribed while events get published.

 

Events per Thread

4000

 

 

Number of Threads

1

 

 

Subscriptions per Thread

6000

 

 

Implementation

Duration(ms)

 

 

Google Guava

2535

 

 

SimpleBus

162259

 

 

EventBus

4893

 

 

MBassador

622

 

 

Events per Thread

200

 

 

Number of Threads

20

 

 

Subscriptions per Thread

300

 

 

Implementation

Avg(ms)

Min(ms)

Max(ms)

Google Guava

2257

2054

2387

SimpleBus

31367

26477

33569

EventBus

4510

4136

4670

MBassador

761

626

831

 

Events per Thread

600

 

 

Number of Threads

20

 

 

Subscriptions per Thread

900

 

 

Implementation

Avg(ms)

Min(ms)

Max(ms)

Google Guava

12575

11905

12970

SimpleBus

101030

87618

103493

EventBus

34814

32038

38350

MBassador

2497

2231

2660

 

Events per Thread

1000

 

 

Number of Threads

50

 

 

Subscriptions per Thread

1500

 

 

Implementation

Avg(ms)

Min(ms)

Max(ms)

Google Guava

396176

359027

403041

SimpleBus

> 15 min.

 

 

EventBus

OutOfMemoryException

 

 

MBassador

32525

22746

34087

 

Results

 

The shown performance characteristics of the compared implementations indicate that

  1. Listener subscription is an expensive operation for all implementations but MBassador and Guava

  2. Concurrent access does generally slow down the bus performance because of higher contention/synchronization.

  3. SimpleBus is by far the slowest implementation

  4. MBassador is by far the fastest implementation in all scenarios. It also offers the best scaling characteristics meaning that higher concurrency rates do not slow down the bus performance as much as the others. This is because MBassador relies on a custom data structure with very fast write operations that do not block readers and at the same time do not copy existing data structures (most other implementations use CopyOnWriteArrayList).

To sum it up, MBassador is not only astonishingly fast but also offers a rich set of features that only EventBus can compare with. Google Guava's event bus implementation improved a lot from version 13 to 14 but is still clearly behind MBassador considering both performance and features. Especially in high concurrency/throughput scenarios MBassador is the clear winner when compared to Guavas.

I would go as far and say that MBassador is the best (Intra-JVM) event bus implementation that is currently available for the Java language. MBassador was the only implementation that did not produce exceptions during test execution (all other implementations threw OutOfMemory or other kinds of exceptions every once in a while). It is also quite low on resources (best memory footprint of all implementations). So, use it, file issues, request features!

I will keep on working on the code adding more unit tests and features. I am currently working on an integration component for the spring environment that supports conditional event dispatch triggered by transaction events (e.g. commit, rollback). For those who have been using EJB3 this will sound familiar and is quite a nice feature. A base implementation with proof of concept already exists and will be released soon. That's it for now. Any feedback is more than welcome.

view model or data transfer object

Well, here I am having to take care of the communication between presentation and domain layer. The first idea was to avoid exposure of the domain objects to the view such that they do not get tied together. Thus, there is the need to introduce a new kind of object that represents the parts of the domain model that need to make it to the view. Everybody agreed on using this so called data transfer object for this purpose. But then you also need to map between the two worlds and a lot of new code and questions arise. Of course one should always have a look how other people treat this issues and in doing so I discovered that a lot of different opinions and patterns are out there.

Generally a DTO is what its name suggests, a simple object without any behaviour that is used to transfer data across system boundaries (read). I also found a post that adds the separation of concern aspect and also points out the maintenance impact of having a lot of DTOs in your codebase. Then there is this so called view model, that some people (including me) confused with DTOs (see this post) but that should have some kind of behaviour. I think the relation between the two is summed up very clearly in this post which also includes an implementation proposal that could be useful in some scenarios and definitely reduces the amount of code one needs to write (I also think that keeping the code DRY (and clear) should have top priority).

Personally I don't have the experience to discuss the view model dto relation but I do know that I dislike all objects that do not know how to behave. Being a value object is not a good reason for existence (a map would do), especially if I use it with general assembly patterns and some reflective framework technology that does not even know my concrete types.

This thing kept me thinking and I ended up having an idea. I don't know if it classifies as a view model but it is a model used by the view althoug it does not necessarily contain view specific information. The more importand thing about it is that is does not create much overhead to the application. I think there must already be posts about that approach since I feel it is the obvious one but my search did not bring up any, so in case I just repeat please have mercy. But before I explain the implementation approach I want to make a point why I think that you should not expose your domain objects directly to the view.

"It's all about separation of concern – decoupling domain model and presentation layer"

It's as simple as that: Decoupling the domain layer from the view layer allows bigger changes to the application frontend to be done without modifications to service or domain layer. Changes can be applied to GUI components and the view model such that view layer and domain model can evolve independently.

Although I feel that both models are strongly related to each other – for me, the view model is in a way a custom representation (=view) of the domain model – they might vary in different aspects. Since the design of the view layer aligns with the steps of user interactions required by the usecases, its model tends to have a different granularity than the domain model. Additionally data types in both models may be different and the view model might provide access to other functionality like syntactical validation, internationalization and even technology specific properties.

For me that's enough good reasons to have some kind of abstraction between view and domain. But I don't want the trade off traditional DTO implementation implies. I don't want to code potentially n data transfer objects AND their corresponding transformations for (maybe only) one domain object. And I don't want to put transformation logic into the service layer or pollute my repositories with toDTO(…) and fromDTO(…) methods. Also, I would like to avoid the use of a generic transformation framework that may require me to define mappings as xml or annotations and introduce just another model (the mapping model) and code artifacts into my code base.

Designing the view model as a thin wrapper around the domain model

So, I asked myself, what if I treat the view model as a thin wrapper around the domain model objects and expose just the properties and functionality that I care about and that is valid for my specific use case? Then I could use the domain objects as delegates and have my IDE generate all the delegate methods I need. It would not be necessary to define any transformations since the objects managed by the view model can directly be used inside the services and domain layer. All additional transformation steps like data type mapping, string mutations, formatting etc. can be encapsulated in the view model class itself. In a lot of cases there wouldn't even be any transformation required.

This approach would allow me to initially define the view model analogous to the domain model and then have the view model evolve over time as it gets tailored to the specific usecase requirements. There would be no mapping overhead, no additional transformations and I could reuse some of the functionality of the domain model that is also valid in the context of the view layer, e.g. all the operations that modify the object graph like adding an address to a person, adding or removing an item to the shopping cart etc.

The only drawback I can see with this approach emerges when view model objects have to cross system boundaries, i.e. serialization. The delegate approach might result in view model objects that reference a set of potentially large domain objects although only a small portion of each is needed. In worst case scenario this might result in considerable performance issues.

But I guess in many scenarios serialization will not be much of an issue. Also, if the addToCart(…) method is available inside the view model there is no need for a service call and serialization (unless the shopping cart should be persistent of course).

 

System.out.println(“Hello World”);

This is my first blog post on my codeblock and the one and only place where the hello world sample will ever appear – this I solemnly swear!

The intention of creating this blog was to have a personal code logbook, a place for reflecting on good and bad coding practices that I encounter during my daily software development routine. Making this place public will hopefully provide me with feedback from other developers and also spread the word of some of the better ideas that sometimes hit my mind. As I am working as a freelance software developer and architect in various projects this blog will not have a particular focus regarding specific technologies. Also, I am not keen on writing about things that might be completely obsolete in two years time. I will rather post and describe some of the implementation patterns I used and that proved to be robust and beneficial. I might also write about what other developers write ( which will always happen when I myself can not come up with any interesting code or idea ) or comment on a good book or talk. I might even and out of mere frustration write a post where I bash, for example, the JPA Criteria API because it is such a poorly designed interface and implementation. When this happens, I will try to find productive ways to deal with that frustration ( I assume everybody who uses 3rd party code knows what I am talking about ) by pointing out workarounds, linking to alternatives or the like.

Besides all that, my main interests in the area of software development focus around

  • Application architecture: How can I determine the right architecture of an application for the set of requirements the application needs to fulfill?
  • Applying the principles of Eric Evans Domain Driven Design approach to software development: I like many of the ideas around DDD, e.g. having the ubiquitous language and adding behavior to the domain model and I think it is a very valuable approach to successful application development
  • Domain Specific Languages: Whether internal or external, I think that having a good dsl for your problem space, can be a key factor for writing maintainable code and clearly aligning the software with its domain. After all, the problem domain is the reason why we develop the application in the first place.
  • Design of clear APIs: A good API is surprisingly difficult to develop but once it has reached a certain degree of stability and expressiveness it can really make your day. Writing code that reads almost like a natural language is fun and makes implementation of new requirements easy.
  • Clean Code: Well, everybody loves clean code. As we read the same code multiple times it should be clear and clean in expressing what it does. It should be free of duplication, have a good design to make extension easy but not disguise its actual functionality behind too many design patterns. Of course, there is a lot more to what is clean code and different developers take different views on it. But everybody can tell good code from bad code according to his own style and there is undoubtedly a consensus about some basic things you should avoid in your code and what are desirable characteristics of good code
  • Agile Development: A big buzz word and headline of ongoing discussions…but that's for a reason. In my opinion software development is a social process as well as a technical challenge and agile methods try to embrace this reality. Having domain experts in your team, pair program your code, writing unit tests and having regular code reviews and refactorings not only helps to write good software but is actually a lot of fun and a lot more social than simply having each developer implement his spec.
  • Java Virtual Machine : Since I am a Java developer, I live in the JVM world. Sadly, I did not have the luck to use some of the more powerful languages like Clojure, Scala or Lua in bigger projects but that will hopefully change over time. My personal favorite language is Smalltalk and there is no week, I do not wish to have some of its features and concise syntax in the Java world. Honestly, the Java evolution is slow, it's syntax is kind of verbose and the Java 7 release without closures really made me sad. But hey, I can use strings in switch statements now!