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