BeetleX.FastHttpApi之控制器调度设计

    为了可以更灵活地在Webapi应用服务中分配线程资源,BeetleX.FastHttpApi在线程调度上直接细化到Action级别;组件不仅可以精准控制每个Action的最大RPS限制,还能精细到控制使用多少线程资源来处理这些API的请求。接下来详细讲解组件针对这一块的实现结构和代码。

需求

        为什么要做到这么精细的控制呢?如果有足够资源那是不用考虑这方面的问题;但实际应用中资源不足是经常需要面对的问题。在整个服务中往往有些API非常占用资源,这个时候就希望通过简单配置来控制API使用的线程数达到一个理想的资源分配结果。在控制上最直接的办法是控制对应的RPS数量,但有时候希望以线程资源的方式来分配。

使用

        组件可以在控制器的Action上根据需求标记对应的限制属性

        //每秒最大处理数100,超过就拒绝[RequestMaxRPS(100)]public object MasRps(IHttpContext context){return DateTime.Now;}//不限制,由框架通过线程池调度public object None(string name, IHttpContext context){return $"Name:{name}|QueueID:{context.Queue?.ID}|Time:{DateTime.Now}";}//所有请求用一个线程有序处理[ThreadQueue(ThreadQueueType.Single)]public object SingleQueue(string name, IHttpContext context){return $"Name:{name}|QueueID:{context.Queue?.ID}|Time:{DateTime.Now}";}//所有请求分配两个线程有序处理[ThreadQueue(ThreadQueueType.Multiple, 2)]public object MultipleQueue(string name, IHttpContext context){return $"Name:{name}|QueueID:{context.Queue?.ID}|Time:{DateTime.Now}";}//根据Name的值一致线程处理,同一值会分配到一个线程中有序处理[ThreadQueue("name")]public object UniqueQueue(string name, IHttpContext context){return $"Name:{name}|QueueID:{context.Queue?.ID}|Time:{DateTime.Now}";}

设计实现

        接下来看一下BeetleX.FastHttpApi组件代码是如何进行工作的。由于需要线程控制,那自然就需要一个队列;组件提供一个NextQueue的队列来完成这方面的工作,每个NextQueue会分配一个线程来处理。

   public class NextQueue : IDisposable{public NextQueue(){mQueue = new System.Collections.Concurrent.ConcurrentQueue<IEventWork>();ID = System.Threading.Interlocked.Increment(ref mID);}public long ID { get; set; }private static long mID;private readonly object _workSync = new object();private bool _doingWork;private int mCount;private System.Collections.Concurrent.ConcurrentQueue<IEventWork> mQueue;public int Count => mCount;//添加任务到队列中public void Enqueue(IEventWork item){mQueue.Enqueue(item);System.Threading.Interlocked.Increment(ref mCount);lock (_workSync){//当前队列是否工作中if (!_doingWork){//获取一个线程进行工作System.Threading.ThreadPool.QueueUserWorkItem(OnStart);_doingWork = true;}}}private void OnError(Exception e, IEventWork work){try{Error?.Invoke(e, work);}catch{}}public static Action<Exception, IEventWork> Error { get; set; }private async void OnStart(object state){while (true){//获取队列任务并执行while (mQueue.TryDequeue(out IEventWork item)){System.Threading.Interlocked.Decrement(ref mCount);using (item){try{//等待任务执行await item.Execute();}catch (Exception e_){OnError(e_, item);}}}lock (_workSync){//队列为空跑出线程if (mQueue.IsEmpty){try{Unused?.Invoke();}catch { }_doingWork = false;return;}}}}public Action Unused { get; set; }public void Dispose(){while (mQueue.TryDequeue(out IEventWork work)){try{work.Dispose();}catch{}}}}

NextQueue是一个支持异步任务的处理队列,它确保添加进来的任务都是有序执行,即使任务内部处理的任务是异步。

ActionContext

        该对象是用于执行控制器方法,包括webapi控制器和Websocket控制器。在这里只讲述控制怎样调度执行的,更详细了解可以查看

https://github.com/beetlex-io/FastHttpApi/blob/master/src/ActionContext.cs

主要讲解一下Execute方法是怎样调用控制器方法的

        internal async Task Execute(IActionResultHandler resultHandler){//验证RPSif (Handler.ValidateRPS()){Handler.IncrementRequest();//是否存在队列控制配置if (Handler.ThreadQueue == null || Handler.ThreadQueue.Type == ThreadQueueType.None){if (Handler.Async)//异步方法{await OnAsyncExecute(resultHandler);}else{//同步方法OnExecute(resultHandler);}}else{//配置了队列控制r妊ActionTask actionTask = new ActionTask(this, resultHandler,new TaskCompletionSource<object>());//获取异步队列var queue = Handler.ThreadQueue.GetQueue(this.HttpContext);//阶列是否有效,为了安全队列都有最大等待数限制,超过就拒绝处理if (Handler.ThreadQueue.Enabled(queue)){this.HttpContext.Queue = queue;//把当前任务插入队列queue.Enqueue(actionTask);//等待队执行结果通知await actionTask.CompletionSource.Task;}else{Handler.IncrementError();resultHandler.Error(new Exception($"{Handler.SourceUrl} process error,out of queue limit!"), EventArgs.LogType.Warring, 500);}}}else{Handler.IncrementError();resultHandler.Error(new Exception($"{Handler.SourceUrl} process error,out of max rps!"), EventArgs.LogType.Warring, 509);}}

GetQueue

        应该方法根据当前请示信息和配置来获取对应的异步队列

        public NextQueue GetQueue(IHttpContext context){//单队执行,永远返回针对当前控制器方法的第一个队列if (Type == ThreadQueueType.Single)return QueueGroup.Queues[0];//轮循当前分配最大队列数else if (Type == ThreadQueueType.Multiple)return QueueGroup.Next();//针对请求数据做一致性队列分配else if (Type == ThreadQueueType.DataUnique){string value = null;if (UniqueName != null){if (string.Compare(UniqueName, "$path", true) == 0){value = context.Request.GetSourcePath();}else if(UniqueName.IndexOf("__")==0){return mUniqueQueueGroup.Has(UniqueName.GetHashCode());}else{value = context.Request.Header[UniqueName];if (value == null)context.Data.TryGetString(UniqueName, out value);}}if (value == null)value = context.Request.GetSourceUrl();return mUniqueQueueGroup.Has(value.GetHashCode());}//如果都没匹配到就获取轮循的下一个return QueueGroup.Next();}

ActionTask

        方法异步任务对象,队列会有序地执行相关对象,这对象的实现非常简单。

        struct ActionTask : IEventWork{public ActionTask(ActionContext context, IActionResultHandler resultHandler, TaskCompletionSource<object> completionSource){Context = context;ResultHandler = resultHandler;CompletionSource = completionSource;}public TaskCompletionSource<object> CompletionSource { get; set; }public ActionContext Context { get; set; }public IActionResultHandler ResultHandler { get; set; }public void Dispose(){}public async Task Execute(){try{if (Context.Handler.Async){//异步方法await Context.OnAsyncExecute(ResultHandler);}else{//同步方法Context.OnExecute(ResultHandler);}}finally{//回调执行完成,让队列继续下一个任务。CompletionSource?.TrySetResult(new object());}}}

总结

        到这里整个线程调度的核心就介绍完成了,如果不了解一些基础知识会感觉完成这些功能很复杂,其实都是一些基础功能的应用; 完成这些功能主要涉及几个基础知识分别是:队列,线程池和用于处理异步回调的TaskCompletionSource对象。

BeetleX

开源跨平台通讯框架(支持TLS)
提供高性能服务和大数据处理解决方案

https://beetlex.io

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/302339.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java类加载机制深度分析

为什么80%的码农都做不了架构师&#xff1f;>>> Java类加载机制 类加载是Java程序运行的第一步&#xff0c;研究类的加载有助于了解JVM执行过程&#xff0c;并指导开发者采取更有效的措施配合程序执行。研究类加载机制的第二个目的是让程序能动态的控制类加载&…

北大清华团队编写!200多个科学实验+视频,和爸爸一起在家做

自从2017年2月份教育部从小学一年级起将科学课列入必修课&#xff0c;学校、家长都意识到科学素养对于孩子成长的重要性。好多家长都跃跃欲试&#xff0c;想陪孩子把科学“玩”起来。可是具体到如何给孩子做科学启蒙&#xff0c;面对的问题还真不少&#xff1a;生活中有哪些科学…

java 枚举工厂_在Java中使用枚举工厂,最佳做法?

Java允许我们在Enum上嵌入数据和行为.我不想在Enum上直接实施一个工厂,因为我认为这不是它的作用.但是我可以把类的引用放在枚举上,并在外部工厂中引用对象.相对于传统的工厂模式,你最好的实现是什么&#xff1f;哪种解决方案在哪种情况下更好&#xff1f;现在,代码.在两种解决…

ASP.Net服务性能优化原则

理理Asp.net性能相关的问题及注意事项。以下这些内容&#xff0c;全部是经验之谈。如果你有别的建议&#xff0c;也可以从后台发给我。服务器性能问题&#xff0c;通常在数据少的时候不会显现&#xff0c;也无需太多关注。但一旦数据量大了&#xff0c;就会变成一个麻烦且必须处…

如何搭建一个指标体系

2019独角兽企业重金招聘Python工程师标准>>> 今天跟大家聊聊&#xff0c;如何搭建一个指标体系。 1、什么是指标体系 “指标体系”这个概念是应用比较广泛的&#xff0c;我们从正式出版物中摘取一个定义&#xff1a; 指标体系&#xff0c;即统计指标体系&#xff0c…

2018年最后一个月最值得关注的13个优质公号

全世界有3.14 % 的人已经关注了数据与算法之美在这个知识千变万化的时代只有不断学习、充实自我&#xff0c;才能跟上时代以下13个顶级公众号能让你扩宽视野&#xff0c;紧跟时代的潮流近现代史研究通讯ID&#xff1a;jxsdyjtx2015▲长按二维码“识别”关注设置为星标近现代史研…

java thread join()_Java Thread join() 的用法

Java Thread中&#xff0c; join() 方法是让调用该方法的主线程执行run()时暂时卡住&#xff0c;等run()执行完成后&#xff0c; 主线程再调用执行join()后面的代码。示例&#xff1a;class ThreadTesterA implements Runnable {private int counter;Overridepublic void run()…

.NET Worker Service 如何优雅退出

上一篇文章中我们了解了 .NET Worker Service 的入门知识[1]&#xff0c;今天我们接着介绍一下如何优雅地关闭和退出 Worker Service。Worker 类从上一篇文章中&#xff0c;我们已经知道了 Worker Service 模板为我们提供三个开箱即用的核心文件&#xff0c;其中 Worker 类是继…

NYOJ -123 士兵杀敌(四)

士兵杀敌&#xff08;四&#xff09; 时间限制&#xff1a;2000 ms | 内存限制&#xff1a;65535 KB难度&#xff1a;5描述南将军麾下有百万精兵&#xff0c;现已知共有M个士兵&#xff0c;编号为1~M&#xff0c;每次有任务的时候&#xff0c;总会有一批编号连在一起人请战&a…

大数据告诉你,中国女人有多勤奋

全世界只有3.14 % 的人关注了数据与算法之美前段时间&#xff0c;美国国家统计局发布了一组关于世界各国劳动参与率的数据&#xff0c;中国赫然位列世界第一&#xff0c;劳动总量世界第一&#xff0c;劳动参与率世界第一。所谓劳动总量&#xff0c;就是所有工作的人的工作时间的…

get+php+mysql_Apache+PHP+MySql 的安装及配置

每一项技术用的人多了&#xff0c;就会有人将其进行优化&#xff0c;做成一个简单、实用、大众化的工具&#xff0c;这对于初识者来说是非常方便的&#xff0c;但是对于长久学习或工作这方面的人技术人员来说是不可取的&#xff0c;所以还是要学习基础的实用方法。因此&#xf…

记一次 .NET 车联网云端服务 CPU爆高分析

一&#xff1a;背景 1. 讲故事前几天有位朋友wx求助&#xff0c;它的程序CPU经常飙满&#xff0c;没找到原因&#xff0c;希望帮忙看一下。这些天连续接到几个cpu爆高的dump&#xff0c;都看烦了????????????&#xff0c;希望后面再来几个其他方面的dump&#xff0…

下载 infoq 网站视频

今天看到 infoq 网站上一个讲 Go 语言的视频&#xff0c;速度太卡了。我家里光纤宽带也没法正常浏览&#xff0c;所以需要研究下如何下载了。 用 FireBug 看了下源代码&#xff0c;抓到其中 flash 播放控件的一个参数里有视频链接如下&#xff1a; <param name"flashva…

java swing 示例_JAVA简单Swing图形界面应用演示样例

JAVA简单Swing图形界面应用演示样例package org.rui.hello;import javax.swing.JFrame;/*** 简单的swing窗体* author lenovo**/public class HelloSwing {public static void main(String[] args) {JFrame framenew JFrame("hello Swing");frame.setDefaultCloseOpe…

group client policy无法登录,谢绝访问

以下是联想提供的临时解决方案&#xff1a; 1、在用户开机时&#xff0c;不停点击F8按键&#xff0c;在弹出的”高级启动选项”中使用键盘上的方向箭头中的向下箭头移动白色高亮条&#xff0c;选择”安全模式”&#xff0c;敲击回车键。   2、进入安全模式界面时&#xff0c;…

.NET上海社区线下Meetup - 5.22 Blazor Day

Blazor 是一个 Web UI 框架&#xff0c;Blazor 旨在简化快速的单页面 .Net 浏览器应用的构建过程&#xff0c;它虽然使用了诸如 CSS 和 HTML 之类的 Web 技术&#xff0c;但它使用 C&#xff03;语言和 Razor 语法代替 JavaScript 来构建可组合的 Web UI 。通过提供用于编译到 …

入门机器学习,开启人工智能大门!

AI这个词相信大家都非常熟悉&#xff0c;近几年来人工智能圈子格外热闹&#xff0c;光是AlphoGo就让大家对它刮目相看。今天小天就来跟大家唠一唠如何进军人工智能的第一步——机器学习。在机器学习领域&#xff0c;Python已经成为了主流。一方面因为这门语言简单易上手&#x…

java集合框架的结构_集合框架(Collections Framework)详解及代码示例

简介集合和数组的区别&#xff1a;数组存储基础数据类型&#xff0c;且每一个数组都只能存储一种数据类型的数据&#xff0c;空间不可变。集合存储对象&#xff0c;一个集合中可以存储多种类型的对象。空间可变。严格地说&#xff0c;集合是存储对象的引用&#xff0c;每个对象…

oracle undo

UNDO表空间用于存放UNDO数据,当执行DML操作(INSERT,UPDATE和DELETE)时,oracle会将这些操作执行前的旧数据 写入到 UNDO段,在oracle9i之前,管理UNDO数据时使用(Rollback Segment)完成的.从oracle9i开始,管理UNDO数据不仅可以使用回滚段,还可以使用UNDO表空间.因为规划和管理回滚…

Unity3D OpenVR 虚拟现实 保龄球打砖块游戏开发

据说水哥买了 Valve Index 设备&#xff0c;既然这个设备这么贵&#xff0c;不开发点有&#xff08;zhi&#xff09;趣&#xff08;zhang&#xff09;游戏就感觉对不起这个设备。本文将来开始着手开发一个可玩性不大&#xff0c;观赏性极强的保龄球打砖块游戏。这仅仅只是一个入…