Giter VIP home page Giter VIP logo

virgil-spring-boot-starter's Introduction

Virgil Spring Boot Starter

OSS Lifecycle

Virgil is a Spring Boot Starter that is developed as a Spring Boot Admin extension and serves as a generic message manager. It supports RabbitMQ with actions such as count/display/republish/drop messages on the queue.

For future releases we will consider adding in additional actions such as download/etc along with Kafka support.

Virgil implements a collection of Actuator endpoints to support the queue actions, it also provides UI as Admin extension to interact with those Actuator endpoints.

Name reference: https://en.wikipedia.org/wiki/Virgil

Getting Started

compile 'com.indeed:virgil-spring-boot-starter'

Build Virgil UI Component

Dev Build

gradle buildFrontEndDev

Prod Build

gradle buildFrontEndProd

Setup Virgil In Your Project

List of settings you can configure in application.yml

  • virgil
    • queues
      • <queue>
        • readName
        • readBinderName
        • [Optional] republishName
        • [Optional] republishBinderName
        • [Optional] republishBindingRoutingKey
    • binders
      • <binderName>
        • name
        • type: rabbit
        • rabbitProperties
          • [Optional] addresses
          • [Optional] host
          • [Optional] port
          • username
          • password
          • [Optional] virtual-host

Example with Single DLQ:

virgil:
  queues:
    virgilDlq:
      readName: virgil-dlq
      readBinderName: virgilExchange
      republishName: virgil-queue
      republishBinderName: virgilExchange
      republishBindingRoutingKey: test.#
  binders:
    virgilExchange:
      name: virgil-exchange
      type: rabbit
      rabbitProperties:
        host: virgil-rabbit
        port: 5672
        username: guest
        password: guest
        virtual-host: /

Example with Many DLQs:

virgil:
  queues:
    virgilDlq:
      readName: virgil-dlq
      readBinderName: virgilExchange
      republishName: virgil-queue
      republishBinderName: virgilExchange
      republishBindingRoutingKey: test.#
    virgilDlq2:
      readName: virgil-queue
      readBinderName: virgilExchange
      republishName: virgil-dlq
      republishBinderName: virgilExchange
      republishBindingRoutingKey: dlq.#
  binders:
    virgilExchange:
      name: virgil-exchange
      type: rabbit
      rabbitProperties:
        host: virgil-rabbit
        port: 5672
        username: guest
        password: guest
        virtual-host: /

Configuration Details

  • binders.<binderName>.rabbitSetings: if addresses is provided it will be used as priority over host and port. If addresses is blank, host and port will be used. Port will default to 5672.

  • queues.queue: if republishName and republishBinderName is not present, we will disable republish option per message

  • If you are using Spring Cloud Stream:

    • If you configure spring.cloud.stream.rabbit.bindings.input.consumer.auto-bind-dlq=true and spring.cloud.stream.bindings.input.group=myGroup, the format of your DLQ should be input.myGroup.dlq;

Supported Functionality

V1

  • One queue per application instance;
  • RabbitMQ;
  • Display total count of messages in queue;
  • Parse out text based body to be displayed
    • Pluggable MessageConverter allows you to replace default utf8 parser with custom message parser
      • By registering a Bean with IMessageConverter as its return type
  • Republish 1 message at a time from queue;
  • Drop 1 or all messages at a time from queue;
  • UI as Spring Boot Admin extension backed by Actuator endpoints;

V2

Stay tuned!

UI Demo

alt text alt text

Visit actuator endpoints

How To Contribute

If you’d like to contribute, please open an issue describing what you want to change and why, or comment on an existing issue. We’d love to have you.

Project Maintainers

Code of Conduct

This project is governed by the Contributor Covenant v 1.4.1.

License

This project uses the Apache 2.0 license.

virgil-spring-boot-starter's People

Contributors

duaneobrien avatar henghongchen avatar reedyrm avatar richardcen avatar sheriffhobo avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

virgil-spring-boot-starter's Issues

"Republish" action will fail sometimes since the target "fingerprint" is not found in messageCache, and it even make the message LOSE FOREVER

The "Publish" action will fail sometimes due to the issue I mentioned. (See Line #38 in PublishMessageEndpoint and #107 in MessageOperator) . In addition, if the action is failed, the target message WILL BE REMOVED and users will LOSE DATA FOREVER.

Root Cause:

Reasons for "Publishing" fail :

  • The messageCache doesn't contain the fingerprint information since the request may be handled by different instances.

Reasons for data loss:

  • Virgil ack & remove the message before publishing the message, but not take the "REVERT" if the publishing behavior failed.

Solution:
For issue #1, let's call the function "getMessages()" again if messageCache doesn't contain the target fingerprint.

For issue #2, let's switch the process in Publish API to do "publish target message" before "ack & remove target message"

Make Virgil endpoint requests close the connection anyway

Currently, it seems that Virgil closes the connection to make "unacked" messages be back to the "READY" state at the end of "getMessage" & "ackCertainMessage" function. If any exception is thrown during the process, the message "consumed" by Virgil will become "unacked" and never recovered back. It would be helpful to make Virgil close the connection anyway as the last step. (even though any exception is thrown)

Solution options:

  1. Add try-catch for the mentioned functions
  2. Move the destroy call to the finally function

"Republish" request action sometimes will fail since target message failed to be acked

Sometimes, when you click the "Republish" button on one specific message, the request will fail even though the message is correct and publishable (they can be re-published successfully with some attempt).

With some local debugging, it seems that the problem comes from the method ackCertainMessage(). During the process of this method, sometimes the target message may become "unacked" when it calls the function "getQueueSize()". In this case, the method will FAIL to ack the target message anymore, which leads to the FAILURE response.

To fix the issue, I suggest to re-design the method "ackCertainMessage()" to make sure that messages WILL NOT BE ACKED unexpected.

Add search field for message body or header/value type

I think it would be a useful feature to add a search that allowed users to search for messages that contained certain header/value combos or text within body.

This may have some performance implications if the DLQ is too large, so it may need to get truncated if any issues.

Change the VirgilAutoConfiguration class to PUBLIC

Sometimes, the clients require to NOT initialize the Virgil configurations due to their specific usages (like integration tests).
Making clients enable/disable Virgil's initialization in a flexible way would be really helpful for all users.

Solution:
just a one-line change on the file VirgilAutoConfiguration.java

Please change the Class to Public

Stop compiling with reactor-netty

We should depend on the runtime for the reactor library to allow folks to decide whether to use jetty, netty, or (another library) for reactor.

This is causing some of our users to have to exclude our reactor-netty library

Improve Virgil rabbit address parsing

Currently Virgil uses its own incantation of @ConfigurationProperties (RabbitSettings) to load the Rabbit configuration. This does not support addresses that are perfectly valid in Spring Boot Rabbit auto-configuration. The end result is that a SpringBoot app using standard rabbit addresses is not able to use those same properties to configure Virgil.

Namely Virgil can not handle an addresses field prefixed w/ "amqp://" nor one that does not specify a port (aka uses the default port). All that Virgil does is send the info down into Rabbit - so really its Rabbit that is not lenient. However, SpringBoot has already solved this with its RabbitProperties config props class that it uses.

Virgil should support the same configuration options that Spring Boot supports.

Acceptance Criteria:

  • Standard Spring Boot supported rabbit addresses property can be used.

Get DLQ Messages endpoint should be sent a default

Without a limit being passed into /virgil/get-dlq-message, it fails to return messages to the dashboard.

http://localhost:8081/private/virgil/get-dlq-messages?limit=1 will succeed while http://localhost:8081/private/virgil/get-dlq-messages fails.

Acceptance Criteria:

  • Frontend code should pass a default limit 200
  • If no limit is received at endpoint, a default limit of 200 should be used.

Notes:
In the future, I can see this being a configurable value on the frontend similiar to how you can change the results on a page during pagination.

The DefaultMessageConverter will cause a 500 error if target message contains non-null stacktrace data

The DefaultMessageConverter uses the ObjectMapper to generate the md5 fingerprint of the target RabbitMQ message. Therefore, all fields inside the target message (spring-amqp) should be Serializable.

This message class contains a field MessageProperties, which contains an attribute, "headers" in <String, Object> type.

If there is any exception stored in the message, the attribute header will store the stack trace messages as one Map Entry "stacktrace"(String) -> "details...." (ByteArrayLongString), in which case that "ByteArrayLongString" is Non-Serializable.

In this case, the ObjectMapper will fail to generate md5 fingerprint and throws an exception like :

No serializer found for class java.io.DataInputStream and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.springframework.amqp.core.Message[\"messageProperties\"]->org.springframework.amqp.core.MessageProperties[\"headers\"]->java.util.HashMap[\"x-exception-stacktrace\"]->com.rabbitmq.client.impl.LongStringHelper$ByteArrayLongString[\"stream\"])","path":"/private/virgil/get-dlq-messages"}

Solution:
To solve this issue, we should configure the ObjectMapper as SerializationFeature.FAIL_ON_EMPTY_BEANS = false, which will ignore all Non-Serializable fields during the process.

OSSshield: Update to Active?

Hello,

The OSSmetadata file has the shield notation as "experimental". Is this the correct notation, or should it be updated to "active"?

Optimize the logic in PublishMessageEndpoint to avoid data loss

With the current implementation of Virgil, it's possible that one message will be lost when the service tries to republish it. According to the comment, if the process crashed in between, or ack success but publish failed, the target message will be missed.

It's very dangerous that any message will be missed unexpectedly during the republishing processing.

Solution:
Please optimize the function to NOT remove the message from Dead Letter Queue until the republishment success.

Drop and Publish endpoint calls fail with status 400

The frontend UI is passing the parameter as fingerprint but the backend controllers are expecting a messageId named parameter. The mismatch is causing a 400.

{"timestamp":"2021-02-27T03:28:30.525+00:00","status":400,"error":"Bad Request","message":"","path":"/actuator/private/virgil/drop-message"}

Support Multiple DLQs

Currently, Virgil is limited to supporting a single DLQ. A user would want to manage multiple DLQs from the same interface without having to create multiple interfaces.

Potential Configuration changes to consider

Multiple Queues with Single Binder

virgil:
  queues:
    dlq1:
      readName: 'dlq1'
      readBinderName: binder1
      republishName: 'originalQueue1'
      republishBinderName: binder1
      republishBindingRoutingKey: 'key1'
    dlq2:
      readName: 'dlq2'
      readBinderName: binder1
      republishName: 'originalQueue2'
      republishBinderName: binder1
      republishBindingRoutingKey: 'key2'
  binders:
    binder1:
      name: 'exchange'
      type: rabbit
      rabbitProperties:
        addresses: 
        username: 
        password: 
        virtual-host: 

Multiple Queues with Multiple Binders

virgil:
  queues:
    dlq1:
      readName: 'dlq1'
      readBinderName: binder1
      republishName: 'originalQueue1'
      republishBinderName: binder1
      republishBindingRoutingKey: 'key1'
    dlq2:
      readName: 'dlq2'
      readBinderName: binder2
      republishName: 'originalQueue2'
      republishBinderName: binder2
      republishBindingRoutingKey: 'key2'
  binders:
    binder1:
      name: 'exchange1'
      type: rabbit
      rabbitProperties:
        addresses: ${secret.store.addresses}
        username: ${secret.store.username}
        password: ${secret.store.password}
        virtual-host: ${secret.store.virtual-host}
    binder2:
      name: 'exchange2'
      type: rabbit
      rabbitProperties:
        addresses: ${secret.store.addresses}
        username: ${secret.store.username}
        password: ${secret.store.password}
        virtual-host: ${secret.store.virtual-host}

Acceptance Criteria:

  • Modify config to support multiple queues and multiple binders
  • UI would need to be able to have a way of swapping between each DLQ

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.