Warning publishing Azure service bus queue trigger can't find named value in local.settings.json - visual-studio

I have a Azure service bus queue trigger function and when I created it it asked me 3 fields, access rights, connection and queue name.
I put in listen for the access rights. For the connection I used the the 'primary connection' name given in the 'RootManageSharedAccessKey' in the service bus I created.
It looks something like this
Endpoint=sb://yogaband2017.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=JG0gwJ90bkbGl1BU=
and I created a queue in my service bus called yogaband and that is what I used for the queue name as the third parameter.
My function looks like this
public static class PostEventEmails
{
[FunctionName("PostEventEmails")]
public static void Run([ServiceBusTrigger("yogaband2017", AccessRights.Listen, Connection = "Endpoint=sb://yogaband2017.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=gkbGl1BU=")]string myQueueItem, TraceWriter log)
{
log.Info($"C# ServiceBus queue trigger function processed message: {myQueueItem}");
}
}
When I publish the function I get this warning
.nuget\packages\microsoft.net.sdk.functions\1.0.2\build\netstandard1.0\Microsoft.NET.Sdk.Functions.Build.targets(31,5): warning : Function [PostEventEmails]: cannot find value named 'Endpoint=sb://yogaband2017.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=0bkbGl1BU=' in local.settings.json that matches 'connection' property set on 'serviceBusTrigger' [C:\Users\Source\Workspaces\YogaBand2017\YogaBand2017\PostEventEmails\PostEventEmails.csproj]
and in my site I can pass the queue a message and I see the message in the queue in my Azure portal, but the function isn't picking up the message and processing it. So I still see '1 message' in the active message count in the queue. I assume it would be 0 after the function picks it up and processes it and I would see the log trace in the window? But I don't so I think the connection isn't correct or I didn't configure something correctly but I don't know what!
Here is what I put into the local.settings.json file
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "Endpoint=sb://yogaband2017.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=bGl1BU=",
"AzureWebJobsDashboard": ""}}
FYI - here is how I send the mesage to the queue in c#
var queueClient = QueueClient.Create("yogaband2017");
BrokeredMessage message = new BrokeredMessage("some test message");
message.MessageId = newEvent.YogaSpaceEventId.ToString();
queueClient.Send(message);
and in my web.config file I added this
<add key="Microsoft.ServiceBus.ConnectionString" value="Endpoint=sb://yogaband2017.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=0gwJ90bkbGl1BU="/>

Connection property of ServiceBusTrigger should be set to a setting name, not connection string itself:
[ServiceBusTrigger("yogaband2017", AccessRights.Listen, Connection = "MyConn")]
You then define a setting with this name in local.settings.json for local development environment:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "... azure storage connection string ...",
"MyConn": "... service bus connection string ..."
}
}
and in App Settings for Azure deployment.
Note that AzureWebJobsStorage is NOT service bus connection.

Related

Detect server disconnect in gRPC Go client

I have a gRPC service simmilar to below
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
I need the client to maintain a long living gRPC connection to the server so that if the server goes down, the client can reconnect and issue SayHello() again.
Based on my understanding there are a few options:
Pass in a statsHandler to grpc.Dial and add retry logic in HandleConn()
Add a new ClientStreaming API that maybe sends a message every few seconds. Check for server side stream close errors and implement retry logic.
Not sure if there is a recommended way for my use case and would appreciate any help.

Calling Azure Function ( Queue Trigger) Doesnt work , but it does when i send i message from azure portal

Im working with a personal project with this details: net core 6 with c# web app and az functions.
I have a Queue Trigger (Az function) that shows a message and sends an email and I want to call it Queue from an Azure Function.
I'm using the right connection string and Queue name because If I send a message from the azure portal, I see the queue message, I'm able to debug it locally and I also receive an Email.
But when i make the call from my code ( another Az Function) , i see the Queue Message from the portal but i cannot debug it locally and i don't receive the email.
So obviously, it works from the portal but not locally ( or prod), something is wrong with my code.
I'm using this code to make the call from my code:
try
{
string conectionstring = "Myconnection";
QueueClient queue = new QueueClient(conectionstring, "myQueueName");
var rspqueue = await queue.SendMessageAsync("MyMessage");
}
catch (Exception ex)
{
throw;
}
This show 201 message and i see the message from the azure portal as i said.
Isn't SendMessageAsync supposed to call my Queue Trigger (Az function) ? Or im getting it wrong?
Regards
I tried to call a Queue Trigger (Az function) from another Az Function. I expect to know how to do that
Well, you are not calling the queue trigger but as the name suggest you are adding messages to the queue and the queue trigger is getting triggered .
Here I have a http trigger which is adding the message in the queue.
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
string connectionString = "<Connection String>";
QueueClient queue = new QueueClient(connectionString, "testr");
string message = Convert.ToBase64String(Encoding.UTF8.GetBytes("Hello World "));
queue.SendMessage(message);
return new OkObjectResult("responseMessage");
}
My Queue trigger is like this
[FunctionName("Function1")]
public void Run([QueueTrigger("testr", Connection = "test")]string myQueueItem, ILogger log)
{
log.LogInformation($"C# Queue trigger function processed: {myQueueItem}");
}
Here I am encoding the string in the http trigger as I am importing a string in the queue trigger.
Queuetrigger output :

JMS and IBM WebSphere not generating COD

I am creating an application that consumes messages from a MQ using JMS. My MQ manager is IBM WebSphere MQ and I am using the IBM jms implementation to consume the messages.
The messasges are coming and going fine. I receive the messages from the other part and I can send messages to them. The problem is that they are not receiving the COD after I consume the message from the queue. They receive the COA, but no COD.
Here is my receive message code:
public byte[] readMsgFromClient() throws JMSException {
byte[] message = null;
QueueReceiver reader = null;
try {
connection = getQueueConnection();
connection.start();
session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(config.getQueueRsp());
((MQQueue) queue).setTargetClient(JMSC.MQJMS_CLIENT_NONJMS_MQ);
reader = session.createReceiver(queue);
JMSBytesMessage byteMessage = (JMSBytesMessage) reader.receive(3000);
if (byteMessage != null) {
message = new byte[(int) byteMessage.getBodyLength()];
byteMessage.readBytes(message);
}
} finally {
if (reader != null) {
reader.close();
}
if (session != null) {
session.close();
}
if (connection != null) {
connection.close();
}
}
return message;
}
Do I have to manually send the COD? DO I have to configure my WebSphere to automatically send the COD? Do I have to notify the WebSphere that my application has consumed the message?
The COD messages are probably ending up the the dead letter queue (DLQ) with an RC (Reason Code) of 2035 (not authorized).
Here is another of those things you learn the hard way:
COA messages are generated under the queue manager's UserId
COD messages are generated under the UserId of the sender's message.
If the sending application's UserId is appl001 then the COD will be generated using the UserId of appl001. If that UserId does not have permission to write to the particular queue then the message will end up in the DLQ.
Generally, the permission issue happens when the sender application is connected to 1 queue manager and the receiver application is connected to another queue manager. i.e. messages are hoping between queue managers.
Hence, the sender's UserId does not have permission to put a message on the remote queue manager.
As stated by #Roger, the permissions to put the COD are based on the UserId in the MQMD of the message that is sent.
If you do not want to add the remote user to your local system you can use the itsoME exit provided in the IBM Redbook "Secure Messaging Scenarios with WebSphere MQ". The latest version is found under the "Additional Material" link.
With this exit you need to have a MCAUSER set on your RCVR or RQSTR channel and configure that channel with the following attributes:
MSGEXIT('itsoME(MsgExit)')
MSGDATA('MCA/')
The result is that UserIdentifier field of the MQMD will be changed to the value of the MCAUSER that is configured on the channel. You would then give that MCAUSER +put and +passid to the XMITQ that returns to the remote queue manager.
The exit can be used for other things such as removing the reporting options if you do not want to allow COA/COD.

MassTransit And Service Fabric Stateful Service?

I've been trying to come up with a demo of a website that uses MassTransit with RabbitMQ to post messages to a service running on Service Fabric as a Stateful service.
Everything was going fine, my client would post a message:
IBusControl bus = BusConfigurator.ConfigureBus();
Uri sendToUri = new Uri($"{RabbitMqConstants.RabbitMqUri}" + $"{RabbitMqConstants.PeopleServiceQueue}");
ISendEndpoint endPoint = await bus.GetSendEndpoint(sendToUri);
await endPoint.Send<ICompanyRequest>(new {CompanyId = id });
My consumer in my service fabric service was defined like:
IBusControl busControl = Bus.Factory.CreateUsingRabbitMq(cfg =>
{
IRabbitMqHost host = cfg.Host(new Uri(RabbitMqConstants.RabbitMqUri), h =>
{
h.Username(RabbitMqConstants.UserName);
h.Password(RabbitMqConstants.Password);
});
cfg.ReceiveEndpoint(host, RabbitMqConstants.PeopleServiceQueue, e =>
{
e.Consumer<PersonInformationConsumer>();
});
});
busControl.Start();
This does allow me to consume the message in my class and I can process it fine. The problem comes when we want to use IReliableDictonary or IReliableQueue or anything that needs to reference the context that is run from the RunAsync function in the service fabric service.
So my question is, how can I configure (is it possible) MassTransit to work within a Stateful Service Fabric Service which knowledge of the service context itself?
Many thanks in advance.
Mike
Update
Ok, I've made some progress on this, if I point the register routines to my message consumer class (eg):
ServiceRuntime.RegisterServiceAsync("ServiceType", context => new PersonInformationConsumer(context)).GetAwaiter().GetResult();
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(PersonInformationConsumer).Name);
Then in my consumer class for my messages I can do the following:
internal sealed class PersonInformationConsumer : StatefulService, IConsumer<ICompanyRequest>
{
private static StatefulServiceContext _currentContext;
#region Constructors
public PersonInformationConsumer(StatefulServiceContext serviceContext) : base(serviceContext)
{
_currentContext = serviceContext;
}
public PersonInformationConsumer() : base(_currentContext)
{
}
I can now successfully call the service message:
ServiceEventSource.Current.ServiceMessage(this.Context, "Message has been consumed, request Id: {0}", context.Message.CompanyId);
The problem I have now is trying to store something on the IReliableDictionary, doing this causes as "Object reference not set to an instance of an object" error :( ... any ideas would be appreciated (although may not read until new year now!)
public async Task Consume(ConsumeContext<ICompanyRequest> context)
{
ServiceEventSource.Current.ServiceMessage(this.Context, "Message has been consumed, request Id: {0}", context.Message.CompanyId);
using (ITransaction tx = StateManager.CreateTransaction())
{
try
{
var myDictionary = await StateManager.GetOrAddAsync<IReliableDictionary<string, long>>("myDictionary");
This is causing the error.... HELP! :)
You'll need to do a bit more to get MassTransit and stateful services working together, there's a few issues to concern yourself here.
Only the master within a stateful partition (n masters within n partitions) will be able to write/update to the stateful service, all replicas will throw exceptions when trying to write back any state. So you'll need to deal with this issue, on the surface it sounds easy until you take in to consideration the master can move around the cluster due to re-balancing the cluster, the default for general service fabric applications is to just turn off the processing on the replicas and only run the work on the master. This is all done by the RunAsync method (try it out, run 3 stateful services with something noddy in the RunAsync method, then terminate the master).
There is also partitioning of your data to consider, due to stateful services scale with partitions, you'll need to create a way to distributing data to separate endpoint on your service bus, maybe have a separate queue that only listens to a given partition range? Say you have a UserCreated message, you might split this on country UK goes to partition 1, US goes to partition 2 etc...
If you just want to get something basic up and running, I'd limit it to one partition and just try putting your bus creation within the the RunAsync and shutdown the bus once a cancelation is requested on the cancelation token.
protected override async Task RunAsync(CancellationToken cancellationToken)
{
var busControl = Bus.Factory.CreateUsingRabbitMq(cfg =>
{
IRabbitMqHost host = cfg.Host(new Uri(RabbitMqConstants.RabbitMqUri), h =>
{
h.Username(RabbitMqConstants.UserName);
h.Password(RabbitMqConstants.Password);
});
cfg.ReceiveEndpoint(host, RabbitMqConstants.PeopleServiceQueue, e =>
{
// Pass in the stateful service context
e.Consumer(c => new PersonInformationConsumer(Context));
});
});
busControl.Start();
while (true)
{
if(cancellationToken.CancellationRequested)
{
//Service Fabric wants us to stop
busControl.Stop();
cancellationToken.ThrowIfCancellationRequested();
}
await Task.Delay(TimeSpan.FromSeconds(1));
}
}

Getting the Queue name from within the MDB

I have 2 Websphere application Server(WAS) applications, one sending a message and the other reading and processing it . I need the queue name to be known in the reading application for my downstream processing.
I am trying to get the queue name (in the reading application) by using the following code . But however I get NullPointerException since the getJMSDestination is returning null.
Queue queue = (Queue)message.getJMSDestination();
logger.info("Queue ID: "+queue.getQueueName());
Note that the queue name is set via the destination object in the sending application.
Is there any other parameters that I am missing to set in the sending application ?
The message should have the destination stored in its JMSDestination property, you can try fetch that instead of using getJMSDestination()
I've using Spring with ActiveMQ, and this appears to work for me:
public void processMessage( Message msg )
{
// Get the queue name from the supplied Message.
String sourceQueueName = "UNKNOWN";
try
{
ActiveMQDestination sourceQueue = (ActiveMQDestination) msg.getJMSDestination();
sourceQueueName = sourceQueue.getPhysicalName();
}
catch ( JMSException e )
{
LOGGER.error( "Cannot get JMSDestination from Message: " + msg, e );
}
....
Does WAS have a Queue object you can cast to that exposes similar methods?

Resources