首页 文章 精选 留言 我的

精选列表

搜索[禅道API],共10004篇文章
优秀的个人博客,低调大师

明修"栈"道——越过Android启动栈陷阱

作者:vivo 互联网大前端团队- Zhao Kaiping 本文从一例业务中遇到的问题出发,以FLAG_ACTIVITY_NEW_TASK这一flag作为切入点,带大家探究Activity启动前的一项重要的工作——栈校验。 文中列举一系列业务中可能遇到的异常状况,详细描述了使用FLAG_ACTIVITY_NEW_TASK时可能遇到的“坑”,并从源码中探究其根源。只有合理使用flag、launchMode,才能避免因为栈机制的特殊性,导致一系列与预期不符的启动问题。 一、问题及背景 应用间相互联动、相互跳转,是实现系统整体性、体验一致性的重要手段,也是最简单的一种方法。 当我们用最常用的方法去startActivity时,竟也会遇到失败的情况。在真实业务中,就遇到了这样一例异常:用户点击某个按钮时,想要“简简单单”跳转另一个应用,却没有任何反应。 经验丰富的你,脑海中是否涌现出了各种猜想:是不是目标Activity甚至目标App不存在?是不是目标Activty没有对外开放?是不是有权限的限制或者跳转的action/uri错了…… 真实的原因被flag、launchMode、Intent等特性层层藏匿,可能超出你此时的思考。 本文将从源码出发,探究前因后果,展开讲讲在startActivity()真正准备启动一个Activity前,需要经过哪些“磨难”,怎样有据可依地解决由栈问题导致的启动异常。 1.1 业务中遇到的问题 业务中的场景是这样的,存在A、B、C三个应用。 (1)从应用A-Activity1跳转至应用B-Activity2; (2)应用B-Activity2继续跳转到应用C-Activity3; (3)C内某个按钮,会再次跳转B-Activity2,但点击后没有任何反应。如果不经过前面A到B的跳转,C直接跳到B是可以的。 1.2 问题代码 3个Activity的Androidmanifest配置如下,均可通过各自的action拉起,launchMode均为标准模式。 <!--应用A--> <activity android:name=".Activity1" android:exported="true"> <intent-filter> <action android:name="com.zkp.task.ACTION_TO_A_PAGE1" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <!--应用B--> <activity android:name=".Activity2" android:exported="true"> <intent-filter> <action android:name="com.zkp.task.ACTION_TO_B_PAGE2" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <!--应用C--> <activity android:name=".Activity3" android:exported="true"> <intent-filter> <action android:name="com.zkp.task.ACTION_TO_C_PAGE3" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> A-1到B-2的代码,指定flag为 FLAG_ACTIVITY_NEW_TASK private void jumpTo_B_Activity2_ByAction_NewTask() { Intent intent = new Intent(); intent.setAction("com.zkp.task.ACTION_TO_B_PAGE2"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);} B-2到C-3的代码,未指定flag private void jumpTo_C_Activity3_ByAction_NoTask() { Intent intent = new Intent(); intent.setAction("com.zkp.task.ACTION_TO_C_PAGE3"); startActivity(intent);} C-3到B-2的代码,与A-1到B-2的完全一致,指定flag为 FLAG_ACTIVITY_NEW_TASK private void jumpTo_B_Activity2_ByAction_NewTask() { Intent intent = new Intent(); intent.setAction("com.zkp.task.ACTION_TO_B_PAGE2"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);} 1.3 代码初步分析 仔细查看问题代码,在实现上非常简单,有两个特征: (1)如果直接通过C-3跳B-2,没有任何问题,但A-1已经跳过B-2后,C-3就失败了。 (2)在A-1和C-3跳到B-2时,都设置了flag为FLAG_ACTIVITY_NEW_TASK。 依据经验,我们推测与栈有关,尝试将跳转前栈的状态打印出来,如下图。 由于A-1跳到B-2时设置了FLAG_ACTIVITY_NEW_TASK,B-2跳到C-3时未设置,所以1在独立栈中,2、3在另一个栈中。示意如下图。 C-3跳转B-2一般有3种可能的预期,如下图:预想1,新建一个Task,在新Task中启动一个B-2;预想2,复用已经存在的B-2;预想3,在已有Task中新建一个实例B-2。 但实际上3种预期都没有实现,所有Activity的任何声明周期都没有变化,界面始终停留在C-3。 看一下FLAG_ACTIVITY_NEW_TASK的官方注释和代码注释,如下图: 重点关注这一段: When using this flag, if a task is already running for the activity you are now starting, then a new activity will not be started; instead, the current task will simply be brought to the front of the screen with the state it was last in. 使用此flag时,如果你正在启动的Activity已经在一个Task中运行,那么一个新Activity不会被启动;相反,当前Task将简单地显示在界面的前面,并显示其最后的状态。 ——显然,官方文档与代码注释的表述与我们的异常现象是一致的,目标Activity2已经在Task中存在,则不会被启动;Task直接显示在前面,并展示最后的状态。由于目标Activty3就是来源Activity3,所以页面没有任何变化。 看起来官方还是很靠谱的,但实际效果真的能一直与官方描述一致吗?我们通过几个场景来看一下。 二、场景拓展与验证 2.1 场景拓展 在笔者依据官方描述进行调整、复现的过程中,发现了几个比较有意思的场景。 PS:上面业务的案例中,B-2和C-3在不同应用内,又在相同的Task内,但实际上是否是同一个应用,对结果的影响并不大。为了避免不同应用和不同Task造成阅读混乱,同一个栈的跳转,我们都在本应用内进行,故业务中的场景等价于下面的【场景0】 【场景0】把业务中B-2到C-3的应用间跳转改为B-2到B-3的应用内跳转 // B-2跳转B-3public static void jumpTo_B_3_ByAction_Null(Context context) { Intent intent = new Intent(); intent.setAction("com.zkp.task.ACTION_TO_B_PAGE3"); context.startActivity(intent);} 如下图,A-1设置NEW_TASK跳转B-2,再跳转B-3,最终设置NEW_TASK想跳转B-2。虽然跳C-3改为了跳B-3,但与之前问题的表现一致,没有反应,停留在B-3。 有的读者会指出这样的问题:如果同一个应用内使用NEW_TASK跳转,而不指定目标的taskAffinity属性,实际是无法在新Task中启动的。请大家忽略该问题,可以认为笔者的操作是已经加了taskAffinity的,这对最终结果并没有影响。 【场景1】如果目标Task和来源Task不是同一个,情况是否会如官方文档所说复用已有的Task并展示最近状态?我们改为B-3启动一个新Task的新Activity C-4,再通过C-4跳回B-2 // B-3跳转C-4public static void jumpTo_C_4_ByAction_New(Context context) { Intent intent = new Intent("com.zkp.task.ACTION_TO_C_PAGE4"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent);}// C-4跳转B-2public static void jumpTo_B_2_ByAction_New(Context context) { Intent intent = new Intent(); intent.setAction("com.zkp.task.ACTION_TO_B_PAGE2"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent);} 如下图,A-1设置NEW_TASK跳转B-2,再跳转B-3,再设置NEW_TASK跳转C-4,最终设置NEW_TASK想跳转B-2。 预想的结果是:不会跳到B-2,而是跳到它所在Task的顶层B-3。 实际的结果是:与预期一致,确实是跳到了B-3。 【场景2】把场景1稍做修改:C-4到B-2时,我们不通过action来跳,改为通过setClassName跳转。 // C-4跳转B-2public static void jumpTo_B_2_ByPath_New(Context context) { Intent intent = new Intent(); intent.setClassName("com.zkp.b", "com.zkp.b.Activity2"); // 直接设置classname,不通过action intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent);} 如下图,A-1设置NEW_TASK跳转B-2,再跳转B-3,再设置NEW_TASK跳转C-4,最终设置NEW_TASK想跳转B-2。 预想的结果是:与场景0一致,会跳到B-2所在Task的已有顶层B-3。 实际的结果是:在已有的Task2中,产生了一个新的B-2实例。 仅仅是改变了一下重新跳转B-2的方式,效果就完全不一样了!这与官方文档中提到该flag与"singleTask" launchMode值产生的行为并不一致! 【场景3】把场景1再做修改:这次C-4不跳栈底的B-2,改为跳转B-3,且还是通过action方式。 // C-4跳转B-3public static void jumpTo_B_3_ByAction_New(Context context) { Intent intent = new Intent(); intent.setAction("com.zkp.task.ACTION_TO_B_PAGE3"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent);} 如下图,A-1设置NEW_TASK跳转B-2,再跳转B-3,再设置NEW_TASK跳转C-4,最终设置NEW_TASK想跳转B-3。 预想的结果是:与场景0一致,会跳到B-2所在Task的顶层B-3。 实际的结果是:在已有的Task2中,产生了一个新的B-3实例。 不是说好的,Activity已经存在时,展示其所在Task的最新状态吗?明明Task2中已经有了B-3,并没有直接展示它,而是生成了新的B-3实例。 【场景4】既然Activity没有被复用,那Task一定会被复用吗?把场景3稍做修改,直接给B-3指定一个单独的affinity。 <activity android:name=".Activity3" android:exported="true" android:taskAffinity="b3.task"><!--指定了亲和性标识--> <intent-filter> <action android:name="com.zkp.task.ACTION_TO_B_PAGE3" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter></activity> 如下图,A-1设置NEW_TASK跳转B-2,再跳转B-3,再设置NEW_TASK跳转C-4,最终设置NEW_TASK想跳转B-3。 ——这次,连Task也不会再被复用了……Activity3在一个新的栈中被实例化了。 再回看官方的注释,就会显得非常不准确,甚至会让开发者对该部分的认知产生严重错误!稍微改变过程中的某个毫无关联的属性(如跳转目标、跳转方式……),就会产生很大差异。 在看flag相关注释时,我们要树立一个意识:Task和Activity跳转的实际效果,是launchMode、taskAffinity、跳转方式、Activity在Task中的层级等属性综合作用的结果,不要相信“一面之词”。 回到问题本身,究竟是哪些原因造就了上面的不同效果呢?只有源码最值得信赖了。 三、场景分析与源码探索 本文以Android 12.0源码为基础,进行探究。上述场景在不同Android版本上的表现是一致的。 3.1 源码调试注意事项 源码的调试方法,许多文章已经有了详细的教学,本文不再赘述。此处只简单总结其中需要注意的事项: 下载模拟器时,不要使用Google Play版本,该版本类似user版本,无法选择system_process进程进行断点。 即使是Google官方模拟器和源码,在断点时,也会有行数严重不对应的情况(比如:模拟器实际会运行到方法A,但在源码中打断点时,实际不能定位到方法A的对应行数),该问题并没有很好的处理方法,只能尽量规避,如使模拟器版本与源码版本保持一致、多打一些断点增加关键行数被定位到的几率。 3.2 初步断点,明确启动结果 以【场景0】为例,我们初步确认一下,为什么B-3跳转B-2会无反应,系统是否告知了原因。 3.2.1 明确启动结果及其来源 在Android源码的断点调试中,常见的有两类进程:应用进程和system_process进程。 在应用进程中,我们能获取到应用启动结果的状态码result,这个result用来告诉我们启动是否成功。涉及堆栈如下图(标记1)所示: Activity类::startActivity() → startActivityForResult() →Instrumentation类::execStartActivity(), 返回值result则是ATMS (ActivityTaskManagerService)执行的结果。 如上图(标记2)标注,ATMS类::startActivity()方法,返回了result=3。 在system_process进程中,我们看一下这个result=3是怎样被赋值的。略去详细断点步骤,实际堆栈如下图(标注1)所示: ATMS类::startActivity() →startActivityAsUser() →ActivityStarter类::execute() →executeRequest() →startActivityUnchecked() → startActivityInner() → recycleTask(),在recycleTask()中返回了结果。 如上图(标注2)所示,result在mMovedToFront=false时被赋值,即result=START_DELIVERED_TO_TOP=3,而START_SUCCESS=0才代表创建成功。 看一下源码中对START_DELIVERED_TO_TOP的说明,如下图: Result for IActivityManaqer.startActivity: activity wasn't really started, but the given Intent was given to the existing top activity. (IActivityManaqer.startActivityActivity的结果:Activity并未真正启动,但给定的Intent已提供给现有的顶层Activity。) “Activity并未真正启动”——是的,因为可以复用 “给定的Intent已提供给现有的顶层Activity”——实际没有,顶层Activity3并没有收到任何回调,onNewIntent()未执行,甚至尝试通过Intent::putExtra()传入新的参数,Activity3也没有收到。官方文档又带给了我们一个疑问点?我们把这个问题记录下来,在后面分析。 满足什么条件,才会造成 START_DELIVERED_TO_TOP的结果呢?笔者的思路是,通过与正常启动流程对比,找出差异点。 3.3 过程断点,探索启动流程 一般来说,在定位问题时,我们习惯通过结果反推原因,但反推的过程只能关注到与问题强关联的代码分支,并不能能使我们很好地了解全貌。 所以,本节内容我们通过顺序阅读的方法,正向介绍startActivity过程中与上述【场景01234】强相关的逻辑。再次简述一下: 【场景0】同一个Task内,从顶部B-3跳转B-2——停留在B-3 【场景1】从另一个Task内的C-4,跳转B-2——跳转到B-3 【场景2】把场景1中,C-4跳转B-2的方式改为setClassName()——创建新B-2实例 【场景3】把场景1中,C-4跳转B-2改为跳转B-3——创建新B-3实例 【场景4】给场景3中的B-3,指定taskAffinity——创建新Task和新B-3实例 3.3.1 流程源码概览 源码中,整个启动流程很长,涉及的方法和逻辑也很多,为了便于大家理清方法调用顺序,方便后续内容的阅读,笔者将本文涉及到的关键类及方法调用关系整理如下。 后续阅读中如果不清楚调用关系,可以返回这里查看: // ActivityStarter.java ActivityStarter::execute() { executeRequest(intent) { startActivityUnchecked() { startActivityInner(); } } ActivityStarter::startActivityInner() { setInitialState(); computeLaunchingTaskFlags(); Task targetTask = getReusableTask(){ findTask(); } ActivityRecord targetTaskTop = targetTask.getTopNonFinishingActivity(); if (targetTaskTop != null) { startResult = recycleTask() { setTargetRootTaskIfNeeded(); complyActivityFlags(); if (mAddingToTask) { return START_SUCCESS; //【场景2】【场景3】从recycleTask()返回 } resumeFocusedTasksTopActivities() return mMovedToFront ? START_TASK_TO_FRONT : START_DELIVERED_TO_TOP;//【场景1】【场景0】从recycleTask()返回 } } else { mAddingToTask = true; } if (startResult != START_SUCCESS) { return startResult;//【场景1】【场景0】从startActivityInner()返回 } deliverToCurrentTopIfNeeded(); resumeFocusedTasksTopActivities(); return startResult; } 3.3.2 关键流程分析 (1)初始化 startActivityInner()是最主要的方法,如下列几张图所示,该方法会率先调用setInitialState(),初始化各类全局变量,并调用reset(),重置ActivityStarter中各种状态。 在此过程中,我们记下两个关键变量mMovedToFront和mAddingToTask,它们均在此被重置为false。 其中,mMovedToFront代表当Task可复用时,是否需要将目标Task移动到前台;mAddingToTask代表是否要将Activity加入到Task中。 (2)计算确认启动时的flag 该步骤会通过computeLaunchingTaskFlags()方法,根据launchMode、来源Activity的属性等进行初步计算,确认LaunchFlags。 此处重点处理来源Activity为空的各类场景,与我们上文中的几种场景无关,故不再展开讲解。 (3)获取可以复用的Task 该步骤通过调用getReusableTask()实现,用来查找有没有可以复用的Task。 先说结论:场景0123中,都能获取到可以复用的Task,而场景4中,未获取到可复用的Task。 为什么场景4不可以复用?我们看一下getReusableTask()的关键实现。 上图(标注1)中,putIntoExistingTask代表是否能放入已经存在的Task。当flag含有NEW_TASK且不含MULTIPLE_TASK时,或指定了singleInstance或singleTask的launchMode等条件,且没有指定Task或要求返回结果 时,场景01234均满足了条件。 然后,上图(标注2)通过findTask()查找可以复用的Task,并将过程中找到的栈顶Activity赋值给intentActivity。最终,上图(标注3)将intentActivity对应的Task作为结果。 findTask()是怎样查找哪个Task可以复用呢? 主要是确认两种结果mIdealRecord——“理想的ActivityRecord” 和 mCandidateRecord——"候选的ActivityRecord",作为intentActivity,并取intentActivity对应的Task作为复用Task。 什么ActivityRecord才是理想或候选的ActivityRecord呢? 在mTmpFindTaskResult.process()中确认。 程序会将当前系统中所有的Task进行遍历,在每个Task中,进行如上图所示的工作——将Task的底部Activity realActivity与目标Activity cls进行对比。 场景012中,我们想跳转Activity2,即cls是Activity2,与Task底部的realActivity2相同,则将该Task顶部的Activity3 r作为“理想的Activity”; 场景3中,我们想跳转Activity3,即cls是Activity3,与Task底部的realActivity2不同,则进一步判断task底部Activity2与目标Activity3的栈亲和行,具有相同亲和性,则将Task的顶部Activity3作为“候选Activity”; 场景4中,所有条件都不满足,最终没能找到可复用的Task。在执行完getReusableTask()后将mAddingToTask赋值为true 由此,我们就能解释【场景4】中,新建了Task的现象。 (4)确定是否需要将目标Task移动到前台 如果存在可复用的Task,场景0123会执行recycleTask(),该方法中会相继进行几个操作:setTargetRootTaskIfNeeded()、 complyActivityFlags()。 首先,程序会执行 setTargetRootTaskIfNeeded(),用来确定是否需要将目标Task移动到前台,使用mMovedToFront作为标识。 在【场景123】中,来源Task和目标Task是不同的,differentTopTask为true,再经过一系列Task属性对比,能够得出mMovedToFront为true; 而场景0中,来源Task和目标Task相同,differentTopTask为false,mMovedToFront保持初始的false。 由此,我们就能解释【场景0】中,Task不会发生切换的现象。 (5)通过对比flag、Intent、Component等确认是否要将Activity加入到Task中 还是在【场景0123】中,recycleTask()会继续执行complyActivityFlags(),用来确认是否要将Activity加入到Task中,使用mAddingToTask作为标识。 该方法会对FLAG_ACTIVITY_NEW_TASK、 FLAG_ACTIVITY_CLEAR_TASK、 FLAG_ACTIVITY_CLEAR_TOP等诸多flag、Intent信息进行一系列判断。 上图(标注1)中,会先判断后续是否需要重置Task,resetTask,判断条件则是FLAG_ACTIVITY_RESET_TASK_IF_NEEDED,显然,场景0123的resetTask都为false。继续执行。 接着,会有多种条件判断按顺序执行。 在【场景3】中,目标Component(mActivityComponent)是B-3,目标Task的realActivity则是B-2,两者不相同,进入了resetTask相关的判断(标注2)。 之前resetTask已经是false,故【场景3】的mAddingToTask脱离原始值,被置为true。 在【场景012】中,相对比的两个Activity都是B-2(标注3),可以进入下一级判断——isSameIntentFilter()。 这一步判断的内容就很明显了,目标Activity2的已有Intent 与 新的Intent做对比。很显然,场景2中由于改为了setClassName跳转,Intent自然不一样了。 故【场景2】的mAddingToTask脱离原始值,被置为true。 总结看一下: 【场景123】的mMovedToFront最先被置为true,而【场景0】经历重重考验,保持初始值为false。 ——这意味着当有可复用Task时,【场景0】不需要把Task切换到前列;【场景123】需要切换到目标Task。 【场景234】的mAddingToTask分别在不同阶段被置为true,而【场景01】,始终保持初始值false。 ——这意味着,【场景234】需要将Activity加入到Task中,而【场景01】不再需要。 (6)实际启动Activity或直接返回结果 被启动的各个Activity会通过resumeFocusedTasksTopActivities()等一系列操作,开始真正的启动与生命周期的调用。 我们关于上述各个场景的探索已经得到答案,后续流程便不再关注。 四、问题修复及遗留问题解答 4.1 问题修复 既然前面总结了这么多必要条件,我们只需要破坏其中的某些条件,就可以修复业务中遇到的问题了,简单列举几个的方案。 方案一:修改flag。B-3跳转B-2时,增加FLAG_ACTIVITY_CLEAR_TASK或FLAG_ACTIVITY_CLEAR_TOP,或者直接不设置flag。经验证可行。 方案二:修改intent属性,即【场景2】。A-1通过action方式隐式跳转B-2,则B-3可以通过setClassName方式,或修改action内属性的方式跳转B-2。经验证可行。 方案三:提前移除B-2。B-2跳转B-3时,finish掉B-2。需要注意的是,finish()要在startActivity()之前执行,以避免遗留的ActivityRecord和Intent信息对后续跳转的影响。尤其是当你把B-2作为自己应用的deeplink分发Activity时,更值得警惕。 4.2 遗留问题 还记得我们在文章开端的某个疑惑吗,为什么没有回调onNewIntent()? onNewIntent() 会通过deliverNewIntent()触发,而deliverNewIntent()仅通过以下两个方法调用。 complyActivityFlags()就是上文3.3.1.5中我们着重探讨的方法,可以发现complyActivityFlags()中所有可能调用deliverNewIntent()的条件均被完美避开了。 而deliverToCurrentTopIfNeeded()方法则如下图所示。 mLaunchFlags和mLaunchMode,无法满足条件,导致dontStart为false,无缘 deliverNewIntent()。 至此,onNewIntent()的问题得到解答。 五、结语 通过一系列场景假设,我们发现了许多出乎意料的现象: 文档提到FLAG_ACTIVITY_NEW_TASK等价于singleTask,与事实并不完全如此,只有与其他flag搭配才能达到相似的效果。这一flag的注释非常片面,甚至会引发误解,单一因素无法决定整体表现。 官方文档提到START_DELIVERED_TO_TOP会将新的Intent传递给顶层Activity,但事实上,并不是每一种START_DELIVERED_TO_TOP都会把新的Intent重新分发。 同一个栈底Activity,前后两次都通过action或都通过setClassName跳转到时,第二次跳转竟然会失败,而两次用不同方式跳转时,则会成功。 单纯使用FLAG_ACTIVITY_NEW_TASK时,跳栈底Activity和跳同栈内其他Activity的效果大相径庭。 业务中遇到的问题,归根结底就是对Android栈机制不够了解造成的。 在面对栈相关的编码时,开发者务必要想清楚,承担新开应用栈的Activty在应用全局承担怎样的使命,要对Task历史、flag属性、launchMode属性、Intent内容等全面评估,谨慎参考官方文档,才能避免栈陷阱,达成理想可靠的效果。 END 猜你喜欢 Android系统服务DropBoxManagerService详解与实践应用 vivo官网App模块化开发方案-ModularDevToo 本文分享自微信公众号 - vivo互联网技术(vivoVMIC)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

Nginx 面试中最常见的 18 道题!

公众号关注“杰哥的IT之旅”,选择“星标”,重磅干货,第一时间送达! 作者:CodeMan 来源:https://segmentfault.com/a/1190000010677483 Nginx的并发能力在同类型网页服务器中的表现,相对而言是比较好的,因此受到了很多企业的青睐,我国使用Nginx网站的知名用户包括腾讯、淘宝、百度、京东、新浪、网易等等。Nginx是网页服务器运维人员必备技能之一,下面为大家整理了一些比较常见的Nginx相关面试题,仅供参考: 1、请解释一下什么是Nginx? Nginx是一个web服务器和反向代理服务器,用于HTTP、HTTPS、SMTP、POP3和IMAP协议。 2、请列举Nginx的一些特性。 Nginx服务器的特性包括: 反向代理/L7负载均衡器 嵌入式Perl解释器 动态二进制升级 可用于重新编写URL,具有非常好的PCRE支持 3、请列举Nginx和Apache 之间的不同点 4、请解释Nginx如何处理HTTP请求。 Nginx使用反应器模式。主事件循环等待操作系统发出准备事件的信号,这样数据就可以从套接字读取,在该实例中读取到缓冲区并进行处理。单个线程可以提供数万个并发连接。 5、在Nginx中,如何使用未定义的服务器名称来阻止处理请求? 只需将请求删除的服务器就可以定义为: Server{listen80;server_name““;return444;} 这里,服务器名被保留为一个空字符串,它将在没有“主机”头字段的情况下匹配请求,而一个特殊的Nginx的非标准代码444被返回,从而终止连接。 6、使用“反向代理服务器”的优点是什么? 反向代理服务器可以隐藏源服务器的存在和特征。它充当互联网云和web服务器之间的中间层。这对于安全方面来说是很好的,特别是当您使用web托管服务时。 7、请列举Nginx服务器的最佳用途。 Nginx服务器的最佳用法是在网络上部署动态HTTP内容,使用SCGI、WSGI应用程序服务器、用于脚本的FastCGI处理程序。它还可以作为负载均衡器。 8、请解释Nginx服务器上的Master和Worker进程分别是什么? Master进程:读取及评估配置和维持 Worker进程:处理请求 9、请解释你如何通过不同于80的端口开启Nginx? 为了通过一个不同的端口开启Nginx,你必须进入/etc/Nginx/sites-enabled/,如果这是默认文件,那么你必须打开名为“default”的文件。编辑文件,并放置在你想要的端口: Like server { listen 81; } 10、请解释是否有可能将Nginx的错误替换为502错误、503? 502 =错误网关 503 =服务器超载 有可能,但是您可以确保fastcgi_intercept_errors被设置为ON,并使用错误页面指令。 Location/{fastcgi_pass127.0.01:9001;fastcgi_intercept_errorson;error_page502=503/error_page.html;#…} 11、在Nginx中,解释如何在URL中保留双斜线? 要在URL中保留双斜线,就必须使用merge_slashes_off; 语法:merge_slashes [on/off] 默认值: merge_slashes on 环境: http,server 12、请解释ngx_http_upstream_module的作用是什么? ngx_http_upstream_module用于定义可通过fastcgi传递、proxy传递、uwsgi传递、memcached传递和scgi传递指令来引用的服务器组。 13、请解释什么是C10K问题? C10K问题是指无法同时处理大量客户端(10,000)的网络套接字。 14、请陈述stub_status和sub_filter指令的作用是什么? Stub_status指令:该指令用于了解Nginx当前状态的当前状态,如当前的活动连接,接受和处理当前读/写/等待连接的总数 Sub_filter指令:它用于搜索和替换响应中的内容,并快速修复陈旧的数据 15、解释Nginx是否支持将请求压缩到上游? 您可以使用Nginx模块gunzip将请求压缩到上游。gunzip模块是一个过滤器,它可以对不支持“gzip”编码方法的客户机或服务器使用“内容编码:gzip”来解压缩响应。 16、解释如何在Nginx中获得当前的时间? 要获得Nginx的当前时间,必须使用SSI模块、date_local的变量。 Proxy_set_header THE-TIME $date_gmt; 17、用Nginx服务器解释-s的目的是什么? 用于运行Nginx -s参数的可执行文件。 18、解释如何在Nginx服务器上添加模块? 在编译过程中,必须选择Nginx模块,因为Nginx不支持模块的运行时间选择。 如果您觉得这篇文章对您有点用的话,麻烦您为本文来个四连:转发分享、点赞、点在看、留言,因为这将是我写作与分享更多优质文章的最强动力! 本公众号全部博文已整理成一个目录,请在公众号后台回复「m」获取! 推荐阅读: 1、 GitHub 标星 5.9K+!1250 个运维工程师面试问题,看这个开源项目就够了! 2、 太赞了!一份适合程序员的精选面试题清单。 3、 想要入职腾讯,后端开发工程师必会的面试题。 4、 IT运维面试问题总结-LVS、Keepalived、HAProxy、Kubernetes、OpenShift等 5、 IT运维面试问题总结-数据库、监控、网络管理(NoSQL、MongoDB、MySQL、Prometheus、Zabbix) 6、 IT运维面试问题总结-运维工具、开源应用(Ansible、Ceph、Docker、Apache、Nginx等) 7、 IT运维面试问题总结-基础服务、磁盘管理、虚拟平台和系统管理 8、 IT运维面试问题总结-Linux基础 关注微信公众号「 杰哥的IT之旅」,后台回复「 1024」查看更多内容,回复「 加群」 备注:地区-职业方向-昵称即可加入读者交流群。 点个[在看],是对杰哥最大的支持! 本文分享自微信公众号 - 杰哥的IT之旅(Jake_Internet)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

8道Python基础面试练习题

1.26个字母大小写成对打印,例如:Aa,Bb...... for i in range(26): print(chr(65+i)+chr(97+i)) 2.一个list包含10个数字,然后生成一个新的list,要求新的list里面的数都比之前的数多1 list=[2,3,6,4,7,5,1,8,9,0] list1=[] for i in list: list1.append(i+1) print(list1) 3.倒序取出每个单词的第一个字母,例如:I am a good boy!方法1 tre='I am a good boy!' t=tre.split() #print(t) t.reverse() list=[] #print(t) for i in t: list.append(i[0]) print(list) 方法2 a = "I AM A BOY" result = [] for i in a.split()[::-1]: result.append(i[0]) print(result) 4.输入一个自己的生日月份,用if和else判断一下当月是不是你的生日月第一种方法,datetime模块获取时间 import datetime date=datetime.datetime.now() #获取当前时间 # print(date.strftime('%Y-%m-%d')) #把当前时间格式化为可读懂的年月日 r=date.strftime('%m') #把当前时间格式化为可读懂的年月日,只取月份 print(r) #Python学习交流QQ群:579817333 t=input('请输入自己的生日月份:') if t==r: print('true') else: print('不是') 第二种方法,time模块获取时间 import time # date=time.time() #获取当前时间 # print(date) # print(time.localtime(time.time()))#按固定格式显示当前时间 # print(time.strftime('%Y-%m-%d')) #把当前时间格式化为可读懂的年月日 # print(time.strftime('%Y-%m-%d',time.localtime(time.time()))) #把时间格式化为可读懂的年月日,后一个参数可省略 # print(time.strftime('%m',time.localtime(time.time()))) #只取月儿份 #t=time.strftime('%m',time.localtime(time.time()))#只取月儿份 t=time.strftime('%m')#只取月儿份 print(t)#Python学习交流QQ群:579817333 r=input('请输入自己的生日月份:') if t==r: print('true') else: print('不是') 5.输入3个字母:e、a、r,如果输入e,那么推出循环,如果输入a,执行continue,如果输入r,那么再读取一次字母,并打印,用死循环实现。 while True: str = input('请输入三个字母:') if str=='r': print(str) if str=='a': continue if str=='e': break else: print('输入有误') 6.输入3个字母:e、a、r,如果输入e,那么退出循环,如果输入a,执行continue,如果输入r,那么再读取一次字母,并打印,只允许输入三次字母,重复输入的字母不算在内。 count = 0 for i in range(3): letter = input("send a letter%d:"%i) if letter == 'e': break elif letter == 'a': continue elif letter == 'r': count += 1 if count == 2: input("send a letter dddd:") 7.把一个字符串"abcdefg"插入到一个list中,每个字母占一个list中的元素位置,例如: ["a","b","c","d","e","f","g"] ls=["a","b","c","d","e","f","g"] s="abcdefg" lt=[] #插入元素到后边 for i in s : ls.extend(i) print(ls) for i in s: ls.append(i) print(ls) #每个元素都插在第一个,或者说倒序插入列表前边 for i in s: ls.insert(0,i) print(ls) 8.['a','b','c','d','e','f','g']操作这个list,拼出一个字符串"adg" lis=['a','b','c','d','e','f','g'] print(len(lis)) t=lis[0]+lis[int(len(lis)/2)]+lis[-1] print(t) 或 print("".join(lis[::3]))

优秀的个人博客,低调大师

企业安全无间道之抓内鬼

【大咖・来了 第7期】10月24日晚8点观看《智能导购对话机器人实践》 观察这些年的信息泄漏案件比例,内部威胁在快速上升。内鬼的范围其实很多,传统上安全会抓账号泄漏、横向移动之类。但如果是商业间谍、搞破坏、内部欺诈这些行为,安全上基本上没能力管。很多安全同学吐槽,安全在公司不受重视,得不到资源,在我看来,是安全目前做的这些事相对于公司的大风险来看,太小,在高层那里不受重视理所当然,当安全有能力为公司发现、收敛更大的风险,地位当然会上升。如果你有能力抓内鬼,汇报层级也会直线上升。本文要和大家说的就是:怎么抓内部威胁。 一、内鬼动机及范围 内鬼的动机一般包括:搞破坏、窃取数据、欺诈、商业间谍、无意犯错、偶然间装逼犯。内部人员作案一般是一个持续过程,在这个过程中有逐渐变化,最后到事件一次发生,多次得手。 内部人员的范围并非是“纯粹”内部人员,也包括生态上下游合作伙伴、外包、访客等任何具有内部访问权限或数据的人。给一个清晰定义,就是基于知识、访问、信任的角色。 所谓知识,如果一个人知道系统的位置、防御措施可被绕过,则为掌握了相关知识。常见例如系统的开发人员可能知道产品的几个0DAY,离职员工掌握测试系统账号密码等。 从技术角度看,IT系统验证凭证有效性,允许访问资源。因此任何获得凭证的人都可被视为内部人员,也即访问角色。即使系统有多因素认证,内部人员也可把短信验证码之类的验证要素提供给其他人员,所以从这个角度来说,IT系统很难完全防范。 还有一种是信任角色,最简单可理解为你的合作伙伴、外包等人群,也包括内部人员。这些人群获得公司一定程度的信任,可以获得部分权限资源,并且以公司名义活动。例如公司的用户数据如果泄漏,在监管和舆论来看,这就是你的问题,而不是外包或代理商。 通常内部抓到的坏人处于公司形象的问题不会公开,而由于不会公开,所以实际案例可能比我们看到的多的多。但其实你可以从法院的公开判决文书找到很多案例。对内部坏人的处理逻辑,首先是内部调查,确定性质和行为。再接下来是走司法程序,但事实上很多公司会开除且不声张。 1. 破坏 对IT系统的破坏可能是大家最不重视的环节了,这些人往往都是技术人群,工作中有较高的系统权限,也是相对信任人群。如果这些人准备删库跑路,实施起来很容易,业界此类案例屡见不鲜。 动机主要是报复,不管是什么原因,总之是员工期望没有得到满足,可能是加薪升职,也可能是绩效,也可能是和主管关系不好。案件一般发生在离职前后,有些情况是在系统放入后门,离职后进行操作,例如前阵时间芜湖某网管的案件,就是掌握了远程路由设备的密码,然后更改配置进行了破坏。其结果一般导致可用性、完整性被破坏。 2. 数据窃取 对很多公司来说,数据或核心资料泄漏是最大担忧,这一类案件层出不穷,从世界巨头商业公司到政府部门。大公司数据泄漏还能活下来,很多小公司因为一个配方、工艺的泄漏,可能就结束了。设计师、工程师、程序员和销售最有可能,一般情况下获取的是自己创造的信息。行为上可能发生在离开公司前后60天之内,方式上有很多,邮件、网盘、U盘、拍照、打印等都有可能。 3. 内部欺诈 这是公司里面最大的群体了,跟其他不同的是,其目标是为钱。而这部分里面有相当多是工资比较低的那部分人群,非专业、非技术人员。由于对钱的需求,所以这些行为可能持续较长时间,如果有一个中低层主管参与的团伙,则更容易获得成功。内部欺诈是破坏公司现有流程实现的,例如客服向用户发放红包,就存在内外勾结的可能。除此之外,特权比较多的人员也是其中一个群体。 另外常见的一种情况是贩卖用户个人敏感信息,例如房产公司销售会把用户手机号卖给装修公司。个人敏感信息相对套现比较容易,需求方也明确,获取难度也不大。 4. 商业间谍 不要觉得商业间谍是一个遥远的事情,在当前的商业竞争形势下,各种套取信息、混入内部的案件比比皆是,只不过被公开报道出来的比较少。商业间谍不是在电视上看到的那种高大上间谍场景,又是色诱又是富家子弟什么的。现实中他们既有可能来自竞争对手,也有可能来自黑产,例如一个某宝店铺,雇用了一个员工,这个员工有相当多的某宝运营经验,在获取了用户地址、联系方式等信息后即辞职,去了下一家。而这个员工,就是间谍的一种,专门获取信息获利。 商业间谍在作案时间上和其他不同,他们可能偶然活跃一次,然后沉静下来,直到下一次。 5. 无意威胁 前面的关注都是怀有恶意的内部人员,无意威胁是那些可能没有恶意动机,但行为会给攻击者提供入口,或对安全态势产生负面影响的人。例如乱下载软件,引入病毒木马,被人社工,U盘笔记本丢失等,都在这类范围内。 6. 装逼犯 这类人群的特点是爱炫耀,尤其如果自己在一家知名大公司工作,为了向人证明自己有内部消息,位高权重等。例如某互联网大厂的内部论坛,就曾有人截图八卦。还有一些泄漏公司通告、张贴自己工资表、利用权限查询男女朋友数据的爱好者。 二、内鬼捕获思路 1. 复杂性 内鬼这事不是一个简单的技术、金钱需求问题,和外部环境、诱惑交织在一起。这些外部环境包括: 内外勾结,内部人员可能一开始是个好人,后来开始为竞争对手、黑灰产等工作。 合作伙伴,合作伙伴手上有大量信息,基于某些业务,可能掌握的是核心信息。 组织架构调整,比如公司被收购、裁员重组等,会对员工产生心理预期的不可预测性,尤其在员工利益受损时,变“坏”的可能性变大。 跨国公司的文化差异,不同国家的宗教信仰、政治态度区别很大,典型如Google前阵时间的某项目,就因为某种原因而被泄漏给了媒体,导致项目终止。 黑灰产,员工参与黑灰产也是一个信号,黑产与内部员工的联系程度如何?员工是否羊毛党爱好者,这些都增加了风险。 2. 威胁时间线检测 内鬼具有一些共同特征,这些特征出现在访问日志、流量、文件等地方,和正常活动混杂在一起,导致大量误报,这是需要解决的问题。特征分布在各个系统日志的时间轴上,需要清洗出来做数据融合,串联起来一个人的行为,这个过程是这里的重活,而且需要多次修正让数据可解释,这取决于数据质量、系统架构、正确方法,当然也需要数据人员的认真细致。 特征分为技术指标和非技术指标两类。非技术指标会涉及HR、法务、管理层等参与,但在这一系列的指标里要注意几点,一是不要因为资历老、级别高就忽略,人是变化的。二是关注心理健康,这方面很多大公司都有心理测试和定期心理辅导。三是对员工应有人道关怀的理解和帮助,而不是简单的指责惩罚。千万不要把这事变成官僚主义的形式,这样不但不能帮助,而且会让员工产生逆反心理。例如员工绩效辅导,就不该是办公室里走个过场,而是需要至少1个小时以上的一对一聊天。 每个人对工作、生活看法都千差万别,HR和leader的工作职责中需要了解个人风格,对工作的期望和目标,对周围同事和上级的看法。当技术指标发生变化时,就需要人工干预防止恶化,因此要把两类指标结合起来,起到预防、检测、响应的作用。 搞破坏、泄漏数据和内部欺诈的人,在时间线上是不同的,根据这个特征可以更好地发现异常,在关键节点加强监控。 3. 建设路线 真的要去做这件事,不是安全技术部门负责这么简单,需要有组织保障。信息安全的这些技术,也不足以保障。 抓一个坏人,可能涉及到内控、信息安全、内部监察、内控、廉政、HR等部门,具体落在哪个部门取决于内部博弈,但一般企业内不会是先设立这么一个组织再来开展活动,而是谁能干这件事,责任就落在谁头上。但整体上是一个跨部门工作组才能完成的工作。 另外,这个团队需要高层授权,解决“谁来监视监视者”的问题。这个组的工作是保密的,因此需要管理好信任,确保监视者会受到监视,因为这个组掌握的信息太多太敏感。你可以简单理解为“东厂”角色,但又不能像东厂那样不受约束、大张旗鼓、人人自危。 解决前面的问题之后,接下来的路线就是: 建立风险处理制度,建立识别评估方法。 提升相关人员的能力。 培训演练,提高员工安全意识。 启动调查的程序 有一些具体操作上需要特别列出来的注意事项: (1) 背调 员工入职一般都有背景调查,但这个是静态的,只是在入职时由外包进行调查。一旦本人发生变化,以前的背调就没什么用了。 (2) 纵深防御 信息安全领域的常见做法,但在管理上也需要有纵深防御。 (3) 员工满意度 员工满意度跟公司规模有关,公司越大江湖越深,不满度可能越高,不满度指标会间接产生影响。 (4) 内部特权人员 特权用户掌握了一些敏感关键权限,并且知道如何绕过监控,对抗调查。所以就是刚才这个谁来监视监视者的问题。这需要公司组织架构上有互相制衡的能力。 (5) 安全规则必定被绕过 在商业组织里,安全是一个支撑角色,赚钱才是核心业务。而安全措施叠加,必定会在一定程度上降低效率,由于效率原因,安全规则也一定不被完全遵守。要么是以免打扰的方式实现安全,要么就要让违规受到必要的惩戒,实际工作中是两者结合使用。 (6) 无意行为危害 无意行为危害更为常见,例如DLP抓到的外发,大量都是业务需要的非故意外发行为,真正的坏人可能就隐藏在这里而被淹没。这需要靠安全意识教育、直接触达的警告来强化安全。 三、检测指标 发现内鬼可通过不同维度的指标监测,指标异常引发报警,从而提升某个员工的关注度。 1. 个人情况指标 个人情况指标可能不会直接造成损害,但会是很多事情的诱因。 这里的最大问题是,你可能无法掌握员工的变化情况。这些信息可能会被他周围的同事和HR知道,需要打通这个信息渠道。例如精神类疾病在职场中常见的是抑郁症,会导致无意犯错、破坏发泄,理论上可以在每年的体检报告上获取这个信息,但这属于侵犯个人隐私,在强保密体系下可以关注使用。另一个重点是绩效为差的员工,待离职员工,这些都带有强烈的离职动机,从而导致窃取数据、搞破坏,这些数据是可以通过HR系统检测到的指标。 2. 背景及行为指标 背景侧重于历史记录,很多公司把敏感岗位背调作为招聘必选项。行为则是根据员工工作上的行为方式逐步形成。 参与某些团体指的是例如国泰航空前阵时间的事件,参与了社会事件而带来的对飞行安全的破坏、泄漏用户信息。而在犯罪前科上,要兼顾考虑各类外包人员。背调不只是在入职前进行,在晋升时也需要进行。其他类型不再赘述。 3. 信息安全指标 内部欺诈是利用工作流程,掌握了规则后获利行为,例如风控部门的员工,就可能掌握规则从而绕过获利,检测上很难发现,但可以通过其他维度,例如与情报、钓鱼、黑灰产关联等。而数据窃取则可能有一些对抗绕过,比如对数据加密,使用代理等,可以根据基线、阈值来做关联判断。商业间谍则考虑账号、设备、竞对关联、行为。 4. 终端指标 终端是指用户的终端电脑、手机等,由于员工可以对终端进行操作,所以他可能会篡改数据,破坏监控agent,因此要额外检测agent和日志的运行情况,尤其是当员工有离职等不良倾向的时候需要重点关联检测。 数据窃取可通过打印、复制外发文件,把这个日志和背景行为指标关联,可以监测到数据窃取、商业间谍行为。内部人员如果登陆其他同事账号,目的可能是隐藏自己,提升权限,又或者代其他员工操作。多次登陆失败则说明账号正在被暴力破解。终端多用户登录代表的风险则更大,但要注意有些例如三班倒的工作岗位、测试岗位会存在公用设备现象,但排除这些岗位也行后,其他人员需要重点关注。当然还有其他维度,例如非正常工作时间,只不过在互联网公司这个太常见了,所以没有加入特征。 对终端的检测几乎发现不到什么内部欺诈,欺诈行为一般出现在业务层。 5. 服务端指标 对应的则是用户在服务端的操作行为: 对集中存放的审计日志进行修改是个明确信号,有人在试图抹掉痕迹。同账号多设备则表明账号可能被泄漏,也可能是横向移动攻击。 四、总结 以上所有的指标,单一来看可能只是一个异常,因此需要多指标关联权重,从而提炼出真正的风险。但指标不仅限于此,可以根据自身业务数据形成更广阔的检测维度,例如一个销售,从来不上传新合同,但总是在大量查询历史合同。某个员工的手机号和采购供应商相同,员工与黑产多次出现在同一地址,同一时间维度内同WIFI出现大量注册等等诸如此类的规则,都能够形成某个单项指标。 除了自身数据,也可接入外部数据验证,例如员工是否多头借贷,历史工作单位验证是否一致等。最后,还可以利用情报数据,反向验证内部人员作案。 空间很大,可做的事情有很多,不要自己给信息安全设置了边界。

优秀的个人博客,低调大师

PaaS下半场,任重且道远

2019年已经到来,PaaS经过懵懂的2015年,觉醒的2016年,试水的2017年,在2018年迎来第一个小高潮。 PaaS(Platform-as-a-Service,平台即服务)是将数据库、开发工具和其他支持应用服务的组件整合为一个平台作为服务提供给开发者,从而使得开发者无需关注应用程序运行和维护时所需要的资源,用户远程使用开发者开发的应用软件。作为云计算模型中的能力层,PaaS可以帮助企业更好地将业务与IT融为一体,从而也成为传统企业数字化转型,进行架构升级的必然选择。 PaaS是当前云计算增长最快的细分市场当前全球云计算三种交付模式中,PaaS份额最小,但增速最快。另一家市场研究公司Gartner则将PaaS细分成几十个领域,每一个细分领域在技术上均走上成熟,开始进入应用和产业化阶段。据前瞻产业研究院发布的《中国云计算产业发展前景与投资战略规划分析报告》统计数据显示,2012年全球PaaS市场规模为12亿美元。2014年全球PaaS市场规模超33亿美元。到了2016年全球PaaS市场规模快速增长至90亿美元。截止到2017年全球PaaS市场规模已经突破100亿美元,达到了114亿美元,同比增长26.7%。 PaaS服务与开发过程中的需求密切相关,细分领域众多。从部署的角度可分为数据库服务、应用开发、应用基础架构、中间件服务,其中APaaS、IPaaS占比较大。 云计算服务交付模式主要有IaaS、PaaS、SaaS三类:IaaS(Infrastructure-as-a-Service,基础设施即服务)是指通过互联网获得计算、存储资源,服务商不参与后续的操作系统、中间件及应用程序开发。 SaaS(Software-as-a-Service,软件即服务)是指通过互联网提供特定的软件产品,服务商介入程度最深。而PaaS是指通过互联网获得软件开发环境及中间件,服务商不参与后续的应用程序开发。 PaaS在云服务产业链中具备承上启下作用。云服务产业链中,位于底部(产业链上游)的一层可以为其上面(产业链下游)的任何一层或者终端客户提供服务。虽然PaaS有众多的细类,不管是哪一个细类,在成熟度上都落后于SaaS和IaaS,其市场发展相对滞后,刚刚到达快速发展期的顶端,但其基础理论基本成熟,即将进入产业化规模的爬坡期阶段。 2018年国内外PaaS市场发展现状目前国内外发展PaaS业务平台企业大多具有行业优势,例如Google、AWS和新浪则凭借其在互联网领的垄断地位将其业务纵向发展到PaaS云计算领域,而Microsoft、Oracle、IBM则凭借其软件行业的领先地位横向发展到PaaS云计算业务中,其他企业发展PaaS业务则较少。 据2017年全球PaaS市场份额比例统计情况来看,AWS、Microsoft、IBM、Salesforce是如今PaaS市场的4个领头羊。分析海外PaaS四大龙头,可以看到:(1)均为平台型PaaS,较强的综合开发能力是其强项所在;(2)上游IaaS、下游SaaS企业跨界具备天然优势。 单看国内PaaS市场,由于我国云计算市场处于起步阶段,PaaS细分市场份额较低。2014年我国PaaS市场规模仅为26亿元,2017年市场规模达到54亿元。 目前,国内平台型PaaS主要以巨头为主,以百度、新浪、阿里、京东为代表。阿里云位居第一且其在PaaS市场的地位难以撼动。其他PaaS服务商份额均很小,市场高度碎片化,目前有电信运营商、互联网巨头、传统软件企业、创业企业四类企业布局。 PaaS未来发展趋势预测1、业务量增长驱动更多PaaS部署:PaaS云计算技术将可提供灵活、可控、高效的业务支持,满足企业不断增长的业务发展需要。 2、IaaS提供商将涵盖PaaS IT:为基础架构提供交付的IaaS技术将逐渐成为一种市场领先的商业模式,为了区别于彼此,IaaS领头羊将会整合一些中间件技术,或者PaaS提供商基于堆栈下层构建绕过它们。 3、公有PaaS将赢得中小企业市场:低廉的订购价格和标准化的中间件服务解决方案将最终推动中小企业市场致力于公共PaaS. 4、私有PaaS将夺得大企业市场:大型企业势必融入云端。但一应俱全的公共PaaS服务存在可靠性问题、安全风险和局限性问题,势必会让这些大企业热衷于私有PaaS.企业云将基于私有PaaS中间件,并最终会寻求一种混合方式来实现。 5、开源PaaS平台将蓬勃发展:随着业务量增加,使得云架构迎来变革,从而出现高度定制化云环境的需求。大企业需要构建于中间件之上的云架构,而企业开发人员和CIO需要可扩展的开源PaaS IT. 6、开源PaaS平台将通过Linux发行版扩展:对开源的PaaS技术而言,Linux解决方案将成为一种更强大的间接发行渠道。 7、PaaS与各大管理工具实现融合:与IaaS一样,PaaS中间件提供商需要灵活高效才能生存下来。PaaS技术(包括开源技术和专有技术)会变得更具有扩展性,并且与Chef、Puppet和Juju等协调和配置管理工具进行融合。 虽然全球PaaS市场起步较晚,但其发展势头强劲。根据Gartner预测,在2020年前PaaS市场的复合增长率将会达到18.24%,增速仅次于IaaS,到2020年规模达到208亿美元。 欢迎转载,转载请注明出处:怡海软件(http://www.frensworkz.com/)

优秀的个人博客,低调大师

护士是患者治愈的一道桥梁!

瑞典隆德大学最新研究表明,医院的护士越少,患者死亡率越高,且该死亡率不会因非护士类护工的替代而降低。智慧医疗应用将改善这一现状! 研究团队于1980年启动该项研究,历时37年,对包括瑞典与英国在内的欧洲9个国家的300家急救医院进行了解和调查。在瑞典,74%的护士表示,她们会因为一些原因丢下一些必须照顾的病人不管,而在英国,这一数字达到了86%。 调查中,护士们被要求回答一份调查问卷,统计他们因倒班、调休等因素无法照顾患者的情况。调查结果显示,便携式医疗仪器对于那些医护人员不足的医院来说,每轮班一次,患者死亡的风险就会增加百分之二。调查中的患者平均由三班医护人员护理,与那些由全职医护人员护理的患者相比,他们的死亡风险上升了不止百分之六。护士对患者看护的越少,患者死亡率也会升高;护士照顾水平不足会直接影响患者的生命。 朗锐慧康对此表示:“护士对于挽救患者生命占据着极大的作用。病人在医院得到的医疗照护如何,不仅仅由治疗医师的水平决定,护理人员也起着不可忽视的作用。因此,医护人员在保证护理质量的同时也应该保证数量。” 现在从医人员不足,医院各项设备资源也不够完善,需要政府和医疗行业从业者的共同努力。朗锐慧康始终致力于提供实用的健康终端产品及技术方案,配套高效可靠的健康信息化平台支撑,帮助行业客户快速实现健康物联网应用实施。根据其方案定制出的多功能健康一体机,能够方便医院对病患的日常检查和护理,包括心电图、尿常规、血压、血氧、体温、脉搏、血糖、检眼镜、检耳镜等嵌入式主板应用检查功能,可开展如居民电子健康档案建立、儿童管理、老年人管理、慢性病管理等国家要求的11项公共卫生管理服务,将对医生和护士提供一定的帮助,减轻了医院人员的负担,患者的康复也能得到更大的保障。 本文由朗锐慧康编辑整理(http://www.lrioh.com)(http://www.lrist.com),转载请注明出处。

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册