A little bit of a setback, but back on track

December 19, 2014

I haven’t had much time to work on it lately, and I’ve been a bit under the weather the last few days. I’ve hardly done anything.

There have been a few minor setbacks, mostly of my own doing. One was not, though.

To make a long story short, something changed in Visual Studio 2013 Update 4 that results in files being locked even though they are no longer in use and their app domain has been unloaded. When the app server synchronizes, it downloads new applications and deletes the ones it no longer needs. The delete is failing because visual studio is locking those files. It works fine without the debugger, but it’s a pain when working on install/synchronization issues. It took a while for me to determine the problem was the debugger. I installed Visual Studio 2015 Ultimate Preview, and it has the same issue.

In other news, I’ve been working on the installer quite a bit, just trying to make it better and cleaner. most of the code in this thing is what I call a “shallow pass”, which is just enough to get it working without being complete junk. The installer is now pretty clean. I want to refactor it one more time because there’s too much in the single class… there are 4 or 5 steps in the install, and they should be separate classes. But, for what it is, it’s pretty solid. But every time I installed to remote machine, it was taking forever to start, and was using as much CPU as it could. It turns out that was because of the RabbitMQ code that I quickly wrote was written to quickly.

The RabbitMQ api allows you to DEQUEUE (with or without a timeout), or DEQUEUEWITHNOWAIT, which returns immediately if there aren’t any messages. Despite it being a shallow pass, there is a throttler to limit the number of channels per application. The throttler iterates the channels on x threads looking for messages. So, maybe there are 10 subscribers, but they are all handled by 2 threads. It uses DEQUEUEWITHNOWAIT to get the message if there is one, and if not it moves on to the next subscriber. The problem is that loop was too tight. I changed it to use DEQUEUE with a 500millisecond wait. That solved it.

Lastly, there is a bus problem which I have fixed, but it needs to be addressed. The app server defaults to the built in SignalR bus. If you call UseRabbitMQBus, it swaps it out with the RabbitMQBus version. The problem is that the the signalr bus was already instantiated and is running. It can’t connect to the server, because there isn’t work, but it’s trying. The quick fix was to add a Start method to the bus so that it doesn’t start working until started. Then, there’s a SignalR bus, instantiated but dormant, which is replaced with the RabbitMQ one, instantiated but dormant. When the server starts, it starts the bus. This isn’t very clean. I believe the cleaner approach will be for the setup code to register a bus factory rather than a bus, and then the server can use the factory to get the instance (then register it directly). I haven’t looked into it yet, but that’s my first impression.

Installer Enhancement

I already mentioned that I did a lot of work on the installer. One new piece of functionality is that it publishes it’s status as it goes. Now the installer page in the admin portal receives a stream of events related to the install. Then, when it’s complete, it gives you the option to jump right to that server’s admin page. (Previously, it redirected automatically. Now that there’s feedback, you can peruse it before moving on.)

What’s Next

I’m not sure. I think I’m going to work on the Application Host Feature. I made a mistake in how the synchronization works… when the feature starts, it synchronizes all applications. This is efficient because shared files are only downloaded once. But, really, it should synchronize the applications as they start incase it has changed since the last time it started. I see this issue when I make configuration changes to the server. Restarting the app doesn’t recognize the changes. The server has to be restarted, and that’s sub par.

Also, I would like to add the option to have process isolation rather than app domain isolation. I have been laying the ground work for this from the beginning. I’m must not sure what technology to use to have the processes talk to each other. My first instinct is named pipes via WCF, but WCF feels like a dirty word these days. SignalR is an option, but I’d be using it for P2P, which isn’t what it’s for. There’s always HTTP, but then that’s public. Name pipes seems like the answer, but I’ll have to look into it before deciding.

Conclusion

The setback have been dealt with in some fashion. Everything is back to working as it should.

And, as always, PLEASE HELP ME!!!


RPC is Working

December 5, 2014

Just a quick update: The service api, client and server, now supports request/reply messages.

image

It’s just quick POC code, nothing shippable. It doesn’t have a timeout, which it needs, incase there isn’t a service running on the other end.

There is one other messaging solution that I want to implement that isn’t built into RabbitMQ. That will probably have to wait a while though. I need to start replacing a lot of this quick hacky code with real code.


RabbitMQ Service Layer – Publish

December 2, 2014

Let me preface by saying the RabbitMQ code is pretty bad. It’s just enough to get it working. The RabbitMQ implementation of the service layer isn’t much more than POC.

Anyway: the idea is to host and consume services, all ignorantly. As a developer writing code to be hosted in the app server, you shouldn’t need to know how the services are being hosted, or how to call them. Today the app server does it using RabbitMQ. Maybe tomorrow it’s using Service Bus for Windows or Web Api. I want all of that abstracted away leaving the developer with simple interfaces.

Here’s a test feature called DemoFeature3. The service “TestService” is associated to the feature. The service starts and stopped as the feature starts and stops.

image

Here is that service:

image

It’s not the most interesting method I’ve ever written. It has 2 simple parameters, and it simply displays them. Whoopty-doo. Notice, though, that there’s nothing special about this class. It has the optional NAME attribute (which is redundant in this case), but that’s it. It’s just a class. It doesn’t even have a service attribute. We know it’s a service because the feature said it is. (Although that may change going forward. Currently I only have one messaging pattern implemented. Attributes may be needed to specify things as more patterns are added.)

Also notice that there is only one method. That’s just laziness. You can put in as many as you want. At this time, though, you cannot have 2 methods of the same name. The binder is simplistic and would have to be improved to support overloads.

When DemoFeature3 starts, the service starts, and it will start listening for messages. The RabbitMQ implementation (which is completely hidden from this code) starts listening on the queue for this service. The queue name and exchange name are known by convention, and will be created as necessary.

What about the client? It’s RabbitMQ, so it’s just a message. You could submit a message from the RabbitMQ admin page if you’d like. Or, you can do it in code. If that code is in the app server (or using it’s client library), then it doesn’t know about RabbitMQ either. The message you send must be a JSON serialized ServiceRequest object.

Loose Typed Client

So far, I only have a loose typed client. I would like to create a strong typed one, but that’s going to require some thought and, ultimately, on-the-fly code generation. That’ll be a big chunk of work, and it’s not on my real short list, but I will get to it. (or, maybe you would like to get to it for me? Still looking for help people!)

The service name, operation name, service type, and parameters are all wrapped up into an object called ServiceRequest. The server picks that apart and uses the container and reflection to instantiate the class and invoke the method.

There is an API to do this. You can assemble a ServiceRequest yourself. It’s just a POCO with some fluent methods to make it… fluent. But, generally, that’s not necessary and you can use a simple helper method.

What follows is AnotherPeskyFeature. This was used previously to demonstrate the local scheduling feature. You put an attribute on a method that says “call me every second”, and the app server will call it every second.

I enhanced it to make as service call every time the method fires.

image

See that the service client is injected in the constructor. Now, you can use the service client to invoke the service: service name, operation name, parameter 1, parameter 2. This is the disadvantage of it being loose: Like non-hateoas REST you need to know what to call.

The IServiceClient interface does not have the method shown above. Instead, that is an extension method of IServiceClient that takes the parameters, converts them to a ServiceRequest, then calls the method that is defined by the interface. I did that because the implementation of that method will never change regardless of the backend technology, so there’s no reason to have multiple implementations doing exactly the same thing.

In this demo, both features are in the same application, which is hosted by a console app. Thus, you see each feature creating it’s output. Even though they are running next to each other, they don’t know that they are.. One feature is putting messages on the queue, and the other is taking them off.

image

If you deployed the application to multiple machines, then all of those machines would consume the same queue so that you can spread the workload.

Enabling It

There’s nothing to do on the hub. On the node:

image

I can easily add an extension method to do all 3 at once. The BUS and SERVICE LAYER have nothing to with each other, though. You can eliminated the UseRabbitMQBus call, and the buss will use it’s SignalR default. (I don’t have a second implementation of the service layer.)

Rabbit MQ Details

I’ve written my own quick API on top of the RabbitMQ API. “Quick” is the key word. I just need it to work. I can spend weeks working on that alone.

The exchange names and queue names are determined by convention. Currently, the client sets up the exchange and queue if needed, but I would prefer the hub do that on the node’s behalf to reduce the rights required by the nodes.

There are 4 sets of credentials: default, bus, service host, service client. If you don’t set specific credentials for any of them, then the default will be used.

Here’s what this stuff looks like in the portal:

image

The application feature “DemoFeature3” depends on core feature “ServiceHost”. Thus, it won’t let you shut down ServiceHost unless you shutdown  DemoFeature3 first.

image

If both are stopped, then ServiceHost will start automatically when you start DemoFeature3. Sweet.

Summary

To host a service: add a ServiceHost attribute to the feature, and pass it the type of the service. You don’t concern yourself with RabbitMQ or any other technology that might be in play. As an application/feature developer, it’s not your concern.

To send to a service: Add an IServiceClient parameter to your constructor, then use it to invoke the service.

What’s Next?

Request/Reply. While it should be avoided when possible, you know that you’re going to need it. The user will invoke the method. The service client will send the request and wait for a response. (In RabbitMQ, this is done via a one-time use response queue.)

Also, like I did for Web API, I will provide the option to have a service also be a feature. That way, if all you want is the service, you can do it via single class. That’s minor.