- 业务足,那必须硬件条件够,也就是服务器配置足够
- 业务不足,但还飙高如何处理?
- 业务足,硬件也足,代码问题?
思考一个场景,您这边需要根据你库中的一个表,持续的去抓取第三方接口数据然后更新入库你会如何处理?
//采用分页脚本去跑脚本
/**
* 对接方提供接口我方抓取:门店订单抓取至商城脚本
* desc: 抓门店订单入库
*/
public function actionGetOrderList()
{
set_time_limit(0);
date_default_timezone_set("PRC");
$store_list = array_column(Store::find()->where(['is_delete' => 0, 'is_recycle' => 0])->select(['id'])->asArray()->all(), 'id');
if (empty($store_list)) {
return;
}
$limit = 100;
//遍历商户
foreach ($store_list as $store_id) {
if($store_id == 1){
$option = Option::get('shop_order_list', 0, 'admin', '1');
}else{
$option = Option::get('shop_order_list', $store_id, 'admin');
}
if(!$option){
continue;
}
$option = \Yii::$app->serializer->decode($option);
if(!$option){
continue;
}
$erp_member_list = Option::get('erp_member_list', $store_id, 'admin', '{"status":"0"}');
if(!$erp_member_list){
continue;
}
$erp_member_list = \Yii::$app->serializer->decode($erp_member_list);
if ($erp_member_list['status'] != 1) {
continue;
}
try {
//循环会员卡结果集合
$membership_data = \Yii::$app->cache->get(CacheKeyEnum::USER_MEMBER_DATA.$store_id);
if(!$membership_data){
$count = UserMember::find()->where(['store_id' =>$store_id])->andWhere(['IS NOT', 'membership_number', null])->count();
\Yii::$app->cache->set(CacheKeyEnum::USER_MEMBER_DATA.$store_id,$count);
usleep(10*1000);
}
$count = \Yii::$app->cache->get(CacheKeyEnum::USER_MEMBER_DATA.$store_id);
$j = \Yii::$app->cache->get(CacheKeyEnum::USER_MEMBER_DATA.$store_id."_i") ? : 0 ; //从上次跑的页数开始跑
for ($i = $j; $i <= floor($count / $limit); $i++) {
$membership_numbers = UserMember::find()->select(['user_id','membership_number','wechat_union_id'])->where(['store_id' => $store_id])->andWhere(['IS NOT', 'membership_number', null])->limit($limit)->offset($i*$limit)->asArray()->all();
$membership_numbers = array_column($membership_numbers,null, 'membership_number');
foreach ($membership_numbers as $k =>$v){
if(empty($v['membership_number'])){
continue;
}
$param = [
'membership_number' => $v['membership_number'],
];
$res = requestCardApi($option['order_list_url'], $param,$option['app_key'],1,true); //true限制超时时间3秒杀;防止对接方超时导致其他商户不可用
if($res['code'] == 0){
$user = UserMember::find()->where(['store_id'=>$store_id,'wechat_union_id'=>$v['wechat_union_id']])->one();
$user_id = $v['user_id'] ? : ( $user ? $user->id : 0 );
$this->saveData($res['data'],$user_id,$store_id);
}else{
\Yii::error($res, date('Y-m-d H:i:s').'商户接口错误:会员卡抓取门店订单记录失败,商户ID:'.$store_id.',会员卡号:'.$v['membership_number']);
}
}
//记录跑的页数,跑完删除缓存,没跑完下次接着跑
\Yii::$app->cache->set(CacheKeyEnum::USER_MEMBER_DATA.$store_id."_i",$i+1);
usleep(10*1000);
if(floor($count / $limit) == $i){
\Yii::$app->cache->delete(CacheKeyEnum::USER_MEMBER_DATA.$store_id."_i");
\Yii::$app->cache->delete(CacheKeyEnum::USER_MEMBER_DATA.$store_id);
}
unset($membership_numbers);
}
} catch (\Exception $e) {
\Yii::error(['data' => $e->getMessage()], date('Y-m-d H:i:s')."会员卡抓取门店订单记录失败商户ID:".$store_id);
}
}
}
//直接大数组全部处理
public function erpCoupon($erp_coupon_type)
{
date_default_timezone_set('PRC');
$list = Store::find()->alias('S')->where(['S.is_delete' => 0, 'S.is_recycle' => 0])
->leftJoin(Option::tableName() . ' as O', 'O.store_id=S.id')
->where(['O.name' => 'interface_list'])
->select(['S.id', 'O.value'])->asArray()->all();
foreach ($list as $key => $value) {
$option = json_decode(json_decode($value['value'], true), true);
if ($option['is_docking'] == 1) {//有开 获取相应的策略的id 的券
$user_member = UserMember::find()
->where(['store_id' => $value['id']])
->andWhere(['<>', 'membership_number', ''])
->select(['membership_number', 'user_id'])
->asArray()
->all();
if (!empty($user_member)) {
$user_ids = array_column($user_member, 'user_id');
$query = UserCoupon::find()
->alias('UC')
->leftJoin(Coupon::tableName() . ' as C', 'UC.coupon_id = C.id')
->where(['>', 'C.apprangeid', 0])
->andWhere(['UC.is_expire' => 0, 'UC.store_id' => $value['id'], 'UC.user_id' => $user_ids, 'UC.erp_coupon_type' => $erp_coupon_type])
->orderBy('UC.addtime DESC')
->select(['UC.id', 'UC.user_id', 'UC.erp_coupon_type', 'UC.is_use', 'UC.is_expire', 'C.apprangeid']);
if ($erp_coupon_type == 2) {
$query->andWhere(['UC.is_use' => 0]);
}
$user_coupon = $query->asArray()->all();
$membership_number_arr = array_column($user_member, 'membership_number', 'user_id');
foreach ($user_coupon as $k => $val) {
$user_exist = array_key_exists($val['user_id'], $membership_number_arr);
if ($user_exist) {
$membership_number = $membership_number_arr[$val['user_id']];
if (empty($membership_number)) continue;
} else {
continue;
}
if ($erp_coupon_type == 0 && $val['is_expire'] == 0 && $val['is_use'] == 0) {
$redis = \Yii::$app->redis;
$cacheKey = CacheKeyEnum::ERP_COUPON . $val['id'];
$cache = $redis->get($cacheKey);
if (!$cache) {
$redis->setex($cacheKey, 300, $val['id']);
$param = [
'insidercardno' => $membership_number,//会员卡号
'apprangeid' => $val['apprangeid'], //策略ID
'num' => '1',
'couponid' => $val['id'], //第三方代金券ID
];
$res = requestApi($option['generate_url'], $param, $option['app_key']);
if ($res['code'] == 1) {
UserCoupon::updateAll(['erp_coupon_type' => 1], ['id' => $val['id']]);
}
}
} else {
if ($val['erp_coupon_type'] == 1 && $val['is_use'] == 1) {//使用下,才能核销
$data = [
'insidercardno' => $membership_number,
'couponid' => $val['id'],
'exectype' => '1'
];
$res = requestApi($option['write_off_url'], $data, $option['app_key']);
if ($res['code'] == 1) {
UserCoupon::updateAll(['erp_coupon_type' => 2], ['id' => $val['id']]);
}
}
if ($val['erp_coupon_type'] == 2 && $val['is_use'] == 0) {// 退货 返回券时,被核销的优惠券 也返回 已领取
$data = [
'insidercardno' => $membership_number,
'couponid' => $val['id'],
'exectype' => '2'
];
$res = requestApi($option['write_off_url'], $data, $option['app_key']);
if ($res['code'] == 1) {
UserCoupon::updateAll(['erp_coupon_type' => 1], ['id' => $val['id']]);
}
}
}
}
}
}
}
}
对比效果;CPU直接爆满;而且使用分页的方式任务更多但CPU反而更小!是具有参考意义的。
当然CPU飙高的原因很多,任务多,处理时间久,线程不够等,我们也可以根据top中sleeping进程数调高初始进程,或者业务上分离等。。。
上面说到的是如何防止CPU飙高,下面说说与第三方对接的坑如何避免502?
第三方提供一个接口,让取线下ERP流水订单展示在线上,但是那帮兔崽子竟然不做分页!!一次全部返回,之前因为一些特殊需求对数据进行了入库处理,现在发现不行!!会处理超时,所有仅在展示时直接调用第三方接口进行缓存!!不要入库了!!!