使用hyperf结合DorisDB数仓的项目实战体验
项目背景
做的是一个医疗数据分析项目,在此之前项目其实已经开发完成并且运行一段时间了,但是因为项目的体验极差以及对开发人员很不友好,与领导商议对项目进行重构
项目逻辑
这个项目完全是一个分析性项目,大致的逻辑就是从病历库中计算数据到当前项目库中,然后从当前库查询展示到前台,主要涉及到数据排名、查询。
数据量:百万级
技术选型
框架:本来打算用的springboot进行重构,因为公司整体的项目都在向java语言转移。但是因为当时业务部门对时间卡的很紧,考虑到这个项目跟语言的关系也不是很大,决定使用hyperf进行快速开发。
(因为以前的版本是用的laravel框架,可以很效率的转移到hyperf)
数据库:之前用的是mysql,但是计算和查询的速度都是不尽人意。因为不是直接的业务数据库,于是决定选用OLAP型数据库搭建数仓。最后在clickhouse和Dorisdb中选择了Dorisdb
数仓搭建
当前项目的计算数据全部基于病历数据库,所以我们要对病历数据库建一个数仓,然后在这个基础上新建几张计算结果的表就可以了,我们平台业务的要求是月更一次,所以对数据实时性的要求很低,但是后面上线之后我们改成了T+1更新模式。
数据同步
因为对实时性没要求,所以采用的是kettle定时脚本的模式,这里不对kettle做过多介绍,其他类似产品还有datax、canal
数仓使用
数仓的具体使用每个产品也都有自己的特点,不做过多阐述。这里具体说下Dorisdb,他本身的语法和mysql极度相似,所以好多sql都是通用的,像laravel、hyperf这些框架的model也是直接支持使用的。这也是当时选用这个数据库的原因之一。其他单独特性自行去官网了解
计算数据
到这一步才算把swoole的优势发挥出来,dorisdb本身也比mysql快,但是框架也起到了很大的作用
直接贴结果,前后计算时间对比:
60w病历数据(汇总字段500个左右) + 100w+随访数据时间参考:
老版(laravel + mysql) 50小时左右
新版(hyperf + dorisdb) 开启多协程(50个) 40分钟左右
新版(hyperf + dorisdb) 一个协程 5个小时
访问速度
在老版中最慢的一个接口需要2min,在更新后接口都能达到秒级
ab测试结果因为老项目停运了当时结果也没保存,所以就不贴出来了
访问速度整体看来,我认为作用是数仓 > 框架处理的,因为之前接口慢的主要原因其实就是在于数据库层面
注意事项
hyperf框架日常使用和laravel基本没有太大区别,只是在运行的时候是基于一个进程的。所以直接改动代码可能不会像fpm框架那样直接看到效果,推荐使用官方推荐的热部署
轮子方面确实与laravel还有很大差距,目前基于hyperf的拓展还是很少的,但是有很多拓展在两种框架都是通用的
中同一个客户端的请求都是单例的,所以你不能直接更改request数据,这个问题我当时郁闷了好久
如果有个跑数据脚本,想启用多个协程,一定要控制协程的数量,比如我跑数据开启的是50个协程,那么一旦数量达到50个后,就要等全部协程工作完成后再开启下一轮的50个协程。这个具体的协程数量可以根据数据库配置和服务器配置自己定义
协程管理可以用WaitGroup来控制,用法也很简单,swoole官方文档和hyperf文档上都有介绍
贴一段跑数据的demo
$wg = new WaitGroup();
RedisUtil::set($this->redisKey, 0);
foreach ($provinces as $province) {
for ($year = 2017; $year <= date('Y'); $year++) {
$cities = $provincesData[$province];
foreach ($cities as $city) {
// 如果协程数量达到50 挂起等待执行完成后开启新协程
if ($wg->count() >= 50) {
$wg->wait();
}
$wg->add();
// 具体业务代码
RedisUtil::incrBy($this->redisKey, count($all));
SyncLog::updateLog($log_id, RedisUtil::get($this->redisKey), 0);
$this->output->writeln("同步数量:".RedisUtil::get($this->redisKey));
$wg->done();
}
}
}
部署
传统的php项目配合nginx转发一下目录可能就ok了,但是swoole的http server是基于一个进程的,所以可以用nginx做一个反向代理。
因为有进程的原因,还是使用docker部署,不然还要守护这个进程,而且官方的dockerfile也为我们准备了最合适的php版本,不用额外去装一些拓展。
关于项目重启期间访问失败的问题,如果一定要项目不能停止,可以将项目分两个端口部署两次用nginx做负载均衡即可
这里贴出dockerfile和部署脚本
# Default Dockerfile
#
# @link https://www.hyperf.io
# @document https://hyperf.wiki
# @contact group@hyperf.io
# @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
FROM hyperf/hyperf:7.4-alpine-v3.11-swoole
LABEL maintainer="Hyperf Developers <group@hyperf.io>" version="1.0" license="MIT" app.name="Hyperf"
##
# ---------- env settings ----------
##
# --build-arg timezone=Asia/Shanghai
ARG timezone
ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \
#APP_ENV=prod \
SCAN_CACHEABLE=(true)
# update
RUN set -ex \
# show php version and extensions
&& php -v \
&& php -m \
&& php --ri swoole \
# ---------- some config ----------
&& cd /etc/php7 \
# - config PHP
&& { \
echo "upload_max_filesize=128M"; \
echo "post_max_size=128M"; \
echo "memory_limit=-1"; \
echo "date.timezone=${TIMEZONE}"; \
} | tee conf.d/99_overrides.ini \
# - config timezone
&& ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
&& echo "${TIMEZONE}" > /etc/timezone \
# ---------- clear works ----------
&& rm -rf /var/cache/apk/* /tmp/* /usr/share/man \
&& echo -e "\033[42;37m Build Completed :).\033[0m\n"
WORKDIR /opt/www
# Composer Cache
# COPY ./composer.* /opt/www/
# RUN composer install --no-dev --no-scripts
COPY . /opt/www
RUN composer install --no-dev -o && php bin/hyperf.php
EXPOSE 9501
ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"]
# 容器名
containerName='你的项目名'
cid=$(docker ps | grep $containerName | awk '{print $1}')
if [ -n "$cid" ]
then
echo "prepare to stop $cid"
docker stop $cid
echo "stop $cid success"
echo "prepare to remove $cid"
docker rm $cid
echo "stop $cid success"
else
echo "no such container"
fi
# 镜像名
oldBuild="qc_doris"
oldImageID=$(docker images | grep $oldBuild | awk '{print $3}')
if [ -n "$oldImageID" ]
then
echo "prepare to remove old image"
docker rmi $oldImageID
echo "remove $oldImageID success"
else
echo "no such image"
fi
docker build -t qc_doris:0.1 .
echo "build success"
#run
docker run -d -v $(pwd)/runtime/logs:/opt/www/runtime/logs \
-v $(pwd)/.env:/opt/www/.env \
-v $(pwd)/static:/opt/www/static \
-p 9501:9501 --privileged=true --name xxx xxx:0.1
echo "running success"
# -d:后台运行; -p:将容器暴露端口映射到服务器端口 imageName
总结
如果从laravel过渡到hyperf整体还是十分丝滑的,甚至你不怎么了解swoole也可以直接使用,但是还是推荐大家把swoole官方的文档啃一遍,有助于理解框架的一些设计。使用体验目前还不错
项目整体重构时间加上数仓建设其实只用了一个月,去年年底上线到现在已经稳定运行了有一段时间。这次重构让业务部门很满意,也让领导很满意。这里推荐大家去拥抱新的技术但是同时也不要为了技术而技术。一个项目开发不仅仅是IT部门的工作还要与业务有各种磨合,如果有些吃力不讨好的技术非要上我觉得是完全没有必要的。
公司因为整体项目都在用java重构,下次有时间我会讲讲如何将php项目用java进行重构以及遇到的各种问题和解决办法。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: