اگر با کتابخانه Masstransit کار کرده باشید، معمولا از متدهای مربوط به Send و یا Publish آن استفاده کرده اید. به این صورت که یک دیتا را ارسال کرده و یک Consumer ثبت کرده‌اید. که اگر زمانی دیتایی ارسال شد، Consumer آن را دریافت کند و پردازش های لازم را انجام دهد. در این روش شما نمیتوانید ریسپانس را از Masstransit دریافت کنید. درواقع برای کارهایی که نیاز به ریسپانس نداشتند میتوانستید از آن استفاده کنید. اما اگر بخواهید ریسپانس را از Consumer دریافت کنید باید از Requests استفاده کنید. کتابخانه Masstransit یک قابلیت به اسم Requests دارد که میتوانید از آن استفاده کنید و دیتای موردنیاز را از Consumer دریافت کنید.
در این مقاله به نحوه پیاده سازی یک Consumer که اطلاعات مربوط به پست‌های یک کاربر را برمی‌گرداند می‌پردازیم. برای اینکار ابتدا باید کتابخانه Masstransit را نصب کرده و سرویس‌های آن را رجیستر کنید:

builder.Services.AddMassTransit(x =>
{
    x.AddConsumer<GetUserPostsConsumer>()
        .Endpoint(e => e.Name = "user-posts");

    x.AddRequestClient<GetUserPostsData>(new Uri("exchange:user-posts"));

    x.UsingInMemory((context, cfg) =>
    {
        cfg.ConfigureEndpoints(context);
    });
});

توجه داشته باشید که نام EndPoint مربوط به Consumer باید در متد AddRequestClient نیز استفاده شود بعد از exchange. در این مثال user-posts استفاده شده است. کلاس GetUserPostsData به عنوان دیتای ورودی Consumer درنظر گرفته می‌شود که قرار است برای Consumer ارسال شود. در اینجا ریسپانس‌ها مشخص نمی‌شوند.
 کلاس GetUserPostsConsumer اطلاعات پست‌های کاربر را برمی‌گرداند:

public class GetUserPostsConsumer : IConsumer<GetUserPostsData>
{
    private readonly List<UserPosts> _userPosts = new()
    {
        new UserPosts { Id = 1, UserId = 1, Name = "John Doe", Title = "First Post" },
        new UserPosts { Id = 2, UserId = 1,  Name = "John Doe", Title = "Second Post" },
        new UserPosts { Id = 3, UserId = 2,  Name = "Jane Smith", Title = "Hello World" }
    };

    public async Task Consume(ConsumeContext<GetUserPostsData> context)
    {
        var userPosts = _userPosts.Where(p => p.UserId == context.Message.UserId).ToList();

        if (userPosts != null && userPosts.Any())
        {
            await context.RespondAsync(new GetUserPostsResult
            {
                UserPosts = userPosts
            });
        }
        else
        {
            await context.RespondAsync(new GetUserPostNotFoundResult { });
        }
    }
}

برای سهولت کار از یک لیست استفاده کرده‌ایم. ورودی Consumer کلاس GetUserPostsData می‌باشد که فقط یک پراپرتی به نام UserId دارد.

public class GetUserPostsData
{
    public int UserId { get; set; }
}

 سپس بر اساس آیدی کاربر لیست پست‌ها از لیست usersPosts خوانده می‌شود. در صورتی که کاربر پستی داشته باشد، با استفاده از متد RespondAsync، کلاس GetUserPostsResult را به همراه لیست پست های کاربر به عنوان ریسپانس برمی‌گردانیم. اما اگر کاربر پستی نداشته باشد، کلاس GetUserPostNotFoundResult را به عنوان ریسپانس برمی‌گردانیم. در واقع ما می‌توانیم چندین نوع ریسپانس داشته باشیم.
سپس به پیاده سازی کلاسی که قرار است دیتای مربوط به پست‌های کاربر را دریافت کند می‌پردازیم:

public class MassTransitPostClient : IPostClient
{
    private readonly IRequestClient<GetUserPostsData> _requestClient;

    public MassTransitPostClient(IRequestClient<GetUserPostsData> requestClient)
    {
        _requestClient = requestClient;
    }

    public async Task<List<UserPosts>> GetUserPostsAsync(int userId)
    {
        var response = await _requestClient.GetResponse<GetUserPostsResult, GetUserPostNotFoundResult>(new GetUserPostsData
        {
            UserId = userId
        });

        if(response.Is(out Response<GetUserPostsResult> userPost))
        {
            return userPost.Message.UserPosts;
        }
        else if (response.Is(out Response<GetUserPostNotFoundResult> userPostNotFound))
        {
            return null;
        }

        return null;
    }
}

ابتدا باید IRequestClient را که  رجیستر کرده‌اید را اینجکت کنید (در زمان رجیستر کردن Masstransit) تا بتوانید از آن استفاده کنید. سپس در متد مربوط به GetUserPostsAsync باید متد GetResponse را فراخوانی کنید و کلاس GetUserPostsData را همراه با آیدی کاربر فراخوانی کنید. متد GetResponse میتواند چندین کلاس را به عنوان ریسپانس در نظر بگیرد. همانطور که در Consumer می‌توانستیم چندین نوع ریسپانس داشته باشیم، باید در زمان فراخوانی آن هم نوع ریسپانس‌های دریافتی را مشخص نماییم.
سپس می‌توانیم با استفاده از متد Is متوجه شویم که کدام نوع از ریسپانس‌ها از سمت Consumer دریافت شده است.

response.Is(out Response<GetUserPostsResult> userPost)

در صورتی که مقدار GetUserPostsResult در سمت Consumer پر شده باشد، میتوانید دیتای مربوط به لیست پست‌های کاربر را دریافت نمایید. در غیر اینصورت ریسپانس مربوط به GetUserPostNotFoundResult دریافت خواهید کرد. 

می‌توانید سرویس‌های داخلی پروژه‌های خودتان را به این صورت پیاده سازی کنید که به جای API از Requests مربوط به کتابخانه Masstransit استقاده کنید.

تجربه:
در یکی از پروژه‌هایی که از این قابلیت استفاده کردیم به صورت زیر عمل کردیم. برای مثال سرویس A نیازمند دریافت دیتایی از سرویس B بود. ابتدا Consumer مربوط به دریافت اطلاعات را در سرویس B پیاده سازی کردیم.

x.AddConsumer<ExampleConsumer>().Endpoint(e => e.Name = "b-service");

سپس در پروژه A متد مربوط به رجیستر کردن متد مربوط به دریافت اطلاعات از سرویس B را نوشتیم:

x.AddRequestClient<GetExampleData>(new Uri("exchange:b-service"));

بعد از مدتی متوجه شدیم در یکی دیگر از سرویس‌ها نیز به همان اطلاعات نیاز داریم. تنها کاری که انجام دادیم آن بود که متد مربوط به AddRequestClient را نیز در سرویس C رجیستر کردیم و دیتای موردنیاز را از سرویس B دریافت کردیم.

Powered by Froala Editor

نظرات