Cyril资料

本文主要介绍Cyril资料 方法和在新技术下所面对的“挑战”,方便大家深入理解Cyril资料 过程。本文也将分享Cyril资料 所遇到的问题和应对策略,怎么解决怎么做的问题。
通过深入本文可以理解代码原理,进行代码文档的下载,也可以查看相应 Demo 部署效果。

项目中是有多个集群的,现在存在一个是:在切换web集群时,如何切换HangFire的周期性任务。

先采取的解决办法是:

  • 每个集群分一个队列,在周期性任务入队时分配当前web集群的集群id单做队列名称。
  • 之前已存在的周期性任务,在其入队时修正到正确的集群执行

通过BackgroundJobServerOptions配置,只监听当前web集群的队列(ps:可参考文档:https://docs.hangfire.io/en/latest/background-processing/configuring-queues.html)

1 //只监听当前集群的队列 2                     var options = new BackgroundJobServerOptions() 3                     { 4                         Queues = new[] { GlobalConfigSection.Current.WebClusterId } 5                     }; 6                     HangfireAspNet.Use(() => new[] { new BackgroundJobServer(options) });

通过实现IElectStateFilter,重写OnStateElection方法,在任务入队前修正其入当前集群队列执行。

配置使用自定义属性

GlobalJobFilters.Filters.Add(new CustomJobFilterAttribute());

重写OnStateElection方法,通过修改enqueuedState的queue熟悉修正队列

 1 /// <summary>  2     /// HangFire Filter  3     /// </summary>  4     public class CustomJobFilterAttribute : JobFilterAttribute, IElectStateFilter  5     {  6         public void OnStateElection(ElectStateContext context)  7         {  8             if (context.CandidateState is EnqueuedState enqueuedState)  9             { 10                 var tenantConfigProvider = ObjectContainer.GetService<ITenantConfigProvider>(); 11                 var contextMessage = context.GetJobParameter<ContextMessage>("_ld_contextMessage"); 12                 var webClusterId = tenantConfigProvider.GetWebClusterIdByTenant(contextMessage.TenantId); 13                 if (enqueuedState.Queue != webClusterId)//修正队列 14                 { 15                     enqueuedState.Queue = webClusterId; 16                 } 17             } 18         } 19  20     }

ps(更多的filter可以参考文档:https://docs.hangfire.io/en/latest/extensibility/using-job-filters.html)

附上HangFire执行失败记录日志实现

CyrilCyril

 1  /// <summary>  2     /// HangFire Filter  3     /// </summary>  4     public class CustomJobFilterAttribute : JobFilterAttribute, IServerFilter  5     {  6   7         public void OnPerforming(PerformingContext filterContext)  8         {  9              10         } 11  12         /// <summary> 13         /// 未成功执行的 14         /// </summary> 15         /// <param name="filterContext"></param> 16         public void OnPerformed(PerformedContext filterContext) 17         { 18             var error = filterContext.Exception; 19             if (error==null) 20             { 21                 return; 22             } 23             //记录日志到后台 24             ILoggerFactory loggerFactory = ObjectContainer.GetService<ILoggerFactory>(); 25             ILogger logger; 26             if (error.TargetSite != null && error.TargetSite.DeclaringType != null) 27             { 28                 logger = loggerFactory.Create(error.TargetSite.DeclaringType.GetUnProxyType()); 29             } 30             else 31             { 32                 logger = loggerFactory.Create(GetType()); 33             } 34             var contextMessage = filterContext.GetJobParameter<ContextMessage>("_ld_contextMessage"); 35             var message = GetLogMessage(contextMessage, error.ToString(), filterContext.BackgroundJob.Id); 36  37             var logLevel = ErrorLevelType.Fatal; 38  39             if (error.InnerException is AppException ex) 40             { 41                 logLevel = ex.ErrorLevel; 42             } 43  44             switch (logLevel) 45             { 46                 case ErrorLevelType.Info: 47                     logger.Info(message, error); 48                     break; 49                 case ErrorLevelType.Warning: 50                     logger.Warn(message, error); 51                     break; 52                 case ErrorLevelType.Error: 53                     logger.Error(message, error); 54                     break; 55                 default: 56                     logger.Fatal(message, error); 57                     break; 58             } 59         } 60  61  62  63         /// <summary> 64         /// 获取当前日志对象 65         /// </summary> 66         /// <returns></returns> 67         private LogMessage GetLogMessage(ContextMessage contextMessage, string errorMessage, string backgroundJobId) 68         { 69             var logMessage = new LogMessage(contextMessage, errorMessage) 70             { 71                 RawUrl = backgroundJobId 72             }; 73             return logMessage; 74         } 75  76  77     }

View Code


现在还有一个问题是,HangFire DashBoard 默认只支持localhost访问,现在我需要可以很方便的在外网通过web集群就能访问到其对应的HangFire DashBoard。

通过文档https://docs.hangfire.io/en/latest/configuration/using-dashboard.html,可以知道其提供了一个登录验证的实现,但是由于其是直接写死了密码在代码中的,觉得不好。(ps:具体的实现可以参考:https://gitee.com/LucasDot/Hangfire.Dashboard.Authorization,https://www.cnblogs.com/lightmao/p/7910139.html)

我实现的思路是,在web集群界面直接打开标签页访问。在web集群后台生成token并在url中携带,在hangfire站点中校验token,验证通过则放行。同时校验url是否携带可修改的参数,如果有的话设置IsReadOnlyFunc放回false。(ps:可参考文档:https://docs.hangfire.io/en/latest/configuration/using-dashboard.html)

在startup页面配置使用dashboard,通过DashboardOptions选择配置我们自己实现的身份验证,以及是否只读设置。

CyrilCyril

 1 public void Configuration(IAppBuilder app)  2         {  3             try  4             {    5                   6                 Bootstrapper.Instance.Start();  7   8                 var dashboardOptions = new DashboardOptions()  9                 { 10                     Authorization = new[] { new HangFireAuthorizationFilter() }, 11                     IsReadOnlyFunc = HangFireIsReadOnly 12                 }; 13                 app.UseHangfireDashboard("/hangfire", dashboardOptions); 14                  15  16             } 17             catch (Exception ex) 18             { 19                 Debug.WriteLine(ex); 20             } 21  22         } 23  24         /// <summary> 25         /// HangFire仪表盘是否只读 26         /// </summary> 27         /// <param name="context"></param> 28         /// <returns></returns> 29         private bool HangFireIsReadOnly(DashboardContext context) 30         { 31             var owinContext = new OwinContext(context.GetOwinEnvironment()); 32             if (owinContext.Request.Host.ToString().StartsWith("localhost")) 33             { 34                 return false; 35             } 36  37             try 38             { 39                 var cookie = owinContext.Request.Cookies["Ld.HangFire"]; 40                 char[] spilt = { ',' }; 41                 var userData = FormsAuthentication.Decrypt(cookie)?.UserData.Split(spilt, StringSplitOptions.RemoveEmptyEntries); 42                 if (userData != null) 43                 { 44                     var isAdmin = userData[0].Replace("isAdmin:", ""); 45                     return !bool.Parse(isAdmin); 46                 } 47             } 48             catch (Exception e) 49             { 50                  51             } 52  53             return true; 54         }

View Code

在HangFireAuthorizationFilter的具体实现中,先校验是否已存在验证后的cookie如果有就不再验证,或者如果是通过localhost访问也不进行校验,否则验证签名是否正确,如果正确就将信息写入cookie。

 1 /// <summary>  2     /// HangFire身份验证  3     /// </summary>  4     public class HangFireAuthorizationFilter : IDashboardAuthorizationFilter  5     {  6         public bool Authorize(DashboardContext context)  7         {  8             var owinContext = new OwinContext(context.GetOwinEnvironment());  9  10             if (owinContext.Request.Host.ToString().StartsWith("localhost")|| owinContext.Request.Cookies["Ld.HangFire"] != null)//通过localhost访问不校验,与cookie也不校验 11             { 12                 return true; 13             } 14  15             var cluster = owinContext.Request.Query.Get("cluster");//集群名称 16             var isAdminS = owinContext.Request.Query.Get("isAdmin");//是否管理员(是则允许修改hangfire) 17             var token = owinContext.Request.Query.Get("token"); 18             var t = owinContext.Request.Query.Get("t");//时间戳 19             if (!string.IsNullOrEmpty(token) && bool.TryParse(isAdminS, out var isAdmin) && long.TryParse(t, out var timestamp)) 20             { 21                 try 22                 { 23                     var isValid = LicenceHelper.ValidSignature($"{cluster}_{isAdmin}", token, timestamp, TimeSpan.FromMinutes(5));//五分钟有效 24                     if (isValid) 25                     { 26                         var ticket = new FormsAuthenticationTicket(0, cluster, DateTime.Now, DateTime.Now + FormsAuthentication.Timeout, false, $"isAdmin:{isAdmin}"); 27                         var authToken = FormsAuthentication.Encrypt(ticket); 28  29                         owinContext.Response.Cookies.Append("Ld.HangFire", authToken, new CookieOptions() 30                         { 31                             HttpOnly = true, 32                             Path = "/hangfire" 33                         }); 34                         return true; 35                     } 36                 } 37                 catch (Exception ex) 38                 { 39  40                 } 41             } 42             return false; 43  44         } 45  46     }

在web的管理后台具体实现为,同选中集群点击后台任务直接访问改集群的HangFire DashBoard

Cyril

 

 Cyril

 

 点击后台任务按钮,后台放回token相关信息,然后js打开一个新的标签页展示dashboard

 1 public ActionResult GetHangFireToken(string clusterName)  2         {  3             var isAdmin=WorkContext.User.IsAdmin;  4             var timestamp = DateTime.UtcNow.Ticks;  5             var token = LicenceHelper.Signature($"{clusterName}_{isAdmin}", timestamp);  6             return this.Direct(new  7             {  8                 isAdmin,  9                 token, 10                 timestamp=timestamp.ToString() 11             }); 12         }

 1 openHangFire:function() {  2         var me = this, rows = me.getGridSelection('查看后台任务的集群', true);  3         if (!rows) {  4             return;  5         }  6         if (rows.length > 1) {  7             me.alert('只能选择一行');  8             return;  9         } 10         var clusterName = rows[0].get('ClusterName'); 11         var opts = { 12             actionName: 'GetHangFireToken', 13             extraParams: { 14                 clusterName: clusterName 15             }, 16             success: function (result) { 17                 var data = result.result; 18                 var url = Ext.String.format("{0}/hangfire?cluster={1}&isAdmin={2}&token={3}&t={4}", 19                     rows[0].get("AccessUrl"), 20                     clusterName, 21                     data.isAdmin, 22                     data.token, 23                     data.timestamp); 24                 window.open(url, clusterName); 25             } 26         }; 27         me.directRequest(opts); 28     }

 

Cyril资料部分资料来自网络,侵权毕设源码联系删除

区块链毕设网(www.qklbishe.com)全网最靠谱的原创区块链毕设代做网站
部分资料来自网络,侵权联系删除!
资源收费仅为搬运整理打赏费用,用户自愿支付 !
qklbishe.com区块链毕设代做网专注|以太坊fabric-计算机|java|毕业设计|代做平台 » Cyril资料

提供最优质的资源集合

立即查看 了解详情