[orleans2.1]这是你没玩过的船新版本

  不知不觉orleans就发布到2.1版本的,但是说也奇怪orleans越是完善我发现园子相关的博客就越少,大概是大佬都在美滋滋用在生产环境,不屑于玩demo了吧。

  但是小弟不才还是只会玩demo,所以只能简单的介绍介绍2.1版本的新玩法了。

  1.新建一个asp.net core的webapi项目,然后引用下面几个nuget包:

1 Microsoft.Orleans.OrleansRuntime
2 Microsoft.Orleans.CodeGenerator.MSBuild
3 Microsoft.Orleans.Transactions
4 Orleans.Providers.MongoDB
5 OrleansDashboard

  2.包装一下orleans的silobuilder类,并且继承IHostedService直接和asp.net core运行在一起

 1     public class SiloWrapper : IHostedService
 2     {
 3         private readonly ISiloHost _silo;
 4         public readonly IClusterClient Client;
 5 
 6         public SiloWrapper()
 7         {
 8             _silo = new SiloHostBuilder()
 9                 .UseLocalhostClustering()
10                 .ConfigureApplicationParts(parts =>
11                     parts.AddApplicationPart(typeof(Grains.IUserGrain).Assembly).WithReferences())
12                     .EnableDirectClient()//2.1新增的功能,单个Host可以直接使用SiloHost的Client,不需要再用ClientBuilder建Client了
13                     .AddMongoDBGrainStorageAsDefault(options =>
14                     {
15                         options.ConnectionString = "mongodb://localhost/OrleansTestApp";
16                     })//配置数据库
17                     .ConfigureLogging(x =>
18                     {
19                         x.AddConsole();
20                         x.SetMinimumLevel(LogLevel.Warning);
21                     })
22                     .UseDashboard(x =>
23                     {
24                         x.HostSelf = false;
25                     })//HostSelf设置为false
26                     .UseTransactions()//2.1的事务配置简化了
27                     .Build();
28 
29             Client = _silo.Services.GetRequiredService<IClusterClient>();//把sliohost的IClusterClient暴露出去。
30         }
31 
32         public async Task StartAsync(CancellationToken cancellationToken)
33         {
34             await _silo.StartAsync(cancellationToken);
35         }
36 
37         public async Task StopAsync(CancellationToken cancellationToken)
38         {
39             await _silo.StopAsync(cancellationToken);
40         }
41     }

  3.Startup类配置:

 1     public class Startup
 2     {
 3         public Startup(IConfiguration configuration)
 4         {
 5             Configuration = configuration;
 6         }
 7 
 8         public IConfiguration Configuration { get; }
 9 
10         public void ConfigureServices(IServiceCollection services)
11         {
12             services.AddSingleton<SiloWrapper>();//注入SiloWrapper
13             services.AddSingleton<IHostedService>(x=>x.GetRequiredService<SiloWrapper>());//同时把SiloWrapper注入为IHostedService
14             services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
15             services.AddSingleton(x => x.GetRequiredService<SiloWrapper>().Client);//注入SiloWrapper的Client
16             services.AddServicesForSelfHostedDashboard();//注入orleans的dashboard
17         }
18 
19         public void Configure(IApplicationBuilder app, IHostingEnvironment env)
20         {
21             if (env.IsDevelopment())
22             {
23                 app.UseDeveloperExceptionPage();
24             }
25 
26             app.UseOrleansDashboard(new OrleansDashboard.DashboardOptions { BasePath = "/dashboard"});//设置一下dashboard的访问路径
27             app.UseMvc();
28         }
29     }

  4.新建一些Grain类,这里只给出一个后面我会贴代码地址出来。

    public class UserGrain:Grain<UserInfo>,IUserGrain
    {
        public ValueTask<UserInfo> GetInfo()//同步代码可以返回ValueTask
        {        
            return new ValueTask<UserInfo>(State);
        }

        public async Task<UserInfo> UpdateInfo(UserInfo info)
        {
            State = info;
            await WriteStateAsync();//更新数据才需要数据库相关的操作
            return State;
        }

        public async Task<uint> GetBalance()
        {
            var account = this.GrainFactory.GetGrain<IAccountGrain>(this.GetPrimaryKeyLong());//通过GrainFactory访问其他grain
            return await account.GetBalance();
        }
    }
    [StatelessWorker]
    public class ATMGrain : Grain, IATMGrain//转账事务的专用grain
    {
        Task IATMGrain.Transfer(long fromAccount, long toAccount, uint amountToTransfer)
        {
            return Task.WhenAll(
                this.GrainFactory.GetGrain<IAccountGrain>(fromAccount).Withdraw(amountToTransfer),
                this.GrainFactory.GetGrain<IAccountGrain>(toAccount).Deposit(amountToTransfer));
        }
    }

    public class AccountGrain : Grain, IAccountGrain//加钱,减钱,查钱啦
    {
        private readonly ITransactionalState<Balance> _balance;

        public AccountGrain(
            [TransactionalState("balance")] ITransactionalState<Balance> balance)
        {
            _balance = balance ?? throw new ArgumentNullException(nameof(balance));
        }

        async Task IAccountGrain.Deposit(uint amount)
        {
            await _balance.PerformUpdate(x => x.Value += amount);
        }

        async Task IAccountGrain.Withdraw(uint amount)
        {
            await _balance.PerformUpdate(x =>
            {
                if (x.Value < amount)
                {
                    throw new InvalidOperationException( "The transferred amount was greater than the balance.");
                }
                return x.Value -= amount;
            });
        }

        Task<uint> IAccountGrain.GetBalance()
        {
            return _balance.PerformRead(x => x.Value);
        }
    }

 5.controller相关的代码,这里也是照旧只贴一部分

    [Route("api/[controller]")]
    public class ValuesController : Controller
    {
        private readonly IClusterClient _client;

        public ValuesController(IClusterClient client)
        {
            _client = client;
        }

        [HttpGet("[action]/{id}")]
        public async Task<object> GetInfo(long id)
        {
            var userGrain = _client.GetGrain<IUserGrain>(id);
            return await userGrain.GetInfo();
        }
   }    

  代码地址:https://github.com/iJzFan/orleansdemo

  可以看到2.1之后配置真的简单了很多,简单几步之后你就能快乐的进行无数据库设计无并发考虑的编程啦。

  最后面是我用jmeter做的一个小测试(不是特别严谨,日志都是开着的,不要太纠结数据),配置嘛就是那个1核两G的腾讯云垃圾主机啦,上面跑了一个两个docker,一个是前面的orleansdemo,一个是mongodb。

  测试条件就是用户1和用户2相互转账( ̄︶ ̄)↗ ,10个线程,分别转1000次(对应的URL:/api/values/atm?from=1&to=2&amount=1和/api/values/atm?from=2&to=1&amount=1)。

  测试条件就是1转2,2转3,3转4,4转1,10个线程,分别转500次(url参考上面)。

  时延还是挺低的,平均才55~61ms,腾讯云那个垃圾主机一秒都能处理150~160的事务请求。

  最最后面贴几个orleans相关的代码库,毕竟我上面的demo还是太小儿科了,

  https://github.com/RayTale/Ray 分布式、高性能、事件溯源、事件驱动、最终一致性框架

  https://github.com/Squidex/squidex Headless CMS and Content Managment Hub

 

posted @ 2018-10-11 08:09 速泡面 阅读(...) 评论(...) 编辑 收藏