SkillAgentSearch skills...

Roque

event & work queueing framework for .Net

Install / Use

/learn @BlogTalkRadio/Roque
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Roque

pronounced "raw-queue"

Roque is an event & work queueing framework for .Net, made simple.

It sits on top of the C# abstractions you know (plain old C# events and methods), and uses Redis behind the scenes to make them work in an async, transparent, distributed, scalable, decoupled and failure-proof way.

Message queueing doesn't get simpler than this!

Really?? ... show me!

Example 1: Image Processing

Lets say we have a website and we want to build thumbnails for uploaded pics, that's a time-consuming operation we can't perform during the lifetime of web request.

1- Create a service interface.


    public interface IImageProcessor {
        void CreateThumbnail(string filename, int width, int height, AlgorithmOptions options);
    }

2- Use it on your application:


    public class ImageBiz {
        IImageProcessor ImageProcessor = RoqueProxyGenerator.Create<IImageProcessor>("images");
        public void ImageUploaded(filename){
            ImageProcessor.CreateThumbnail(filename, 160, 120, new AlgorithmOptions { Quality=0.7 });
        }
    }

Note: add references to Roque.Core and Roque.Redis assemblies to your project.

3- Config a redis-based queue named "images":


    <?xml version="1.0"?>
    <configuration>
      <configSections>
        <section name="roque" type="Cinchcast.Roque.Core.Configuration.Roque, Roque.Core"/>
      </configSections>
      <roque>
        <queues>
          <queue name="images" type="Cinchcast.Roque.Redis.RedisQueue, Roque.Redis">
            <settings>
              <setting key="host" value="localhost"/>
              <!-- Optional, if not specified default Redis port is used: 6379 -->
              <setting key="port" value="6379"/> 
            </settings>
          </queue>
        </queues>
      </roque>
    </configuration>

That's it. You're already enqueuing jobs!, let's set up a worker, hurry up!:

4- Implement your image processor service:


    public class ImageProcessor : IImageProcessor {
        public void CreateThumbnail(string filename, int width, int height, AlgorithmOptions options = null){
            // a time-consuming task, eg: resize the image and save it adding a suffix
            throw new NotImplementedException();
        }
    }

5- Install Roque service on a machine (with access to your Redis server).

6- On the same folder of roque.exe drop the assembly(ies) containing IImageProcessor interface and ImageProcessor class.

7- On the worker Roque.exe.config:


    <?xml version="1.0"?>
    <configuration>
      <configSections>
        <section name="roque" type="Cinchcast.Roque.Core.Configuration.Roque, Roque.Core"/>
      </configSections>
      <roque>
        <queues>
          <queue name="images" type="Cinchcast.Roque.Redis.RedisQueue, Roque.Redis">
            <settings>
              <setting key="host" value="localhost"/>
            </settings>
          </queue>
        </queues>
        <workers>
          <!-- a worker poping jobs from "images" queue --> 
          <worker name="images" queue="images" autoStart="true"/>
        </workers>    
      </roque>
      <castle>
        <components>
          <!-- using Castle Windsor to tell Roque what image processing service to use. type name must be fully qualified --> 
          <component service="Acme.Images.IImageProcessor" type="Acme.Images.ImageProcessor, Acme.Images"/>
        </components>
      </castle>
    </configuration>

8- Start Roque Service to start processing images!

(or you can roque.exe from a console, use: roque.exe /debug to attach your VisualStudio and debug your image processor)

You're done, now you can start adding more workers to get automatic load balancing by repeating steps 5 to 8.

To check the status of your queues you can run: roque.exe status

C:\>roque status /maxage=10 /maxlength=500000
Redis send-pump is starting
roque Information: 0 : [REDIS] connected to localhost:6379
Queue images has 262570 pending jobs. Next job was created < 1sec ago.
Queue audiofiles has 3342 pending jobs. Next job was created 12sec ago. [TOO OLD]
Queue zipping is empty.
ERROR: 1 queue have too old pending jobs

run roque.exe without arguments to see al options.

That's awesome! but I want events, I need decoupling, I want multiple and easy to add/replace/remove subscribers. But I don't want to read books on Message Queues.

(If you wonder what's the difference check the queue diagrams below showing a work queue and a pub/sub queue)

Example 2: Website User Sign-up post tasks.

Let's change the approach, let's suppose we want to perform several differnt tasks each time a user signs up. These tasks include creating a thumbnail of users profile pic, and sending a welcome email. (we could add logging, stats, analytics, etc.)

We don't want to clutter our user entity with the execution of this tasks. We already know a good solution to this problem: events.

1- Create an event-raising interface.


    public interface IUserEvents {
        event EventHandler<UserEventArgs> UserSignedUp;
    }

2- Throw events on your application


    public class UserBiz : IUserEvents {

        public event EventHandler<UserEventArgs> UserSignedUp;

        public void SignUp(string username, string password, string email) {

            // TODO: insert the user in my database

            var handler = UserSignedUp;
            if (handler != null){
                handler(this, new UserEventArgs(username, email));
            }

            // TIP: if you want you can save a few lines writting an extension method for Exception
            // UserSignedUp.Raise(new UserEventArgs(username, email));
        }
    }

    public class BizEventsInitializer {
        // call this on app startup
        public void Init() {
            // make all events on IUserEvents raised by this instance available for remote subscription
            RoqueEventBroadcaster.SubscribeToAll<IUserEvents>(UserBiz.Instance);
        }
    }

3- Config redis-based events queue:


    <?xml version="1.0"?>
    <configuration>
      <configSections>
        <section name="roque" type="Cinchcast.Roque.Core.Configuration.Roque, Roque.Core"/>
      </configSections>
      <roque>
        <queues>
          <!-- Reserved name _events is used by default by RoqueEventBroadcaster -->
          <queue name="_events" type="Cinchcast.Roque.Redis.RedisQueue, Roque.Redis">
            <settings>
              <setting key="host" value="localhost"/>
            </settings>
          </queue>
        </queues>
      </roque>
    </configuration>

Your website is ready!, You're events are available, they'll get in your queues as soon as you add subscribers for them.

Note: If no subscribers are found for an event, nothing is sent to Redis. You _events queue will always be empty (it won't even exist on Redis), as events never get directly enqueued, they get broadcasted to other queues.

4- Add some subscribers:


    public class ThumbnailCreator {
        public void SubscribeTo(IUserEvents userEvents) {
            userEvents.UserSignedUp+= UserEvents_UserSignedUp;
        }
        public void UserEvents_UserSignedUp(object sender, UserEventArgs args) {
            // let's reuse or image processing service here
            new ImageProcessor().CreateThumbnail("pics/"+args.Username+".jpg", 160, 120);
        }
    }

    public class UserGreeter {
        public void SubscribeTo(IUserEvents userEvents) {
            userEvents.UserSignedUp+= UserEvents_UserSignedUp;
        }
        public void UserEvents_UserSignedUp(object sender, UserEventArgs args) {
            MailSender.SendWelcomeEmail(args.Username, args.Email);
        }
    }

5- Install Roque service on a machine (if you didn't before).

6- On the same folder of roque.exe drop the assembly(ies) containing IUserEvents interface and your ThumbnailCreator and UserGreeter classes.

7- On Roque.exe.config:


    <?xml version="1.0"?>
    <configuration>
      <configSections>
        <section name="roque" type="Cinchcast.Roque.Core.Configuration.Roque, Roque.Core"/>
      </configSections>
      <roque>
        <queues>
          <queue name="images" type="Cinchcast.Roque.Redis.RedisQueue, Roque.Redis">
            <settings>
              <setting key="host" value="localhost"/>
            </settings>
          </queue>
          <queue name="greetings" type="Cinchcast.Roque.Redis.RedisQueue, Roque.Redis">
            <settings>
              <setting key="host" value="localhost"/>
            </settings>
          </queue>
        </queues>
        <workers>
          <!-- a worker poping jobs from "images" queue --> 
          <worker name="images" queue="images" autoStart="true">
            <subscribers>
                <!-- all events that ThumbnailCreator subscribes to will be broadcasted to this worker's queue (images) --> 
                <subscriber type="Acme.Images.ThumbnailCreator, Acme.Images"/>
            </subscribers>
          </worker>
          <!-- a worker poping jobs from "greettings" queue --> 
          <worker name="greetings" queue="greetings" autoStart="true">
            <subscribers>
                <!-- all events that UserGreeter subscribes to will be broadcasted to this worker's queue (greetings) --> 
                <subscriber type="Acme.Messaging.UserGreeter, Acme.Messaging"/>
            </subscribers>
          </worker>
        </workers>    
      </roque>
    </configuration>

Now this requires som

View on GitHub
GitHub Stars24
CategoryDevelopment
Updated3y ago
Forks10

Languages

C#

Security Score

75/100

Audited on Jan 19, 2023

No findings