ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Dapr牵手.NET学习笔记:Actor小试

2022-04-21 20:33:04  阅读:196  来源: 互联网

标签:Task Console app Actor Dapr var new 小试


Actor模型是一种避免线程共享数据,相同Actor实体串行化的方案,所以不便dapr的其他功能,几乎都是非编程入侵的,相反,Dapr Acror深度定制的,关于Actor,.net中有一些通用框架,比如Akka.net,微软的Orleans,还有最近复活的Proto actor。Dapr下的Actor,是dapr实现了一些库,基于这些库来实现actor模型编程的。

本篇开个小头,实际体会一下actor的作用,actor的一大作用就是实例隔离,相同实例不共享内存,不同实例间还是可以并行的,当然这个实现并不与OOP中的实例相等,还是看下面这个小例子吧,通过代码来感觉。

一、首先定义一个类库项目
需要引用Nuget包 Dapr.Actors

public interface IAccountActor : IActor
{
    Task<string> GetTimeAsync(string inTime);
}

二、定义一个asp.net api项目

实现上面定义的接口,需要引入Nuget包Dapr.Actors.AspNetCore

public class AccountActor: Actor, IAccountActor
 {      
     public AccountActor(ActorHost host) : base(host)
     {           
     }
     public async Task<string> GetTimeAsync(string inTime)
     {
         Console.WriteLine($"{this.Id}开始");
         Task.Delay(3000).Wait();
         Console.WriteLine($"{this.Id}结束");
         return await  Task.FromResult($"Actor ID:{this.Id} 传入时间:{inTime},返回时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
     }
 }

需要在向Services中注入Actor

using OrderFactoryService;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//注入Actor
builder.Services.AddActors(options =>
{
    options.HttpEndpoint = "http://localhost:3999";    
    options.Actors.RegisterActor<AccountActor>();
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseAuthorization();

app.UseRouting();
app.UseEndpoints(endpoints =>
{   
   //Map Actor Handler
    endpoints.MapActorsHandlers();
});
app.MapControllers();
app.Run();

为了对比测试,可以定义一个/gettime的api,比较并串行

[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
    private readonly ILogger<HomeController> _logger;

    public HomeController(ILogger<HomeController> logger)
    {
        _logger = logger;
    }

    [HttpGet("/gettime")]
    public IActionResult Get(string inTime)
    {
        Task.Delay(3000).Wait();
        return Ok($"传入时间:{inTime},返回时间:{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
    }
}

三、添加一个Actor客户端项目

需要引用Nuget包 Dapr.Actors

using Dapr.Actors;
using Dapr.Actors.Client;
using IOrderFactoryActory.Interfaces;

Console.WriteLine("回车开始");
Console.ReadLine();

//调用api是并行的
var client = new HttpClient();
var httpTask1 = new Task(async () =>
{
    Console.WriteLine(await client.GetStringAsync("http://localhost:5000/gettime?intime=" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")));
});
var httpTask2 = new Task(async () =>
{
    Console.WriteLine(await client.GetStringAsync("http://localhost:5000/gettime?intime=" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")));
});
httpTask1.Start();
httpTask2.Start();

//相同ID的actor是串行的,不同ID的actor是并行的
var factory = new ActorProxyFactory(new ActorProxyOptions { HttpEndpoint = "http://localhost:3999" });
var account1 = CreateActor(factory, "11111111111");
var account2 = CreateActor(factory, "22222222222");
var actorTask1_1 = new Task(async () =>
{
    Console.WriteLine(await account1.GetTimeAsync(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")));
});
var actorTask1_2 = new Task(async () =>
{
    Console.WriteLine(await account1.GetTimeAsync(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")));
});
var actorTask2 = new Task(async () =>
{
    Console.WriteLine(await account2.GetTimeAsync(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")));
});
actorTask1_1.Start();
actorTask1_2.Start();
actorTask2.Start();

Console.WriteLine("回车结束");
Console.ReadLine();

static IAccountActor CreateActor(ActorProxyFactory factory, string accountNo)
{
    var actorType = "AccountActor";
    var actorId = new ActorId(accountNo);   
    return factory.CreateActorProxy<IAccountActor>(actorId, actorType);
}

四、开始测试

启动sidecar

dapr run --app-id account --app-port 5000 --dapr-http-port 3999

运行结果:

  可以通过上面的例子看到,web api的传入时间和返回时间几乎相同,说明他们是并行运行的,都在内部等了3秒;Actor有两个实例,是通过ActorID来区分实例的,ID为1开头的两个实例虽然传入时间几乎相同,但在返回时间上,第二次明显是排在第一次返回后的(这正是Actor的串行基本准则),ID为2开头的,可以与1并行。

  实际场景是什么呢?前一段时间开发了一套账务系统,场景是有很多账户批量入帐,当然有可能有相同帐户同时入帐,入帐时需要取出旧的帐户余额,加上本次入帐金额,然后更新掉帐户余额;因为是通过web api调过来的并发,处理办法是在表的数据行上用行级锁(DBA会骂娘的),保证两个相同帐户入帐时,不会同时取,然后都用旧余额相加。但如果这里用Actor,就可以释放数据库的压力(DBA会很开心的),相同帐户的Actor是串行执行,所以在业务层就避免了并发,不同帐户不受影响,关键是Actor是细小的颗粒,可以大量创建销毁。篇幅和时间所限,下一篇会用例子来实现这个场景。

 想要更快更方便的了解相关知识,可以关注微信公众号   

标签:Task,Console,app,Actor,Dapr,var,new,小试
来源: https://www.cnblogs.com/ljknlb/p/16175897.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有