Skip to content

radicalgeek/SampleMicroservice

Repository files navigation

Summary:

This is a prototype/Sample micro-service implimentation in .NET 4.5.1
This prototype achives the following micro-service design principles

1). In built metrics and logging
	- It is intended that micro-services will output logs to text files that will then be parsed by Splunk
	- Textual content should be in Key="value" format. 
	- Performance metrics should also be logged 
2). Loosly couple contracts
	- Both publishing and subscribing contracts use the C# 4 dynamic types and JSON objects. This means that if the contract has new
	  properties added to it, the service will ignore then new properties and continue to work. It also mean that there
	  is no compile time checking, as these objects are not evaluated untill run-time. Unit testing becomes all the more important
3). Lightweight platform agnostic hosting. 
	- Using Topshelf and keeping .NET framework libraries to a minimum means this service can be hosted with 
	  Mono on Unix and Linux machines (as well as windows). This further means that lighweight virtualisation technologies such as
	  docker can be used to build new environments quickly. 
4). Data repository. 
	- MongoDB is just one type of datastore. By using the repository pattern we ensure we can easily swap the implimentation
	  for other data stores suck as Keep/Cassandra. 
5). Event driven Publisher/Subscriber (observer pattern) message bus. 
	- Using RabbitMQ as a service bus/message queue this service is capable of participating in an event driven system. 


------------------------------------------------------------------------------------
Dependancies:

This sample microservice has the following libraries, frameworks and dependancies

Application Dependancies:

.NET 4.5.1 - A minimal amount of the .NET framwork is required in order to maintain cross platform compatability (mono)
Topshelf - A light weight service hosting framework compatible with mono
RabbitMQ/EasyNetQ - RabbitMQ is a popular message bus, EasyNetQ is a .NET wrapper for the RabbitMQ API
NLog - A popular logging framework
MongoDB gen10 - A popular NoSQL document data store. Note the use of an old version of the c# driver. 
MongoDB.Repository - A robust generic repository pattern for MogoDB. No support yet for mongo c# driver 2.0
Ninject - A popular Dependancy Injection framework NOTE: not using NuGet, but rather the mono build from the projects TeamCity builds (in lib folder)
Netwonsoft.Json - .NET JSON serialisation framework


Environment Dependancies:

Docker
RabbitMQ
MongoDB

Development dependancies:

boot2docker
visual studio 2013

---------------------------------------------------------------------------------
Local Dev machine setup:

In order to run this service in your development environment (your PC), ensure
you have installed boot2docker. Once you have it installed you will need to get RabbitMQ and MongoDB
running in docker containers, as well as storage containers. 

forst create the following file path on your local machine 

C:\var\log\rook


This path will be used to store the logs when the service is running. When the service runs in a docker container environment, this path will resolve to the 
persistant logging storage container.

Now download and install boot2docker, then launch it from the start menu icon. Text initilise your boot2docker vm

$ boot2docker init

Run any export commands the output tells you to, then start the vm:

$ boot2docker up

It is important in most environments to start up the message bus first. But before we do that, 
we first need ensure we can connect to any containers we create. Get the ip address of your boot2docker VM

$ boot2docker ip

For this sample add the following enteries to your host file, replacing the IP with the IP address reported by the last command

192.168.59.103	rook-queue
192.168.59.103	rook-sample-db

Now run the following comands to set up the boot2docker-vm

$ boot2docker ssh
$ mkdir /var/queue
$ mkdir /var/logs/
$ mkdir /var/logs/rook
$ mkdir /var/data/
$ mkdir /var/data/rook

You are now ready to fire up the RabbitMQ container and its data store. Ensure you exit the docker vm and 
return to the boot2docker shell

$ docker create --name=rook-queue-datastore -v=//var/queue:/var/lib/rabbitmq/mnesia debian:wheezy

$ docker run -d --hostname rook-queue --name rook-queue -p 8080:15672 -p 5672:5672 --volumes-from rook-queue-datastore rabbitmq:3-management

This will start up RabbitMQ on your local docker host, and expose the managment page on http://rook-queue:8080
the message bus will be avaliable on rook-queue:5672

Next create your logging storage container

$ docker run --name rook-logs -v //var/log/rook:/var/log/rook debian:wheezy

And Fianly MongoDB

$ docker run -d --hostname rook-sample-db -p 27017:27017 --name rook-sample-db -v //var/data/rook:/data/db  mongo --smallfiles


+++++++++++++++
you can also just run the setuplocaldev.sh file in the boot2docker shell to run all of the above commands.

-------------------------------------------------------------------------------------------------------------------------------

Local Debug:


Hitting debug in visual studio will then fire up the service in a console window, and connect to the database and queue you have
in docker containers. Logs will be output to both the console window, and to the logging directory C:\var\log\rook on your machine. 

you will have a working copy of both the message queue and the database, connected just as they would in any other environment. 

You can populate the database with data as you need to for your development. 

You can also write intergration tests that require instances of these services to be avaliable, that are executed by calling public methods
from test projects 

Finaly, the unit testing projects are able to exercise the logic indipendently of any services

--------------------------------------------------------------------------------------------

Local Build:

It is important that you test the application in a docker container. Doing so runs the .net executable using mono. 

This gives you the opertunity to interact with the service just as it would be done in other environments. 
To excercise the service simply place messages on the bus that the service is listening for.  

First build the project in visual studio, then using the same docker shell window you used to start your environment, navigate 
to the build output directory of the service. 

Next build a docker image containing the service with the follow command

$ docker build -t sampleservice .

$ docker run -d --name rook-sample --link rook-sample-db:db --link rook-queue:queue --volumes-from rook-logs sampleservice

You can now intergration test this solution. Note that logs will be stored in /var/log/rook on the boot2docker-vm

-----------------------------------------------------------------------------------------------

Remote Build:

All code should be commited to Git. 


-----------------------------------------------------------------------------------------------
Message contracts and Queue requirments:

a typical message fontract is a JSON object that looks like this. 

{ “uuid”: “12345678-1234-1234-1234-1234567890AB”,             //standard Guid format
  “modifiedBy”: “user-service”,                               //the last service to decorate this message
  “modifyTime”: “2015-08-03T13:07:51.4005517Z”,               //Date/Time in Coordinated Universal time (UTC)
  “source”: “ui-api”,										  //the service that initiated the message
  “pubTime”: “2015-08-03T13:07:51.4005517Z”,                  //Date/Time in Coordinated Universal time (UTC)
  “method”: “get”,                                            //the "method" is a HTTP operation name that maps to crud opperations
  “need”:
    { “UserUuid”: “12345678-1234-1234-1234-1234567890AB” }    //A list of Needs. The user Id "Needed" for product service this would be ProductUuid
  “solutions”:												  //A List of complex object that services decorate the message with
    [ { “firstname”: “John”,
         “lastname”: “Doe” },
      { “product”: “digiwise” },
      { “status”: “active” } ]
 }


 Needs and solutions differ depending on the data required or Needed. 

 Needs represent a service requesting something, where as a Solution represents a service providing something. 

 All messages are sent to a common exchange that copies the messages on to every queue. Each service has it's own queue. 


 UserService    -> |                        | -> UserServiceQueue    -> UserService
                   | -> message Exchange -> |
 ProductService -> |                        | -> ProductServiceQueue -> ProductService

 This way all services opperate on messages in parallel. 

-----------------------------------------------------------------------------------------------

Usage:

To use this sample micro-service take the following steps. Lets say in this example we are creating a widgets service

1. Branch this code base giving the new branch the name widgetservice
2. Replace all occuronces of the words "Prototype" and "Sample" with the word Widget
3. Adjust the rules around whether to pick up a message in ____.cs
4. Adjust the proccessing of messages in ______.cs
5. Configure environment vars such as DB name, queue name etc.
6. Copy the builds in jenkins from this project
7. Deploy

About

a sample microservice implimentation in .NET, intended for a cross platform eco-system

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published