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();
//自动等待所有异步调用完成,并提交所有事务 }

 

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

posted @ 2020-07-23 09:59  IWing  阅读(1121)  评论(3编辑  收藏  举报