# 61-啥时候放寒假呀 **Repository Path**: openeuler2020/team-1212781478 ## Basic Information - **Project Name**: 61-啥时候放寒假呀 - **Description**: 白菜练习生 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2020-12-29 - **Last Updated**: 2021-06-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 复赛以来改进的部分: - 增加了对边界条件的补充测试,包括不存在的人脸图像、格式不正确的url等; - 利用简单工厂+策略模式,优化了对不同响应结果的处理那部分代码,使得更容易扩展; - 增加了日志记录信息; # 61号题目复赛文档 - 题目:基于openLooKeng的UDF框架实现SQL与AI结合 - 题目编号:61 - 队伍:啥时候放寒假呀 - 团队编号:1212781478 - 队员:余扬、王爽、王益、李博一 文档主要分为七个部分: - 一、代码结构 - 二、项目背景 - 三、题目完成程度 - 四、功能描述 - 五、方案设计 - 六、安装使用和操作指引 - 七、使用示例 可运行demo的获取方式见**安装使用**部分。 #### 一、代码结构 ``` src ├── main ├── assembly ├── assembly.xml # 打包规则 ├── java ├── com/fisheep ├── aisrvice #每个UDF核心实现 ├── FaceSearchService.java #人脸识别相关的UDF的实现 ├── factory #工厂类存放的目录 ├── ResponseCodeProcessorFactory.java ├── pojo #反序列化后封装http响应结果 ├── FaceSearchResponse.java ├── FaceSearchResult.java ├── FaceSearchUser.java ├── singleton #存放一些单例对象 ├── AiServiceConfigSingleton.java #存放配置文件解析之后的单例对象 ├── GsonSingleton.java #Gson单例 ├── strategy #策略模式存放目录 ├── AccesstokenExpiredStrategy.java #处理token的策略 ├── DefaultErrorStrategy.java #缺省策略 ├── ImageErrorStrategy.java #处理图像内容错误的策略 ├── ParameterFormatErrorStrategy.java #处理参数格式错误的策略 ├── ResponseCodeProcessor.java #执行策略的context ├── ResponseCodeProcessStrategy.java #所有策略的接口 ├── utils #工具类 ├── Base64Util.java # base64 #编码相关的工具 ├── Cache.java #简单缓存 ├── GsonUtil.java #解析Json的工具 ├── HttpClientUtil.java #发出Http请求的工具 ├── FaceRecognitionFunctionPlugin.java ├── FaceRecognitionFunctions.java #人脸识别的UDF定义,入口 ├── resources ├── ai-service-config.yml #配置文件,存放UDF的配置信息 ├── test ``` #### 二、项目背景 在当前的大数据时代背景下,一个企业可能有数十种异构的数据存储系统,每一个系统都足够的复杂到往往需要一个团队专门来维护,最终导致了这些系统彼此独立的运行。大量数据之间因为缺少有效的关联方式,使得数据中更深层次的更有价值的信息得不到有效挖掘。大数据和AI是相辅相成的关系,它们之间的联系越来越紧密。 openLooKeng用于充当数据之间的桥梁,支持对这些异构数据库中的数据进行探索分析以及批处理等操作;同时,openLooKeng提供了用户自定义函数(UDF)框架,使用户能够很灵活地添加自定义函数。因此可以基于openLooKeng的UDF能力,实现在SQL语句中直接调用AI算法进行人脸图片分析。 #### 三、题目完成程度 - 基本功能完成; - 部署在openEuler上进行测试完成:UDF对以下所有测试图片用例(包括正向和反向用例)均正常工作,对于正向用例会返回正确人脸id,反向用例返回空,测试环境如下: - 操作系统:VMware虚拟机安装的,openEuler-20.03-LTS-x86_64; - MySQL:mysql Ver 8.0.22 for Linux on x86 64 (MySQL Community Server - GPL); - openLooKeng:hetu-server-1.0.1; - 测试用例:选用的是电视剧《大江大河》中的演员剧照,剪裁处理之后见:`http://120.26.179.169:8080/faces/` - 正向用例: - 人脸图像库里面存在的人脸,例如:`http://120.26.179.169:8080/faces/sonyunhui.png`; - 传入的图像中包含多张人脸,但是只有主角一张人脸最明显,例如:`http://120.26.179.169:8080/faces/songyunhui_mutifaces.png`; - 反用例: - 人脸库中不存在的人脸,例如:`http://120.26.179.169:8080/faces/huge.png`; - 人脸图像的路径不是url格式,例如:`opiposidfgmji`; - 人脸图片不存在:`http://120.26.179.169:8080/faces/xiaoming.png`; #### 四、功能描述 > 功能 UDF的功能是接收一张人脸图像(base64编码格式或者图像的url),UDF调用人脸识别服务(我选的是百度AI的人脸识别服务),去搜索人脸库中是否包含此张人脸图像,如果包含则返回对应的人的id,不存在则返回空; 包含如下两个UDF,其作用如名字所示(接收图像base64编码、接收图像url): ```bash Function | Return Type | > ---------------------------------+------------------------------+-------------------------------> face_search_by_base64 | varchar | varchar > face_search_by_image_url | varchar | varchar > ``` > 例子 模拟考勤打卡场景,假设MySQL数据库中存在一张用户表:user_info,包含用户的基本信息,其中user_no是每个用户的唯一编号, 表如下: ```sql +----+-------------+-------------+----------------------+-----------+ | id | user_no | phone | email | name | +----+-------------+-------------+----------------------+-----------+ | 4 | 1223965229 | 15922513997 | noahyu@126.com | 余扬 | | 5 | 3396648765 | 13348679903 | yang_xun@163.com | 杨巡 | | 6 | 784487653 | 17765639087 | xunjianxinag@qq.com | 寻建祥 | | 7 | 38509876 | 16578909987 | songyunhui@gmail.com | 宋运辉 | | 8 | 90578646572 | 13578690034 | chengkaiyan@qq.com | 呈开颜 | | 9 | 177828332 | 13578690035 | leidongbao@126.com | 雷东宝 | +----+-------------+-------------+----------------------+-----------+ ``` 在openLooKeng中,通过如下sql语句可以直接获取该人脸图像对应的用户信息: ``` sql SELECT name FROM user_info WHERE user_no = face_search_by_image_url('http://120.26.179.169:8080/faces/宋运辉.png'); ``` #### 五、方案设计 openLooKeng通过SPI的思想进行扩展,我们只需要将我们的UDF以服务提供者插件的方式添加进去即可。 > 整体来看 整体来看,UDF主要工作就是将收到的人脸图像经过处理,然后到人脸库中查询该人脸的id,最后返回查询结果,如下图所示: ![image-20210320191812361](https://gitee.com/fisheeps/image-repository/raw/master/typro/20210320191812.png) > UDF内部流程设计 UDF内部的主要会做如下几方面的工作: - 第一次调用时会加载配置文件,配置文件里面包含`人脸库的地址、应用密钥、图像质量、人脸相似性阈值`等信息。 - 将人脸图像转为Base64编码格式。 - HTTP请求体里面是json字符串的形式,为了节约每次都通过序列化来构造这个json字符串所带来的时间开销,我将json字符串里面通用的部分缓存了起来,每次直接进一次行拼接即可。 - 解析HTTP响应结果,根据响应结果判断是否需要更新token然后重试。 - 返回人脸id; 其内部工作流程图如下所示: ![image-20210320192135359](https://gitee.com/fisheeps/image-repository/raw/master/typro/20210320192135.png) #### 六、安装使用和操作指引 > **环境要求:** - JDK 8, 64bit. - Maven 3.3.9+(用于构建). - 一台已经部署了openLooKeng的机器,版本不限,新的较好. - 安装MySQL 5.5+. - 向百度AI申请一个人脸识别服务免费版:[【百度人脸识别服务申请】](https://cloud.baidu.com/doc/FACE/s/xk37c1jn6). > **安装:** 1. 克隆下项目之后,在项目根目录运行以下命令将项目打包: ```bash mvn clean package -DskipTests ``` 等待相关依赖下载并完成打包之后,构建结果会放在生成的target目录中,如下: ```bash face-recognition-functions/ ├── face-recognition-functions.iml ├── pom.xml ├── README.en.md ├── README.md ├── src │ ├── main │ └── test └── target ├── archive-tmp ├── classes ├── face-recognition-functions-1.0 ├── face-recognition-functions-1.0.jar ├── face-recognition-functions-1.0-make-assembly.tar.gz ├── face-recognition-functions-1.0.zip ├── generated-sources ├── generated-test-sources ├── maven-archiver ├── maven-status └── test-classes ``` 2. 找到该udf打包生成的压缩文件`face-recognition-functions-1.0-make-assembly.tar.gz`,将之在任意地方解压会得到两个目录: ```bash . ├── face-recognition-functions-1.0 │ ├── animal-sniffer-annotations-1.14.jar │ ├── checker-qual-2.5.2.jar │ ├── error_prone_annotations-2.1.3.jar │ ├── face-recognition-functions-1.0.jar │ ├── gson-2.8.6.jar │ ├── guava-26.0-jre.jar │ ├── j2objc-annotations-1.1.jar │ ├── jsr305-3.0.2.jar │ └── snakeyaml-1.27.jar └── udf-config └── ai-service-config.yml ``` 3. 目录`face-recognition-functions-1.0`便是udf的jar包以及它的依赖的jar包;而目录`udf-config`是本udf的一些配置文件,例如调用http人脸识别服务需要的一些配置信息如url、应用密钥等; 4. 将`face-recognition-functions-1.0`拷贝到部署的openlookeng所在目录的plugin目录下面; 5. 将`udf-config`目录拷贝到部署的openlookeng所在目录的etc目录下面;里面的`ai-service-config.yml`文件即是存放这个UDF配置信息,如下: ```yaml baidu-ai: face-search: # 用于获取token, api-key: 0Ghei9nh1jlpFBc2a3F9T4d1 secret-key: ocGEkC6h4qmFlouG30Xcx2U8nqOIuhAT # 用于获取token的api oauth-url: https://aip.baidubce.com/oauth/2.0/token # 用于人脸搜索的api url: https://aip.baidubce.com/rest/2.0/face/v3/search # 在人脸库的中的哪些组里面进行搜索 group-id-list: - fisheep_group # 上传的图像的编码类型 image-type: BASE64 # 人脸图像质量要求 quality-control: LOW # 活体检测控制程度 liveness-control: NORMAL ``` 注意:这里白嫖的是百度AI的免费[【百度人脸识别服务】](https://cloud.baidu.com/doc/FACE/s/xk37c1jn6),个人免费赠送2QPS。因此申请成功之后,需要将上面的配置信息改为你自己的信息。 6. 重启openLooKeng服务; 启动时打印出来的日志中可以看见如下类似的日志,就说明该插件加载成功: ![image-20210320213906892](https://gitee.com/fisheeps/image-repository/raw/master/typro/20210320213906.png) 7. 启动成功之后用`openlk cli`进行连接,执行`show functions;`并进行过滤以`face`开头的函数,即可找到该udf,如下所示: ```bash (base) fisheep@fisheep-computer:~/usr/local/hetu-server-1.0.1/bin$ java -jar hetu-cli-1.0.1-executable.jar --server localhost:8090 lk> show functions; Function | Return Type | > ---------------------------------+------------------------------+-------------------------------> face_search_by_base64 | varchar | varchar > face_search_by_image_url | varchar | varchar > ``` #### 七、使用示例 1. 首先需要在MySQL中建立`user_info`表如下: ```base +----+-------------+-------------+----------------------+-----------+ | id | user_no | phone | email | name | +----+-------------+-------------+----------------------+-----------+ | 4 | 1223965229 | 15922513997 | noahyu@126.com | 余扬 | | 5 | 3396648765 | 13348679903 | yang_xun@163.com | 杨巡 | | 6 | 784487653 | 17765639087 | xunjianxinag@qq.com | 寻建祥 | | 7 | 38509876 | 16578909987 | songyunhui@gmail.com | 宋运辉 | | 8 | 90578646572 | 13578690034 | chengkaiyan@qq.com | 呈开颜 | | 9 | 177828332 | 13578690035 | leidongbao@126.com | 雷东宝 | +----+-------------+-------------+----------------------+-----------+ ``` 2. 首先需要在`hetu-server-1.0.1/etc/catalog`目录下面建立一个`mysql.properties`文件,里面存放的是连接MySQL服务器的信息,可参考 [MySQL连接器](https://openlookeng.io/zh-cn/docs/docs/connector/mysql.html)。 3. 重启openLooKeng服务,然后用`openlk cli`连接服务。 4. 准备一张人脸图片,用于模拟考勤场景,然后在`openlk cli`中执行如下SQL语句: ```sql lk> SELECT * FROM mysql.openlk.user_info WHERE user_no = face_search_by_image_url ('http://120.26.179.169:8080/faces/sonyunhui.png'); ``` 会得到如下结果,根据人脸图片,准确的从数据库中查到了该人脸对应的人员的信息: ```sql id | user_no | phone | email | name ----+----------+-------------+----------------------+-------- 7 | 38509876 | 16578909987 | songyunhui@gmail.com | 宋运辉 (1 row) Query 20210320_140644_00006_cga4a, FINISHED, 1 node Splits: 17 total, 17 done (100.00%) 0:01 [1 rows, 0B] [0 rows/s, 0B/s] ``` #### 参考资料 - openLooKeng官方网站:[openLooKeng](https://openlookeng.io/zh-cn/docs/docs/start.html) - Martin Kleppmann著,赵军平等译.数据密集型应用系统设计[M].中国电力出版社,2017.89-92