异步redis队列实现 数据入库的方法

业务需求

app客户端向服务端接口发送来json 数据 每天 发一次 清空缓存后会再次发送

出问题之前业务逻辑:

php 接口 首先将 json 转为数组 去重 在一张大表中插入不存在的数据

该用户已经存在 和新增的id

入另一种详情表

问题所在:

当用户因特殊情况清除缓存 导致app 发送json串 入库并发高 导致CPU 暴增到88% 并且居高不下

优化思路:

1、异步队列处理

2、redis 过滤(就是只处理当天第一次请求)

3、redis 辅助存储app名称(验证过后批量插入数据app名称表中)

4、拼接插入的以及新增的如详细表中

解决办法:

1、接口修改 redis 过滤 + 如list队列 并将结果存入redis中

首先 redis将之前的历史数据放在redis 哈希里面 中文为键名 id 为键值

<?php
/**
 * Created by haiyong.
 * User: jia
 * Date: 2017/9/18
 * Time: 20:06
 */
namespace App\Http\Controllers\App;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;

class OtherAppController extends Controller{

 /**
  * app应用统计接口
  * @param Request $request
  * @return string
  */
 public function appTotal(Request $request)
 {
  // //历史数据入库
  //$redis = Redis::connection('web_active');
  // $app_name = DB::connection('phpLog')->table('app_set_name')->where("appName", '<>', ' ')->lists('id', 'appName');
  // $str = '';
  // foreach ($app_name as $key => $val) {
  //  $str.= "{$val} {$key} ";
  // }
  // $redis->hmset('app_name', $app_name);
  // echo $str;exit;
  $result = $request->input('res');
  $list = json_decode($result, true);
  if (empty ($list) || !is_array($list)) {
   return json_encode(['result' => 'ERROR', 'msg' => 'parameter error']);
  }
  $data['uid'] = isset($list['uid']) ? $list['uid'] : '20001' ;
  $data['time'] = date('Y-m-d');
  $redis_key = 'log_app:'.$data['time'];
  //redis 过滤
  $redis = Redis::connection('web_active');
  //redis 键值过期设置
  if (empty($redis->exists($redis_key))) {
   $redis->hset($redis_key, 1, 'start');
   $redis->EXPIREAT($redis_key, strtotime($data['time'].'+2 day'));
  }
  //值确定
  if ($redis->hexists($redis_key, $data['uid'])) {
   return json_encode(['result' => 'SUCCESS']);
  } else {
   //推入队列
   $redis->hset($redis_key, $data['uid'], $result);
   $redis->rpush('log_app_list', $data['time'] . ':' . $data['uid']);
   return json_encode(['result' => 'SUCCESS']);
  }
 }
}

2、php 脚本循环 监控redis 队列 执行逻辑 防止内存溢出

mget 获取该用户的app id 不存在就会返回null

通过判断null 运用redis 新值作为自增id指针 将null 补齐 之后批量入mysql 并跟新redis 哈希 和指针值 并入库 详情表

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;

class AppTotal extends Command
{
 /**
  * The name and signature of the console command.
  *
  * @var string
  */
 protected $signature = 'AppTotal:run';

 /**
  * The console command description.
  *
  * @var string
  */
 protected $description = 'Command description';

 /**
  * Create a new command instance.
  *
  * @return void
  */
 public function __construct()
 {
  parent::__construct();
 }

 /**
  * Execute the console command.
  *
  * @return mixed
  */
 public function handle()
 {
   //历史数据入库
  // $redis = Redis::connection('web_active');
  // $app_name = DB::connection('phpLog')->table('app_set_name')->where("appName", '<>', ' ')->lists('id', 'appName');
  // $redis->hmset('app_name', $app_name);
  // exit;
   while(1) {
   $redis = Redis::connection('web_active');
   //队列名称
   $res = $redis->lpop('log_app_list');
   //开关按钮
   $lock = $redis->get('log_app_lock');
   if (!empty($res)) {
    list($date,$uid) = explode(':',$res);
    $result = $redis->hget('log_app:'.$date, $uid);
    if (!empty($result)) {
      $table_name = 'app_total'.date('Ym');
      $list = json_decode($result, true);
      $data['uid'] = isset($list['uid']) ? $list['uid'] : '20001' ;
      $data['sex'] = isset($list['sex']) ? $list['sex'] : '' ;
      $data['device'] = isset($list['device']) ? $list['device'] : '' ;
      $data['appList'] = isset($list['list']) ? $list['list'] : '' ;
      //数据去重 flip比unique更节约性能
      $data['appList'] = array_flip($data['appList']);
      $data['appList'] = array_flip($data['appList']);
      $data['time'] = date('Y-m-d');
      //app应用过滤
      $app_res = $redis->hmget('app_name', $data['appList']);
      //新增加app数组
      $new_app = [];
      //mysql 入库数组
      $mysql_new_app = [];
   //获取当前redis 自增指针
   $total = $redis->get('app_name_total');
   foreach ($app_res as $key =>& $val) {
    if (is_null($val)) {
     $total += 1;
     $new_app[$data['appList'][$key]] = $total;
     $val = $total;
     array_push($mysql_new_app,['id' => $total, 'appName'=> $data['appList'][$key]]);
    }
   }
   if (count($new_app)){
    $str = "INSERT IGNORE INTO app_set_name (id,appName) values";
    foreach ($new_app as $key => $val) {
    $str.= "(".$val.",'".$key."'),";
    }
    $str = trim($str, ',');
    //$mysql_res = DB::connection('phpLog')->table('app_set_name')->insert($mysql_new_app);
    $mysql_res = DB::connection('phpLog')->statement($str);
    if ($mysql_res) {
     // 设置redis 指针
     $redis->set('app_name_total', $total);
     // redis 数据入库
     $redis->hmset('app_name', $new_app);
    }
  }
    // 详情数据入库
    $data['appList'] = implode(',', $app_res);
      //app统计入库
      DB::connection('phpLog')->statement("INSERT IGNORE INTO ".$table_name." (uid,sex,device,`time`,appList)
  values('".$data['uid']."',".$data['sex'].",'".$data['device']."','".$data['time']."','".$data['appList']."')");
      //log 记录 当文件达到123MB的时候产生内存保错 所有这个地方可是利用日志切割 或者 不写入 日志
      Storage::disk('local')->append(DIRECTORY_SEPARATOR.'total'.DIRECTORY_SEPARATOR.'loaAppTotal.txt', date('Y-m-d H:i:s').' success '.$result."\n");
  } else {
   Storage::disk('local')->append(DIRECTORY_SEPARATOR.'total'.DIRECTORY_SEPARATOR.'loaAppTotal.txt', date('Y-m-d H:i:s').' error '.$result."\n");
  }
   }
   //执行间隔
   sleep(1);
   //结束按钮
   if ($lock == 2) {
    exit;
   }
   //内存检测
   if(memory_get_usage()>1000*1024*1024){
    exit('内存溢出');//大于100M内存退出程序,防止内存泄漏被系统杀死导致任务终端
   }
  }
 }
}

3、执定 定时任务监控脚本执行情况

crontab -e

/2 * * * * /bin/bash /usr/local/nginx/html/test.sh 1>>/usr/local/nginx/html/log.log 2>&1

test.sh 内容 (查看执行命令返回的进程id 如果没有就执行命令开启)

#!/bin/bash
alive=`ps -ef | grep AppTotal | grep -v grep | awk '{print $2}'`
if [ ! $alive ]
then
 /usr/local/php/bin/php /var/ms/artisan AppTotal:run > /dev/null &
fi

记得授权哦 chmod +x test.sh

笔者用的laravel 框架 将命令激活丢入后台

执行命令

 /usr/local/php/bin/php /var/ms/artisan AppTotal:run > /dev/null &

完事直接 ctrl -c 结束就行 命令以在后台运行 可以用shell 中的命令查看进程id

这样就实现队列异步入库

还有很多问题需要优化!!大致功能已经实现!!!!!!

优化完成后cpu

以上这篇异步redis队列实现 数据入库的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • redis实现简单队列

    在工作中,时常会有用到队列的场景,比较常见的用rabbitMQ这些专业的组件,官网地址是:http://www.rabbitmq.com,重要的是官方有.net的客户端,但是如果对rabbitMQ不熟悉的话,建议使用第三方封装好的 EasyNetQ,rabbitMQ比较适合对安全性,稳定性要求较高的地方,但有时我们也会有对这方面要求不是很高的场景,比如:文章阅读数,实时性要求不是很高的地方,所以我想到了用redis来做队列. redis 的List结构本身就是一个链表 (双向链表),所以符合我们

  • 详解thinkphp+redis+队列的实现代码

    1,安装Redis,根据自己的PHP版本安装对应的redis扩展(此步骤简单的描述一下) 1.1,安装 php_igbinary.dll,php_redis.dll扩展此处需要注意你的php版本如图: 1.2,php.ini文件新增 extension=php_igbinary.dll;extension=php_redis.dll两处扩展 ok此处已经完成第一步redis环境搭建完成看看phpinfo 项目中实际使用redis 2.1,第一步配置redis参数如下,redis安装的默认端口为6

  • redis中队列消息实现应用解耦的方法

    消息队列的场景 我们都知道,消息是在两台计算机之间传送的数据单位,这个"消息"可以非常简单,例如只包含文本字符串,也可以更复杂,可能包含嵌入对象.而所谓的"消息队列"是在消息的传输过程中保存消息的容器.在web程序中,可能我们需要将用户的请求数据更新或者添加到数据库中,但是在高炳发的情况下,虽然作为用户的我们不知道后台是什么原因,但是依旧会抱怨或者吐槽这个程序反应缓慢,比如在过去的几年里,你有没有吐槽过12306抢票很难?反应很慢?有没有在使用某个程序的时候收到&q

  • Redis 实现队列原理的实例详解

    Redis 实现队列原理的实例详解 场景说明: ·用于处理比较耗时的请求,例如批量发送邮件,如果直接在网页触发执行发送,程序会出现超时 ·高并发场景,当某个时刻请求瞬间增加时,可以把请求写入到队列,后台在去处理这些请求 ·抢购场景,先入先出的模式 命令: rpush + blpop 或 lpush + brpop rpush : 往列表右侧推入数据 blpop : 客户端阻塞直到队列有值输出 简单队列: simple.php $stmt = $pdo->prepare('select id, c

  • 异步redis队列实现 数据入库的方法

    业务需求 app客户端向服务端接口发送来json 数据 每天 发一次 清空缓存后会再次发送 出问题之前业务逻辑: php 接口 首先将 json 转为数组 去重 在一张大表中插入不存在的数据 该用户已经存在 和新增的id 入另一种详情表 问题所在: 当用户因特殊情况清除缓存 导致app 发送json串 入库并发高 导致CPU 暴增到88% 并且居高不下 优化思路: 1.异步队列处理 2.redis 过滤(就是只处理当天第一次请求) 3.redis 辅助存储app名称(验证过后批量插入数据app名

  • Django结合使用Scrapy爬取数据入库的方法示例

    在django项目根目录位置创建scrapy项目,django_12是django项目,ABCkg是scrapy爬虫项目,app1是django的子应用 2.在Scrapy的settings.py中加入以下代码 import os import sys sys.path.append(os.path.dirname(os.path.abspath('.'))) os.environ['DJANGO_SETTINGS_MODULE'] = 'django_12.settings' # 项目名.se

  • bootstrap jquery dataTable 异步ajax刷新表格数据的实现方法

    异步请求 var postData = { "env_name" : new_env_name, "env_url": new_env_url, "env_desc" : new_env_desc }; $.ajax({ type: 'POST', url : '/test_env_add/', data : postData, dataType : 'json', success : function(data){ $('#table_test

  • mayfish 数据入库验证代码

    一般在把数据写入数据库之前,先对将要写入的数据进行校验,可以避免出现比较严重的安全问题(例如一般性的SQL注入攻击). mayfish 可以灵活的自定义将要执行写入的数据内容的校验规则,以减少开发人员手动对每一个字段的数据进行校验的麻烦. 例子如下: 一.首先定义数据库模块 复制代码 代码如下: <?php class MemberModel extends AppModel { /** 设置数据库表名称 **/ protected $tableName = "members";

  • 详解Angular结合zTree异步加载节点数据

    1 前提准备 1.1 新建一个angular4项目 参考://www.jb51.net/article/119668.htm 1.2 去zTree官网下载zTree zTree官网:点击前往 三少使用的版本:点击前往 1.3 参考博客 //www.jb51.net/article/133284.htm 2 编程步骤 从打印出zTree对象可以看出,zTree对象利用init方法来实现zTree结构:init方法接收三个参数 参数1:一个ul标签的DOM节点对象 参数2:基本配置对象 参数3:标题

  • Python的Flask框架应用调用Redis队列数据的方法

    任务异步化 打开浏览器,输入地址,按下回车,打开了页面.于是一个HTTP请求(request)就由客户端发送到服务器,服务器处理请求,返回响应(response)内容. 我们每天都在浏览网页,发送大大小小的请求给服务器.有时候,服务器接到了请求,会发现他也需要给另外的服务器发送请求,或者服务器也需要做另外一些事情,于是最初们发送的请求就被阻塞了,也就是要等待服务器完成其他的事情. 更多的时候,服务器做的额外事情,并不需要客户端等待,这时候就可以把这些额外的事情异步去做.从事异步任务的工具有很多.

  • PHP基于Redis消息队列实现发布微博的方法

    本文实例讲述了PHP基于Redis消息队列实现发布微博的方法.分享给大家供大家参考,具体如下: phpRedisAdmin :github地址  图形化管理界面 git clone [url]https://github.com/ErikDubbelboer/phpRedisAdmin.git[/url] cd phpRedisAdmin git clone [url]https://github.com/nrk/predis.git[/url] vendor 首先安装上述的Redis图形化管理

  • 在ssm项目中使用redis缓存查询数据的方法

    在项目中常常需要后台程序的持久层查询数据库来获取数据,然后将数据交给服务层.控制层,最后才交给视图层.如果数据访问缓慢,就会影响程序的运行. 为了加快程序的运行,可以将数据放入缓存中,包括数据缓存和页面缓存. 所谓缓存,就是将程序或系统经常要调用的对象存在内存中,一遍其使用时可以快速调用,不必再去创建新的重复的实例.这样做可以减少系统开销,提高系统效率. 其中页面缓存主要是oscache,可以整页或者指定网页某一部分缓存,同时指定他的过期时间,这样在此时间段里面访问的数据都是一样的 . 数据缓存

  • 关于layui的下拉搜索框异步加载数据的解决方法

    思路分析:当我使用layui默认的下拉搜索框的时候,layui会默认渲染出一个HTML结构,所以我把渲染出来的这个结果直接给复制出来,这样css的样式就不用从头到尾写一遍了, 前端代码(我用的是jsp): <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC &quo

  • vue异步axios获取的数据渲染到页面的方法

    我们在vue,数据很多事异步获取来的,如果在template直接使用,会报错,undefined. 因为先渲染后得到的数据,那如何才能不报错呢? computed!!! 举个例子 index.vue 忽略坑人的传参方式... created(){ this.init() this.axios.post('/wanwei/appserver/eqInfo/eqBaseInfo?reqjson={"eq_code":"BJTE1W03011SF001SBQDGPXTGYKG001

随机推荐