一种任务的调度方法及相关装置与流程

文档序号:16775546发布日期:2019-02-01 18:42阅读:170来源:国知局
一种任务的调度方法及相关装置与流程

本发明涉及开发平台领域,尤其涉及一种任务的调度方法及相关装置。



背景技术:

对于直播平台来说,需要对直播平台所直播的内容进行审核,以确认直播的内容是正规合法的内容,而不能是色情直播。现有技术中,确认直播的内容是否正规合法,可以通过鉴黄服务框架的机器来进行识别。而实际应用中,对于鉴黄服务框架来说,当直播间同时开播数量比较多时会造成用于鉴别是否违规的请求非常多,并且很有可能同一时间的请求量非常大,这样会导致很多请求超时得不到处理。

那么基于有多台识别服务机器的鉴黄服务框架来说,如何合理的设计使得所有的请求任务不会造成超时或者丢弃,是一个急需解决的问题。



技术实现要素:

本发明实施例提供了一种任务的调度方法及相关装置,用于提高任务调度的效率。

本发明实施例的第一方面提供了一种任务的调度方法,包括:主机器接收图片识别请求,所述图片识别请求用于请求识别图片;所述主机器将所述图片识别请求进行转换,得到识别任务,并将所述识别任务放入识别队列;当调度所述识别队列中的当前任务时,所述主机器根据当前系统时间得到随机数;所述主机器根据所述随机数和副机器的个数,在所述副机器中确定目标副机器,所述主机器为多台机器中的任一台机器,所述副机器为所述多台机器中除所述主机器以外的其他机器,所述主机器用于管理所述副机器;所述主机器将所述当前任务调度给所述目标副机器,以使得所述目标副机器执行所述当前任务。

在一种可能的实施例中,所述主机器将所述图片识别请求进行转换,得到识别任务:所述主机器通过调用系统函数structtask将所述图片识别请求转换为所述识别任务,所述识别任务的识别分数用于表示所述识别图片的识别结果。

在一种可能的实施例中,所述方法还包括:所述主机器定义存储接口和删除接口,所述存储接口用于在所述识别队列中插入任务,所述删除接口用于在所述识别队列中删除任务。

在一种可能的实施例中,所述主机器定义存储接口和删除接口包括:所述主机器定义自动锁对象,所述自动锁对象用于进行多线程的同步互斥;所述主机器将所述自动锁对象传入互斥变量wirte_mutex;所述主机器调用接口push_back将所述任务存入到所述任务队列的尾端;所述主机器取出所述任务队列的头端的任务;所述主机器调用接口pop_front将所述任务队列的头端的任务删除。

在一种可能的实施例中,所述主机器根据当前系统时间得到随机数包括:所述主机器调用函数time()获取所述当前系统时间;所述主机器将所述当前系统时间作为函数rand()的输入以得到所述随机数。

在一种可能的实施例中,所述方法还包括:所述主机器通过string函数设计内存数据结构,并通过标准模板库stl的list链表存储所述内存数据结构,所述内存数据结构用于管理所述副机器的注册和注销,所述内存数据结构包括所述副机器的编号,所述副机器的ip地址和所述副机器的名称。

在一种可能的实施例中,所述主机器根据所述随机数和所述副机器的个数,在所述副机器中确定目标副机器包括:所述主机器根据所述随机数据中和所述副机器的个数进行求余计算,得到余数;所述主机器在所述副机器中确定编码为所述余数的目标副机器。

本发明实施例的第二方面提供了一种主机器,包括:收发单元,用于接收图片识别请求,所述图片识别请求用于请求识别图片;转换单元,用于将所述图片识别请求进行转换,得到识别任务,并将所述识别任务放入识别队列;获得单元,当调度所述识别队列中的当前任务时,用于根据当前系统时间得到随机数;确定单元,用于根据所述随机数和副机器的个数,在所述副机器中确定目标副机器,所述主机器为多台机器中的任一台机器,所述副机器为所述多台机器中除所述主机器以外的其他机器,所述主机器用于管理所述副机器;调度单元,用于将所述当前任务调度给所述目标副机器,以使得所述目标副机器执行所述当前任务。

本发明第三方面提供了一种电子设备,包括存储器、处理器,其特征在于,所述处理器用于执行存储器中存储的计算机管理类程序时实现如上述任意一项所述的方法的步骤。

本发明第四方面提供了一种计算机可读存储介质,其上存储有计算机管理类程序,其特征在于:所述计算机管理类程序被处理器执行时实现如上述任意一项所述的方法的步骤。

从以上技术方案可以看出,本发明实施例具有以下优点:主机器接收图片识别请求,所述图片识别请求用于请求识别图片;所述主机器将所述图片识别请求进行转换,得到识别任务,并将所述识别任务放入识别队列;当调度所述识别队列中的当前任务时,所述主机器根据当前系统时间得到随机数;所述主机器根据所述随机数和副机器的个数,在所述副机器中确定目标副机器,所述主机器为多台机器中的任一台机器,所述副机器为所述多台机器中除所述主机器以外的其他机器,所述主机器用于管理所述副机器;所述主机器将所述当前任务调度给所述目标副机器,以使得所述目标副机器执行所述当前任务。本发明实施例中,所有的请求任务不会造成超时或者丢弃,同时对于多台识别机器又能满负荷的进行识别任务,并且不会导致一台识别任务很多,而其他机器又处于空闲状态,提高了任务调度的效率。

附图说明

图1为本发明实施例提供的一种可能的任务的调度方法的流程图;

图2为本发明实施例提供的一种可能的主机器的结构示意图;

图3为本发明实施例提供的一种可能的电子设备的硬件结构示意图;

图4为本发明实施例提供的一种可能的计算机可读存储介质的硬件结构示意图。

具体实施方式

本发明实施例提供了一种任务的调度方法及相关装置,用于提高任务调度的效率。

下面将结合本发明实施例中的附图,对本发明实施例中的技术方案进行清楚、完整地描述,显然,所描述的实施例仅仅是本发明一部分实施例,而不是全部的实施例。基于本发明中的实施例,本领域技术人员在没有做出创造性劳动前提下所获得的所有其他实施例,都属于本发明保护的范围。

本申请的说明书和权利要求书及上述附图中的术语“第一”、“第二”、“第三”、“第四”等(如果存在)是用于区别类似的对象,而不必用于描述特定的顺序或先后次序。应该理解这样使用的数据在适当情况下可以互换,以便这里描述的实施例能够以除了在这里图示或描述的内容以外的顺序实施。此外,术语“包括”和“具有”以及他们的任何变形,意图在于覆盖不排他的包含,例如,包含了一系列步骤或单元的过程、方法、系统、产品或设备不必限于清楚地列出的那些步骤或单元,而是可包括没有清楚地列出的或对于这些过程、方法、产品或设备固有的其它步骤或单元。

对于鉴黄服务框架来说,当直播间同时开播数量比较多时会造成请求非常多,并且很有可能同一时间的请求量非常大,这样会导致很多请求超时得不到处理。那么基于有多台识别服务的机器来说,如何合理的设计使得所有的请求任务不会造成超时或者丢弃,同时对于多台识别机器又能满负荷的进行识别任务,并且不会导致一台识别任务很多,而其他机器又处于空闲状态。基于此,本文还提供了一种任务的调度方法,具体的实现方法如下:

101、主机器接收图片识别请求;

本申请实施例中,使用http的请求形式,通过向主机器发送图片识别请求,以请求主机器对图片进行鉴黄,并返回鉴黄结果。需要说明的是,该图片识别请求中需要携带上房间号,图片id,图片内容。具体实现如下:

本实施例中,基于开源的boost库提供的http请求来创建http的图片识别请求,包括:

创建一个请求的数据流存储对象boost::asio::streambufrequest,将其绑定到stl的输出流对象上std::ostreamrequest_stream(&request),然后输入一些http的头信息:

request_stream<<"post"<<url<<"http/1.0\r\n",其中url是鉴黄服务器的http接口地址。

request_stream<<"host:"<<host<<":"<<port<<"\r\n",其中host则对应的是接口名称,port对应的是端口号。

request_stream<<"accept:*/*\r\n";request_stream<<"content-length:"<<data.length()<<"\r\n";request_stream<<"content-type:application/x-www-form-urlencoded\r\n";

request_stream<<"connection:close\r\n\r\n";

再输入房间号,图片id号和图片内容,图片高度和图片宽带,以得到该图片识别请求:

request_stream<<roomid;request_stream<<pictureid;request_stream<<pictouredata;

request_stream<<width;request_stream<<height;

在得到图片识别请求后,将该请求消息发送出去,使得主机器接收图片识别请求,具体地,通过boost提供的接口boost::asio::write(socket,request)将图片识别请求通过socket方式发送到主机器上。

102、主机器将图片识别请求进行转换,得到识别任务,并将识别任务放入识别队列;

主机器接收到图片识别请求后,首先对每一个图片识别请求,需要将该图片识别请求进行打包成一个任务,从而后续将任务放到任务队列中,有请求过来时,master机器则将请求打包成任务放入到任务队列中。master机器后续则从任务队列中不断的取任务来分配到其他识别服务机器上。具体设计如下:

structtask{

intcount;intimagid;introomid;dataimagedata;stringimagename;intscore;inttype;

}

主机器收到一个图片识别请求则会将其转换成这样一个结构的对象,即一个这样的结构对象就是一个识别任务,识别完成后,则将结果赋值给此对象的识别分数score。

当同一时刻有大量的请求到来时,为了不会丢失请求,本文设计了任务队列。当大量请求来时,先将请求转换成一个识别任务,然后将任务放入到任务队列中,这样任务队列可以将同时并发的请求转换成非并发的请求。

master机器则从任务队列中取出任务来调度识别机器来进行识别。具体实现如下:

封装了一个c++语言的类来完成任务队列的存储删除取任务:

classtaskqueue{

由于队列是不断的进行插入任务和取任务删除任务,所以本文使用stl的list链表来作为任务的队列,具体实现函数如下:

std::list<task>lst_buffer;

接着对于任务队列为了防止多线程操作,造成队列数据污染,所以需要加入锁来进行多线程的同步互斥,那么定义一个互斥变量mutexwrite_mutex和往队列存储任务的接口posttask,具体实现函数如下:

voidposttask(taskt){

首先定义一个自动锁对象scopedlocklock,然后其传入本文定义的互斥变量write_mutex,具体实现函数包括:

scopedlocklock(write_mutex);

然后调用list的接口push_back来将任务存入到队列的尾端。

lst_buffer.push_back(t);

}

接下来需要编写取任务队列的接口readtask,具体实现函数如下:

taskreadtask(){

类似的,首先定义一个自动锁对象scopedlocklock,然后其传入本文定义的互斥变量write_mutex。

scopedlocklock(write_mutex);

然后从队列中取出队列头部的一个任务t:

taskt=lst_buffer.front();

接着则调用接口pop_front将队列头部的任务删除掉:

lst_buffer.pop_front();

然后返回取出的任务:returnt;

}。

另外,master机器则需要从队列中不断的获取任务,为后续来进行任务的调度。本文则会创建一个独立的线程来不断的取任务,具体包括:

首先创建一个线程对象threadthread;

然后线程对象绑定其执行函数。

thread(boost::bind(&run,null));

绑定后,此线程则会去执行run函数,接下来编写run函数:

voidrun(){

需要说明的是,该run函数是一个while循环来不断的从任务队列中取任务,具体实现如下:

while(true){

首先从队列获取任务t:

taskt=readtask();

如果队列中没有任务,则线程会暂停一会,减少cpu的消耗。

if(t==null){

sleep(10);

}

如果队列中有任务,则后续会进行任务调度。

else{

}。

103、当调度识别队列中的当前任务时,主机器根据当前系统时间得到随机数;

104、主机器根据所述随机数和副机器的个数,在所述副机器中确定目标副机器;

105、主机器将当前任务调度给目标副机器,以使得目标副机器执行所述当前任务。

当调度识别队列中的当前任务时,主机器根据当前系统时间得到随机数。具体地,master机器从队列中获取到了可以执行的任务后,则需要进行任务的调度。系统中搭建了多个识别服务的机器,并且master机器对识别服务的所有实例进行了管理,那么此时则可以获取到所有存活的实例来进行任务的调度。

首先创建一个任务实例的管理对象manager:managermgr;

在通过调用接口getcount()获取现有实例的总数:intncount=mgr.getcount();然后本文的调度策略可以使用随机调度,从现有识别任务中进行随机的调度。从理论上来说,随机调度是可以分布比较均匀的。

具体实现如下:

依据时间设置随机种子。

首先获取当前系统时间,即timetime=time();

然后调用系统函数srand来设置随机种子:srand(time);接着则生成一个随机数nrand:intnrand=rand();然后从随机数据中对实例个数求余数,那么余数是谁则落在那个实例即目标副机器上去调度识别任务:intno=nrand%ncount;从而取出一个任务则会调用一次生成随机数据,来调度任务。再通过序号从识别实例中获取对应的目标副机器:aimachineai=mgr.getaimachine(no);然后调用该目标副机器来进行任务的执行,即将当前任务调度给目标副机器。

本申请实施例中,将所有的鉴黄识别请求在master机器上设计成了缓存队列的形式,同时在master机器上也设计了负载均衡的调度控制,使得任务尽量均匀的分布到不同的机器上,同时当有机器处于停机或者崩溃时,调度可以将任务分发到其他机器,而不影响整个机器的性能,同时当任务超时或者识别失败时,master机器可以将任务再次发送给其他机器进行识别,防止失败任务丢弃掉。

实施例2、一种违规图片的识别方法;

实际应用中,随着人工智能深度学习的普及,现在都会采用深度学习来智能的识别图片中是否包含有色情内容。通常深度学习比较消耗计算机资源,而对于直播平台来说,同时存在上万个直播间,并且要不间断的每隔预置时长30秒对直播间内容进行截图,查看其是否包含色情内容。因此会通过专用的深度学习机器,其具有大量的图形处理器(graphicsprocessingunit,gpu)计算资源。对于鉴黄服务框架则要利用好深度学习机器,并且使得程序更具有健壮性,同时又能满足对直播平台的所有直播间都能够覆盖到。因此本文提供一种违规图片的识别方法,具体实现包括以下:

步骤1、直播平台服务器遍历所有直播间,以获得各直播间的直播信息;

本发明实施例,通过设计一个对直播平台的直播间内容是否涉及违规内容的服务化框架的平台,那么对于在线直播的每一个直播间都需要对其进行违规内容的智能识别。那么直播平台服务器则需要去遍历所有当前在直播的直播间,然后依次对每个直播间请求鉴黄服务,得到鉴黄结果。具体实现如下:

对于当前直播平台的每一个直播间,直播平台会设计各直播间的直播信息,包括直播间的房间号,直播状态(是否在直播),直播间的名称即房间号,直播间的分区,直播间的视频流地址等其他信息。为了效率方面的提升,本申请实施例中,设计了一个redis,其中redis是一个开源的使用ansic语言编写、支持网络、可基于内存亦可持久化的日志型、key-value数据库,作为内存的数据存储和查询数据库。由于每个直播间房间号都是不一样的,所以本实施例中,使用房间号做为key值,而房间的其他信息做为value来存储。

需要说明的是,本申请实施例中,会使用一个map来存储直播间的直播信息。包括创建一个map对象:map<string,string>mapinfo;然后将直播间的直播信息都存储到map对象中,例如mapinfo[stauts]=1,其中status标示当前直播间是否开播,status为1表示开播,status为0表示关播;例如mapinfo[name]=“666”,其中name表示直播间的名称;例如mapinfo[type]=“户外”;其中type表示直播间的分类;mapinfo[url]=www.douyu.aaaaa.com,其中url表示视频流的地址。

再创建一个redis对象rediscontext来链接内存数据库,具体函数表现包括:rediscontext*context=redisconnect("127.0.0.1",6379),其中"127.0.0.1"表示ip地址,6379表示端口号。

可选的,链接完内存数据库后,可使用redis对象rediscommand来存储这个数据,具体函数表现包括:rediscommand(context,"setkey:%s%s",roomid,mapinfo),其中参数context表示之前创建的对象,roomid表示房间号作为key值,mapinfo则是直播间的其他信息。

生成了redis的内存数据库后,则可以从中获取到所有的直播间信息,并且从中取出当前正在直播的直播间的视频流地址。

步骤2、直播平台服务器从待审核直播间的直播视频中截取关键帧图片;

在步骤101中从内存数据库可以得到每个直播间的直播信息,从而得到了当前正在直播的直播间的视频流地址信息。有了视频流地址,则可以从视频中截取关键帧的图片内容作为后续的图片鉴黄识别,即依据图片内容通过深度学习的模型来识别图片是否包含违规内容。

本申请实施例中,可以使用开源的视频编解码库ffmpeg来对视频截取关键帧图片,该关键帧图片用于进行鉴黄识别。首先调用ffmpeg的库初始化接口av_register_all()来初始化。接下来调用应用程序编程接口(applicationprogramminginterface,api)函数av_open_input_file(&pformatctx,url,null,0,null)打开视频流。进而得到视频流的上下文指针,即pcodecctx=pformatctx->streams[videostream]->codec;创建一帧视频帧对象,即pframe=avcodec_alloc_frame();并创建一帧图像帧对象pframergb=avcodec_alloc_frame(),再创建得到视频帧对象和图像帧对象后,再填充视频帧,具体函数表现包括:

avpicture_fill((avpicture*)pframergb,buffer,pix_fmt_rgb24,pcodecctx->width,pcodecctx->height)。

填充视频帧后,解码该视频帧,具体解码方式可通过以下函数实现:

avcodec_decode_video(pcodecctx,pframe,&framefinished,packet.data,packet.size),从中可以得到当前视频的一个关键帧,函数表现如下:sws_scale(pswsctx,pframe->data,pframe->linesize,0,pcodecctx->height,pframergb->data,pframergb->linesize)。

步骤3、直播平台服务器向鉴黄服务器发送请求消息;

直播平台服务器截取到了视频流的关键帧图片,获取到了对应的图片内容后,接下来则是设计如何将图片内容来调用鉴黄服务,即请求鉴黄服务器对图片内容进行鉴黄识别。本申请实施例中,使用http的请求形式,直播平台服务器通过向鉴黄服务器发送请求消息,以请求鉴黄服务器的http服务来对图片进行鉴黄,并请求鉴黄服务返回鉴黄结果。需要说明的是,该请求消息中需要携带上房间号,图片id,图片内容。具体实现如下:

本实施例中,基于开源的boost库提供的http请求来创建http的请求消息,包括:

创建一个请求的数据流存储对象boost::asio::streambufrequest,将其绑定到stl的输出流对象上std::ostreamrequest_stream(&request),然后输入一些http的头信息:

request_stream<<"post"<<url<<"http/1.0\r\n",其中url是鉴黄服务器的http接口地址。

request_stream<<"host:"<<host<<":"<<port<<"\r\n",其中host则对应的是接口名称,port对应的是端口号。

request_stream<<"accept:*/*\r\n";request_stream<<"content-length:"<<data.length()<<"\r\n";request_stream<<"content-type:application/x-www-form-urlencoded\r\n";

request_stream<<"connection:close\r\n\r\n";

再输入房间号,图片id号和图片内容,图片高度和图片宽带,以得到该请求消息:

request_stream<<roomid;request_stream<<pictureid;request_stream<<pictouredata;

request_stream<<width;request_stream<<height;

在得到请求消息后,将该请求消息发送出去,具体地,通过boost提供的接口boost::asio::write(socket,request)将请求消息通过socket方式发送到鉴黄服务器上。

步骤4、鉴黄服务器搭建接收接口;

需要说明的是,本申请实施例提供的是一个鉴黄服务,服务是以提供对外的http的接口,服务不断的接收http的请求来完成对应的请求的功能,并在请求中调用鉴黄模块来实现对关键帧图片的鉴黄结果。所以本步骤中,会搭建一个鉴黄服务的http的服务程序来接收请求鉴黄的请求消息。类似的,本申请实施例中,基于开源库boost来搭建一个http的服务器程序来接收请求消息。

首先创建boost的对象,具体实现函数包括:boost::asio::ip::tcp::resolverresolver(io_service_);然后绑定服务器的ip和端口号,具体实现函数包括:boost::asio::ip::tcp::resolver::queryquery(address,port,其中address表示服务器的ip,port表示端口号。然后绑定该端口以进行侦听,具体地,实现函数包括:通过函数acceptor_.bind(endpoint)实现端口绑定;通过函数acceptor_.listen()实现侦听。

当帧听到请求后,创建异步处理接口来处理请求,具体函数实现如下:acceptor_.async_accept(new_connection_->socket(),boost::bind(&server::handle_accept,this,boost::asio::placeholders::error));需要说明的是,异步处理接口则在接口server::handle_accept中处理。另外,在此接口server::handle_accept中,创建一个链接对象开始处理请求,创建链接对象的实现函数包括:connection_manager_.start(new_connection_)。

在创建了链接对象后,需要对请求进行解析,即首先创建解析对象,具体实现函数包括:result;request_parser::result_typeresult;然后调用prase方法来进行解析,具体函数实现包括:request_parser_.parse(request_,buffer_.data(),buffer_.data()+bytes_transferred)。在解析完请求消息后,从中获取到对应的参数信息包括房价号,图片id,图片内容,图片宽度和图片高度等。

步骤5、鉴黄服务器创建鉴黄识别服务功能得到图片识别结果。

鉴黄服务器接收到鉴黄图片的请求消息,并获取了请求消息的内容后,需要去调用鉴黄模块来识别待识别图片即关键帧图片,并得到待识别图片的识别结果。首先,创建鉴黄模块的句柄对象,void*phandle=ai_create();然后通过调用其接口ai_create来创建句柄phandle。得到句柄后,需要初始化鉴黄模块,具体的,可调用其对应的接口ai_init来初始化,ai_init(phandle,modulefilepath,bingpu),其中phandle是创建的模块的句柄,modulefilepath是模型的文件名称,bingpu是选择的gpu芯片的编号。在初始化鉴黄模块后,调用其接口ai_predict来识别图片,并得到图片的打分,分值越高则说明违规的可能性越大,具体函数实现如下:ai_predict(phandle,image,width,height,score);其中参数phandle是创建的句柄,image是图片的内容,width是图片的宽带,height是图片的高度,且识别结果存储在score字段,即通过score可以知道图片是否涉及违规。

步骤6、直播平台服务器接收鉴黄服务器发送的鉴别结果;

步骤7、直播平台服务器确定打分超过预设数值;

步骤8、直播平台服务器确认关键帧图片违规,并对关键帧图片对应的直播间进行预置处理。

在鉴黄服务器得到了鉴别结果后,则需要将该鉴别结果返回给请求者即直播平台服务器,需要说明的是,鉴别结果则是该待识别图片的打分的分值,该分值则表明了待识别图片是违规内容的分值。例如,满分是100分,本文则设定为鉴别结果为90分及以上的待识别图片为违规图片。具体实现如下:

鉴黄服务器得到了请求消息后则调用鉴黄服务器的识别模块,根据识别模块得到了识别结果。接下来则需要将识别结果返回给直播平台服务器,具体数值则通过http的返回给直播平台服务器。具体地,先定义一个返回的buffers,函数实现包括:std::vector<boost::asio::const_buffer>buffers。再填充相应的头信息:

buffers.push_back(boost::asio::buffer(h.name));

buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator))。

在填充完相应的头信息后,继续填充相应的结果请求数据,具体函数实现如下:buffers.push_back(boost::asio::buffer(content)),其中content即为相应的结果。本申请实施例中,则需要将识别的分数填入到相应结果中。

在得到了请求结果后,鉴黄服务器会将鉴黄识别模块识别的分数返回给直播平台服务器,对于直播间内容审核人员则可以得到审核的分数,例如系统可以自动的对识别分数高的认为是违禁的直播间推送给审核人员,从而可以人为的做二次确认,如果确认有问题则可以对直播间进行警告或者处罚。另外,本实施例可以通过自动识别过滤出不需要人为确认的直播间,从而减少审核人员的工作量。

实施例3、一种管理多台识别机器的方法

另外,实际应用中,对于鉴黄服务框架来说,由于直播间数量比较多,并且对于每一个直播间都是采取每隔预置时长如30秒钟会截取直播间的视频中的一张图片来调用鉴黄服务,那么对于一台识别服务化的机器则满足不了线上的需求。因此需要有多台基于gpu的识别机器(深度学习使用gpu进行大量的计算)。而对于识别服务框架则需要设计一种方法来依据请求来调度不同的识别机器,同时对于多台机器也可以起到容灾备份的目的。因此本申请实施例还提供了一种管理多台识别机器的方法,具体的实现方法如下所示:

步骤1、设计基于每台机器进行编号和ip分配。

首先对于每一台鉴黄机器,都具有cpu资源和gpu资源,那么每台机器都可以独立的进行鉴黄图片的识别得到识别的分数。因此首先需要对每台机器进行编号以及分配其固定的ip地址,具体编号则可以按照机器编写序号。例如机器1编写其序号为no1,然后对于的ip地址则可以分配为192.168.1.1;对于机器2则可以编写序号为no2,其对应的ip地址也可以分配为192.168.1.2;故对于每一台机器都可以按照此种方法进行编号和分配ip地址,需要注意的是,必须需要保障每台机器的编号都是唯一的,并且ip也是唯一的。

步骤2、设计一台机器做为主机器,即master机器。

挑选多台机器中的其中一台机器做为master机器,其具有管理其他机器的智能,相当于是一个管理中心。

步骤3、设计数据结构来管理所有的机器注册和注销。

本文在设计一个内存数据结构来对所有机器的注册和注销进行管理。首先定义一个结构体aimachine表明是一台机器。

structaimachine{

stringname;stringip;stringno;

}

其中该数据结构包含有机器的名称name,机器的ip地址ip,机器的编号no。定义好机器信息后,接下来本文定义一个管理类manager来管理机器的注册和注销:

classmanager{

首先定义一个存储变量来存储所有的机器,本文可以使用stl的list结构来存储机器的结构信息,具体函数实现如下:

stl::list<aimachine>m_list;

其中list链表则存储所有注册的机器信息。

再编写接口来注册一台机器,具体函数实现如下:

voidregister(constaimachine&data){

本文则通过调用list链表的push_back来存储机器信息,从而实现了注册一台机器到管理类中,具体函数实现如下:

m_list.push_back(data);

}

voidunregister(contstring&name){

需要说明的是,本文还可以通过机器的名称来注销一台机器,包括:

首先定义一个迭代器list<aimachine>::iteratoritr;

然后对迭代器进行初始化itr=m_list.begin();

接下来则遍历链表查找该台机器进行注销:

for(;itr!=m_list.end();++itr){

需要说明的是,itr!=m_list.end()表示不是链表的结尾。

通过if函数对比机器的名称是否一致,如果一致则说明是该台机器,则通过链表的删除接口erase来删除该台机器,具体实现函数如下:

需要说明的是,本申请实施例中,还可以通过ip地址来进行注销的接口,具体实现函数如下:

voidunregister(contstring&ip){

首先定义一个迭代器,实现函数包括:list<aimachine>::iteratoritr;

然后对迭代器进行初始化,实现函数包括:itr=m_list.begin();

接下来则遍历链表查找该台机器进行注销。

for(;itr!=m_list.end();++itr){

其中itr!=m_list.end()表示不是链表的结尾。

再通过if函数对比机器的ip是否一致,如果一致则说明是该台机器,则通过链表的删除接口erase来删除该台机器,具体实现函数如下:

最后,再编写获取实例个数的接口,具体实现函数如下:

intgetcount(){

returnm_list.size();

}

}

步骤4、设计副机器即非master机器的注册。

首先本文挑选了一台机器做为master机器用来管理其他的机器,每台非master机器都有master机器的ip地址,本文设计当非master机器开机启动后,则会请求连接master机器,从而告知master机器现在有一台鉴黄识别机器启动了,需要注册到识别机器群中,使得master后续可以调度识别任务到此台机器中。具体实现本文则可以通过在master创建一个传输控制协议(transmissioncontrolprotocol,tcp)网络通道,非master机器启动后,创建tcp通道来连接master机器,并告知master该非master机器的名称,非master机器的编号。具体可以设计这样一条协议,使得当非master机器启动后则发送该协议到master机器中,具体的,该协议可为如下协议:

type@=register/name@=machine1/no@=3/。

当master机器收到此协议后,则通过管理类manager来管理机器的注册。即首先定义一个aimachinedata对象,然后给其进行赋值,具体实现函数如下:

data.name=machine1;data.no=3;data.ip=ip;

其中ip地址则可以通过链接过来的socket获取到,然后管理类managermgr;可以通过其注册方法mgr.register(data)来实现注册。

步骤5、设计非master机器的注销。

当任意一台非master机器注册后,如果非master机器存在问题或者其他原因可以主动的进行注销,注销后则master机器不会再往其发送图片鉴黄识别任务。具体注销也是通过发送一条注销的协议type@=unregister/;那么master机器收到后则可以对该台机器进行注销。当master收到此协议后,则通过管理类manager来管理机器的注销。其中ip地址则可以通过连接过来的socket获取到。然后管理类managermgr通过其注册方法mgr.unregister(ip)来实现注销。

步骤6、设计非master机器的心跳包活策略。

需要说明的是,当任意一台非master机器注册后,如果非master机器由于程序原因导致崩溃或者卡死等其他原因,并且最终其由于有问题所以并不会进行注销操作,这样则会导致其处于假死状态或者机器已经关闭了。那么如果其没有在master机器进行注销,将导致master机器以为此机器一直存在,并不断的发送识别任务,而最终所有的识别任务都失效了。因此需要设计一种心跳保活的策略,非master每隔预置时长如30s则向master机器发送一个心跳包,同时master机器也会给对方回复一个心跳包,从而只要机器存活,则双方不断的发送心跳包,告知对方都存活。当任意一方收不到对方的心跳包后则认为对方出现了问题,则会重现进行注销和注册。可选的,本实施例中,心跳协议则可以设计成type@=keeplive/,可以理解的是,master机器和非master机器双方都是发送这样的协议内容。

实施例4、一种图片识别方法;

对于本文的鉴黄识别服务来说,每种图片都会使用gpu资源,而gpu是整个识别中的最核心的资源,同时是也是最容易产生瓶颈的资源,而通过整个系统的运行,本文发现对于直播间来说,采取使用预置时长的策略来截取视频中的图片进行识别,发现对于很多情况下,连续的一段时间内,直播间的内容没有发送很大的变化。可以理解的是,图片若相似的,其对应的识别结果也是相似的,因此可以通过gpu的算法来对图片进行相似度的识别,如果图片相似则直接返回之前的识别结果,从而不会调用到gpu的识别资源,以减少产生瓶颈的可能性。那么需要设计一套可以使用图片相似过来同样图片的算法,故本文提供了一种图片识别方法,具体实现如下:

步骤1、设计历史特征的图片命名。

对于直播间来说,相似图片的识别只会对同一个直播间来说,不同的直播间直播的内容不一样,所以也不存在相似图的识别问题。那么对于每个直播间来说直播间号是直播平台唯一区分每个直播间的标识信息,那么对于每个直播间需要进行识别的图片本文则会对图片进行命名,其中命名包括该图片所属的直播间号加上时间戳,从而即可以知道该图片所属的直播间,也不会存在图片有重名的情况,还可以知道图片产生的时间。具体实现如下:

imagename=roomid+time();

其中imagename是图片的名称;roomid是房间号;time()则是获取系统时间戳。

步骤2、设计历史特征的存储和更新。

对于直播间来说,需要设计一个队列来存储历史特征。由于当前识别的图片可能和之前的图片是相似的,所以本文设置队列长度是n张图片,如10张图片,每次都和这10张图片进行对比。可以理解的是,队列长度设置太长或者太短都会影响效果,如果设置太长,那么对比会消耗大量时间,如果设置太短那么有可能是相似的没有对比到,因此队列长度的设置可以基于实际应用场景。本文使用stl的链表list来存储图片的历史特征,具体实现如下:

structimagedata{

image;imagename;imagefeature;score;

}

其中,该结构imagedata来存储一个图片的特征和图片,其中结构中包括图片的原始数据image,图片的名称imagename,图片的特征数据imagefeature,score则是图片识别的分数值。

然后定义一个链表变量list<imagedata*>listimagedata来存储一个房间的历史特征。对于整个直播网站来说,需要查询所有的直播间,所以本文使用了stl的键值对容器map来存储所有直播间的历史特征,其中键使用房间号,而值则使用了链表变量,具体实现函数包括:map<int,list<imagedata*>*>maplistdata。

接下来则编写接口来实现读取历史特征和更新历史特征。当直播间有一张图片需要进行识别时,首先会依据该图片的所属房间号来拉取对应的所有历史特征值,其中本文则编写了接口getimagdata来获取该房间的所有特征值,输入参数为房间号nroomid,具体实现函数包括:

list<imagedata*>*getimagdata(intnroomid){

首先定义一个迭代器:

map<int,list<imagedata*>*>::iteratoritr;

然后通过房间号来查找对应的所有特征值的链表,并返回查找到的结果:

itr=maplistdata.find(nroomid);其中调用map容器的find函数来查找。

return(*itr).second;

}

在得到了该房间的所有特征值的链表后,可以遍历链表来一一匹配图像的特征值。

同样的首先定义一个链表的迭代器itr,具体实现函数如下:

list<imagedata*>::itratoritr;

然后初始化迭代器:itr=listimagedata.begain();

进而编写for循环来对比特征值是否相似:

for(;itr!=listimagedata.end();++itr){

其中调用了相似度检测的算法接口similardetect_predict,其输入参数有2个,一个是链表中的图片特征,另一个则是当前需要对比的图片的特征,其中返回值result则是返回结果,需要说明的是,如果返回值是true则表明相似,否则不相似。

intresult=similardetect_predict((*itr).imagefeature,imagefeature);

if(result==true){

returntrue;

}

}

如果当前图片和历史特征对比都没有相似的,则说明当前直播间的直播内容变化很大,所以需要将历史特征进行更新,踢掉存在时间最久的特征,并且将当前的特征更新到特征库中。具体则是通过调用其接口pop_front函数listimagedata.pop_front()来踢掉存在时间最久的特征数据;然后通过函数listimagedata.push_back(imagedata)将最新的特征存储到链表中。

步骤3、识别服务流程修改。

本文加入了相似图的识别流程后,那么整个鉴黄的识别流程则发生了变化,鉴黄服务框架收到鉴黄的请求后,首先会则会获取该直播间的历史特征,并将请求与历史特征进行比较,如果匹配上,则直接返回历史特征图片识别的鉴黄分数结果,那么此流程则终止,不需要进行后续的基于gpu的深度学习的鉴黄识别模块,从而可以节省gpu资源,而整个匹配都是使用cpu进行计算。而如果请求在历史特征库中都没有找到相似图片,则整个流程需要使用gpu的深度学习的鉴黄识别模块,并且将识别的结果更新到历史特征库中。从而历史特征库总是保存最近的图片识别结果。

实施例5、一种调度gpu的方法。

对于本文的鉴黄识别服务来说,每种图片都会使用gpu资源,而gpu是整个识别中的最核心的资源,同时也是最容易产生瓶颈的资源。那么在整个鉴黄识别服务中,本文则提出了一种充分利用gpu的方案。本文首先会获取系统中gpu显卡的数量,然后对所有的识别任务设计一个任务队列,本文的调度模型中,则会不定时的获取每块gpu显卡当前的使用率,依据使用率,来分配任务到对应的gpu上进行处理。从而防止有的gpu处于空闲,而有的gpu又处于满负荷运行,使得调度更为合理和高效。具体实现方案如下:

步骤1、获取系统中gpu数量。

首先本文需要获取当前系统gpu的数量,从而为后续gpu的调度提供调度的数量。具体则通过显卡提供的api函数enumnvidiadisplayhandle来枚举当前gpu数量。具体实现如下:

设计一个数组来存储所有的gpu的句柄。

nvphysicalgpuhandlegpuhandle[max]={0};

本文编写一个循环来遍历gpu,查看其是否存在,如果不存在则说明是最后一个。首先定义一个显卡数量变量,并初始化为0:intncount=0;

然后编写循环来遍历所有的显卡gpu,具体实现函数如下:

for(intnindex=0;nindex<0xffffffff;++nindex){

再通过调用系统提供的api函数enumnvidiadisplayhandle来判断当前枚举的gpu是否存在,如果存在返回值则是nvstatus_ok,否则则是失败。如果返回成功,本文则将数量加1,如果返回失败则直接返回ncount值,即表示gpu的数量,具体实现函数如下:

if(enumnvidiadisplayhandle(nindex,&nvdisplaycardhandle)==nvstatus_ok){

gpuhandle[ncount]=nvdisplaycardhandle;

如果存在,则将当前gpu的句柄存储到句柄数组中。

步骤2、获取系统中每块gpu的使用率。

在获取到当前系统gpu的数量和gpu的句柄数组后,需要编写接口来查询每块gpu当前的使用率,从而后续则可以依据使用率来进行任务调度。具体实现如下:

首先定义一个数组来存储当前gpu的使用率,intgpuusage[max]={0};

并且此数组对应于上个步骤中的gpu的句柄数组,接下来编写for循环来遍历gpu的所有句柄:

首先定义一个遍历起始下标intnno=0,并且在上个步骤中获取到了显卡gpu的数量:

for(nno=0;nno<ncount;++nno){

接下来则调用系统api函数gpu_getusages来获取显卡的使用率,具体实现函数如下:

gpu_getusages(pcardinfo->sgpuinfo[nindex].nvgpuhandle,pnvusages);

gpuusage[nno]=pnvusages->nusage;

}

因此通过循环则可以得到所有gpu的使用率。

需要注意的是,由于使用率是一个动态变化的,所以需要通过定时器来不断的获取使用率,从而实时的进行更新。本文则编写一个定时器函数来每间隔预置时长如10秒钟来获取一次使用率,具体实现如下:

首先编写一个定时器的回调函数,此函数则是定时器到时会去执行的函数;voidtimerfunc(){

再调用上面编写的获取gpu使用率的功能来更新每块显卡的使用率:

for(nno=0;nno<ncount;++nno){

接下来则调用系统api函数gpu_getusages来获取显卡的使用率,具体实现函数如下:

gpu_getusages(pcardinfo->sgpuinfo[nindex].nvgpuhandle,pnvusages);

gpuusage[nno]=pnvusages->nusage;

}

}

接下来调用系统api函数settimer来创建一个定时器,其函数原型如下:

uint_ptrsettimer(

hwndhwnd,

uint_ptrnidevent,uintnelapse,

timerproclptimerfunc//

);

其中,hwnd用于表示窗口句柄;nidevent用于表示定时器id,多个定时器时,可以通过该id判断是哪个定时器;nelapse用于表示时间间隔,该单位为毫秒;lptimerfunc用于表示回调函数。

具体调用则是通过函数settimer(null,0,10000,timerfunc);其中窗口句柄设置为null,id则设置为0,时间间隔则设置为10秒即10000毫秒,定时器的回调函数则设置为之前编写的timerfunc。

步骤3、查找gpu使用率最小的编号。

在分配任务时,需要获取当前gpu使用率最小的显卡来分配执行任务。具体实现如下:首先本文定义一个当前gpu使用的最小的变量nminusage,并且初始化为第一个gpu显卡,intnminusage=gpuusage[0];

并且定义一个变量来记录当前最小的gpu的编号intnminno=0;

接下来则遍历循环查找使用率最小的gpu,需要说明的是,由于第一块已经赋值给nminusage,此时循环则从第二块gpu显卡开始。

for(intnno=1;nno<ncount;++nno){

如果当前遍历的gpu的使用率低于最小的使用率则替换掉当前最小的gpu使用率,并且记录其编号,具体实现函数如下:

if(gpuusage[nno]<nminusage){

nminusage=gpuusage[nno];

nminno=nno。

}

}

通过以上查询即可得到当前最小的gpu使用率的编号nminno。

步骤4、任务调度。

本文会对任务使用一个任务队列来缓存所有的识别任务,并且会使用队列中的任务来调度到对应的gpu进行执行。

首先创建一个线程对象threadthread;

然后线程对象绑定其执行函数:thread(boost::bind(&run,null))。绑定后,此线程则会去执行run函数,接下来编写run函数。

voidrun(){

此函数则是一个while循环来不断的从任务队列中取任务,具体实现函数如下:

while(true){

首先从队列获取任务t:

taskt=readtask();

如果队列中没有任务,则线程会暂停一会,减少cpu的消耗,有任务,则会调用之前编写的接口来获取当前gpu使用率最小的gpu的编号,然后从将该任务分配到该gpu进行执行:

if(t==null){

sleep(10);

}

else{

}。

上面从任务的调度方法的角度对本发明实施例进行了描述,下面从获取装置的角度对本发明实施例进行描述。

请参阅图2,图2为本发明实施例提供的一种可能的主机器的实施例示意图,其中,该主机器具体包括:

收发单元201,用于接收图片识别请求,所述图片识别请求用于请求识别图片;

转换单元202,用于将所述图片识别请求进行转换,得到识别任务,并将所述识别任务放入识别队列;

获得单元203,当调度所述识别队列中的当前任务时,用于根据当前系统时间得到随机数;

确定单元204,用于根据所述随机数和副机器的个数,在所述副机器中确定目标副机器,所述主机器为多台机器中的任一台机器,所述副机器为所述多台机器中除所述主机器以外的其他机器,所述主机器用于管理所述副机器;

调度单元205,用于将所述当前任务调度给所述目标副机器,以使得所述目标副机器执行所述当前任务。

请参阅图3,图3为本发明实施例提供的电子设备的实施例示意图。

如图3所示,本发明实施例提供了一种电子设备,包括存储器310、处理器320及存储在存储器320上并可在处理器320上运行的计算机程序311,处理器320执行计算机程序311时实现以下步骤:接收图片识别请求,所述图片识别请求用于请求识别图片;将所述图片识别请求进行转换,得到识别任务,并将所述识别任务放入识别队列;当调度所述识别队列中的当前任务时,根据当前系统时间得到随机数;根据所述随机数和副机器的个数,在所述副机器中确定目标副机器,为多台机器中的任一台机器,所述副机器为所述多台机器中除以外的其他机器,用于管理所述副机器;将所述当前任务调度给所述目标副机器,以使得所述目标副机器执行所述当前任务。

可选的,在一种可能的实施例中,所述处理器320具体用于:通过调用系统函数structtask将所述图片识别请求转换为所述识别任务,所述识别任务的识别分数用于表示所述识别图片的识别结果。

可选的,在一种可能的实施例中,所述处理器320还用于:定义存储接口和删除接口,所述存储接口用于在所述识别队列中插入任务,所述删除接口用于在所述识别队列中删除任务。

可选的,在一种可能的实施例中,所述处理器320具体用于:定义自动锁对象,所述自动锁对象用于进行多线程的同步互斥;将所述自动锁对象传入互斥变量wirte_mutex;调用接口push_back将所述任务存入到所述任务队列的尾端;取出所述任务队列的头端的任务;调用接口pop_front将所述任务队列的头端的任务删除。

可选的,在一种可能的实施例中,所述处理器320具体用于:调用函数time()获取所述当前系统时间;将所述当前系统时间作为函数rand()的输入以得到所述随机数。

可选的,在一种可能的实施例中,所述处理器320还用于:通过string函数设计内存数据结构,并通过标准模板库stl的list链表存储所述内存数据结构,所述内存数据结构用于管理所述副机器的注册和注销,所述内存数据结构包括所述副机器的编号,所述副机器的ip地址和所述副机器的名称。

可选的,在一种可能的实施例中,所述处理器320具体用于:根据所述随机数据中和所述副机器的个数进行求余计算,得到余数;在所述副机器中确定编码为所述余数的目标副机器。

由于本实施例所介绍的电子设备为实施本发明实施例中一种主机器所采用的设备,故而基于本发明实施例中所介绍的方法,本领域所属技术人员能够了解本实施例的电子设备的具体实施方式以及其各种变化形式,所以在此对于该电子设备如何实现本发明实施例中的方法不再详细介绍,只要本领域所属技术人员实施本发明实施例中的方法所采用的设备,都属于本发明所欲保护的范围。

请参阅图4,图4为本发明实施例提供的一种计算机可读存储介质的实施例示意图。

如图4所示,本实施例提供了一种计算机可读存储介质400,其上存储有计算机程序411,该计算机程序411被处理器执行时实现如下步骤:接收图片识别请求,所述图片识别请求用于请求识别图片;将所述图片识别请求进行转换,得到识别任务,并将所述识别任务放入识别队列;当调度所述识别队列中的当前任务时,根据当前系统时间得到随机数;根据所述随机数和副机器的个数,在所述副机器中确定目标副机器,为多台机器中的任一台机器,所述副机器为所述多台机器中除以外的其他机器,用于管理所述副机器;将所述当前任务调度给所述目标副机器,以使得所述目标副机器执行所述当前任务。

可选的,在一种可能的实施例中,该计算机程序411被处理器执行时具体用于实现如下步骤:通过调用系统函数structtask将所述图片识别请求转换为所述识别任务,所述识别任务的识别分数用于表示所述识别图片的识别结果。

可选的,在一种可能的实施例中,该计算机程序411被处理器执行时还用于实现如下步骤:定义存储接口和删除接口,所述存储接口用于在所述识别队列中插入任务,所述删除接口用于在所述识别队列中删除任务。

可选的,在一种可能的实施例中,该计算机程序411被处理器执行时具体用于实现如下步骤:定义自动锁对象,所述自动锁对象用于进行多线程的同步互斥;将所述自动锁对象传入互斥变量wirte_mutex;调用接口push_back将所述任务存入到所述任务队列的尾端;取出所述任务队列的头端的任务;调用接口pop_front将所述任务队列的头端的任务删除。

可选的,在一种可能的实施例中,该计算机程序411被处理器执行时具体用于实现如下步骤:调用函数time()获取所述当前系统时间;将所述当前系统时间作为函数rand()的输入以得到所述随机数。

可选的,在一种可能的实施例中,该计算机程序411被处理器执行时还用于实现如下步骤:通过string函数设计内存数据结构,并通过标准模板库stl的list链表存储所述内存数据结构,所述内存数据结构用于管理所述副机器的注册和注销,所述内存数据结构包括所述副机器的编号,所述副机器的ip地址和所述副机器的名称。

可选的,在一种可能的实施例中,该计算机程序411被处理器执行时具体用于实现如下步骤:根据所述随机数据中和所述副机器的个数进行求余计算,得到余数;在所述副机器中确定编码为所述余数的目标副机器。

需要说明的是,在上述实施例中,对各个实施例的描述都各有侧重,某个实施例中没有详细描述的部分,可以参见其它实施例的相关描述。

本领域内的技术人员应明白,本发明的实施例可提供为方法、系统、或计算机程序产品。因此,本发明可采用完全硬件实施例、完全软件实施例、或结合软件和硬件方面的实施例的形式。而且,本发明可采用在一个或多个其中包含有计算机可用程序代码的计算机可用存储介质(包括但不限于磁盘存储器、cd-rom、光学存储器等)上实施的计算机程序产品的形式。

本发明是参照根据本发明实施例的方法、设备(系统)、和计算机程序产品的流程图和/或方框图来描述。应理解可由计算机程序指令实现流程图和/或方框图中的每一流程和/或方框、以及流程图和/或方框图中的流程和/或方框的结合。可提供这些计算机程序指令到通用计算机、专用计算机、嵌入式计算机或者其他可编程数据处理设备的处理器以产生一个机器,使得通过计算机或其他可编程数据处理设备的处理器执行的指令产生用于实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的装置。

这些计算机程序指令也可存储在能引导计算机或其他可编程数据处理设备以特定方式工作的计算机可读存储器中,使得存储在该计算机可读存储器中的指令产生包括指令装置的制造品,该指令装置实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能。

这些计算机程序指令也可装载到计算机或其他可编程数据处理设备上,使得在计算机或其他可编程设备上执行一系列操作步骤以产生计算机实现的处理,从而在计算机或其他可编程设备上执行的指令提供用于实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的步骤。

尽管已描述了本发明的优选实施例,但本领域内的技术人员一旦得知了基本创造概念,则可对这些实施例作出另外的变更和修改。所以,所附权利要求意欲解释为包括优选实施例以及落入本发明范围的所有变更和修改。

显然,本领域的技术人员可以对本发明进行各种改动和变型而不脱离本发明的精神和范围。这样,倘若本发明的这些修改和变型属于本发明权利要求及其等同技术的范围之内,则本发明也意图包括这些改动和变型在内。

当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1