{"id":25777284,"url":"https://github.com/Accenture/mercury","last_synced_at":"2025-02-27T06:07:27.845Z","repository":{"id":37952796,"uuid":"158250453","full_name":"Accenture/mercury","owner":"Accenture","description":"Reference engine for event-driven applications","archived":false,"fork":false,"pushed_at":"2024-12-16T20:50:11.000Z","size":11548,"stargazers_count":82,"open_issues_count":5,"forks_count":39,"subscribers_count":24,"default_branch":"main","last_synced_at":"2025-02-23T23:11:17.997Z","etag":null,"topics":["activemq-artemis","cloud-connectors","composable-architecture","cryptography","distributed-tracing","event-driven","event-over-http","event-over-mesh","event-stream","hazelcast","inter-service-communications","kafka","microservices","msgpack","reactive","rest-automation","spring-boot","streams","tibco","vertx"],"latest_commit_sha":null,"homepage":"https://accenture.github.io/mercury/","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Accenture.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-11-19T15:48:19.000Z","updated_at":"2025-02-19T20:27:43.000Z","dependencies_parsed_at":"2023-10-12T04:18:23.740Z","dependency_job_id":"482b3756-72b1-4f91-ae4b-34ae3d3f3ec7","html_url":"https://github.com/Accenture/mercury","commit_stats":{"total_commits":1004,"total_committers":16,"mean_commits":62.75,"dds":0.3794820717131474,"last_synced_commit":"e659caf000c2a714d921a52cbc85e13f56cef981"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Accenture%2Fmercury","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Accenture%2Fmercury/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Accenture%2Fmercury/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Accenture%2Fmercury/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Accenture","download_url":"https://codeload.github.com/Accenture/mercury/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240987436,"owners_count":19889335,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activemq-artemis","cloud-connectors","composable-architecture","cryptography","distributed-tracing","event-driven","event-over-http","event-over-mesh","event-stream","hazelcast","inter-service-communications","kafka","microservices","msgpack","reactive","rest-automation","spring-boot","streams","tibco","vertx"],"created_at":"2025-02-27T06:01:40.169Z","updated_at":"2025-02-27T06:07:27.829Z","avatar_url":"https://github.com/Accenture.png","language":"Java","funding_links":[],"categories":["微服务库"],"sub_categories":[],"readme":"# Welcome to the Mercury project\n\nThe Mercury project is created with one primary objective -\n`to make software easy to write, read, test, deploy, scale and manage.`\n\nMercury has been powering mission-critical cloud native production applications for the last few years.\nIn version 3, Mercury has evolved to the next level. It is bringing event-driven design and the best of\npreemptive and cooperative multitasking as a foundation to build \"composable applications.\"\n\nYou have low-level control to precisely tune your application for optimal performance and throughput\nusing three function execution strategies:\n\n1. kernel thread pool\n2. coroutine\n3. suspend function\n\nMercury version 3 achieves virtual threading using coroutine and suspend function.\n\nIn version 4, it fully embraces Java 21 virtual thread feature that was officially available since 12/2023.\n\nAugust, 2024\n\n# Mercury version 3\n\nMercury version 3 is a software development toolkit for event-driven programming.\n\nEvent-driven programming allows functional modules to communicate with each other using events instead\nof direct method calls. This allows the functions to be executed asynchronously, improving overall\napplication performance.\n\nIMPORTANT: You should use Mercury version 4 unless you need backward compatibility for your production systems.\n\nPlease visit Mercury Version 4 using the links below:\n\n[Mercury v4: https://github.com/Accenture/mercury-composable](https://github.com/Accenture/mercury-composable)\n\n[Documentation: https://accenture.github.io/mercury-composable/](https://accenture.github.io/mercury-composable/)\n\n# Differences between Mercury version 3 and 4\n\nThe key differences of Mercury version 3 and the latest Mercury-Composable version 4 are:\n\n| Category             | Mercury 3.0                 | Mercury 4.x                                   |\n|:---------------------|:----------------------------|:----------------------------------------------|\n| Java version         | Supports Java 1.8 or higher | Requires Java 21 or higher                    |\n| Event management     | Orchestration by code       | Event choreography by configuration           |\n| Multitasking         | Coroutine and kernel        | Java 21 virtual threads, coroutine and kernel |\n| Functional isolation | KernelThreadRunner          | Virtual Threads and KernelThreadRunner        |\n\n# Breaking changes\n\nBy default, the system runs all functions as \"coroutines\" where previous versions run them using\na kernel thread pool.\n\nThe `CoroutineRunner` annotation has been removed and replaced with the new `KernelThreadRunner` annotation.\n\nThe \"rest.automation.yaml\" key is renamed as \"yaml.rest.automation\" after we unify the parsing behavior\nof application.properties with application.yml.\n\n# Write your first composable application\n\nTo get started with your first application, please refer to the [Chapter 4, Developer Guide](guides/CHAPTER-1.md).\n\n# Introduction to composable architecture\n\nIn cloud migration and IT modernization, we evaluate application portfolio and recommend different\ndisposition strategies based on the 7R migration methodology.\n```text\n7R: Retire, retain, re-host, re-platform, replace, re-architect and re-imagine.\n```\n\nThe most common observation during IT modernization discovery is that there are many complex monolithic applications\nthat are hard to modernize quickly.\n\nIT modernization is like moving into a new home. It would be the opportunity to clean up and to improve for\nbusiness agility and strategic competitiveness.\n\nComposable architecture is gaining visibility recently because it accelerates organization transformation towards\na cloud native future. We will discuss how we may reduce modernization risks with this approach.\n\n# Composability\n\nComposability applies to both platform and application levels.\n\nWe can trace the root of composability to Service Oriented Architecture (SOA) in 2000 or a technical bulletin on\n\"Flow-Based Programming\" by IBM in 1971. This is the idea that architecture and applications are built using\nmodular building blocks and each block is self-contained with predictable behavior.\n\nAt the platform level, composable architecture refers to loosely coupled platform services, utilities, and\nbusiness applications. With modular design, you can assemble platform components and applications to create\nnew use cases or to adjust for ever-changing business environment and requirements. Domain driven design (DDD),\nCommand Query Responsibility Segregation (CQRS) and Microservices patterns are the popular tools that architects\nuse to build composable architecture. You may deploy application in container, serverless or other means.\n\nAt the application level, a composable application means that an application is assembled from modular software\ncomponents or functions that are self-contained and pluggable. You can mix-n-match functions to form new applications.\nYou can retire outdated functions without adverse side effect to a production system. Multiple versions of a function\ncan exist, and you can decide how to route user requests to different versions of a function. Applications would be\neasier to design, develop, maintain, deploy, and scale.\n\nComposable architecture and applications contribute to business agility.\n\n# Building a composable application\n\n## Microservices\n\nSince 2014, microservices architectural pattern helps to decompose a big application into smaller pieces of\n“self-contained” services. We also apply digital decoupling techniques to services and domains. Smaller is better.\nHowever, we are writing code in the same old fashion. One method is calling other methods directly. Functional and\nreactive programming techniques are means to run code in a non-blocking manner, for example Reactive Streams, Akka,\nVertx, Quarkus Multi/Uni and Spring Reactive Flux/Mono. These are excellent tools, but they do not reduce the\ncomplexity of business applications.\n\n## Composable application\n\nTo make an application composable, the software components within a single application should be loosely coupled\nwhere each component has zero or minimal dependencies.\n\nUnlike traditional programming approach, composable application is built from the top down. First, we describe\na business transaction as an event flow. Second, from the event flow, we identify individual functions for\nbusiness logic. Third, we write user story for each function and write code in a self-contained manner.\nFinally, we write orchestration code to coordinate event flow among the functions, so they work together\nas a single application.\n\nThe individual functions become the building block for a composable application. We can mix-n-match different\nsets of functions to address different business use cases.\n\n# Event is the communication conduit\n\nCloud native applications are deployed as containers or serverless functions. Ideally, they communicate using events.\nFor example, the CQRS design pattern is well accepted for building high performance cloud native applications.\n\n\u003e Figure 1 - Cloud native applications use event streams to communicate\n\n![figure-1.png](diagrams/figure-1.png)\n\nHowever, within a single application unit, the application is mostly built in a traditional way.\ni.e. one function is calling other functions and libraries directly, thus making the modules and libraries\ntightly coupled. As a result, microservices may become smaller monolithic applications.\n\nTo overcome this limitation, we can employ “event-driven design” to make the microservices application unit composable.\n\nAn application unit is a collection of functions in memory and an “event bus” is the communication conduit to connect\nthe functions together to form a single executable.\n\n\u003e Figure 2 – Functions use in-memory event bus to communicate\n\n![figure-2.png](diagrams/figure-2.png)\n\n# In-memory event bus\n\nFor a composable application, each function is written using the first principle of “input-process-output” where\ninput and output payloads are delivered as events. All input and output are immutable to reduce unintended bugs\nand side effects.\n\nSince input and output for each function is well-defined, test-driven development (TDD) can be done naturally.\nIt is also easier to define a user story for each function and the developer does not need to study and integrate\nmultiple levels of dependencies, resulting in higher quality code.\n\n\u003e Figure 3 - The first principle of a function\n\n![figure-3.png](diagrams/figure-3.png)\n\nWhat is a “function”? For example, reading a record from a database and performing some data transformation,\ndoing a calculation with a formula, etc.\n\n\u003e Figure 4 - Connecting output of one function to input of another\n\n![figure-4.png](diagrams/figure-4.png)\n\nAs shown in Figure 4, if function-1 wants to send a request to function-2, we can write “event orchestration code”\nto put the output from function-1 into an event envelope and send it over an in-memory event bus. The event system\nwill transport the event envelope to function-2, extract the payload and submit it as “input” to function-2\n\n# Function execution strategy\n\nIn event-driven application design, a function is executed when an event arrives as “input.” When a function\nfinishes processing, your application can command the event system to route the result set (“output”) as an\nevent to another function.\n\n\u003e Figure 5 - Executing function through event flow\n\n![figure-5.png](diagrams/figure-5.png)\n\nAs shown in Figure 5, functions can send/receive events using an in-memory event bus (aka \"event loop\").\n\nThis event-driven architecture provides the foundation to design and implement composable applications.\nEach function is self-contained and loosely coupled by event flow.\n\nA function receiving an event needs to be executed. There are three ways to do that:\n\n1. Kernel thread pool\n2. Coroutine\n3. Suspend function\n\n## Kernel thread pool\n\nJava supports “preemptive multitasking” using kernel threads. Multiple functions can execute in parallel.\nPreemptive multitasking leverages the multiple cores of a CPU to yield higher performance.\n\nPreemptive multitasking is performed at the kernel level and the operating system is doing the context switching.\nAs a result, the maximum number of kernel threads is small. As a rule of thumb, a moderately fast computer can\nsupport ~200 kernel threads.\n\n\u003e Figure 6 - Multitasking of kernel threads at the hardware and OS level\n\n![figure-6.png](diagrams/figure-6.png)\n\n## Coroutine\n\nMany modern programming languages such as GoLang, Kotlin, Python and Node.js support “cooperative multitasking”\nusing “event loop” or “coroutine.” Instead of context switching at the kernel level, functions are executed orderly\nby yielding to each other. The order of execution depends on the event flow of each business transaction.\n\nSince the functions are running cooperatively, the overheads of context switching are low. “Event loop” or\n“Coroutine” technology usually can support tens of thousands of “functions” running in “parallel.”\nTechnically, the functions are running sequentially. When each function finishes execution very quickly,\nthey appear as running concurrently.\n\n\u003e Figure 7 - Cooperative multitasking of coroutines\n\n![figure-7.png](diagrams/figure-7.png)\n\nJava 1.8 and higher versions support event loop with open sources libraries such as Lightbend Akka and Eclipse Vertx.\nA preview “virtual thread” technology is available in Java version 19. It brings cooperative multitasking by running\ntens of thousands of “virtual threads” in a single kernel thread. This is a major technological breakthrough to close\nthe gap with other modern programming languages.\n\n## “Suspend function”\n\nIn a typical enterprise application, many functions are waiting for responses most of the time.\nIn preemptive multitasking, these functions are using kernel threads and consuming CPU time.\nToo many active kernel threads would turn the application into slow motion.\n\n“Suspend function” not only avoids overwhelming the CPU with excessive kernel threads but also leverages the\nsynchronous request-response opportunity into high throughput non-blocking operation.\n\nAs the name indicates, “suspend function” can be suspended and resumed. When it is suspended, it yields control\nto the event loop so that other coroutines or suspend functions can run.\n\nIn Node.js and GoLang, coroutine and suspend function are the same. Suspend function refers to the “async/await”\nkeywords or API of coroutine. In Kotlin, the suspend function extends a coroutine to have the suspend/resume ability.\n\nA function is suspended when it is waiting for a response from the network, a database or from another function.\nIt is resumed when a response is received.\n\n\u003e Figure 8 - Improving throughput with suspend function\n\n![figure-8.png](diagrams/figure-8.png)\n\nAs shown in Figure 8, a “suspend function” can suspend and resume multiple times during its execution.\nWhen it suspends, it is not using any CPU time, thus the application has more time to serve other functions.\nThis mechanism is so efficient that it can significantly increase the throughput of the application.\ni.e. it can handle many concurrent users, and process more requests.\n\n# Performance and throughput\n\nThe ability to select an optimal function execution strategy for a function is critical to the success of a\ncomposable application. This allows the developer to have low level control of how the application performs and scales.\n\nWithout an optimal function execution strategy, performance tuning is usually an educated guess.\n\nIn composable application architecture, each function is self-contained and stateless. We can predict the performance\nof each function by selecting an optimal function execution strategy and evaluate it with unit tests and observability.\nPredicting application performance and throughput at design and development time reduces modernization risks.\n\nThe pros and cons of each function execution strategy are summarized below:\n\n| Strategy         | Advantage                                                                                                    | Disadvantage                                                                   |\n|:-----------------|:-------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------|\n| Kernel threads   | Highest performance in terms of\u003cbr/\u003eoperations per seconds                                                   | Lower number of concurrent threads\u003cbr/\u003edue to high context switching overheads |\n| Coroutine        | Highest throughput in terms of\u003cbr/\u003econcurrent users served by virtual\u003cbr/\u003ethreads concurrently               | Not suitable for long running tasks                                            |\n| Suspend function | Sequential \"non-blocking\" for\u003cbr/\u003eRPC (request-response) that\u003cbr/\u003emakes code easier to read and\u003cbr/\u003emaintain | Not suitable for long running tasks                                            |\n\nAs shown in the table above, performance and throughput are determined by function execution strategies.\n\nFor example, single threaded event driven network proxies such as nginx support twenty times more concurrent \nconnections than multithreaded application servers.\n\nOn the other hand, Node.js is not suitable for long running tasks. When one function takes more time to execute, \nall other functions are blocked and thus degrading the overall application performance. The latest Node.js language \nadds kernel threads using the “web worker” technology to alleviate this limitation. However, web worker API is more \ntedious than multithreading in Java and other programming languages.\n\n# The best of both worlds\n\nIf we simplify event-driven programming and support all three function execution strategies, we can design and \nimplement composable applications that deliver high performance and high throughput.\n\nThe “virtual thread” feature in the upcoming Java version 19 will be a good building block for function execution \nstrategies. Currently it is available as a “preview” feature.\n\nWhen it becomes available later in 2023, it will have a significant impact on the Java community. It will be at par\nwith other programming languages that support event loop. It supports non-blocking sequential programming without \nexplicitly using the “async” and “await” keywords. All current open sources libraries that provide event loop \nfunctionality would evolve.\n\nTo accelerate this evolution, we have implemented Mercury version 3.0 as an accelerator to build composable \napplications. It supports the two pillars of composable application – In-memory event bus and selection of \nfunction execution strategies.\n\nIt integrates with Eclipse Vertx to hide the complexity of event-driven programming and embraces the three function \nexecution strategies using kernel thread pool, coroutine and suspend function. The default execution strategy is\n\"coroutine\" unless you specify the function using the \"KernelThreadRunner\" annotation. To simplify writing\n“suspend function,” you can implement the “KotlinLambdaFunction” class and copy-n-paste your existing Java code\ninto the new Kotlin class, the IDE will automatically convert code for you. With 90% conversion efficiency, \nyou may need minor touch up to finish the rest.\n\nWe can construct a composable application with self-contained functions that execute when events arrive. \nThere is a simple event API that we call the “Post Office” to support sequential non-blocking RPC, async, \ndrop and forget, callback, workflow, pipeline, streaming and interceptor patterns.\n\nSequential non-blocking RPC reduces the effort in application modernization because we can directly port sequential \nlegacy code from a monolithic application to the new composable cloud native design.\n\n# What is \"event orchestration\"?\n\nIn traditional programming, we write code to make calls to different methods and libraries. In event-driven \nprogramming, we write code to send events, and this is “event orchestration.” We can use events to make RPC call \njust like traditional programming. It is viable to port legacy orchestration logic into event orchestration code.\n\nTo further reduce coding effort, we can use Event Script to do “event orchestration.” This would replace code with \nsimple event flow configuration.\n\nTo use event script, please upgrade to Mercury v4.\n\n[Mercury v4: https://github.com/Accenture/mercury-composable](https://github.com/Accenture/mercury-composable)\n\n[Documentation: https://accenture.github.io/mercury-composable/](https://accenture.github.io/mercury-composable/)\n\n# How steep is the learning curve for a developer?\n\nThe developer can use any coding style to write the individual functions, no matter it is sequential, object-oriented,\nor reactive. One may use any favorite frameworks or libraries. There are no restrictions.\n\nThere is a learning curve in writing “event orchestration.” Since event orchestration supports sequential \nnon-blocking RPC, the developer can port existing legacy code to the modern style with direct mapping. \nTypically, the learning curve is about two weeks. If you are familiar with event-driven programming, the learning\ncurve would be lower. To eliminate this learning curve, the developer may use Event Script that replaces orchestration\ncode with event flow configuration files. Event Script is designed to have virtually zero API integration for\nexceptionally low learning curve.\n\n# Conclusion\n\nComposability applies to both platform and application levels. We can design and implement better cloud native \napplications that are composable using event-driven design and the three function execution strategies.\n\nWe can deliver application that demonstrates both high performance and high throughput, an objective that has been \ntechnically challenging with traditional means. We can scientifically predict application performance and throughput \nin design and development time, thus saving time and ensuring consistent product quality.\n\nComposable approach also facilitates the migration of monolithic application into cloud native by decomposing the \napplication to functional level and assembling them into microservices according to domain boundary. \nIt reduces coding effort and application complexity, meaning less project risks.\n\nJava version 19 is introducing a new “virtual thread” feature in 2023 that will make it at par with other modern \nprogramming languages such as GoLang and Node.js. Since Java has the largest enterprise-grade open sources and \ncommercial libraries with easy access to a large pool of trained developers, the availability of virtual thread \ntechnology would retain Java as the best option for application modernization and composable applications.\n\nMercury and Event Script version 3.0 bring virtual thread technology with Kotlin coroutine and suspend function \nbefore Java version 19 becomes mainstream.\n\nThis opens a new frontier of cloud native applications that are composable, scalable, and easy to maintain, \nthus contributing to business agility.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAccenture%2Fmercury","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAccenture%2Fmercury","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAccenture%2Fmercury/lists"}