Orleans之Hello World
接触Orleans 有一段时间了,之前也翻译了一系列官网文档,今天我们就来一个实际的例子,来看看到底如何用这个东西来开发项目,当然经典的也是醉人的,我们就从HelloWorld开始吧。
通过前面的知识准备我们知道Orleans 项目需要n个服务端(就是silohost),n个客户端(就是调用方),然后就是提供的actors(在Orleans 中成为grain),废话少说。
首先建立一个解决方案,叫做OrleansSamples
然后,增加一个模块解决方案,叫做HelloWorlds,在解决方案下增加两个类库Sample.Interfaces,Sample.Implements,其中Sample.Implements引用Sample.Interfaces,
这两个项目中引用Orleans的核心库,你可以手动一个一个引用进来,但还是老老实实的用nuget吧。
nuget的引用两种方式一种,通过图形化的方式,另一种通过命令的方式,命令:Install-Package Microsoft.Orleans.Core (注:在这里可以找到所需的包http://dotnet.github.io/orleans/NuGets)
记得引用完后如果在nuget里有更新就更新一下,对于新版本,可能里面有些库会没有进来,否则就会报错,反正我是这么做,做完这一切,项目结构如下:
在Sample.Interfaces中增加一个接口IUserService,并且继承接口IGrainWithIntegerKey(关于这个接口有姊妹接口,关于这些接口后续会陆续讲到)
代码如下:
namespace Sample.Interfaces { public interface IUserService:IGrainWithIntegerKey { Task<bool> Exist(string mobileNumber); } }
在Sample.Implements项目中增加实现类UserService,并且继承Grain(Grain这个基类同时也提供了相应的泛型实现Grain<>,关于他们的不同点,以及功能,后续会讲到),且实现IUserService接口
代码如下:
namespace Sample.Implements { public class UserService : Grain, IUserService { public Task<bool> Exist(string mobileNumber) { return Task.FromResult<bool>(mobileNumber=="18612478956"); } } }
好了到此为止,我们已经开发好actor虽然简单,接下来我们接着增加服务启动寄宿项(关于寄宿项,可以是控制台、windows服务、winfrom、asp.net ),这里我们采用控制台,下面我们创建一个服务控制台应用程序(Server)
引用上面创建两个项目:Sample.Implements、Sample.Interfaces。(注:其实这两个项目不一定要引用进来,只要在生成项目的目录下存在他们的编译好的dll即可,silo用来自动加载启动这个他们)
引用orleans项目中服务端的类库(使用nuget命令:Install-Package Microsoft.Orleans.Server
)
项目结构如下:
代码如下:
namespace Server { class Program { static void Main(string[] args) { using (var host = new SiloHost("Default")) { host.InitializeOrleansSilo(); host.StartOrleansSilo(); Console.WriteLine("启动成功!"); Console.ReadLine(); host.StopOrleansSilo(); } } } }
好一切准备就绪,我们F5吧,
当你看到这得时候是不是觉得成功了,真的成功了吗,不一定吧!
哦对了怎么没看到日志呢,好我们在项目目录下看看日志:
果然有日志文件,一看文件名称,直接告诉我们发生错误了,好让我们打开看看吧。
从发生的异常看出,好像少了一个配置文件,在orleans 服务启动时,需要一个配置文件,这个配置文件可以是OrleansConfiguration.xml或者orleans.config或者orleans.config.xml
好知道原因了,知道该怎么做了吧,在server根目录下创建一个xml文件OrleansConfiguration.xml,将该文件的属性“复制到输出目录”值更改为"如果较新则复制"
该文件中填充配置内容,如下(详细配置请看配置一节,此处不解释)
<?xml version="1.0" encoding="utf-8" ?> <OrleansConfiguration xmlns="urn:orleans"> <Globals> <StorageProviders> <Provider Type="Orleans.Storage.MemoryStorage" Name="MemoryStore" /> <Provider Type="Orleans.Storage.MemoryStorage" Name="Default" /> <!--<Provider Type="Orleans.Storage.AzureTableStorage" Name="AzureStore"/>--> </StorageProviders> <SeedNode Address="localhost" Port="22222"/> <Messaging ResponseTimeout="30s"/> </Globals> <Defaults> <Networking Address="localhost" Port="22222"/> <ProxyingGateway Address="localhost" Port="40000" /> <Tracing DefaultTraceLevel="Info" TraceToConsole="false" TraceToFile="{0}-{1}.log" PropagateActivityId="false" BulkMessageLimit="1000"> <TraceLevelOverride LogPrefix="Application" TraceLevel="Info" /> <!-- <TraceLevelOverride LogPrefix="Runtime.Dispatcher" TraceLevel="Verbose" /> <TraceLevelOverride LogPrefix="AssemblyLoader.Silo" TraceLevel="Warning" /> --> </Tracing> <Statistics MetricsTableWriteInterval="30s" PerfCounterWriteInterval="30s" LogWriteInterval="300s" WriteLogStatisticsToTable="true" StatisticsCollectionLevel="Info"/> </Defaults> </OrleansConfiguration>
再次启动F5,当看到启动成功的输出时,我们再次看看生成的日志:
这次发现比之前生成的东西多了,但是当我们继续往下浏览的时候发现有个异常:
Exc level 0: System.IO.FileNotFoundException: 未能加载文件或程序集“Microsoft.Extensions.DependencyInjection.Abstractions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60”或它的某一个依赖项。系统找不到指定的文件。
在 Orleans.Runtime.Startup.ConfigureStartupBuilder.ConfigureStartup(String startupTypeName)
在 Orleans.Runtime.Startup.ConfigureStartupBuilder.Orleans.Runtime.Startup.IStartupBuilder.ConfigureStartup(String startupTypeName)
在 Orleans.Runtime.Silo..ctor(String name, SiloType siloType, ClusterConfiguration config, ILocalDataStore keyStore)
原来是少了程序集:Microsoft.Extensions.DependencyInjection
知道原因了,我们通过nuget来引用这个类库,引用成功,再次运行,然后查看日志,异常消失,但是有个问题,每次打开日志文件要看,是否有错误,或者一些关于服务的监控内容,这样是不是很麻烦,其实我们可以更改一下配置信息,让它输出到控制台,这样在开发过程中就方便多了,可以时时看到动态信息,如下:
打开OrleansConfiguration.xml 文件找到<Tracing DefaultTraceLevel="Info" TraceToConsole="false" TraceToFile="{0}-{1}.log" 这个节点,将TraceToConsole的值更改为true保存,再次运行,如下:
好了一切都完美了,接下来我们在继续开发客户端。
在解决方案下创建一个控制台应用程序Client,引用客户端相关类库:Install-Package Microsoft.Orleans.Client
引用项目:Sample.Interfaces
代码如下:
namespace Client { class Program { static void Main(string[] args) { System.Threading.Thread.Sleep(15000); GrainClient.Initialize(); while (true) { Console.WriteLine("请输入用户手机号:"); var mobileNumber = Console.ReadLine(); //这里由于我们采用的grain继承的是IGrainWithIntegerKey ,所以我们采用调用数值类型的key=10来创建这个grain, //可能有人会问key是干嘛的,他是唯一标识这个grain的,当你指定一个key的时候,Orleans 会创建一个,它首先到 //你的存储介质中找(如果你配置了的话,默认采用内存存储,这种方式适合开发期,生产环境需要保持状态的,所以需要配置到能持久化存储的地方去,比如sqlserver等) //如果找到了就直接返回,如果没找到就根据你指定的这个key然后创建一个,这个就是grain的激活,具体详细的,可以看官方问的关于Grain一章。 var userService = GrainClient.GrainFactory.GetGrain<IUserService>(10); //C#的一种新的表达式语法,这样就方便多了,省的我们拼接字符串。 Console.WriteLine($"用户{mobileNumber},{(userService.Exist(mobileNumber).Result?"已经存在":"不存在")}"); } } } }
在client项目下记得要创建配置文件,文件名称叫做ClientConfiguration.xml
内容如下:
<?xml version="1.0" encoding="utf-8" ?> <ClientConfiguration xmlns="urn:orleans"> <Gateway Address="localhost" Port="40000"/> <!-- To turn tracing off, set DefaultTraceLevel="Off" and have no overrides. For the trace log file name, {0} is replaced by "Client" and {1} is the current time. --> <Tracing DefaultTraceLevel="Info" TraceToConsole="false" TraceToFile="{0}-{1}.log" BulkMessageLimit="1000"> <TraceLevelOverride LogPrefix="Runtime" TraceLevel="Info" /> <TraceLevelOverride LogPrefix="Application" TraceLevel="Info" /> <TraceLevelOverride LogPrefix="AssemblyLoader" TraceLevel="Warning" /> </Tracing> <Statistics MetricsTableWriteInterval="300s" PerfCounterWriteInterval="30s" LogWriteInterval="300s" WriteLogStatisticsToTable="true" StatisticsCollectionLevel="Info"/> <Messaging ResponseTimeout="30s" ClientSenderBuckets="8192" MaxResendCount="0"/> </ClientConfiguration>
注:要记得更改文件属性哦
一切准备就绪,下来让改一下启动方式为多项目启动,然后就F5等待飞吧!
终于看到胜利的果实了,哈哈!接下来我们接着说说另外一种开发方式以及发布方式。
上面的这种开发方式为了说明开发的那些具体步骤,需要引用那些类库,以及客户端如何去调用,步骤比较麻烦,尤其是服务端的开发、引用类库,也没有相应的单元测试,接下来我们看看另外一种服务端的开发方式。
跟上面的大体步骤一样
1.创建接口类库
2.创建实现类库
3.开发测试服务寄宿程序
在开始之前首先要确认一下你是否安装了Orleans的vs模版插件,如果安装了那么如下图:
如果没有安装,赶紧去下载一个吧地址在:http://dotnet.github.io/orleans/NuGets
找到这一节,如下图:
点开里面有你想要的插件,然后安装重启vs
1.创建接口类库
2.创建实现类库
3.创建服务寄宿程序
服务创建完之后,发现下面有自动生成的一个类OrleansHostWrapper,并且在Program下自动生成了很多代码,代码的大体意思就是,将服务端启动程序的逻辑封装在OrleansHostWrapper,然后启动是单独创建一个应用程序域,增加一些测试例子代码,方便多了吧,我们不需要写任何服务端的服务启动代码,在实际开发过程中我们只需要关心业务模块
即接口创建,接口实现,方便多了吧。
将相应的代码贴入进去,还记得上面出现的那个异常吗,记得要将Microsoft.Extensions.DependencyInjection类库引用进来哦,F5吧。
一切如预期所料,成功!
接下来我们创建一个单元测试程序库,方便服务端的程序单元测试Sample.Test
引用项目Sample.Implements、Sample.Interfaces
引用Orleans测试包 PM> Install-Package Microsoft.Orleans.TestingHost
我们创建一个测试类就叫UserServiceTest,继承自Orleans 测试库TestingSiloHost
[ClassCleanup] public static void ClassCleanup() { // Optional. // By default, the next test class which uses TestignSiloHost will // cause a fresh Orleans silo environment to be created. StopAllSilosIfRunning(); }
测试代码如下:
namespace Sample.Test { [TestClass] public class UserServiceTest: TestingSiloHost { [ClassCleanup] public static void ClassCleanup() { // Optional. // By default, the next test class which uses TestignSiloHost will // cause a fresh Orleans silo environment to be created. StopAllSilosIfRunning(); } [TestMethod] public async void TestExist() { var grain = GrainFactory.GetGrain<IUserService>(10); bool bo = await grain.Exist("18612478956"); Assert.IsTrue(bo); } } }
记得增加两个配置文件,
ClientConfigurationForTesting.xml
<?xml version="1.0" encoding="utf-8" ?> <ClientConfiguration xmlns="urn:orleans"> <Gateway Address="localhost" Port="40000"/> <!-- To turn tracing off, set DefaultTraceLevel="Off" and have no overrides. For the trace log file name, {0} is replaced by "Client" and {1} is the current time. --> <Tracing DefaultTraceLevel="Info" TraceToConsole="false" TraceToFile="{0}-{1}.log" BulkMessageLimit="1000"> <TraceLevelOverride LogPrefix="Runtime" TraceLevel="Info" /> <TraceLevelOverride LogPrefix="Application" TraceLevel="Info" /> <TraceLevelOverride LogPrefix="AssemblyLoader" TraceLevel="Warning" /> </Tracing> <Statistics MetricsTableWriteInterval="300s" PerfCounterWriteInterval="30s" LogWriteInterval="300s" WriteLogStatisticsToTable="true" StatisticsCollectionLevel="Info"/> <Messaging ResponseTimeout="30s" ClientSenderBuckets="8192" MaxResendCount="0"/> </ClientConfiguration>
OrleansConfigurationForTesting.xml
<?xml version="1.0" encoding="utf-8"?> <OrleansConfiguration xmlns="urn:orleans"> <Globals> <StorageProviders> <Provider Type="Orleans.Storage.MemoryStorage" Name="MemoryStore" /> <Provider Type="Orleans.Storage.MemoryStorage" Name="Default" /> <!--<Provider Type="Orleans.Storage.AzureTableStorage" Name="AzureStore"/>--> </StorageProviders> <SeedNode Address="localhost" Port="22222"/> <Messaging ResponseTimeout="30s"/> </Globals> <Defaults> <Networking Address="localhost" Port="22222"/> <ProxyingGateway Address="localhost" Port="40000" /> <Tracing DefaultTraceLevel="Info" TraceToConsole="false" TraceToFile="{0}-{1}.log" PropagateActivityId="false" BulkMessageLimit="1000"> <TraceLevelOverride LogPrefix="Application" TraceLevel="Info" /> <!-- <TraceLevelOverride LogPrefix="Runtime.Dispatcher" TraceLevel="Verbose" /> <TraceLevelOverride LogPrefix="AssemblyLoader.Silo" TraceLevel="Warning" /> --> </Tracing> <Statistics MetricsTableWriteInterval="30s" PerfCounterWriteInterval="30s" LogWriteInterval="300s" WriteLogStatisticsToTable="true" StatisticsCollectionLevel="Info"/> </Defaults> </OrleansConfiguration>
更改xml文件属性为:如果较新则复制
测试项目结构大体如下:
好,测试编写完成,如何操作你懂得,这里就不废话了。
接下来说一下部署,部署吧,各有各的妙招,控制台、winform、windows服务等等,这里我说一个框架自带的一个部署控制台怎么用
记得Orleans 里面有这么一个程序OrleansHost.exe,他是干什么用的呢,对了就是用来部署的。
我们来看看他的源码,弄清楚他到底是做了一件什么事情
如下图:
打开这个文件,可以发现这个文件跟上面我们通过模版创建的server中的文件OrleansHostWrapper很相似,对了,这个就是为了保持开发部署的一致性,所以这个就可以用来直接部署了
我们在server的Debug下找到相应的程序,将程序复制到某个盘符比如D:\demo下面
如下图:
然后将配置文件拷贝进来OrleansConfiguration.xml
一切准备就绪,我们运行把,双击StartOrleans.cmd服务启动
运行成功.
你会发现这个服务部署于我们的开发互不影响,当我们开发好一个grain的时候,直接编译丢到这个部署目录下,别的地方就可以访问了,可以让我们重点关注业务逻辑,而不需要关心那些复杂的配置或者服务的开启关闭等等。
实例代码