masstransit - consumers are no registered and activated - autofac

I'm trying to register consumers but no success using mass transit.
I registered MT using Autofac using module approach.
Firstly - I created some simple message:
public class SimpleMessage
{
public string msg { get; set; }
}
and I've managed to send them into queue:
var endpointTest = await _busControl.GetSendEndpoint(new Uri("queue:queueTest"));
await endpointTest.Send(new SimpleMessage
{
msg = "test"
});
Then I created a consumer:
public class SimpleMessageConsumer : IConsumer<SimpleMessage>
{
private readonly ILogger _logger;
public SimpleMessageConsumer(ILogger logger)
{
_logger = logger;
}
public async Task Consume(ConsumeContext<SimpleMessage> context)
{
_logger.Info($"got msg from queue: {context.Message}");
}
}
But it won't run when the message appeared in the queue. My configuration is:
public class BusModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<BusSettings>().As<IBusSettings>();
builder.AddMassTransit(cfg =>
{
cfg.AddConsumer<SimpleMessageConsumer, SimpleMessageConsumerDefinition>();
cfg.Builder.Register(context =>
{
var busSettings = context.Resolve<IBusSettings>();
var logger = context.Resolve < ILogger >();
var busControl = Bus.Factory.CreateUsingRabbitMq(bus =>
{
bus.AutoDelete = busSettings.AutoDelete;
bus.Durable = busSettings.Durable;
bus.Exclusive = busSettings.Exclusive;
bus.ExchangeType = busSettings.Type;
//bus.UseNServiceBusJsonSerializer();
bus.Host(busSettings.HostAddress, busSettings.Port, busSettings.VirtualHost, null, h =>
{
h.Username(busSettings.Username);
h.Password(busSettings.Password);
});
bus.ReceiveEndpoint("queueTest", ec =>
{
ec.Consumer(() => new SimpleMessageConsumer(logger));
});
});
return busControl;
}).SingleInstance().As<IBusControl>().As<IBus>();
});
}
}
in program.cs
I have:
services.AddMassTransitHostedService();
and
containerBuilder.RegisterModule<BusModule>();
Such I mentioned - sending a msg to queue works but consumer wasn't running.
Can you help me what did I do wrong? how should I fix the configuration? in order to activate the consumer?

I've updated your configuration to work properly, using the actual bus configuration methods instead of mixing the two solutions:
public class BusModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<BusSettings>().As<IBusSettings>();
builder.AddMassTransit(cfg =>
{
cfg.AddConsumer<SimpleMessageConsumer, SimpleMessageConsumerDefinition>();
cfg.UsingRabbitMq((context,cfg) =>
{
var busSettings = context.GetRequiredService<IBusSettings>();
var logger = context.GetRequiredService<ILogger>();
//bus.UseNServiceBusJsonSerializer();
bus.Host(busSettings.HostAddress, busSettings.Port, busSettings.VirtualHost, null, h =>
{
h.Username(busSettings.Username);
h.Password(busSettings.Password);
});
bus.ReceiveEndpoint("queueTest", ec =>
{
// i'm guessing these apply to the receive endpoint, not the bus endpoint
ec.AutoDelete = busSettings.AutoDelete;
ec.Durable = busSettings.Durable;
ec.Exclusive = busSettings.Exclusive;
ec.ExchangeType = busSettings.Type;
ec.ConfigureConsumer<SimpleMessageConsumer>(context);
});
});
});
}
}

Related

EntityFrameworkCore Task.WhenAll() A second operation started on this context before a previous operation completed

I want to read data from database. For this I create a query and queryhandler classes
QueryHandler
public class OrderGetQueryHandler: IQueryHandler<OrderGetQuery, OrderDTO>
{
private readonly GoodWillWebDbContext _context;
private readonly IQueryDispatcher _queryDispatcher;
public OrderGetQueryHandler(GoodWillWebDbContext context, IQueryDispatcher queryDispatcher)
{
_context = context;
_queryDispatcher = queryDispatcher;
}
private bool CheckPartnerBlock(BlockTypes blockType, decimal debtOverdue, bool payOff)
{
if (blockType == BlockTypes.Block)
return true;
if (blockType == BlockTypes.NotBlock)
return false;
if (blockType == BlockTypes.PreliminaryPayment)
return payOff;
return debtOverdue <= 0;
}
public async Task<OrderDTO> HandleAsync(OrderGetQuery query)
{
var order = await _context.Orders.FindAsync(query.OrderID);
if (order != null)
{
var getCustomerTask = _context.Partners.FindAsync(order.CustomerID).AsTask();
var getCuratorTask = _context.Users.FindAsync(order.CuratorID).AsTask();
var getPaymentTask = _context.Payments.OrderByDescending(x => x.PaymentID).FirstOrDefaultAsync(x => x.CustomerID == order.CustomerID);
var getOrderLinesTask =
_queryDispatcher.HandleAsync<OrderLinesGetQuery, OrderLineDTO[]>(
new OrderLinesGetQuery(query.OrderID));
await Task.WhenAll(getCustomerTask, getCuratorTask, getOrderLinesTask, getPaymentTask);
var priceRange = await _context.PriceRanges.FindAsync(getCustomerTask.Result.PriceRangeID);
return new OrderDTO
(
order.OrderID,
getCustomerTask.Result.Name,
getOrderLinesTask.Result,
order.CustomerID,
order.OrderStateID,
order.CanDelete,
order.CreationDate,
getPaymentTask.Result.DebtBank,
getPaymentTask.Result.DebtOverdue,
this.CheckPartnerBlock(getCustomerTask.Result.BlockTypeID, getPaymentTask.Result.DebtOverdue, order.PayOff),
priceRange.Name,
order.ReservationDate,
Mapper.Convert<DeliveryInfoDTO, BaseEntities.Entities.Sales.Order>(order)
);
}
throw new NullReferenceException();
}
}
this queryhandler i use in ASP.NET WEB Application. My startup class is
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
string connection = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<GoodWillWebDbContext>(options =>
options.UseSqlServer(connection), ServiceLifetime.Transient);
services.AddScoped<IQueryHandler<OrdersGetQuery, BaseEntities.DTO.Sales.Order.OrderDTO[]>, OrdersGetQueryHandler>();
services.AddScoped<IQueryHandler<OrderGetQuery, Sales.Queries.DTO.Order.OrderDTO>, OrderGetQueryHandler>();
services.AddScoped<ICommandDispatcher, CommandDispatcher>();
services.AddScoped<IQueryDispatcher, QueryDispatcher>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
I set ServiceLifetime.Transient for my context, but I still get an exception: InvalidOperationException A second operation started on this context before a previous operation completed.
What's wrong?
It seems you're running multiple operations on the context without waiting for the previous ones to end, which EF doesn't like:
var getCustomerTask = _context.Partners.FindAsync(order.CustomerID).AsTask();
var getCuratorTask = _context.Users.FindAsync(order.CuratorID).AsTask();
var getPaymentTask = _context.Payments.OrderByDescending(x => x.PaymentID).FirstOrDefaultAsync(x => x.CustomerID == order.CustomerID);
Either make these call sync or use the await keyword.

Polly Retry with RX Observable.Interval

I'm new to Polly and I'm trying to apply the Retry policy, so that I can have it manually handling the retry connection in case of IBMMQ connection issue.
Please, consider the following code:
public class ReconnectException : Exception
{
}
public class QueueMonitor : IObservable<Message>, IDisposable
{
private readonly MQQueue mqQueue;
private readonly MQQueueManager queueManager;
private readonly string queueName;
private IDisposable timer;
private readonly object lockObj = new object();
private bool isChecking;
private readonly TimeSpan checkingFrequency;
private readonly List<IObserver<Message>> observers;
private TimeSpan reconnectInterval;
private readonly IScheduler scheduler;
private readonly int maxReconnectCount;
private static readonly ILog Logger = LogProvider.For<AonQueueManager>();
private readonly Policy pollyPolicy;
public QueueMonitor(IConfiguration configuration, string queueName, IScheduler scheduler = null)
{
this.queueManager = QueueFactory.GetIstance(configuration);
this.queueName = queueName;
this.scheduler = scheduler ?? Scheduler.Default;
checkingFrequency = configuration.GetValue("checkingFrequency", new TimeSpan(0, 0, 5));
reconnectInterval = configuration.GetValue("reconnectInterval", new TimeSpan(0, 0, 5));
maxReconnectCount = configuration.GetValue("maxReconnectCount", 3);
observers = new List<IObserver<Message>>();
pollyPolicy = Policy.Handle<ReconnectException>().WaitAndRetry(maxReconnectCount, _ => TimeSpan.FromSeconds(2));
mqQueue = queueManager.AccessQueue(queueName,
MQC.MQOO_INPUT_AS_Q_DEF // open queue for input
+ MQC.MQOO_FAIL_IF_QUIESCING); // but not if MQM stopping
}
public void Start()
{
var x = pollyPolicy.ExecuteAndCapture(CreateTimer);
}
private void CreateTimer()
{
Logger.DebugFormat("Repeating timer started, checking frequency: {checkingFrequency}", checkingFrequency);
timer = Observable.Interval(checkingFrequency, scheduler).Subscribe(_ =>
{
lock (lockObj)
{
if (isChecking) return;
Logger.Log(LogLevel.Debug, () => "Listening on queues for new messages");
isChecking = true;
var mqMsg = new MQMessage();
var mqGetMsgOpts = new MQGetMessageOptions { WaitInterval = checkingFrequency.Milliseconds };
// 15 second limit for waiting
mqGetMsgOpts.Options |= MQC.MQGMO_WAIT | MQC.MQGMO_FAIL_IF_QUIESCING |
MQC.MQCNO_RECONNECT_Q_MGR | MQC.MQOO_INPUT_AS_Q_DEF;
try
{
mqQueue.Get(mqMsg, mqGetMsgOpts);
if (mqMsg.Format.CompareTo(MQC.MQFMT_STRING) == 0)
{
var text = mqMsg.ReadString(mqMsg.MessageLength);
Logger.Debug($"Message received : [{text}]");
Message message = new Message { Content = text };
foreach (var observer in observers)
observer.OnNext(message);
}
else
{
Logger.Warn("Non-text message");
}
}
catch (MQException ex)
{
if (ex.Message == MQC.MQRC_NO_MSG_AVAILABLE.ToString())
{
Logger.Trace("No messages available");
//nothing to do, emtpy queue
}
else if (ex.Message == MQC.MQRC_CONNECTION_BROKEN.ToString())
{
Logger.ErrorException("MQ Exception, trying to recconect", ex);
throw new ReconnectException();
}
}
finally
{
isChecking = false;
}
}
});
}
public IDisposable Subscribe(IObserver<Message> observer)
{
if (!observers.Contains(observer))
observers.Add(observer);
return new Unsubscriber(observers, observer);
}
public void Dispose()
{
((IDisposable)mqQueue)?.Dispose();
((IDisposable)queueManager)?.Dispose();
timer?.Dispose();
}
}
public class Unsubscriber : IDisposable
{
private readonly List<IObserver<Message>> observers;
private readonly IObserver<Message> observer;
public Unsubscriber(List<IObserver<Message>> observers, IObserver<Message> observer)
{
this.observers = observers;
this.observer = observer;
}
public void Dispose()
{
if (observer != null) observers.Remove(observer);
}
}
The problem I've is that when an exception is thrown inside the lamda ( throw new ReconnectException();), Polly doesn't catch it (and I understand why, since it's on another thread) and the application quits since it's on a different thread.
This code is a part of a library,so I don't know that if in every project the Global exceptions are correctly handed.
How do I get it "catched" by the Polly's code?
Thanks in advance
The code posted in the question applies the policy only to the act of creating the timer (the execution of CreateTimer()), not to the code executed by the timer (the lambda inside the .(Subscribe(_ => { }) call).
This is the same as the behaviour if the call to CreateTimer() was surrounded by a try { } catch { }. The catch would only cover the act of executing the CreateTimer() method, the creation of the timer.
For the Polly policy to govern exceptions thrown within the lambda, it needs to be applied within the lambda, to the relevant block/group of statements which are expected to throw the exception.
For example, you might code:
pollyPolicy.ExecuteAndCapture(() => mqQueue.Get(mqMsg, mqGetMsgOpts));
(with a policy configured to govern the particular MQException/s you want to handle).
Or you can apply the policy to a wider group of statements - just as with a try { } clause.
pollyPolicy.ExecuteAndCapture(() =>
{
// ...
mqQueue.Get(mqMsg, mqGetMsgOpts));
// ...
}

Converting event based API to Rx.Net

I'm trying to convert an existing event-based API to a Reactive Observable API. The concrete API I'm working with is the NSNetServiceBrowser in Xamarin.iOS. This API let you browse for network devices using Zeroconf/Bonjour. However, the question would apply to any API of this kind.
The NsNetServiceBrowser offers various events of interest:
- FoundService
- NotSearched
- ServiceRemoved
The FoundService event is raised when a service is discovered, and the NotSearched is raised when the search fails.
I would like to combine the FoundService and NotSerched events, into an observable of NSNetService.
My current implementation looks like this:
public IObservable<NSNetService> Search()
{
var foundObservable = Observable
.FromEventPattern<NSNetServiceEventArgs>(
h => serviceBrowser.FoundService += h,
h => serviceBrowser.FoundService -= h)
.Select(x => x.EventArgs);
var notSearchedObservable = Observable
.FromEventPattern<NSNetServiceErrorEventArgs>(
h => serviceBrowser.NotSearched += h,
h => serviceBrowser.NotSearched -= h)
.Select(x => x.EventArgs);
var serviceObservable = Observable.Create(
(IObserver<NSNetServiceEventArgs> observer) =>
{
notSearchedObservable.Subscribe(n =>
{
string errorMessage = $"Search for {serviceType} failed:";
foreach (var kv in n.Errors)
{
log.Error($"\t{kv.Key}: {kv.Value}");
errorMessage += $" ({kv.Key}, {kv.Value})";
}
observer.OnError(new Exception(errorMessage));
});
foundObservable.Subscribe(observer);
return System.Reactive.Disposables.Disposable.Empty;
}).Select(x => x.Service);
serviceBrowser.SearchForServices(serviceType, domain);
return serviceObservable;
}
The code looks clunky and I have a gut feeling I'm not using System.Reactive correctly? Is there a more elegant way to combine event pairs, where one is producing and the other is signaling error? This is a common pattern in existing event based APIs in .NET.
Here is a small console app (depending only on System.Reactive) illustrating the type of API I want to Reactify:
using System;
using System.Reactive;
using System.Reactive.Linq;
using System.Threading.Tasks;
namespace ReactiveLearning
{
class Program
{
static void Main(string[] args)
{
var browser = new ServiceBrowser();
var observableFound =
Observable.FromEventPattern<ServiceFoundEventArgs>(
h => browser.ServiceFound += h,
h => browser.ServiceFound -= h)
.Select(e => e.EventArgs.Service);
var observableError =
Observable.FromEventPattern<ServiceSearchErrorEventArgs>(
h => browser.ServiceError += h,
h => browser.ServiceError -= h);
var foundSub = observableFound.Subscribe(s =>
{
Console.WriteLine($"Found service: {s.Name}");
}, () =>
{
Console.WriteLine("Found Completed");
});
var errorSub = observableError.Subscribe(e =>
{
Console.WriteLine("ERROR!");
}, () =>
{
Console.WriteLine("Error Completed");
});
browser.Search();
Console.ReadLine();
foundSub.Dispose();
errorSub.Dispose();
Console.WriteLine();
}
}
class ServiceBrowser
{
public EventHandler<ServiceFoundEventArgs> ServiceFound;
public EventHandler<ServiceSearchErrorEventArgs> ServiceError;
public void Search()
{
Task.Run(async () =>
{
for (var i = 0; i < 5; ++i)
{
await Task.Delay(1000);
ServiceFound?.Invoke(this, new ServiceFoundEventArgs(new Service($"Service {i}")));
}
var r = new Random();
if (r.NextDouble() > 0.5)
{
ServiceError?.Invoke(this, new ServiceSearchErrorEventArgs());
}
});
}
}
class ServiceFoundEventArgs : EventArgs
{
public Service Service { get; private set; }
public ServiceFoundEventArgs(Service service) => Service = service;
}
class ServiceSearchErrorEventArgs : EventArgs {}
class Service
{
public event EventHandler<EventArgs> AddressResolved;
public event EventHandler<EventArgs> ErrorResolvingAddress;
public string Name { get; private set; }
public string Address { get; private set; }
public Service(string name) => Name = name;
public void ResolveAddress()
{
Task.Run(async () =>
{
await Task.Delay(500);
var r = new Random();
if (r.NextDouble() > 0.5)
{
Address = $"http://{Name}.com";
AddressResolved?.Invoke(this, EventArgs.Empty);
}
else
{
ErrorResolvingAddress?.Invoke(this, EventArgs.Empty);
}
});
}
}
}
Thank you for the excellent sample code. You need to make use of the excellent Materialize & Dematerialize operators. Here's how:
var observableFoundWithError =
observableFound
.Materialize()
.Merge(
observableError
.Materialize()
.Select(x =>
Notification
.CreateOnError<Service>(new Exception("Error"))))
.Dematerialize()
.Synchronize();
using (observableFoundWithError.Subscribe(
s => Console.WriteLine($"Found service: {s.Name}"),
ex => Console.WriteLine($"Found error: {ex.Message}"),
() => Console.WriteLine("Found Completed")))
{
browser.Search();
Console.ReadLine();
}
The Materialize() operator turns an IObservable<T> into and IObservable<Notification<T>> which allows the standard OnError and OnCompleted to be emitted through the OnNext call. You can use Notification.CreateOnError<T>(new Exception("Error")) to construct elements of observable which you can turn back into an IObservable<T> with Dematerialize().
I've thrown the Synchronize() to ensure that you've created a valid observable. The use of Materialize() does let you construct observables that don't follow the regular observable contract. Part of what Synchronize() does is just ensure only one OnError and only one OnCompleted and drops any OnNext that comes after either of the two.
Try this as a way to do what you wanted in the comments:
static void Main(string[] args)
{
var browser = new ServiceBrowser();
var observableFound =
Observable.FromEventPattern<ServiceFoundEventArgs>(
h => browser.ServiceFound += h,
h => browser.ServiceFound -= h)
.Select(e => e.EventArgs.Service);
var observableError =
Observable.FromEventPattern<ServiceSearchErrorEventArgs>(
h => browser.ServiceError += h,
h => browser.ServiceError -= h);
var observableFoundWithError = observableFound
.Materialize()
.Merge(
observableError
.Materialize()
.Select(x => Notification.CreateOnError<Service>(new Exception("Error"))))
.Dematerialize()
.Synchronize();
Func<Service, IObservable<Service>> resolveService = s =>
Observable.Create<Service>(o =>
{
var observableResolved = Observable.FromEventPattern<EventArgs>(
h => s.AddressResolved += h,
h => s.AddressResolved -= h);
var observableResolveError = Observable.FromEventPattern<EventArgs>(
h => s.ErrorResolvingAddress += h,
h => s.ErrorResolvingAddress -= h);
var observableResolvedWithError =
observableResolved
.Select(x => s)
.Materialize()
.Merge(
observableResolveError
.Do(e => Console.WriteLine($"Error resolving: {s.Name}"))
.Materialize()
.Select(x => Notification.CreateOnError<Service>(new Exception($"Error resolving address for service: {s.Name}"))))
.Dematerialize()
.Synchronize();
s.ResolveAddress();
return observableResolvedWithError.Subscribe(o);
});
using (
observableFoundWithError
.Select(s => resolveService(s))
.Switch()
.Subscribe(
s => Console.WriteLine($"Found and resolved service: {s.Name} ({s.Address})"),
ex => Console.WriteLine($"Found error: {ex.Message}"),
() => Console.WriteLine("Found Completed")))
{
browser.Search();
Console.ReadLine();
}
}
public class ServiceBrowser
{
public event EventHandler<ServiceFoundEventArgs> ServiceFound;
public event EventHandler<ServiceSearchErrorEventArgs> ServiceError;
public void Search() { }
}
public class Service
{
public event EventHandler<EventArgs> AddressResolved;
public event EventHandler<EventArgs> ErrorResolvingAddress;
public string Name;
public string Address;
public void ResolveAddress() { }
}
public class ServiceFoundEventArgs : EventArgs
{
public Service Service;
}
public class ServiceSearchErrorEventArgs : EventArgs
{
}
I might require a bit of tweaking - perhaps an Observable.Delay in there. Let me know if it works.

Is it possible to instruct ServicePartitionClient to talk to a specific node in service fabric?

I have
public class HttpCommunicationClient : HttpClient, ICommunicationClient
{
public HttpCommunicationClient()
: base(new HttpClientHandler() { AllowAutoRedirect = false, UseCookies = false })
{
}
public HttpCommunicationClient(HttpMessageHandler handler)
: base(handler)
{
}
public HttpCommunicationClient(HttpMessageHandler handler, bool disposeHandler)
: base(handler, disposeHandler)
{
}
#region ICommunicationClient
string ICommunicationClient.ListenerName { get; set; }
ResolvedServiceEndpoint ICommunicationClient.Endpoint { get; set; }
ResolvedServicePartition ICommunicationClient.ResolvedServicePartition { get; set; }
#endregion ICommunicationClient
}
and
public class HttpCommunicationClientFactory : CommunicationClientFactoryBase<HttpCommunicationClient>
{
private readonly Func<HttpCommunicationClient> _innerDispatcherProvider;
public HttpCommunicationClientFactory(IServicePartitionResolver servicePartitionResolver = null, IEnumerable<IExceptionHandler> exceptionHandlers = null, string traceId = null)
: this(() => new HttpCommunicationClient(), servicePartitionResolver, exceptionHandlers, traceId)
{
}
public HttpCommunicationClientFactory(Func<HttpCommunicationClient> innerDispatcherProvider, IServicePartitionResolver servicePartitionResolver = null, IEnumerable<IExceptionHandler> exceptionHandlers = null, string traceId = null)
: base(servicePartitionResolver, exceptionHandlers, traceId)
{
if (innerDispatcherProvider == null)
{
throw new ArgumentNullException(nameof(innerDispatcherProvider));
}
_innerDispatcherProvider = innerDispatcherProvider;
}
protected override void AbortClient(HttpCommunicationClient dispatcher)
{
if (dispatcher != null)
{
dispatcher.Dispose();
}
}
protected override Task<HttpCommunicationClient> CreateClientAsync(string endpoint, CancellationToken cancellationToken)
{
var dispatcher = _innerDispatcherProvider.Invoke();
dispatcher.BaseAddress = new Uri(endpoint, UriKind.Absolute);
return Task.FromResult(dispatcher);
}
protected override bool ValidateClient(HttpCommunicationClient dispatcher)
{
return dispatcher != null && dispatcher.BaseAddress != null;
}
protected override bool ValidateClient(string endpoint, HttpCommunicationClient dispatcher)
{
return dispatcher != null && dispatcher.BaseAddress == new Uri(endpoint, UriKind.Absolute);
}
}
and is using it like below
var servicePartitionClient = new ServicePartitionClient<HttpCommunicationClient>(_httpClientFactory,
_options.ServiceUri,
_options.GetServicePartitionKey?.Invoke(context),
_options.TargetReplicaSelector,
_options.ListenerName,
_options.OperationRetrySettings);
using (var responseMessage = await servicePartitionClient.InvokeWithRetryAsync(httpClient => ExecuteServiceCallAsync(httpClient, context)))
{
await responseMessage.CopyToCurrentContext(context);
}
The question is now, if I know at the time of using ServicePartitionClient that I would like it to connect to a specific node, is there any way to do so?
The case is that its a gateway application that forward requests to other services and I would like it to behave like with sticky sessions.
It makes more sense to think in terms of services than nodes. So rather than connecting to a specific node, you're actually connecting to a specific instance of a service.
When you're connecting to a service, if it's stateless, it shouldn't matter which instance you connect to, by definition of it being stateless. If you find that a user is tied to a specific instance of a service, that service is stateful (it's keeping track of some user state), and that's exactly the type of scenario that stateful services are meant to handle.
I found a solution, where I in the ExecuteServiceCallAsync call below reads a cookie from request with the information about which node it was connected to if its a sticky session, and if no cookie is present i set one with the information from the request. If the node dont exist any more the cookie is updated to new node.
using (var responseMessage = await servicePartitionClient.InvokeWithRetryAsync(httpClient => ExecuteServiceCallAsync(httpClient, context)))
{
await responseMessage.CopyToCurrentContext(context);
}

Vert.x - RxJava - zip observables

I am trying to zip to observables using Vert.x and RxJava. I don't know if I am misunderstanding something or this is just some kind of bug. Here is the code.
public class BusVerticle extends Verticle {
public void start() {
final RxVertx rxVertx = new RxVertx(vertx);
Observable<RxMessage<JsonObject>> bus = rxVertx.eventBus().registerHandler("busName");
Observable<RxHttpClientResponse> httpResponse = bus.mapMany(new Func1<RxMessage<JsonObject>, Observable<RxHttpClientResponse>>() {
public Observable<RxHttpClientResponse> call(RxMessage<JsonObject> rxMessage) {
RxHttpClient rxHttpClient = rxVertx.createHttpClient();
rxHttpClient.coreHttpClient().setHost("localhost").setPort(80);
return rxHttpClient.getNow("/uri");
}
});
Observable<RxMessage<JsonObject>> zipObservable = Observable.zip(bus, httpResponse, new Func2<RxMessage<JsonObject>, RxHttpClientResponse, RxMessage<JsonObject>>() {
public RxMessage<JsonObject> call(RxMessage<JsonObject> rxMessage, RxHttpClientResponse rxHttpClientResponse) {
return rxMessage;
}
});
zipObservable.subscribe(new Action1<RxMessage<JsonObject>>() {
public void call(RxMessage<JsonObject> rxMessage) {
rxMessage.reply();
}
});
}
}
I want to make an HTTP request using information from the received message and then zip both observables, the event bus and the HTTP response, in order to reply to the message with information from the HTTP response.
I am not getting any response for the message where I am sending it.
Thanks in advance!
I have solved it with a workaround. Some kind of mixed solution.
public class BusVerticle extends Verticle {
public void start() {
final RxVertx rxVertx = new RxVertx(vertx);
vertx.eventBus().registerHandler("busName", new Handler<Message<JsonObject>>() {
public void handle(final Message<JsonObject> message) {
RxHttpClient rxHttpClient = rxVertx.createHttpClient();
rxHttpClient.coreHttpClient().setHost("localhost").setPort(80);
Observable<RxHttpClientResponse> httpRequest = rxHttpClient.getNow("/uri");
httpRequest.subscribe(new Action1<RxHttpClientResponse>() {
public void call(RxHttpClientResponse response) {
container.logger().error(response.statusCode());
message.reply(new JsonObject().putString("status", "ok"));
}
});
}
});
}
}