Lazy queues inside ConsumerDefinition - masstransit

I have ConsumerDefinition:
public class RequestSentConsumerDefinition : ConsumerDefinition<RequestSentConsumer>
{
protected override void ConfigureConsumer(
IReceiveEndpointConfigurator endpoint,
IConsumerConfigurator<RequestSentConsumer> consumer)
{
var rabbitmq = endpoint as RabbitMqReceiveEndpointConfiguration;
rabbitmq.Lazy = true;
consumer.UseMessageRetry(retry => retry
.Incremental(3, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1)));
}
}
Is there more elegant way to set queue laziness?
Maybe MT has something like RabbitMQConsumerDefinition or ConsumerDefinition<RequestSentConsumer, RabbitMqReceiveEndpointConfiguration> in any external package?

For transport-specific configuration in a consumer definition, you should pattern match against the receive endpoint configurator, and apply the transport-specific configuration as needed.
protected override void ConfigureConsumer(
IReceiveEndpointConfigurator endpointConfigurator,
IConsumerConfigurator<RequestSentConsumer> consumerConfigurator)
{
if(endpointConfigurator is IRabbitMqReceiveEndpointConfigurator rmq)
rmq.Lazy = true;
}

Related

Vert.x aerospike client to use context-bound event loop

Standard java aerospike client's methods have overloads allowing to provide EventLoop as an argument. When running in vert.x that client is not aware of context-bounded event loop and just fallbacks to if (eventLoop == null) { eventLoop = cluster.eventLoops.next(); } which could(and likely does) causes context switching/level of concurrency which in turn affects performance (it's still in theory, but I want to prove it), because there is no guarantee that aerospike requests will run on the same event loop as coming http request according to Vert.x Multi Reactor pattern. Open source aerospike clients like vertx-aerospike-client also have such a disadvantage. Using vert.x there is no way(at least I'm not aware of) to retrieve context-bounded event loop and pass it to aerospike client.
Vert.x has method to retrieve Context Vertx.currentContext() but retrieving EventLoop is not available.
Any ideas?
Finally I've built this:
public class ContextEventLoop {
private final NettyEventLoops eventLoops;
public ContextEventLoop(final NettyEventLoops eventLoops) {
this.eventLoops = Objects.requireNonNull(eventLoops, "eventLoops");
}
public EventLoop resolve() {
final ContextInternal ctx = ContextInternal.current();
final EventLoop eventLoop;
if (ctx != null
&& ctx.isEventLoopContext()
&& (eventLoop = eventLoops.get(ctx.nettyEventLoop())) != null) {
return eventLoop;
}
return eventLoops.next();
}
}
#NotNull
public EventLoops wrap(final EventLoops fallback,
final Supplier<#NotNull EventLoop> next) {
return new EventLoops() {
#Override
public EventLoop[] getArray() {
return fallback.getArray();
}
#Override
public int getSize() {
return fallback.getSize();
}
#Override
public EventLoop get(int index) {
return fallback.get(index);
}
#Override
public EventLoop next() {
return next.get();
}
#Override
public void close() {
fallback.close();
}
};
}

How to create instance specific message queues in springboot rest api

I have a number of microservices, each running in its own container in a load balanced environment. I have a need for each instance of these microservices to create a rabbitmq queue when it starts up and delete it when it stops. I have currently defined the following property in my application properties file:
config_queue: config_${PID}
My message queue listener looks like this:
public class ConfigListener {
Logger logger = LoggerFactory.getLogger(ConfigListener.class);
// https://www.programcreek.com/java-api-examples/index.php?api=org.springframework.amqp.rabbit.annotation.RabbitListener
#RabbitListener(bindings = #QueueBinding(
value = #Queue(value = "${config_queue}",
autoDelete = "true"),
exchange = #Exchange(value = AppConstants.TOPIC_CONFIGURATION,
type= ExchangeTypes.FANOUT)
))
public void configChanged(String message){
... application logic
}
}
All this works great when I run the microservice. A queue with prefix config and process id gets created and is auto deleted when I stop the service.
However, when I run this service and others in their individual docker containers, all services have the same PID and that is 1.
Does anybody have any idea how I can create specify a queue that is unique to that instance.
Thanks in advance for your help.
Use an AnonymousQueue instead:
#SpringBootApplication
public class So72030217Application {
public static void main(String[] args) {
SpringApplication.run(So72030217Application.class, args);
}
#RabbitListener(queues = "#{configQueue.name}")
public void listen(String in) {
System.out.println(in);
}
}
#Configuration
class Config {
#Bean
FanoutExchange fanout() {
return new FanoutExchange("config");
}
#Bean
Queue configQueue() {
return new AnonymousQueue(new Base64UrlNamingStrategy("config_"));
}
#Bean
Binding binding() {
return BindingBuilder.bind(configQueue()).to(fanout());
}
}
AnonymousQueues are auto-delete and use a Base64 encoded UUID in the name.

spring integration publish subscribe between beans

Thanks for reading ahead of time. In my main method I have a PublishSubscribeChannel
#Bean(name = "feeSchedule")
public SubscribableChannel getMessageChannel() {
return new PublishSubscribeChannel();
}
In a service that does a long running process it creates a fee schedule that I inject the channel into
#Service
public class FeeScheduleCompareServiceImpl implements FeeScheduleCompareService {
#Autowired
MessageChannel outChannel;
public List<FeeScheduleUpdate> compareFeeSchedules(String oldStudyId) {
List<FeeScheduleUpdate> sortedResultList = longMethod(oldStudyId);
outChannel.send(MessageBuilder.withPayload(sortedResultList).build());
return sortedResultList;
}
}
Now this is the part I'm struggling with. I want to use completable future and get the payload of the event in the future A in another spring bean. I need future A to return the payload from the message. I think want to create a ServiceActivator to be the message end point but like I said, I need it to return the payload for future A.
#org.springframework.stereotype.Service
public class SFCCCompareServiceImpl implements SFCCCompareService {
#Autowired
private SubscribableChannel outChannel;
#Override
public List<SFCCCompareDTO> compareSFCC(String state, int service){
ArrayList<SFCCCompareDTO> returnList = new ArrayList<SFCCCompareDTO>();
CompletableFuture<List<FeeScheduleUpdate>> fa = CompletableFuture.supplyAsync( () ->
{ //block A WHAT GOES HERE?!?!
outChannel.subscribe()
}
);
CompletableFuture<List<StateFeeCodeClassification>> fb = CompletableFuture.supplyAsync( () ->
{
return this.stateFeeCodeClassificationRepository.findAll();
}
);
CompletableFuture<List<SFCCCompareDTO>> fc = fa.thenCombine(fb,(a,b) ->{
//block C
//get in this block when both A & B are complete
Object theList = b.stream().forEach(new Consumer<StateFeeCodeClassification>() {
#Override
public void accept(StateFeeCodeClassification stateFeeCodeClassification) {
a.stream().forEach(new Consumer<FeeScheduleUpdate>() {
#Override
public void accept(FeeScheduleUpdate feeScheduleUpdate) {
returnList new SFCCCompareDTO();
}
});
}
}).collect(Collectors.toList());
return theList;
});
fc.join();
return returnList;
}
}
Was thinking there would be a service activator like:
#MessageEndpoint
public class UpdatesHandler implements MessageHandler{
#ServiceActivator(requiresReply = "true")
public List<FeeScheduleUpdate> getUpdates(Message m){
return (List<FeeScheduleUpdate>) m.getPayload();
}
}
Your question isn't clear, but I'll try to help you with some info.
Spring Integration doesn't provide CompletableFuture support, but it does provide an async handling and replies.
See Asynchronous Gateway for more information. And also see Asynchronous Service Activator.
outChannel.subscribe() should come with the MessageHandler callback, by the way.

How to invoke TraceListener.Write(object) through TraceSource

We have a custom TraceListener implementation which only logs when a specific object (LogMessage) is received. This all works well when using directly with the Trace.Write(object) method.
Due to Performance reason, I want to separate the Listener, so all non-relevant Trace messages are not passed to the listener. Therefore I created a specific TraceSource whith only this listener attached.
Now I struggle to pass my custom log object (LogMessage) to the listener using the TraceSource. The TraceSource.TraceData(TraceEventType, int, object) always invokes the TraceListener.Write(string) method, not the TraceListener.Write(object) method.
Is there any way I can pass the custom object to the Listener using the TraceSource?
Sample code:
using System.Diagnostics;
namespace Sample
{
public class LogMessage
{
public byte[] Data { get; set; }
//...
}
public class Sample
{
public void Foo()
{
var ts = new TraceSource("Test");
var lm = new LogMessage();
//lm.Data = ...;
//this works: calls the Write(object) method in listener
Trace.Write(lm);
//this doesn't work: calls the Write(string) method in listener
ts.TraceData(TraceEventType.Information, 0, lm);
}
}
public class MyListener : TraceListener
{
public override void Write(string message)
{
//not in use
}
public override void WriteLine(string message)
{
//not in use
}
public sealed override void Write(object o)
{
if (o is LogMessage)
{
//do someting with the LogMessage
}
}
}
}
Thanks
Thomas
maybe it's too late for an answer but anyway :
By using a tool like JustDecompile you can easily see that TraceSource.TraceData uses TraceListener.TraceData method which itself basically calls WriteLine with object.ToString() for message.
So you'll have to override the ToString method for your class LogMessage in order to do as you want.

How can I access previously set fields in a seam #Observer method?

My current setup is JBoss Seam 2.2 on JBoss 4.2.3.GA.
I have two Beans like so:
#Name("mailingManager")
#Scope(ScopeType.PAGE)
public class MailingMgr {
private Mailing selectedMailing;
#Observer("mailing.letter.success")
public void recordSuccess(final Object arg) {
if (null != selectedMailing) { // store arg }
}
public void send() {
selectedMailing = new Mailing();
if ('EMAIL' == determineType()) {
EmailSender mailer = (EmailSender) Component.getInstance(EmailSender.class);
mailer.send(getAddresses());
}
// ... more options
}
}
#Name("emailSender")
#Scope(ScopeType.PAGE)
public class EmailSender {
public void send(final Set<String> addresses) {
for (String addr : addresses) {
// ... create a mail
Events.instance().raiseEvent("mailing.letter.success", getGeneratedMail());
}
}
}
The problem is that when recordSuccess() is called selectedMailing is always null.
As a workaround I'm setting selectedMailing in the conversation context manually before calling any code that could potentially trigger my events, and then annotate my field with #In(required=false) to inject it again before recordSuccess is called. But is there a more elegant solution (keeping the decoupling intact)? And why isn't the calling bean reused to handle the event?

Resources