Can someone help me understand why I'm getting response messages (CreditAuthorizationResponse) in my skipped queue (mtSubscriber_creditAuthRequest_queue_skipped)? The sender is receiving the responses as expected, but they are also going to the skipped queue.
I've created the following consumer, which is working as expected except for the messages going into the skipped queue:
class CreditAuthorizationConsumer : IConsumer<CreditAuthorizationRequest>
{
private Func<string, Task> _outputDelegate2;
public CreditAuthorizationConsumer(Func<string, Task> outputDelegate)
{
_outputDelegate2 = outputDelegate;
}
public async Task Consume(ConsumeContext<CreditAuthorizationRequest> context)
{
await _outputDelegate2($"Received: {context.Message}: {context.Message.CardNumber}");
await context.RespondAsync<CreditAuthorizationResponse>(new CreditAuthorizationResponse(true));
await _outputDelegate2($"Sent CreditAuthorizationResponse for card request {context.Message.CardNumber}");
}
}
Here is where I'm sending the request:
private async Task SendCreditAuthRequestAsync(int numberToSend)
{
for (int i = 0; i < numberToSend; i++)
{
var cardNumber = generateCardNumber();
await SendRequestAsync(new CreditAuthorizationRequest(cardNumber), "mtSubscriber_creditAuthRequest_queue");
await WriteOutputAsync($"Sent credit auth request for card {cardNumber}.");
}
}
Here is where I'm initializing my client-side bus:
private void InitializeBus()
{
_messageBus = Bus.Factory.CreateUsingRabbitMq(sbc =>
{
var host = sbc.Host(new Uri(hostUriTextBox.Text), h =>
{
h.Username("guest");
h.Password("guest");
});
sbc.ReceiveEndpoint(host, "mtSubscriber_creditAuthResponse_queue", endpoint =>
{
endpoint.Handler<CreditAuthorizationResponse>(async context =>
{
await WriteOutputAsync($"Received: {context.Message}: {context.Message.IsAuthorized}");
});
});
});
}
Here is where I'm initializing my service-side bus:
private void InitializeBus()
{
_messageBus = Bus.Factory.CreateUsingRabbitMq(sbc =>
{
var host = sbc.Host(new Uri(hostUriTextBox.Text), h =>
{
h.Username("guest");
h.Password("guest");
});
sbc.ReceiveEndpoint(host, "mtSubscriber_creditAuthRequest_queue", endpoint =>
{
endpoint.Consumer(() => new CreditAuthorizationConsumer(WriteOutputAsync));
});
}
}
Alexey Zimarev was right -- my responses were bound to my request queue (in addition to the response queue). Simply deleting that binding resolved the problem and it hasn't come back. thanks!
Related
I have a problem with my Angular Promise<void> in one of my services.
The service establishes SignalR connection with the server's hub. And I am waiting with broadcasting to the hub for connection to be established:
export class SignalRService {
private hubConnection: signalR.HubConnection;
private url = environment.apiUrl + 'messageHub';
private thenable: Promise<void>;
public startConnection = (): void => {
if (!this.hubConnection) {
const token = this.auth.getAuth().token;
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(this.url, { accessTokenFactory: () => token })
.build();
this.startHubConnection();
this.hubConnection.onclose(() => this.connectionHubClosed());
}
};
private startHubConnection() {
this.thenable = this.hubConnection.start();
this.thenable
.then(() => console.log('Connection started!'))
.catch((err) => console.log('Error while establishing connection :('));
}
public broadcastGameState = (): void => {
this.thenable.then(() => {
const name = this.auth.getAuth().displayName;
let message = this.game.getGame();
this.hubConnection
.invoke('SendMessage', message)
.catch((err) => console.error(err));
});
};
But after I will close my connection with:
public stopConnection = (): void => {
if (this.hubConnection) {
this.hubConnection.stop();
}
};
and use again my service, I can not use my thenable anymore, and broadcastGameState does not wait anymore for it, throwing an error:
Error: Cannot send data if the connection is not in the 'Connected'
State.
I do not understand why?
xxh you got your point and made me thinking, so I found the problem :) After calling:
public stopConnection = (): void => {
if (this.hubConnection) {
this.hubConnection.stop();
}
};
on connection close is called:
private connectionHubClosed(): void {
//trigerred when lost connection with server
alert('todo: SignalR connection closed');
}
BUT this.hubConnection is still with assigned value, so because of if (!this.hubConnection) new this.thenable was skipped.
I'm using MT version 6.3.2.
I have built a web api which then send the message to consumer queue. I have another process that consumes the message.
Send in api:
var endpoint = await Bus.GetSendEndpoint(QueueUri);
await endpoint.Send(command);
In the consumer process, I use HostBuilder to register autofac
var host = new HostBuilder()
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>((hostBuilderContext, builder) =>
{
builder.RegisterModule(module);
}
In module I register an IBus instance, I have added a filter like this:
builder.Register(cc =>
{return Bus.Factory.CreateUsingRabbitMq(rabbit =>
{
rabbit.Durable = true;
rabbit.Host(ServerName, h =>
{
h.Username(Username);
h.Password(Password);
});
rabbit.ReceiveEndpoint(Queue, rec =>
{
rec.AddPipeSpecification(new MyFilterSpecification<T>());
rec.Consumer<TConsumer>(cc);
});
});
})
.As<IBusControl>()
.As<IBus>()
.SingleInstance();
I also registered a BackgroundService to start and stop the bus.
In MyFilter, I would like to resolve a dependency registered with InstancePerLifetimeScope in the Send method of the filter.
public class MyFilter<T> : IFilter<T> where T : class, PipeContext
{
public Task Send(T context, IPipe<T> next)
{
context.TryGetPayload(out ILifetimeScope scope);
// scope here is null
}
}
As above, the scope is null. However later on in the pipeline the same code in my Consumer.Consume() returns a value. I've seen code samples where you can get ILifetimeScope in filter, I'm not sure why it's not working for me.
How can I resolve my dependency in filters? I'm not able to use constructor injection as that only works for singleton dependencies.
If you are sending outside of a consumer, using ISendEndpointProvider, you need to create a scope as shown below. The lifetime scope will be included in the payload and available to the filter.
public static async Task Main()
{
var builder = new ContainerBuilder();
builder.AddMassTransit(x =>
{
x.AddBus(provider => Bus.Factory.CreateUsingInMemory(cfg =>
{
cfg.ConfigureSend(s => s.UseFilter(new MySendFilter()));
}));
});
var container = builder.Build();
var busControl = container.Resolve<IBusControl>();
await busControl.StartAsync();
try
{
using var scope = container.BeginLifetimeScope();
var provider = scope.Resolve<ISendEndpointProvider>();
var endpint = await provider.GetSendEndpoint(new Uri("queue:some-address"));
await endpoint.Send<SomeMessage>(new { Value = "Hello"});
}
finally
{
await busControl.StopAsync();
}
}
class MySendFilter :
IFilter<SendContext>
{
public async Task Send(SendContext context, IPipe<SendContext> next)
{
var lifetimeScope = context.GetPayload<ILifetimeScope>();
await next.Send(context);
}
public void Probe(ProbeContext context)
{
}
}
My web socket connection code :
public connect(): Subject<MessageEvent> {
if (!this.subject) {
this.subject = this.create(this.url);
}
this.ws.onerror = () => {
this.close();
let refresh = setInterval(() => {
this.subject = null;
this.connect();
this.ws.onopen = () => {
clearInterval(refresh)
}
}, 5000);
}
return this.subject;
}
private create(url: string){
this.ws = new WebSocket(url);
const observable = Observable.create((obs: Subject<MessageEvent>) => {
this.ws.onmessage = obs.next.bind(obs);
this.ws.onerror = obs.error.bind(obs);
this.ws.onclose = obs.complete.bind(obs);
this.ws.onclose = function () {
console.log("trying to reconnect");
this.connect();
}
return this.ws.close.bind(this.ws);
});
const observer = {
next: (data: any) => {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data));
}
}
};
return Subject.create(observer, observable);
}
I want to reconnect web socket if connection closes. At the moment the function gets truggered when i stop the web socket. BUt is not connecting again .I See error "this.connect is not a function" .How to work with angular recursive functions?
Don't use function keyword to create your callback when using this inside of it if you aren't aware of how it changes the this reference depending on the execution context, use arrow function instead
To make it reconnect, change this
this.ws.onclose = function () {
console.log("trying to reconnect");
this.connect();
}
To this
this.ws.onclose = () => {
console.log("trying to reconnect");
this.subject = null;
this.connect();
}
I want to make the UserDataGenerator class works like a traditional SYNC class.
My expectation is that userData.outputStructure can give me the data prepared.
let userData = new UserDataGenerator(dslContent)
userData.outputStructure
getFieldDescribe(this.inputStructure.tableName, field) is a ASYNC call which invokes Axios.get
Below is my current progress but it's still not waiting for the data ready when I print out the userData.outputStructure
export default class UserDataGenerator {
inputStructure = null;
outputStructure = null;
fieldDescribeRecords = [];
constructor(dslContent) {
this.outputStructure = Object.assign({}, dslContent, initSections)
process()
}
async process() {
await this.processSectionList()
return this.outputStructure
}
async processSectionList() {
await this.inputStructure.sections.map(section => {
this.outputStructure.sections.push(this.processSection(section));
})
}
async processSection(section) {
let outputSection = {
name: null,
fields: []
}
let outputFields = await section.fields.map(async(inputField) => {
return await this._processField(inputField).catch(e => {
throw new SchemaError(e, this.inputStructure.tableName, inputField)
})
})
outputSection.fields.push(outputFields)
return outputSection
}
async _processField(field) {
let resp = await ai
switch (typeof field) {
case 'string':
let normalizedDescribe = getNormalizedFieldDescribe(resp.data)
return new FieldGenerator(normalizedDescribe, field).outputFieldStructure
}
}
You're trying to await arrays, which doesn't work as you expect. When dealing with arrays of promises, you still need to use Promise.all before you can await it - just like you cannot chain .then on the array.
So your methods should look like this:
async processSectionList() {
const sections = await Promise.all(this.inputStructure.sections.map(section =>
this.processSection(section)
));
this.outputStructure.sections.push(...sections);
}
async processSection(section) {
return {
name: null,
fields: [await Promise.all(section.fields.map(inputField =>
this._processField(inputField).catch(e => {
throw new SchemaError(e, this.inputStructure.tableName, inputField)
})
))]
};
}
I'm going to use Angular2 to receive websocket incoming messages and update a webpage based on those received messages. Right now, I'm using a dummy echo websocket service and will replace it.
From my understanding, the function which receive websocket messages has to return an observable that is subscribed by a handler who will update the webpage. But I can't figure out how to return an observable.
Code snippet is attached below. The MonitorService creates a websocket connection and return an observable containing the received messages.
#Injectable()
export class MonitorService {
private actionUrl: string;
private headers: Headers;
private websocket: any;
private receivedMsg: any;
constructor(private http: Http, private configuration: AppConfiguration) {
this.actionUrl = configuration.BaseUrl + 'monitor/';
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
this.headers.append('Accept', 'application/json');
}
public GetInstanceStatus = (): Observable<Response> => {
this.websocket = new WebSocket("ws://echo.websocket.org/"); //dummy echo websocket service
this.websocket.onopen = (evt) => {
this.websocket.send("Hello World");
};
this.websocket.onmessage = (evt) => {
this.receivedMsg = evt;
};
return new Observable(this.receivedMsg).share();
}
}
Below is another component which subscribes to the observable returned from above and updates webpages correspondingly.
export class InstanceListComponent {
private instanceStatus: boolean
private instanceName: string
private instanceIcon: string
constructor(private monitor: MonitorService) {
this.monitor.GetInstanceStatus().subscribe((result) => {
this.setInstanceProperties(result);
});
}
setInstanceProperties(res:any) {
this.instanceName = res.Instance.toUpperCase();
this.instanceStatus = res.Status;
if (res.Status == true)
{
this.instanceIcon = "images/icon/healthy.svg#Layer_1";
} else {
this.instanceIcon = "images/icon/cancel.svg#cancel";
}
}
}
Now, I'm running into this error in the browser console
TypeError: this._subscribe is not a function
I put it on a plunker and I added a function for sending message to the Websocket endpoint. Here is the important edit:
public GetInstanceStatus(): Observable<any>{
this.websocket = new WebSocket("ws://echo.websocket.org/"); //dummy echo websocket service
this.websocket.onopen = (evt) => {
this.websocket.send("Hello World");
};
return Observable.create(observer=>{
this.websocket.onmessage = (evt) => {
observer.next(evt);
};
})
.share();
}
Update
As you mentioned in your comment, a better alternative way is to use Observable.fromEvent()
websocket = new WebSocket("ws://echo.websocket.org/");
public GetInstanceStatus(): Observable<Event>{
return Observable.fromEvent(this.websocket,'message');
}
plunker example for Observable.fromEvent();
Also, you can do it using WebSocketSubject, although, it doesn't look like it's ready yet (as of rc.4):
constructor(){
this.websocket = WebSocketSubject.create("ws://echo.websocket.org/");
}
public sendMessage(text:string){
let msg = {msg:text};
this.websocket.next(JSON.stringify(msg));
}
plunker example
Get onMessage data from socket.
import { Injectable } from '#angular/core';
import {Observable} from 'rxjs/Rx';
#Injectable()
export class HpmaDashboardService {
private socketUrl: any = 'ws://127.0.0.0/util/test/dataserver/ws';
private websocket: any;
public GetAllInstanceStatus(objStr): Observable<any> {
this.websocket = new WebSocket(this.socketUrl);
this.websocket.onopen = (evt) => {
this.websocket.send(JSON.stringify(objStr));
};
return Observable.create(observer => {
this.websocket.onmessage = (evt) => {
observer.next(evt);
};
}).map(res => res.data).share();
}
**Get only single mesage from socket.**
public GetSingleInstanceStatus(objStr): Observable<any> {
this.websocket = new WebSocket(this.socketUrl);
this.websocket.onopen = (evt) => {
this.websocket.send(JSON.stringify(objStr));
};
return Observable.create(observer => {
this.websocket.onmessage = (evt) => {
observer.next(evt);
this.websocket.close();
};
}).map(res => res.data).share();
}
}
A different approach I used is with subject:
export class WebSocketClient {
private client: WebSocket | undefined;
private subject = new Subject<string>();
...
private connect() {
const client = new WebSocket(fakeUrl);
const client.onmessage = (event) => {
this.subject.next(event.data);
};
}
private watch() { return this.subject } // can be mapped
}
And using it will be in my opinion clearer:
const client = new WebSocketClient(); // can also be injected
client.connect();
client.watch().subscribe(x => ...);
Happy coding!