JMS微服務開發示例(二)編寫分布式事務


在上一篇,我們寫了簡單的Hello world微服務,現在,我們往這個微服務當中,加入一個支持分布式事務的函數,因為不想寫太長的代碼,我就不用數據庫做演示了,只是簡單給大家演示一下,怎么把事務的提交、回滾,放到一個委托當中。

using System;
using System.Collections.Generic;
using System.Text;
using JMS;
using Microsoft.Extensions.Logging;

namespace MyHelloworldService
{
    class HelloworldController : MicroServiceControllerBase
    {
        static List<string> Users = new List<string>();

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

        /// <summary>
        /// 哈嘍方法
        /// </summary>
        /// <param name="time">我當前的時間</param>
        /// <returns>中文問候語</returns>
        public string Hello(DateTime time)
        {
            return $"你好,你給的時間是: {time.ToShortDateString()}";
        }

        /// <summary>
        /// 添加用戶
        /// </summary>
        /// <param name="tranDelegate">當第一個參數為TransactionDelegate類型,表示這是一個事務委托</param>
        /// <param name="username">用戶名</param>
        /// <returns>是否添加成功</returns>
        public bool AddUser(TransactionDelegate tranDelegate , string username)
        {
            if (Users.Contains(username))
                return false;

            //把提交放到委托
            tranDelegate.CommitAction = () => {                
                _logger.LogInformation("提交事務成功");
            };

            //把回滾放到委托
            tranDelegate.RollbackAction = () => {
                lock (Users)
                {
                    Users.Remove(username);
                }
                _logger.LogInformation("回滾事務成功");
            };

            lock (Users)
            {
                Users.Add(username);
            }
            return true;
        }

        /// <summary>
        /// 獲取所有用戶名
        /// </summary>
        /// <returns></returns>
        public string[] GetAllUsers()
        {
            return Users.ToArray();
        }
    }
}
AddUser函數,由於第一個參數是TransactionDelegate類型,所以這個函數支持分布式事務,把事務的提交與回滾,托管給這個變量即可。


客戶端同樣預先調用這段代碼,重新生成一次HelloWorld.cs:
            using ( var tran = CreateMST() )
            {
                var api = tran.GetMicroService("Hello world");
                var code = api.GetServiceClassCode("TestApplication" , "HelloWorldApi");
                File.WriteAllText("../../../HelloWorldApi.cs", code, Encoding.UTF8);
            }
 
        
調用端代碼改為這樣:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Text;
using System.Threading;
using Way.Lib;

namespace TestApplication
{
    class Program
    {
        static IServiceProvider ServiceProvider;
        static RemoteClient CreateMST()
        {
            var logger = ServiceProvider.GetService<ILogger<RemoteClient>>();
            return new RemoteClient("192.168.40.131", 7900, null, logger);
        }
        static void Main(string[] args)
        {
            Thread.Sleep(3000);//等服務啟動完畢

            ServiceCollection services = new ServiceCollection();
            services.AddLogging(loggingBuilder =>
            {
                loggingBuilder.SetMinimumLevel(LogLevel.Debug);
                loggingBuilder.AddConsole();
            });
            ServiceProvider = services.BuildServiceProvider();

            using ( var tran = CreateMST() )
            {
tran.SetHeader("auth" , "abc");//自定義header信息
var api = tran.GetMicroService<HelloWorldApi>(); var ret = api.Hello(DateTime.Now); Console.WriteLine(ret); api.AddUser("Jack1"); api.AddUser("Jack2"); var allusers = api.GetAllUsers(); Console.WriteLine("回滾前用戶列表:{0}" , allusers.ToJsonString()); tran.Rollback();//回滾所有事務 allusers = api.GetAllUsers(); Console.WriteLine("回滾后用戶列表:{0}", allusers.ToJsonString()); } } } }
跑一下工程,效果如下:

 方法二

上面,為了實現事務,方法的第一個參數,必須是TransactionDelegate類型,這樣,如果每個方法都要支持事務,那么,很可能每個方法都要寫一遍相同的委托代碼,這樣就有點繁瑣,

如果委托的代碼都一樣,我們可以實例化 this.TransactionControl 屬性,這樣也能起到事務委托的效果,代碼如下:

 

using System;
using System.Collections.Generic;
using System.Text;
using JMS;
using Microsoft.Extensions.Logging;
using Org.BouncyCastle.Bcpg;

namespace MyHelloworldService
{
    class HelloworldController : MicroServiceControllerBase
    {
        DBContext _db;

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

        public override void OnAfterAction(string actionName, object[] parameters)
        {
            base.OnAfterAction(actionName, parameters);

            if(_db != null)
            {
                this.TransactionControl = new TransactionDelegate(this.TransactionId);
                this.TransactionControl.CommitAction = () => {
                    _db.CommitTransaction();
                };
                this.TransactionControl.RollbackAction = () => {
                    _db.RollbackTransaction();
                };
            }
        }

        /// <summary>
        /// 添加用戶
        /// </summary>
        /// <param name="username">用戶名</param>
        /// <returns>是否添加成功</returns>
        public bool AddUser(string username)
        {
            _db = new DBContext();
            _db.Insert(new User
            {
                Name = username
            });
            return true;
        }

        /// <summary>
        /// 獲取所有用戶名
        /// </summary>
        /// <returns></returns>
        public string[] GetAllUsers()
        {
            return _db.Users.ToArray();
        }

    }
}

 

這是數據庫事務的大概例子,在Controller里面,定義 DBContext 局部變量 _db,然后在AfterAction里,把_db事務的提交、回滾,交給 this.TransactionControl 。

this.TransactionControl 是用來針對整個Controller所有函數,設置分布式事務委托。

系統處理優先級:

當函數中第一個參數為TransactionDelegate類型,並且里面的委托不為空,那么,事務由這個參數進行處理。

如果函數中沒有定義TransactionDelegate參數,而this.TransactionControl不為空,而且委托也不為空,那么,事務由 this.TransactionControl 進行處理。

 客戶端異步調用微服務

            using ( var tran = CreateMST() )
            {
tran.BeginTransaction();
var api = tran.GetMicroService<HelloWorldApi>(); //異步調用AddUser api.AddUserAsync("Jack1");
//同步調用AddUser
api.AddUser("Jack2"); tran.Commit();
//自動等待所有異步調用完成,並提交所有事務 }

 

上一篇 示例(一)     下一篇 示例(三)分布式鎖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM