为了账号安全,请及时绑定邮箱和手机立即绑定

【轻知识】记录一次代码优化以及想法

标签:
MySQL

发布之前对文章中的一些代码做了重命名和删除。你懂的。姑且之前的接口叫做old-share-api ,新接口叫做new-share-api。

写本文,只是想通过一个比较简单的接口,来记录下优化的想法。这个简单的接口,大概做的事情就是页面的分享,生成一个图片,分享到朋友圈或者发给好友。

重构

重构测试

线上环境:

old-share-api(lumen框架,php7) 4核8g
new-share-api  (yaf,php7) 2核4g

测试代码片段(循环发送请求):

$uid = 790729;while($start < $end) {// 发送一百个请求$start++;
$uid++;
$params = ['uid'=>$uid,'link'=>'https://m.yanshinian.com/index.html?jsonData=/act_html/cache/data/7f690e132491_43656.json','pid'=>14,'request_token'=>'000092659614c41548570563',
];
$result = $client->post($url, ['form_params' => $params])->data();echo \GuzzleHttp\json_encode($result);
}

代码说明:

页面4365 的分享模块,没有加ad_id,所以只是简单的,拿shareKey,生成二维码。
为什么$uid 累计发送请求呢?因为用同一个uid 第二次请求的时候会走缓存。old从数据库拿之前分享的。new-share从redis拿。

返回结果值:

{"status":0,"msg":"分享成功","data":{"shareImage":"https://img-cdn.yanshinian.com/o_1cs8fvits1o2nlim1rhs1uqg5v.jpg?watermark/1/image/aHR0cHM6Ly9pbWFnZS1fd3VkwuandvMTY2/dissolve/100/gravity/NorthWest/dx/545/dy/950/ws/0%7cimageView2/2/q/80/format/jpg/quality/75!%7cimageslim","pageUrl":"https://m.yanshinian.com/index.html?jsonData=/act_html/cache/data/7f690e132491_43656.json","secne":"ye32d72L","ad_material_info":[

]
}
}

线上测试结果(尽可能量化)

下面是不走缓存的情况下(走缓存的没测,一个取redis,一个取mysql,就不测了)

old-share

统计了23次请求,平均 1.3秒多。

cat  /home/www/logs/nginx/old-share.yanshinian.com.access.log | grep "690e132491_4365" |  awk -F'\001' '{sum+=$7;lineCount+=1} END {print "lineCount= " lineCount; print "sum= " sum; print "average = " sum/NR}'lineCount= 23
sum= 30.427
average = 1.32291

new-share 50次请求 0.4秒(当然抽出23次还是快)

cat /home/www/logs/nginx/new-share.yanshinian.com.access.log  | grep "690e132491_4365" |  awk -F'\001' '{sum+=$7;lineCount+=1} END {print "lineCount= " lineCount; print "sum= " sum; print "average = " sum/NR}'lineCount= 50sum= 20.908average = 0.41816

重构前的思考

xmind 梳理图 仅供参考

webp

old-share-api重构.png

思考如下:

1.接口影响范围

比如,这个接口调用的点有多少个,列出来,每个调用点可能传的参数不一样。

如果参数不一样,那么做的事情不一样,能否拆成几个接口。

2.梳理逻辑

把没用的逻辑拿掉。

把函数瘦身。

梳理出耗时的点。

3.重构的预期(要做测试)

afp分享慢,重构就是为了快。重构之后要做测试。

回顾下代码

old  片段

// 查询当前活动页是否已被分享$pageShareRecord = DB::connection($this->db)->table('share_record')
->select('id', 'module', 'image', 'page_url', 'updated_at', 'add_data', 'scene')
->where(['page_id' => $page_id, 'ad_uid' => $uid])
->orderBy('id', 'desc')->first();/**
* 对比module_sort,区分当前活动页是都变动,发生变动返回新的连接
* 一期上线之后,后续添加trackId用来统计,trackId格式变动,用来处理旧的分享trackId错误的情况:没有trackId、错误trackId
*/if (
strnatcmp($pageInfo['module_sort'], $pageShareRecord['module']) != 0 ||
strpos($pageShareRecord['page_url'], '&trackId') === false ||
strpos($pageShareRecord['page_url'], ':_:') !== false) {// 获取 shareKey$shareKey = $this->getShareKey($uid);// 此路径不再走  静态页中 lua 环节$uri = str_replace("cache/data/", "", strstr(strstr($link, 'act_html'), '.json', true) . '.json');// 因json内容过大,curl get 可能获取到的数据不完整,使用 guzzle 代替$client = new \GuzzleHttp\Client();
$host = env('ADMIN_HOST');

$response = $client->request('GET', $host . '/' . $uri . '?request_token=' . $request_token);if ($response->getStatusCode() != 200) {throw new \Exception("guzzle request file content error");
}

$originJsonData = $response->getBody()->getContents();// 获取活动页中商品数据,使用 trackId,组装商品链接$pageProductInfo = json_decode($originJsonData)), true);if (!empty($pageProductInfo['module']) && is_array($pageProductInfo['module'])) {foreach ($pageProductInfo['module'] as $k => &$v) {if ($v['type_id'] == 16 && $is_promotion) {  // 这里取出 分享模块的数据,有分享图$image = $v['value']['qr_bg_img'];if (isset($v['value']['ad_id']) && $v['value']['ad_id']) {
$productList = $this->getProductListByApi([$v['value']['ad_id']], $uid, $trackId);
$v['value']['ad_material_info']['ad_sales_url'] = $productList['data']['data'][$v['value']['ad_id']];
$addData = $v['value']['ad_material_info'];
}
}
}
} else {throw new \Exception("module parameter does not exist [curl afp result] : " . json_encode($pageInfo, JSON_UNESCAPED_UNICODE));
}
$link = str_replace('/data', '/page/share', $link) . '&trackId=' . $trackId . '&shareKey=' . $shareKey; //替换完路径,nginx lua走其他处理$pid = $request->input('pid', 0);
$hostConfig = config('host');if (isset($hostConfig[$pid])) {
$tempArr = explode('.com', $link);
$link = $hostConfig[$pid] . $tempArr[1];
}if ($is_promotion) {

$scene = '';if ($res = self::wxaCodeImge($link, $image, $uid, $pid)) {list($imageUrl, $scene) = $res;
} else {
$imageUrl = self::_promotionImge($link, $image, $uid);
}
$image = $imageUrl;

new-share 片段

// 2 根据用户id 拿去数据库的缓存$userShareInfoKey = sprintf(RedisKey::USER_SHARE_INFO, $uid, $pageId);
$userShareInfo = json_decode(RedisHelper::get($userShareInfoKey), true);// 3 根据页面id 拿取 页面分享模块的 缓存$pageShareInfo = json_decode(RedisHelper::get(sprintf(RedisKey::PAGE_INFO, $pageId)), true);// 4 比较签名 这个签名 是分享模块缓存数据的签名,用户分享后的缓存数据也需要 带上,为的是以后对比判断if (isset($userShareInfo["sign"]) && $userShareInfo["sign"] == $pageShareInfo["sign"]) { // 签名相等,说明分享模块数据没有变更过,直接返回分享的数据$this->success($userShareInfo, "分享成功");
}// 如果签名不同 走分享流程// 获取shareKey$shareKey = UserModel::getShareKey($uid);if ($shareKey == "") {throw new Exception("shareKey异常");
}
$link = str_replace('/data', '/page/share', $link) . '&trackId=' . $trackId . '&shareKey=' . $shareKey; //替换完路径,nginx lua走其他处理// 1 拿到背景图// 2 生成 小程序码 或 二维码$scene = '';if ($pid == 14) {
$xcxCodeResult = Image::getXcxcode($link, $shareKey);
$image = $xcxCodeResult['imgUrl'].'&imageView2/2/w/166';
$scene = $xcxCodeResult['sence'];
} else {
$image = Image::getQrCode($link);
}

$shareImage = Image::getSharePageImage($pageShareInfo['share_module']['qr_bg_img'], $image);// 水印图$result = ['shareImage'=>$shareImage,'pageUrl'=>$link,'secne'=> $scene,'ad_material_info'=>[],
];// 把分享模块的sign 值附上$result['sign'] = $pageShareInfo['sign']; // 设置签名RedisHelper::setex($userShareInfoKey, ($pageShareInfo['end_time']), json_encode($result));$this->success($result, '分享成功');

具体优化了哪些呢?

1.减少了一次网络请求

$client->request('GET', $host . '/' . $uri . '?request_token=' . $request_token);

拿页面的json数据,最初这么做两个目的

  • 主要是拿通过request_token 获取动态的数据,然后生成新的页面(后来复用一个页面,生成不同页面的需求就废了)

  • 其次拿页面中分享模块的数据

优化后:

分享模块数据存入redis。从redis拿。

2.减少了两次mysql查询操作

DB::connection($this->db)->table('page')->select('module_sort', 'start_time')->where('id', $page_id)->first();
DB::connection($this->db)->table('share_record')
->select('id', 'module', 'image', 'page_url', 'updated_at', 'add_data', 'scene')
->where(['page_id' => $page_id, 'ad_uid' => $uid])
->orderBy('id', 'desc')->first();
if (
strnatcmp($pageInfo['module_sort'], $pageShareRecord['module']) != 0 ||strpos($pageShareRecord['page_url'], '&trackId') === false ||strpos($pageShareRecord['page_url'], ':_:') !== false)

这样做的目的,是为了比较页面是否变更过。如果页面变更了(strnatcmp($pageInfo['module_sort'], $pageShareRecord['module']) != 0)。用户下一次分享,不从缓存中(mysql)拿数据,重新生成一次分享数据。

优化之后:

缓存用了redis。对比通过sign字段。页面分享数据,在发布的时候缓存到redis,对数据加密保存一个sign字段。用户在手机分享后,把这个sign也放到自己缓存中。下次做sign对比。

虽然mysql少了。redis对应的就增加了。

  1. 代码拆分

这个就不说了。

文摘

1.《系统运维之为什么每个团队存在大量的烂代码》

优化性能的2个观点:

  • 优化主要部分,把一次网络I/O改为内存计算带来的收益远大于我们捯饬编译器优化之类的东西。

  • 性能优化之后要有量化数据,明确说出优化后哪个指标提升了多少。

具体优化措施,无外乎以下几类:

  • 让计算靠近存储

  • 优化算法的时间复杂度

  • 减少无用的操作

  • 并行计算



作者:言十年
链接:https://www.jianshu.com/p/a5d064066155


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消