Thrift Nonblocking Server c# - c#

Recently we replaced our internal messaging library with Apache Thrift. In our setup we have a couple of c# services and several c++ 'clients' who connect to them. In the beginning we used TThreadPoolServer for the server implementation.
_server = new TThreadPoolServer(processor, serverTransport);
Because it worked so well we also replaced our last server. This server needs to communicate with more than 400 clients. The problem we saw that as soon client number 100 connected our the server stopped accepting new connections and it did not even call c# timers. It kind of seem to choke till first clients disconnected.
By reading this SO Large number of simulteneous connections in thrift we decided to change to TThreadedServer and increased max number of threads to 500 and we have a working solution.
_server = new TThreadedServer(processor
, serverTransport
, new TTransportFactory()
, new TTransportFactory()
, new TBinaryProtocol.Factory()
, new TBinaryProtocol.Factory()
, 500
, DebugLogThriftServer);
Still I would like to have the TNonblockingServer as it is implemented in the Thrift c++ library has. I was wondering if someone already wrote one for c#.

This is an RPC framework that uses the standard thrift Protocol, and it is the same effect as using thrift IDL to define the service, that is, thrify can be compatible with code that uses thrift IDL, which is very helpful for cross-platform.
[ThriftStruct]
public class LogEntry
{
[ThriftConstructor]
public LogEntry([ThriftField(1)]String category, [ThriftField(2)]String message)
{
this.Category = category;
this.Message = message;
}
[ThriftField(1)]
public String Category { get; }
[ThriftField(2)]
public String Message { get; }
}
[ThriftService("scribe")]
public interface IScribe
{
[ThriftMethod("getMessages")]
List<LogEntry> GetMessages();
[ThriftMethod]
ResultCode Log(List<LogEntry> messages);
}
public class Scribe : IScribe
{
public List<LogEntry> GetMessages()
{
return new List<LogEntry>
{
new LogEntry { Category = "c1", Message = Guid.NewGuid().ToString() },
new LogEntry { Category = "c2", Message = Guid.NewGuid().ToString() },
new LogEntry { Category = "c3", Message = Guid.NewGuid().ToString() }
};
}
public ResultCode Log(List<LogEntry> messages)
{
return ResultCode.TRY_LATER;
}
}
you can try it: https://github.com/endink/Thrifty

Related

MassTransit RabbitMq message is not being consumed. C#

I have library API in ASP NET Core 3.1 MVC where users can borrow, return and follow borrowed book status. I want to create email notification so when book is returned, all the users that are following this specific book status will recieve email notification that its available.
I want to use RabbitMQ with MassTransit and handle the emails on different web service.
This is my code that is sending messages to the rabbit queue:
public async Task SendNotificationStatus(Book book, CancellationToken cancellationToken)
{
var endpoint = await _bus.GetSendEndpoint(new System.Uri($"rabbitmq://{_rabbitHostName}/library-notifications"));
var bookSpectators = await _userRepository.GetSpectatorsByBookId(book.Id, cancellationToken);
foreach (var user in bookSpectators)
{
NotifyStatusReturn rabbitMessage = new NotifyStatusReturn
{
NotificationType = NotificationTypes.BookReturn,
RecipientAddress = user.EmailAddress,
RecipientLogin = user.Login,
SentDate = DateTime.UtcNow,
BookTitle = book.Title
};
await endpoint.Send(rabbitMessage);
}
}
And when it comes to the notifications service i've created the project with templates provided on MassTransit website - https://masstransit-project.com/usage/templates.html#installation
First I ran dotnet new mtworker -n LibraryNotifications and then after going inside the project folder dotnet new mtconsumer
I've added MassTransit.RabbitMq 8.0.0 package via NugerPackage Manager.
In Contracts folder created via mtconsumer template i ve changed the name of the record to NotifyStatusReturn which look like this:
namespace Contracts
{
public record NotifyStatusReturn
{
public string NotificationType { get; set; }
public string RecipientAddress { get; set; }
public string RecipientLogin { get; set; }
public DateTime SentDate { get; set; }
public string BookTitle { get; set; }
}
}
And in Program.cs swapped the x.UsingInMemory() to
x.UsingRabbitMq((context, cfg) =>
{
cfg.Host("localhost", "/", h =>
{
h.Username("guest");
h.Password("guest");
});
cfg.ConfigureEndpoints(context);
});
When i return the book, the message goes into library-notifications_skipped queue as a dead-letter. All the bindings seems okay for me and i really dont know what is the reason that my messages are not being consumed. Could anybody help me with this issue?
As per the documentation:
MassTransit uses the full type name, including the namespace, for message contracts. When creating the same message type in two separate projects, the namespaces must match or the message will not be consumed.
Make sure that your message type has the same namespace/type in each project.

How do I save API data (a converted object) into an SQL server in a console application

I've managed to build a console application that successfully requests API data and converts it into a JSON object. However, I don't know how to put these DTO files (recently stored API objects) into my SQL server.
So far I have this for the console application.
class Program
{
static void Main(string[] args)
{
getRequestData();
}
public static void getRequestData()
{
var client = new RestClient(URL);
var request = new RestRequest();
var response = client.Execute(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
string rawResponse = response.Content;
AllRequests.Rootobject result = JsonConvert.DeserializeObject<AllRequests.Rootobject>(rawResponse);
}
}
}
As you can see, the console application program.cs file successfully obtains a get request then converts the API data into an object (A Json object I think). Here is the DTO file (AllRequests) that the data is being saved into.
class AllRequests
{
public class Rootobject
{
public Operation operation { get; set; }
}
public class Operation
{
public Result result { get; set; }
public Detail[] details { get; set; }
}
public class Result
{
public string message { get; set; }
public string status { get; set; }
}
public class Detail
{
public string requester { get; set; }
public string workorderid { get; set; }
public string accountname { get; set; }
}
}
When testing the application in debug mode, the objects are stored correctly and everything is fine. However, I don't know how to save these objects into an SQL database. I do already have an SQL server, however, I'm not sure how to push the already converted data and save it into a retrospective table. So for instance, saving this data into a Request table in my SQL database.
At the moment I only know how to make API calls in this console application. I can't find any data that will assist me with completing the second part of this task, which is storing the data into an SQL database.
Any help would be greatly appreciated, apologise if there is too much unnecessary information.
There are a few methods to make your application interact with a Database. You can setup your application to use Entity Framework (EF) to manage your DB. It is a powerfull and very popular ORM; there are a lot of resources in the web for help.
Personaly, I like to work with Dapper, that is very simple to use in comparison with EF. You just have to provide your connection-string to create your SqlConnection object.
You can find a simple tutorial in this link.
I avoid EF and other tools frankly. Just more hills to learn.
In .Net 6, I use RestSharp to do the API calls and conversion to JSON and Microsoft.Data.SqlClient to save the data into SQL Server.
Get your API call to respond
var client = new RestClient("your API url");;
Apply the API call
var req = new RestRequest();
req.AddHeader("x-api-key", "your API key");
Get a response object in JSON by default
RestResponse resp = client.Execute(req);
Make sure it worked
if (resp.IsSuccessful) {}
Deserialise your response, where RootObject is my name for my SQL Server table schema as a Class
RootObject ro = new RootObject();
ro = JsonConvert.DeserializeObject<RootObject>(resp.Content);
Build a List<>
//iterate through each device data and add it into a DataRow
List<DeviceClass> device = new List<DeviceClass>();
DataTable dt = GenericList2Datatable<DeviceClass>(device);
//only do anything if the API call worked!
if (!dt.HasErrors)
{
foreach (Device d in ro.Devices)
{
here do your mapping from "ro" object over to data objects like DataRow and DataTable and put them across to SQL server further down. Plenty on the web on how to do that.
The GenericList2Datatable() method I found on the web. Again google it.
public static DataTable GenericList2Datatable<T>(List<T> list)
{}

Set IP address of a network interface useing c# and .Net API without WMI

I'm a newcomer on .Net and c# programming.
I'm trying to perform a small program to set up in a few clics the IP address of my local network card. (What I want to achieve is going faster than through the windows configuration menus)
So I've made a little research and I found several nice examples that fits my needs.
Anyway, I've realised that I can get a network device IP config via .Net namespaces (System.Net.Networkinformation...) but I can't set a new one (I mean, this API is full of getters but no setters) so, as far as I googled, to set an IP I must perform WMI calls.
My question is if there is a method to do it via .Net (I didn't found it by de moment).
Thanks in advance!
You can use ORMi library as it is a WMI wrapper that makes WMI access simpler.
1) Define your class:
[WMIClass("Win32_NetworkAdapterConfiguration")]
public class NetworkAdapterConfiguration
{
public int Index { get; set; } //YOU MUST SET THIS AS IT IS THE CIM_KEY OF THE CLASS
public string Caption { get; set; }
public string Description { get; set; }
public uint IPConnectionMetric { get; set; }
public UInt32 InterfaceIndex { get; set; }
public string WINSScopeID { get; set; }
public bool SetStatic(string ip, string netmask)
{
int retVal = WMIMethod.ExecuteMethod(this, new { IPAddress = new string[] { ip }, SubnetMask = new string[] { netmask } });
if (retVal != 0)
Console.WriteLine($"Failed to set network settings with error code {retVal}");
return retVal == 0;
}
}
2) Use:
WMIHelper helper = new WMIHelper("root\\CimV2");
NetworkAdapterConfiguration networkInterface = helper.Query<NetworkAdapterConfiguration>().ToList()
.Where(n => n.Description == "Intel(R) Ethernet Connection").SingleOrDefault();
networkInterface.SetStatic("192.168.0.35", "255.255.255.0");

Do I have to serialize/deserialize the object in silverlight application with WCF?

I want to create and consume a WCF Service in Silverlight. I have created a service that returns this model from a database:
namespace SilverlightWithWCFService.Web
{
[DataContract]
public class Customer
{
[DataMember]
public string CustomerName { get; set; }
[DataMember]
public string CompanyName { get; set; }
[DataMember]
public string ContactName { get; set; }
}
}
The service looks like this:
namespace SilverlightWithWCFService.Web
{
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class SampleService
{
[OperationContract]
public List<Customer> CustomerList()
{
var custList = new List<Customer>();
// populate custList
return custList;
}
}
}
}
In my Silverlight application, I added a Service Reference. This method calls the service operation:
public Page()
{
InitializeComponent();
SampleServiceClient client = new SampleServiceClient();
client.CustomerListCompleted += new EventHandler<CustomerListCompletedEventArgs>(client_CustomerListCompleted);
client.CustomerListAsync();
}
void client_CustomerListCompleted(object sender, CustomerListCompletedEventArgs e)
{
CustomerGrid.ItemsSource = e.Result;
}
So my question is: I don't know how the Silverlight work with WCF. Do I have to serialize something on WCF side and deserialize the return value on client side? If so, what code is missing? (Where?)
UPDATE:
I think based on some online questions. Should I deserialize the returned e.Result in the completed event code?
Do I have to serialize something on WCF side and deserialize the return value on client side?
No, when you consume the webservice the underlying code will do all that for you.
Don't get hung up on it being Silverlight. Just think of Silverlight as the same as a console application. Whatever one has to do in the console app to consume the webservices, one will have to do in Silverlight. The only difference is that you will need to handle the calls in an async manner, but that is separate from the consuming of the webservice which your question pertains.
Note there was a competing technology to do all the updates of the webservice during a compile. That was called RIA services and that is a different animal all together.
I would recommend you use WCF web services, but update the size of the send/receive buffers for you will max those out easily on any true data transfers.

Can't de-serialize Message.Body when reading from MSMQ

I've seen several other posts here with the same problem, but none offer a solution. And what's really odd is that this works in dev, but not in prod.
I am submitting a message to a queue as follows:
public void QueueMessage(LeadSubmissionMessage message)
{
using (var queue = new MessageQueue(MessageQueuePath))
{
queue.DefaultPropertiesToSend.Recoverable = true; // always send as recoverable
queue.Send(message);
}
}
This is the LeadSubmissionMessage class:
[Serializable]
public class LeadSubmissionMessage
{
public long LeadId { get; set; }
public long UserId { get; set; }
public DateTime DateTime { get; set; }
}
This is the message, in raw text:
<?xml version="1.0"?>
<LeadSubmissionMessage xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<LeadId>194018</LeadId>
<UserId>300</UserId>
<DateTime>2016-05-17T14:52:30.1484784Z</DateTime>
</LeadSubmissionMessage>
That all works fine. But on the receiving end, and only in production, when we do this:
body = message.Body;
It throws this error:
System.InvalidOperationException: Cannot deserialize the message passed as an argument. Cannot recognize the serialization format.
at System.Messaging.XmlMessageFormatter.Read(Message message)
at System.Messaging.Message.get_Body()
It works find in Dev and Staging. I'm trying to minimize and eliminate the points where things could be different, but I've run out of things to check. They are all running the same build version (release). Any MSMQ related config keys match (except for the obvious queue names and locations). One possible variation is the version of MSMQ installed on the machine? But I'm not sure how to check that. Would the OS make a difference? Can't imagine it would.
I'm using it in the following way:
private static MyMessage RecieveMessage()
{
if (!MessageQueue.Exists(QueueName))
{
return null;
}
using (var msmq = new MessageQueue(QueueName))
{
msmq.Formatter = new XmlMessageFormatter(new Type[] { typeof(MyMessage) });
var message = msmq.Receive();
return message != null && message.Body is MyMessage ? (MyMessage)message.Body : null;
}
}
Not sure what's the problem in your case, but you could try to make it similar to my approach and see if it's working for you.

Categories

Resources