`JOB`的正确打开方式

文章目录

  • `JOB`的正确打开方式
    • 简介
    • 工作原理
    • 使用场景
    • 使用方式
    • 注意事项
    • 启动`JOB`失败的情况
    • `JOB`正确打开方式
      • 错误方式
      • 正确方式
      • 进阶方式
      • 终极方式
    • 总结

JOB的正确打开方式

  • 最近有一些小伙伴在使用JOB时,由于使用不当,引起一些问题。例如把license占满,JOB运行的方法有一些没有执行等。

通常我们这样使用Job命令:j ##class(className).methodName(args),实际上这种方式会引起一些未知问题。正确方式见下文。

  • 笔者通过这篇文章把JOB的正确使用方式介绍一下,避免在日后使用JOB时出现如上的一些问题。

简介

JOB 命令用于在操作系统级别启动一个新的进程。该进程可以执行特定的方法。

工作原理

当使用 JOB 命令时,会在当前进程的上下文中启动一个新的进程。新的进程会从特定的代码开始执行,并在该代码或命令返回后退出。

注:因为JOB每次启动一个进程。由于IRIS的特性,单用户启用超过20个进程时,这20个进程将转化为占用对应进程数量的license

注:JOB 命令功能类似于JAVA开启一个线程,在单独的环境中运行程序。但是线程是轻量级的,一个进程可以包含多个线程。进程是重量级的,开销要比线程大。在IRISCaché中,没有线程的概念。如果想使用异步功能,只能由JOB命令,开启一个进程。

使用场景

  • 多进程:开发人员可以使用 JOB 命令来创建新的进程,从而提高应用程序的并发性能和响应能力。例如,可以在后台启动一个长时间运行的任务(如数据导入)而不会阻塞前台服务。
  • 异步:执行第三方应用程序接口:JOB 命令可以在新的进程中直接执行不需要判断返回值且耗时比较长的第三方应用程序接口。

使用方式

  • JOB的简写方式为J,可以使用以下参数方式启动:
JOB:pc routine(routine-params):(process-params):timeout
JOB:pc routine(routine-params)[joblocation]:(process-params):timeout

J:pc ##class(className).methodName(args):(process-params):timeout
J:pc ..methodName(args):(process-params):timeout
J:pc $CLASSMETHOD(className,methodName,args):(process-params):timeout
  1. 简单启用JOB

编写一个需要运行的方法,该方法挂起10秒。

ClassMethod Process()
{
	h 10
	w "执行Process",!
	s ^yx("PID") = $job
}
ClassMethod Job()
{
	w "do执行",!
	d ..Process()
	w "do结束",!
	
	w "job执行",!
	j ..Process()
	w "job结束"
}

在这里插入图片描述

可以看到通过DO运行时,当前进程挂起10秒后继续运行,JOB运行时,该方法为瞬间启动,没有阻塞当前进程。

  • 启动JOB进程后,会为其分配单独的内存分区,并为其分配唯一的JOB编号(也称为进程IDPID)。JOB进程号存储在$JOB特殊变量中。

注:当JOB启动的方法运行完成时,创建的进程也随之销毁。

在这里插入图片描述

  1. 使用JOB调用其他命名空间方法。

示例如下,在其他命名空间创建如下方法:

Class M.Job Extends %RegisteredObject
{

ClassMethod JobOtherNamespace()
{
	s ^yx("JobOtherNamespace") = $i(^yx("JobOtherNamespace"))
}

}

在这里插入图片描述

  • 使用JOB命令时,要指定:(namespace)参数。
ClassMethod JobNameSpace()
{
	j ##class(M.Job).JobOtherNamespace():("yx")
}

如下可以看到,每次启动JOB时,yx命名空间Global自增1

USER>d ##class(M.Job).JobNameSpace()
 
USER>zw ^|"yx"|yx("JobOtherNamespace")
^|"yx"|yx("JobOtherNamespace")=1
 
USER>d ##class(M.Job).JobNameSpace()
 
USER>zw ^|"yx"|yx("JobOtherNamespace")
^|"yx"|yx("JobOtherNamespace")=2
 
USER>d ##class(M.Job).JobNameSpace()
 
USER>zw ^|"yx"|yx("JobOtherNamespace")
^|"yx"|yx("JobOtherNamespace")=3

注意事项

  • JOB只能按值传递参数,不能传递引用参数与多维数组传。如果使用JOB使用引用传递参数,Studio将提示如下错误。

在这里插入图片描述

  • JOB不能使用可变参数。

在这里插入图片描述

  • 不能将对象引用传递给JOB进程。 这是因为对调用上下文中存在的对象的引用不能在新的进程JOB上下文中引用。传递OREF会将空字符串("")传递到JOB进程。
ClassMethod JobObj()
{
	s m = ..%New()
	w "当前对象", m,!
	j ..Obj(m)
}

ClassMethod Obj(m)
{
	s ^yx("Obj") = m
}
USER>d ##class(M.Job).JobObj()
当前对象1@M.Job
 
USER>zw ^yx("Obj")
^yx("Obj")=""
  • 启动Job时需要指定参数timeout - 在超时并中止JOB之前等待JOB进程启动的秒数。前面的冒号是必需的。必须将超时指定为整数值或表达式。如果JOB进程超时,则将停止该进程,并将$test特殊变量设置为0
ClassMethod JobTimeOut()
{
	for i = 1 : 1 : 500 {
		j ..Process()::3
		w "i:",i," $test:",$test,!
	}
}

注:如果启动JOB没有指定超时参数,那么该进程将无限期等待,导致死进程。

在这里插入图片描述

启动JOB失败的情况

  • 内存不足。
  • 超过许可的进程数量。
  • 其他等。

JOB正确打开方式

错误方式

在介绍如何正确使用JOB之前,首先看一下错误的使用方式造成的后果。运行JobTimeOut()方法。

在这里插入图片描述

可以看到当license被当前用户占满,这种情况下,其他正常的请求将都会等待,导致业务卡顿。

在这里插入图片描述

在这里插入图片描述

正确方式

首先需要了解2个系统变量$ZPARENT$ZCHILD

  • $ZPARENT包含JOB处理当前进程的进程的PID,如果当前进程不是通过JOB命令创建的,则为0

Process()方法里增加s ^yx("PARENT") = $zparent

ClassMethod JobParent()
{
	w "$job前:",$job,!
	w "$zparent前:",$zparent,!
	j ..Process()::3
	w "$job后:",$job,!
	w "$zparent后:",$zparent,!
}
USER>d ##class(M.Job).JobParent()
$job前:20568
$zparent前:0
$job后:20568
$zparent后:0
 
USER>zw ^yx("PARENT")
^yx("PARENT")=20568
  • $ZCHILD 包含JOB命令创建的最后一个进程的PID,无论是否成功。
ClassMethod JobChild()
{
	w "$job前:",$job,!
	w "$zchild前:",$zchild,!
	j ..Process()::3
	w "$job后:",$job,!
	w "$zchild后:",$zchild,!
}
USER>d ##class(M.Job).JobChild()
$job前:20568
$zchild前:23940
$job后:20568
$zchild后:25880
 
USER>d ##class(M.Job).JobChild()
$job前:20568
$zchild前:25880
$job后:20568
$zchild后:32308
  • 确认JOB是否启用成功,必须要判断$zchild前后的值与超时。

通过使用$ZCHILD,可以通过比较运行JOB命令前后的$ZCHILD值来确定远程JOB的执行状态。如果BEFOREAFTER值不同,并且AFTER值为非零,则AFTER``$ZCHILD值是新创建的JOBPID,表示进程已成功创建。如果AFTER值为零,或者AFTER值与BEFORE值相同,则不会创建JOB

正确使用JOB的方式如下:

  1. 设置JOB超时参数。
  2. 判断是否超时。
  3. 判断$ZCHILD系统变量前后是否不一致并且不为0
  4. 根据返回值判断JOB是否启动成功。如果不判断JOB是否启动成功,会发生JOB运行的方法有没有执行等问题。
ClassMethod JobCorrect()
{
	s curChildID = $zchild
	j ..Process()::3
	s nowChildID = $zchild
	if '$test {
		q "JOB启动超时" _ " "_  $zchild
	}
	if (nowChildID '= 0)&&(curChildID '= nowChildID){
		q "JOB启动成功" _ " "_  $zchild
	}
	q "JOB启动失败" _ " "_  $zchild
}

注:$ZCHILD只能告诉JOB已创建;不会告诉JOB是否成功运行程序。要确定JOB进程运行的程序是否无错误并运行到完成,最好的做法是在正在运行的代码中提供某种类型的日志记录和错误捕获。JOB机制不会以任何方式通知父进程进程错误或进程终止,无论成功与否。

注:JOB进程之间的通信。首选方案是使用父子进程都知晓的Global。使用Global目的是允许进程之间交换信息。

进阶方式

前面"正确的方式",仅能判断JOB是否正确启动。但无法判断由JOB引起的死进程。

  1. 首先创建一个 通用的JOB程序。
  • 由于JOB不能使用可变参数,所以需要将参数做一次处理转化。
ClassMethod Run(className, methodName, params)
{
	s $zt = "Error"
	if (params '= "") {
		s arg = $ll(params)
		for i = 1 : 1 : arg {
			s arg(i) = $lg(params, i)
		}
	} else {
		s arg = 0
	}	

	d $classmethod(className , methodName, arg...)
	q $$$OK
Error 
	s $zt = ""
	q $ze
}
  • 该方法为JOB启动通用方法,该方法为布尔类型,返回$$$YES代表JOB启动成功,返回$$$NO代表启动失败。
ClassMethod IsRunJob(classname, methodname, arg...) As %Boolean
{
	s $zt = "Error"
	s params = ""
	if ($d(arg)) {
		for i = 1 : 1 : arg {
		   	s params = params _ $lb(arg(i))
		}
	}

	s curChildID = $zchild
	j ..Run(classname, methodname, params)::3
	s nowChildID = $zchild
	if '$test {
		q $$$NO
	}
	if (nowChildID '= 0)&&(curChildID '= nowChildID){
		q $$$YES
	}
	q $$$NO
	
Error 
	s $zt = ""
	q $$$NO
}
  1. 在循环中启动350JOB,将JOB开启的进程全部结束掉防止有出现死进程。
  • 这里将JOB进程进行缓存,用于统一结束。
ClassMethod JobFinishProcess(num)
{
	s array = ""
	for i = 1 : 1 : num {
		s bool = ..IsRunJob("M.Job","Process")
		if (bool) {
			s array(i) = $zchild
			w "进程开启成功:" _ i _ " JOB进程ID:" _ $zchild,!
		} else {
			w "进程开启失败:" _ i _ " JOB进程ID:" _ $zchild,!
		}
	}
	
	d ..TerminateProcess(.array, 0)

	q $$$OK
}
  • TerminateProcess方法,增加了程序挂起时间,可以设定一个阈值,当程序在这个阈值内没有结束,认为为死进程,将其结束掉。
ClassMethod TerminateProcess(ByRef array, time)
{
	h time
	s i = ""
	for {
		s i = $o(array(i))
		q:(i = "")
		s ret = $system.Process.Terminate(array(i))
		s desc = $s(ret = "1" : "成功", ret = "0" : "失败 - 进程无法结束", ret = "-1" : "失败 - 进程没有响应", ret = "-2" : "失败 - 进程处于非活动状态死进程", ret = "-3" : "失败 - 没有此pid进程", ret = "-4" : "失败 - 进程包含开放的事务,该事务当前处于停止或回滚状态。")
		w "结束进程状态:" _ ret _ " " _ desc,!
	}
	q $$$OK
}
  • 如下图可以观察到,成功将没有结束的进程终止掉。

注:实际上结束进程状态:-3 失败 - 没有此pid进程,是缓存的JOB进程ID已经运行完成进程已结束,认为成功。

在这里插入图片描述

代码d ..TerminateProcess(.array, 0)如果设置了阈值时间,例如设置10分钟,会导致生产程序返回结果耗时过久,这里可以将DO调用改为JOB调用。

  • 设置临时Global缓存Job进程ID,通过s bool = ..IsRunJob("M.Job", "TerminateProcessJob", 0)调用后台结束进程程序。
ClassMethod JobFinishProcess1(num)
{
	k ^yx("JobPid")
	s array = ""
	for i = 1 : 1 : num {
		s bool = ..IsRunJob("M.Job","Process")
		if (bool) {
			s ^yx("JobPid", i) = $zchild
			w "进程开启成功:" _ i _ " JOB进程ID:" _ $zchild,!
		} else {
			w "进程开启失败:" _ i _ " JOB进程ID:" _ $zchild,!
		}
	}
	
	s bool = ..IsRunJob("M.Job", "TerminateProcessJob", 0)
	if ('bool) {
		w "终结程序启动失败",!
	}else {
		w "终结程序启动成功",!
	}

	q $$$OK
}
ClassMethod TerminateProcessJob(time)
{
	s ^yx("TerminateProcessJob") = time
	h time
	s i = ""
	for {
		s i = $o(^yx("JobPid", i))
		q:(i = "")
		s ret = $system.Process.Terminate(^yx("JobPid", i))
		s desc = $s(ret = "1" : "成功", ret = "0" : "失败 - 进程无法结束", ret = "-1" : "失败 - 进程没有响应", ret = "-2" : "失败 - 进程处于非活动状态死进程", ret = "-3" : "失败 - 没有此pid进程", ret = "-4" : "失败 - 进程包含开放的事务,该事务当前处于停止或回滚状态。")
		w "结束进程状态:" _ ret _ " " _ desc,!
	}
	q $$$OK
}

终极方式

细心的朋友应该发现进阶方式存在了如下问题:

  • 终止进程方法也使用JOB启用,如果JOB启用失败了,那么死进程还是结束不了。
  • 终止进程方法结束的PID也有问题,如果JOBPID已经完成,其他并发的用户请求在此时间段内又生成了相同的PID进程号,会把正常的业务进程结束掉。
  • 用户启用的JOB数量没有限制,单个用户就能把license占满,导致正常的业务请求失败,应该限制单用户开启的JOB数量,结合当前的license数量,应设置一个阈值,超过此阈值的JOB不允许开启。
  • 如果不判断返回值,如何保证调用的JOB程序全部启用成功,应设置缓存失败的JOB方法与参数,设置后台重试。

这里我已经将代码封装到拓展系统命令ZSAFEJOB中,使用拓展系统命令ZSAFEJOB启用需要后台调用的程序即可。

ZSAFEJOB命令包含功能:

下面演示ZSAFEJOB命令使用方式:

ZSAFEJOB "classname_methodname(params)"
ZSJ "classname_methodname(params)"
  • classname - 类名。
  • methodname - 查询名。
  • params - 使用单引号包裹参数,逗号分割的参数列表。
  • 需要返回值时,可使用%zjob 变量判断Job是否启用成功。

  • 有参数时:需要将参数使用单引号包裹,多个参数使用逗号分割。如下使用ZSJ调用如下Sum方法。
ClassMethod Sum(a, b, c)
{
	s sum = a + b + c
	s ^yx("Sum") = sum
	q sum
}
USER>zsj "M.Job_Sum('1','2','3')"

在这里插入图片描述

  • 无参数时,可省略括号。如下使用ZSJ调用如下NoParams方法。
ClassMethod Process()
{
	s ^yx("NoParams",$i(^yx("NoParams"))) = ""
	h 20
	w "执行Process",!
}

示例Process()方法挂起进程20秒,zsj命令为异步,并没有挂起等待,输出记录的Global验证调用成功。

USER>zsj "M.Job_Process"

在这里插入图片描述

使用其他拓展系统命令可参考如下连接:https://yaoxin.blog.csdn.net/article/details/130157870


  • 验证超过license阈值,使用job失败,防止占用过多license,导致正常业务失败。示例中的阈值为当前license最大数量300*0.8 = 240
ClassMethod SafeJob(num)
{
	k ^yx("JobPid")
	s array = ""
	for i = 1 : 1 : num {
		zsj "M.Job_Process"
		w i," ",%zjob,! 
	}
	q $$$OK
}

在这里插入图片描述

  • 验证将启动失败的Job方法,进行重新调用。如下图示例:238239,启用的JOB失败,然后使用方法JobTask进行重试,观察可发现重试2次成功。

在这里插入图片描述

JobTask方法需要挂任务进行重试,包含以下功能:

  • 删除由Job开启的死进程,判断规则为进程持续时间超过10分钟,则把该进程进行结束。
  • 重试Job启动失败的方法。

在这里插入图片描述

总结

本篇由浅入深讲解了Job命令的使用方式,了解Job命令的原理,可防止一些意外的情况,并推荐使用ZSAFEJOB命令使用JOB命令功能。

以上是个人对JOB命令的一些理解,由于个人能力有限,欢迎大家提出意见,共同交流。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

操作系统第四章——文件管理(下)

竹本无心,却节外生枝,藕却有孔,但出淤泥而不染,人生如梦,却却不随人愿,万般皆是命,半点不由人 文章目录 4.1.5 逻辑结构VS物理结构4.1.6 文件的基本操作知识总览创建文件删除文件打开文件关闭文…

【弹性分布式EMA】在智能电网中DoS攻击和虚假数据注入攻击(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

GPC_APDU_Transport_over_SPI-I2C_v1.0_PublicRelease

GPC_APDU_Transport_over_SPI-I2C_v1.0_PublicRelease.pdf 目录 1 简介 越来越多的设备,如移动设备、可穿戴设备或其他 IoT(物联网)设备现在正在使用焊接安全元件 (SE)。 这产生了支持 SPI 或 I2C 等物理接口的新需求,以代替以前…

Java 反序列化漏洞

反序列化漏洞是指程序在反序列化期间,通过特殊的调用链引发的一系列安全问题。编程语言中只要存在序列化,反序列化功能就可能存在反序列化的安全问题。这里只针对Java和PHP进行讨论。 序列化漏洞概述 序列化的存在主要是为了存储和传输,将这…

如何设置工业设备的振动监测阈值

工业设备的振动阈值设置是确保设备正常运行和及时维护的关键步骤。本文将介绍一些常见的方法和策略,帮助您正确设置工业设备的振动阈值。 1. ISO 10816 振动烈度表格: ISO 10816 是一项国际标准,提供了设备振动水平的参考值。该标准将设备按…

【移动计算技术(Android)】期末复习

目录 选择题 选择题知识点汇总 Activity Intent Broadcast BroadcastReceiver 如何自定义Receiver 如何注册接收器 Service SharedPreferences 三种访问模式 如何创建 如何存储/修改 如何读取 内部存储 openFileOutput openFileInput SD卡 资源文件 SQLite…

Java学习路线(13)——Collection集合类:List集合与Set集合

一、集合类体系结构 二、部分Collection类型对象 Collection集合特点 List系列集合是有序、可重复、有索引。 ArrayList:有序、可重复、有索引LinkedList:有序、可重复、有索引 Set系列集合是无序、不重复、无索引。 HashSet:无序、不重复…

下载YouTube视频的一种方法

文章目录 工具名称下载方法使用方法1.只下载音频2.下载音频转换成mp3(加上-x –audio-format参数)3.下载视频(带音频)ID:22 | EXT:mp4 | 1280*720 下载的数据集:YouCook2 工具名称 yt-dlp 下载…

ajax使用

说明:ajax是一门异步处理请求的技术;可以实现不重新加载整个页面的情况下,访问后台后服务;比如百度搜索引擎,输入关键字后,下面会实时出现待选项,这就是一种异步处理请求的技术。 原生Ajax 原…

chatgpt赋能python:Python中未定义变量的默认值

Python中未定义变量的默认值 在Python编程中,有时候我们会使用未经定义的变量。如果这些变量没有被定义,那么它们将没有任何值。在这篇文章中,我们将讨论Python中未定义变量默认值的问题,并深入研究为什么这些默认值如此重要。 …

如何保证三个线程按顺序执行?不会我教你

👨‍🎓作者:bug菌 ✏️博客:CSDN、掘金、infoQ、51CTO等 🎉简介:CSDN|阿里云|华为云|51CTO等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12…

linux内核内存管理slab

一、概述 linux内存管理核心是伙伴系统,slab,slub,slob是基于伙伴系统之上提供api,用于内核内存分配释放管理,适用于小内存(小于1页)分配与释放,当然大于1页…

基于html+css的图展示99

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

运动员最佳配对问题——算法设计与分析(C实现)

目录 一、问题简述 二、分析 三、代码展示 四、结果验证 一、问题简述 问题描述:羽毛球队有男女运动员各n人。给定2个n*n矩阵P和Q。P[i][j]是男运动员i和女运动员j配对组成混合双打的男运动员竞争优势;Q[i][j]是男运动员i和女运动员j配合的女运动员竞…

SSM框架学习-拦截器

1. 简介 在Spring框架中,拦截器是一种很重要的组件,它们允许在请求到达控制器之前或之后执行一些代码。拦截器在请求处理的特定点进行拦截,然后通过逻辑来决定是否将控制器的处理传递给下一个处理程序。 在Spring中,拦截器是由实现…

KVM虚拟化技术学习-基础入门

1.虚拟化技术概述 虚拟化[Virtualization]技术最早出现在 20 世纪 60 年代的 IBM ⼤型机系统,在70年代的 System 370 系列中逐渐流⾏起来,这些机器通过⼀种叫虚拟机监控器[Virtual Machine Monitor,VMM]的程序 在物理硬件之上⽣成许多可以运⾏…

Codeforces Round 764 (Div. 3)

比赛链接 Codeforces Round 764 A. Plus One on the SubsetB. Make APC. Division by Two and PermutationD. Palindromes ColoringE. Masha-forgetful A. Plus One on the Subset Example input 3 6 3 4 2 4 1 2 3 1000 1002 998 2 12 11output 3 4 1题意: 你可…

计算机网络考试多选题汇总Ⅱ

https://cadyin.blog.csdn.nethttps://blog.csdn.net/qq_38639612?spm1010.2135.3001.5421 计算机网络考试多选题汇总 1、在Windows中,任务管理器的作用是() A.终止未响应的应用程序 B.终止进程的运行 C.查看系统当前的信息 …

车载网络测试 - CANCANFD - 基础篇_01

目录 问题思考: 一、为什么需要总线? 二、什么是CAN总线? 三、为什么是CAN总线? 四、曾经的车用总线 1、SAEJ1850(Class2) 2、SAEJ1708 3、K-Line 4、BEAN 5、 byteflight, K-Bus 6、D2B 五、当前的车用总线 1、CAN 2、LIN 3、FlexRay 4、MOST 六…

python-sqlite3使用指南

python下sqlite3使用指南 文章目录 python下sqlite3使用指南开发环境sqlite3常用APICRUD实例参考 开发环境 vscode ​ 开发语言: python vscode SQLite插件使用方法: 之后在这里就可以发现可视化数据: sqlite3常用API Python 2.5.x 以上…