These days, I’m working on Orleans and Actor-based systems as I mentioned in my post titled “Overview of Orleans“. In this article, I will try to explain how we can build loosely coupled and scalable RESTful services using Orleans as a middle-tier.

In addition to the practicality that we have gained from Orleans, it also brings a new approach to the architecture. It gives us location transparency, also everything is handled with Scalable Grains(Virtual Actors), without any Reentrancy and Concurrency problems. Sounds nice, isn’t it?

Anyway, although actor-based systems look very interesting to me, I can say that I gained a different perspective with using the Orleans project in my almost 10 years software development experience.

Using Orleans as a Middle-Tier

It is possible to build distributed, high-scale applications without thinking about any reliability, distributed resource management or scalability bottlenecks using the virtual actor model that Orleans implements.

Let’s continue with an example. Let’s assume, we are developing a vehicle tracking system for users. While drivers driving their cars, we will collect tracking data. When an object is passed to an Orleans Grain, it is serialized and deserialized. We used “[Immutable]” attribute on message contract for better serialization performance since serialization is a performance centric operation.

Basically, we will develop a REST endpoint and a Silo that runs behind like the above picture.

Building Silo

Firstly let’s create a “VehicleTracking.Common” class library. We will create messages in here that will pass between the Grains.

“VehicleInfo” message is defined as below.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

using System;

using Orleans.Concurrency;

namespace VehicleTracking.Common

{

[Immutable]

public class VehicleInfo

{

public long DeviceId { get; set; }

public string Location { get; set; }

public string Direction { get; set; }

public DateTime Timestamp { get; set; }

}

}

When an object is sent to across node, it is serialized then deserialized with the binary serializer. So Grains cannot access the same object and change their internal state. Also, we used “[Immutable]” attribute on message contract for better serialization performance since serialization is a performance centric operation.

[Immutable] Messages

The serialization process is performed so the objects can access the Grains in the different Silos. On the other hand, deep-copy operations are performed for Grains in the same Silo. This serialization operations can be made slightly more efficient for Grains on the same Silo. It is possible with the using “[Immutable]” attribute, so the serialization operations can be bypass.

Let’s create called “VehicleTracking.GrainInterfaces” class library, then define “IVehicleGrain” interface.

C#

1

2

3

4

5

6

7

8

9

10

11

using System.Threading.Tasks;

using Orleans;

using VehicleTracking.Common;

namespace VehicleTracking.GrainInterfaces

{

public interface IVehicleGrain : IGrainWithIntegerKey

{

Task SetVehicleInfo(VehicleInfo info);

}

}

We defined “SetVehicleInfo” method in the “IVehicleGrain” interface. We will use this method while collecting drivers location info. By the way, if we look at the method name, we can see how it looks like an RPC method name definition. Orleans clients and Grains communicate with each other via PRC, therefore we defined method name an RPC style.

Now, let’s create one more interface called “IVehicleTrackingGrain”.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

using System.Threading.Tasks;

using Orleans;

using VehicleTracking.Common;

namespace VehicleTracking.GrainInterfaces

{

public interface  IVehicleTrackingGrain : IGrainWithIntegerKey

{

Task SetVehicleTrackingInfo(VehicleInfo info);

Task Subscribe(IVehicleTrackingObserver observer);

Task Unsubscribe(IVehicleTrackingObserver observer);

}

}

While drivers driving their cars, we will collect the location data then pass with “SetVehicleTrackingInfo” method, over “IVehicleGrain”. When the location data is passed, we will send notifications to client’s subscribers.

Now, we will define an observer to send notifications. Therefore, let’s create another “IVehicleTrackingObserver” interface.

C#

1

2

3

4

5

6

7

8

9

10

using Orleans;

using VehicleTracking.Common;

namespace VehicleTracking.GrainInterfaces

{

public interface IVehicleTrackingObserver : IGrainObserver

{

void ReportToVehicle(VehicleInfo info);

}

}

That’s all. Now, we can start Grains implementations.

Firstly let’s implement “IVehicleTrackingGrain” interface for observing operations.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

using System;

using System.Threading.Tasks;

using Orleans;

using VehicleTracking.Common;

using VehicleTracking.GrainInterfaces;

using Orleans.Concurrency;

namespace VehicleTracking.Grains

{

[Reentrant]

public class VehicleTrackingGrain : Grain, IVehicleTrackingGrain

{

private ObserverSubscriptionManager<IVehicleTrackingObserver> _observers;

private VehicleInfo _vehicleInfo;

public override Task OnActivateAsync()

{

_observers = new ObserverSubscriptionManager<IVehicleTrackingObserver>();

RegisterTimer(Callback, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5));

return base.OnActivateAsync();

}

Task Callback(object callbackState)

{

if (_vehicleInfo != null)

{

_observers.Notify(x => x.ReportToVehicle(_vehicleInfo));

_vehicleInfo = null;

}

return TaskDone.Done;

}

public Task SetVehicleTrackingInfo(VehicleInfo info)

{

_vehicleInfo = info;

return TaskDone.Done;

}

public Task Subscribe(IVehicleTrackingObserver observer)

{

_observers.Subscribe(observer);

return TaskDone.Done;

}

public Task Unsubscribe(IVehicleTrackingObserver observer)

{

_observers.Unsubscribe(observer);

return TaskDone.Done;

}

}

}

We performed the observing operations with the “ObserverSubscriptionManager” helper in Orleans. It provides an easy way to process, such as subscribing and sending a notification. The “OnActivateAsync” method is called at the end of the Grain activation process. We used the “RegisterTimer” method here so that we can perform callback operations on Grains as periodic. If we look at the callback method, if the “_vehicleInfo” field is not null, all subscribed clients will get notifications via the “ReportToVehicle” method.

[Reentrant] Attribute

We have used the “[Reentrant]” attribute, that we have defined above to overcome bottlenecks in the network and to apply some performance optimizations. According to Carl Hewitt, as conceptually messages are processed one at a time in the actor model. In Orleans, concurrent processingcan be provided with techniques such as the “[Reentrant]” attribute. In this way, where it may be necessary Grains will not block in the face of some costly operations. However, we are advised to be careful at the points we need to use, otherwise, we may face race-conditions situations.

Now, we can implement “IVehicleGrain” interface as follows:

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

using System.Threading.Tasks;

using Orleans;

using Orleans.Concurrency;

using VehicleTracking.Common;

using VehicleTracking.GrainInterfaces;

namespace VehicleTracking.Grains

{

[Reentrant]

public class VehicleGrain : Grain, IVehicleGrain

{

private long _currentGrainId;

public override Task OnActivateAsync()

{

_currentGrainId = this.GetPrimaryKeyLong();

return base.OnActivateAsync();

}

public async Task SetVehicleInfo(VehicleInfo info)

{

//some business logics...

var vehicleTrackingGrain = GrainFactory.GetGrain<IVehicleTrackingGrain>(_currentGrainId);

await vehicleTrackingGrain.SetVehicleTrackingInfo(info);

}

}

}

Let’s assume, we are getting vehicle location data with the “SetVehicleInfo” method, then processing it for vehicle tracking operations with some business logics. When the business logics processed, we are passed the message to “VehicleTrackingGrain” for notification step.

Now, we completed all implementations. We can create now Orleans Dev/Test Host as follows:

转存失败重新上传取消

Then create a new class called “VehicleTrackingObserver”.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

using System;

using VehicleTracking.Common;

using VehicleTracking.GrainInterfaces;

namespace VehicleTracking.TestSilo

{

public class VehicleTrackingObserver : IVehicleTrackingObserver

{

public void ReportToVehicle(VehicleInfo info)

{

Console.WriteLine($"The vehicle id {info.DeviceId} moved to {info.Direction} from {info.Location} at {info.Timestamp.ToShortTimeString()} o'clock.");

}

}

}

We implemented “IVehicleTrackingObserver” interface at the above code block. At this point, when drivers drive their cars, we will write the vehicle tracking notifications on the console screen.

Let’s refactor “Program.cs” as follows:

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

using System;

using Orleans;

using Orleans.Runtime.Configuration;

using VehicleTracking.GrainInterfaces;

namespace VehicleTracking.TestSilo

{

/// <summary>

/// Orleans test silo host

/// </summary>

public class Program

{

static void Main(string[] args)

{

// The Orleans silo environment is initialized in its own app domain in order to more

// closely emulate the distributed situation, when the client and the server cannot

// pass data via shared memory.

AppDomain hostDomain = AppDomain.CreateDomain("OrleansHost", null, new AppDomainSetup

{

AppDomainInitializer = InitSilo,

AppDomainInitializerArguments = args,

});

var config = ClientConfiguration.LocalhostSilo();

GrainClient.Initialize(config);

// TODO: once the previous call returns, the silo is up and running.

//       This is the place your custom logic, for example calling client logic

//       or initializing an HTTP front end for accepting incoming requests.

Console.WriteLine("Orleans Silo is running.\nPress Enter to terminate...");

var vehicleTrackingObserver = new VehicleTrackingObserver();

var vehicleTrackingObserverRef = GrainClient.GrainFactory

.CreateObjectReference<IVehicleTrackingObserver>(vehicleTrackingObserver).Result;

var vehicleTrackingGrain = GrainClient.GrainFactory.GetGrain<IVehicleTrackingGrain>(1);

vehicleTrackingGrain.Subscribe(vehicleTrackingObserverRef).Wait();

hostDomain.DoCallBack(ShutdownSilo);

Console.ReadLine();

}

static void InitSilo(string[] args)

{

hostWrapper = new OrleansHostWrapper(args);

if (!hostWrapper.Run())

{

Console.Error.WriteLine("Failed to initialize Orleans silo");

}

}

static void ShutdownSilo()

{

if (hostWrapper != null)

{

hostWrapper.Dispose();

GC.SuppressFinalize(hostWrapper);

}

}

private static OrleansHostWrapper hostWrapper;

}

}

We performed subscription operations with using “IVehicleTrackingObserver” and “IVehicleTrackingGrain”. In this project, we will initialize Orleans Test Silo also write the notifications that coming from the observer on the console screen.

Defining the REST Endpoint

We are ready to coding REST endpoint. Let’s create an empty Web API project called “VehicleTracking.Api”. Then add “Microsoft.Orleans.Core” package via NuGet Package Manager as follows:

转存失败重新上传取消

After this, we should initialize Test Silo in the “Global.asax” as follows, so we can communicate with Silo.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

using Orleans;

using System.Web.Http;

namespace VehicleTracking.Api

{

public class WebApiApplication : System.Web.HttpApplication

{

protected void Application_Start()

{

GlobalConfiguration.Configure(WebApiConfig.Register);

var config = Orleans.Runtime.Configuration.ClientConfiguration.LocalhostSilo();

GrainClient.Initialize(config);

}

}

}

Now, we can communicate with Silo. Let’s add our first controller called “VehicleTracking”, then coding as follows:

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

using Orleans;

using System;

using System.Threading.Tasks;

using System.Web.Http;

using VehicleTracking.Common;

using VehicleTracking.GrainInterfaces;

namespace VehicleTracking.Api.Controllers

{

public class VehicleTrackingController : ApiController

{

[Route("api/vehicle-trackings")]

public async Task Post(long deviceId, string location, string direction)

{

var vehicleGrain = GrainClient.GrainFactory.GetGrain<IVehicleGrain>(deviceId);

VehicleInfo trafficInfo = new VehicleInfo()

{

DeviceId = deviceId,

Location = location,

Direction = direction,

Timestamp = DateTime.Now

};

await vehicleGrain.SetVehicleInfo(trafficInfo);

}

}

}

Now, we have a POST endpoint. At this point, we are passed vehicle tracking info to Grain with using “SetVehicleInfo” method incoming from “VehicleGrain” instance.

We are ready for the test steps. Firstly we have to initialize Silo. Let’s start the “VehicleTracking.TestSilo” project, then the “VehicleTracking.Api” project.

转存失败重新上传取消

After this, start the “VehicleTracking.Api” project too. Now, let’s send a POST request to “/api/vehicle-trackings?deviceId=1&location=Taksim Square&direction=Bagdat Street” endpoint via Postman as follows:

转存失败重新上传取消

As a result, we can see that the notification operation of the vehicle tracking info, that we sent via the REST endpoint is written on the console screen via observer.

Conclusion

We have built a system that works loosely coupled and scalable with using the Orleans Silo as a middle-tier behind of the REST endpoint. Also without any thread locking or concurrency problems.

I hope this article would help who needs any information about using the Orleans as a middle-tier. Currently, I’m researching on the Orleans to work with the Docker. At the same time, I will try to share my experience on the Orleans in new articles.

Sample project: https://github.com/GokGokalp/orleans-vehicletracking-sample

References:

https://dotnet.github.io/orleans/Tutorials/Front-Ends-for-Orleans-Services.html

https://dotnet.github.io/orleans/Tutorials/Concurrency.html

Bu makale toplam (2027) kez okunmuştur.

Building Loosely Coupled and Scalable RESTful Services using Orleans相关推荐

  1. maven集成spring_Maven集成测试和Spring Restful Services

    maven集成spring 介绍 我的原始博客通过一个非常简单的示例展示了如何分离Maven单元和集成测试. http://johndobie.blogspot.com/2011/06/seperat ...

  2. Maven集成测试和Spring Restful Services

    介绍 我的原始博客通过一个非常简单的示例展示了如何分离Maven单元和集成测试. http://johndobie.blogspot.com/2011/06/seperating-maven-unit ...

  3. 保存查看翻译:Thrift: Scalable Cross-Language Services Implementation中文翻译(Thrift:Œ可扩展的跨语言服务实现)...

    工作之余抽点时间出来写写博文,希望对新接触的朋友有帮助.今天在这里和大家一起学习一下保存查看 本文给出Thrift: Scalable Cross-Language Services Implemen ...

  4. CXF(2.7.10) - RESTful Services, JSON Support

    在 CXF(2.7.10) - RESTful Services 介绍了 REST 风格的 WebService 服务,数据传输是基于 XML 格式的.如果要基于 JSON 格式传输数据,仅需要将注解 ...

  5. 【译】SEDA: An Architecture for Well-Conditioned, Scalable Internet Services

    Matt Welsh, David Culler, and Eric Brewer                                Computer Science Division   ...

  6. Loosely Coupled: The Missing Pieces of Web Services

    版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章原始出版.作者信息和本声明.否则将追究法律责任. http://blog.csdn.net/topmvp - topmvp Buildin ...

  7. Prism : Communicating Between Loosely Coupled Components

    注意:这一系列随笔都基于这个假设 -------- Prsim 提供了4中方式:Commanding,Event Aggregation,Region Context,Shared Services ...

  8. Spring loosely coupled example

    其实Spring的反转控制,只是将依赖放到了配置文件中取管理,修改完配置文件后,还是要refresh整个context, 来使得对应的依赖生效. http://www.mkyong.com/sprin ...

  9. 松散架构(Loosely Coupled Architecture)

    接触了一些采用Linux/Unix工具的架构的应用以后,我发现我越来越喜欢松散的架构.即除了接口(UI),然后是算法,最后是面向领域的工具或DSL.而Microsoft的架构大都反其道而行之,原因是: ...

最新文章

  1. 【转】ASP.NET Page事件的执行顺序
  2. linux文件需求管理,CaliberRM 需求管理系统
  3. 软件测试-PR录制脚本程序的时候出现license invalid,error code=-13或者-24的错误
  4. unicode编码转ascii编码
  5. 容器精华问答 | 如何进行跨机器的Container做Link ?
  6. python 代码片段23
  7. 大并发服务器不得不说的技术--TCP_CORK
  8. Padavan路由器无法启用FRP的解决方法(固件版本3.4.3.9-099_11-23)
  9. 归并算法(Java实现)
  10. html播放韰 寸 频,asp.net 汉字转换拼音及首字母实现代码
  11. 线性可分支持向量机与软间隔最大化
  12. IP地址分类及对应范围
  13. H3C交换机链路聚合配置
  14. Tether操纵市场了吗?
  15. 从0开始学股票第四课之量能的基本知识之成交量
  16. 学计算机画素描吗,怎样在电脑上画素描?
  17. 计算机毕业设计Java成都某4S店销售管理系统(源码+系统+mysql数据库+lw文档)
  18. Subway UVA - 10691
  19. GPU地址空间的相关概念
  20. 仿新浪微博的图片加载

热门文章

  1. 每日一练——Python基础(六)
  2. linux脚本编写后怎么退出,linux脚本编写退出拍摄pdf
  3. 数据结构与计算机网络,如何把计算机原理,操作系统,数据结构和计算机网络结合起来...
  4. 新闻无限分类_社区故事03 | 垃圾分类,是一种快乐的生活方式
  5. 四舍五入VS银行家舍入
  6. 数字城市:智慧水库(泉舟时代)
  7. “源”来是你-Vol.32 | 知名图数据平台 Neo4j 招聘中国社区经理
  8. c盘清理小技巧(亲测,效果还可以)
  9. 关于意志力,不得不说的十二件事
  10. 解决Spring Spring Data JPA 错误: Page 1 of 1 containing UNKNOWN instances