基于Redis位图实现系统用户登录统计

项目需求,试着写了一个简单登录统计,基本功能都实现了,日志数据量小。具体性能没有进行测试~ 记录下开发过程与代码,留着以后改进!

1. 需求 

实现记录用户哪天进行了登录,每天只记录是否登录过,重复登录状态算已登录。不需要记录用户的操作行为,不需要记录用户上次登录时间和IP地址(这部分以后需要可以单独拿出来存储) 区分用户类型 查询数据需要精确到天

2. 分析

  考虑到只是简单的记录用户是否登录,记录数据比较单一,查询需要精确到天。以百万用户量为前提,前期考虑了几个方案

2.1 使用文件

  使用单文件存储:文件占用空间增长速度快,海量数据检索不方便,Map/Reduce操作也麻烦

  使用多文件存储:按日期对文件进行分割。每天记录当天日志,文件量过大

2.2 使用数据库

不太认同直接使用数据库写入/读取

  • 频繁请求数据库做一些日志记录浪费服务器开销。 
  • 随着时间推移数据急剧增大 
  • 海量数据检索效率也不高,同时使用索引,易产生碎片,每次插入数据还要维护索引,影响性能

  所以只考虑使用数据库做数据备份。

2.3 使用Redis位图(BitMap)

  这也是在网上看到的方法,比较实用。也是我最终考虑使用的方法,

  首先优点:

  数据量小:一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。我们知道8个bit可以组成一个Byte,所以bitmap本身会极大的节省储存空间。1亿人每天的登陆情况,用1亿bit,约1200WByte,约10M 的字符就能表示。

  计算方便:实用Redis bit 相关命令可以极大的简化一些统计操作。常用命令 SETBITGETBITBITCOUNTBITOP

  再说弊端:

  存储单一:这也算不上什么缺点,位图上存储只是0/1,所以需要存储其他信息就要别的地方单独记录,对于需要存储信息多的记录就需要使用别的方法了

3. 设计3.1 Redis BitMap

  Key结构:前缀_年Y-月m_用户类型_用户ID

标准Key: KEYS loginLog_2017-10_client_1001
检索全部: KEYS loginLog_*
检索某年某月全部: KEYS loginLog_2017-10_*
检索单个用户全部: KEYS loginLog_*_client_1001
检索单个类型全部: KEYS loginLog_*_office_*
...  

  每条BitMap记录单个用户一个月的登录情况,一个bit位表示一天登录情况。

设置用户1001,217-10-25登录: SETBIT loginLog_2017-10_client_1001 25 1
获取用户1001,217-10-25是否登录:GETBIT loginLog_2017-10_client_1001 25
获取用户1001,217-10月是否登录: GETCOUNT loginLog_2017-10_client_1001
获取用户1001,217-10/9/7月是否登录:BITOP OR stat loginLog_2017-10_client_1001 loginLog_2017-09_client_1001 loginLog_2017-07_client_1001
...

  关于获取登录信息,就得获取BitMap然后拆开,循环进行判断。特别涉及时间范围,需要注意时间边界的问题,不要查询出多余的数据

  获取数据Redis优先级高于数据库,Redis有的记录不要去数据库获取

  Redis数据过期:在数据同步中进行判断,过期时间自己定义(我定义的过期时间单位为“天”,必须大于31)。

  在不能保证同步与过期一致性的问题,不要给Key设置过期时间,会造成数据丢失。

上一次更新时间: 2107-10-02
下一次更新时间: 2017-10-09
Redis BitMap 过期时间: 2017-10-05

这样会造成:2017-10-09同步的时候,3/4/5/6/7/8/9 数据丢失 

 所以我把Redis过期数据放到同步时进行判断  

  我自己想的同步策略(定时每周一凌晨同步):

一、验证是否需要进行同步:

1. 当前日期 >= 8号,对本月所有记录进行同步,不对本月之前的记录进行同步

2. 当前日期 < 8号,对本月所有记录进行同步,对本月前一个月的记录进行同步,对本月前一个月之前的所有记录不进行同步

二、验证过期,如果过期,记录日志后删除[/code]3.2 数据库,表结构

  每周同步一次数据到数据库,表中一条数据对应一个BitMap,记录一个月数据。每次更新已存在的、插入没有的

3.3 暂定接口 

  •  设置用户登录
  •  查询单个用户某天是否登录过
  • 查询单个用户某月是否登录过
  •  查询单个用户某个时间段是否登录过
  •  查询单个用户某个时间段登录信息
  •  指定用户类型:获取某个时间段内有效登录的用户
  •  全部用户:获取某个时间段内有效登录的用户

4. Code

  TP3中实现的代码,在接口服务器内部库中,Application\Lib\

  ├─LoginLog

  │├─Logs 日志目录,Redis中过期的记录删除写入日志进行备份

  │├─LoginLog.class.php 对外接口

  │├─LoginLogCommon.class.php 公共工具类

  │├─LoginLogDBHandle.class.php 数据库操作类

  │├─LoginLogRedisHandle.class.php Redis操作类

4.1 LoginLog.class.php

<?php

namespace Lib\LoginLog;
use Lib\CLogFileHandler;
use Lib\HObject;
use Lib\Log;
use Lib\Tools;

/**
* 登录日志操作类
* User: dbn
* Date: 2017/10/11
* Time: 12:01
* ------------------------
* 日志最小粒度为:天
*/

class LoginLog extends HObject
{
private $_redisHandle; // Redis登录日志处理
private $_dbHandle;  // 数据库登录日志处理

public function __construct()
{
$this->_redisHandle = new LoginLogRedisHandle($this);
$this->_dbHandle  = new LoginLogDBHandle($this);

// 初始化日志
$logHandler = new CLogFileHandler(__DIR__ . '/Logs/del.log');
Log::Init($logHandler, 15);
}

/**
* 记录登录:每天只记录一次登录,只允许设置当月内登录记录
* @param string $type 用户类型
* @param int  $uid 唯一标识(用户ID)
* @param int  $time 时间戳
* @return boolean
*/
public function setLogging($type, $uid, $time)
{
$key = $this->_redisHandle->getLoginLogKey($type, $uid, $time);
if ($this->_redisHandle->checkLoginLogKey($key)) {
return $this->_redisHandle->setLogging($key, $time);
}
return false;
}

/**
* 查询用户某一天是否登录过
* @param string $type 用户类型
* @param int  $uid 唯一标识(用户ID)
* @param int  $time 时间戳
* @return boolean 参数错误或未登录过返回false,登录过返回true
*/
public function getDateWhetherLogin($type, $uid, $time)
{
$key = $this->_redisHandle->getLoginLogKey($type, $uid, $time);
if ($this->_redisHandle->checkLoginLogKey($key)) {

// 判断Redis中是否存在记录
$isRedisExists = $this->_redisHandle->checkRedisLogExists($key);
if ($isRedisExists) {

// 从Redis中进行判断
return $this->_redisHandle->dateWhetherLogin($key, $time);
} else {

// 从数据库中进行判断
return $this->_dbHandle->dateWhetherLogin($type, $uid, $time);
}
}
return false;
}

/**
* 查询用户某月是否登录过
* @param string $type 用户类型
* @param int  $uid 唯一标识(用户ID)
* @param int  $time 时间戳
* @return boolean 参数错误或未登录过返回false,登录过返回true
*/
public function getDateMonthWhetherLogin($type, $uid, $time)
{
$key = $this->_redisHandle->getLoginLogKey($type, $uid, $time);
if ($this->_redisHandle->checkLoginLogKey($key)) {

// 判断Redis中是否存在记录
$isRedisExists = $this->_redisHandle->checkRedisLogExists($key);
if ($isRedisExists) {

// 从Redis中进行判断
return $this->_redisHandle->dateMonthWhetherLogin($key);
} else {

// 从数据库中进行判断
return $this->_dbHandle->dateMonthWhetherLogin($type, $uid, $time);
}
}
return false;
}

/**
* 查询用户在某个时间段是否登录过
* @param string $type 用户类型
* @param int  $uid 唯一标识(用户ID)
* @param int  $startTime 开始时间戳
* @param int  $endTime  结束时间戳
* @return boolean 参数错误或未登录过返回false,登录过返回true
*/
public function getTimeRangeWhetherLogin($type, $uid, $startTime, $endTime){
$result = $this->getUserTimeRangeLogin($type, $uid, $startTime, $endTime);
if ($result['hasLog']['count'] > 0) {
return true;
}
return false;
}

/**
* 获取用户某时间段内登录信息
* @param string $type   用户类型
* @param int  $uid    唯一标识(用户ID)
* @param int  $startTime 开始时间戳
* @param int  $endTime  结束时间戳
* @return array 参数错误或未查询到返回array()
* -------------------------------------------------
* 查询到结果:
* array(
*   'hasLog' => array(
*     'count' => n,                 // 有效登录次数,每天重复登录算一次
*     'list' => array('2017-10-1', '2017-10-15' ...) // 有效登录日期
*   ),
*   'notLog' => array(
*     'count' => n,                 // 未登录次数
*     'list' => array('2017-10-1', '2017-10-15' ...) // 未登录日期
*   )
* )
*/
public function getUserTimeRangeLogin($type, $uid, $startTime, $endTime)
{
$hasCount  = 0;    // 有效登录次数
$notCount  = 0;    // 未登录次数
$hasList  = array(); // 有效登录日期
$notList  = array(); // 未登录日期
$successFlg = false;  // 查询到数据标识

if ($this->checkTimeRange($startTime, $endTime)) {

// 获取需要查询的Key
$keyList = $this->_redisHandle->getTimeRangeRedisKey($type, $uid, $startTime, $endTime);

if (!empty($keyList)) {
foreach ($keyList as $key => $val) {

// 判断Redis中是否存在记录
$isRedisExists = $this->_redisHandle->checkRedisLogExists($val['key']);
if ($isRedisExists) {

// 存在,直接从Redis中获取
$logInfo = $this->_redisHandle->getUserTimeRangeLogin($val['key'], $startTime, $endTime);
} else {

// 不存在,尝试从数据库中读取
$logInfo = $this->_dbHandle->getUserTimeRangeLogin($type, $uid, $val['time'], $startTime, $endTime);
}

if (is_array($logInfo)) {
$hasCount += $logInfo['hasLog']['count'];
$hasList = array_merge($hasList, $logInfo['hasLog']['list']);
$notCount += $logInfo['notLog']['count'];
$notList = array_merge($notList, $logInfo['notLog']['list']);
$successFlg = true;
}
}
}
}

if ($successFlg) {
return array(
'hasLog' => array(
'count' => $hasCount,
'list' => $hasList
),
'notLog' => array(
'count' => $notCount,
'list' => $notList
)
);
}

return array();
}

/**
* 获取某段时间内有效登录过的用户 统一接口
* @param int  $startTime 开始时间戳
* @param int  $endTime  结束时间戳
* @param array $typeArr  用户类型,为空时获取全部类型
* @return array 参数错误或未查询到返回array()
* -------------------------------------------------
* 查询到结果:指定用户类型
* array(
*   'type1' => array(
*     'count' => n,           // type1 有效登录总用户数
*     'list' => array('111', '222' ...) // type1 有效登录用户
*   ),
*   'type2' => array(
*     'count' => n,           // type2 有效登录总用户数
*     'list' => array('333', '444' ...) // type2 有效登录用户
*   )
* )
* -------------------------------------------------
* 查询到结果:未指定用户类型,全部用户,固定键 'all'
* array(
*   'all' => array(
*     'count' => n,           // 有效登录总用户数
*     'list' => array('111', '222' ...) // 有效登录用户
*   )
* )
*/
public function getOrientedTimeRangeLogin($startTime, $endTime, $typeArr = array())
{
if ($this->checkTimeRange($startTime, $endTime)) {

// 判断是否指定类型
if (is_array($typeArr) && !empty($typeArr)) {

// 指定类型,验证类型合法性
if ($this->checkTypeArr($typeArr)) {

// 依据类型获取
return $this->getSpecifyTypeTimeRangeLogin($startTime, $endTime, $typeArr);
}
} else {

// 未指定类型,统一获取
return $this->getSpecifyAllTimeRangeLogin($startTime, $endTime);
}
}
return array();
}

/**
* 指定类型:获取某段时间内登录过的用户
* @param int  $startTime 开始时间戳
* @param int  $endTime  结束时间戳
* @param array $typeArr  用户类型
* @return array
*/
private function getSpecifyTypeTimeRangeLogin($startTime, $endTime, $typeArr)
{
$data = array();
$successFlg = false; // 查询到数据标识

// 指定类型,根据类型单独获取,进行整合
foreach ($typeArr as $typeArrVal) {

// 获取需要查询的Key
$keyList = $this->_redisHandle->getSpecifyTypeTimeRangeRedisKey($typeArrVal, $startTime, $endTime);
if (!empty($keyList)) {

$data[$typeArrVal]['count'] = 0;    // 该类型下有效登录用户数
$data[$typeArrVal]['list'] = array(); // 该类型下有效登录用户

foreach ($keyList as $keyListVal) {

// 查询Kye,验证Redis中是否存在:此处为单个类型,所以直接看Redis中是否存在该类型Key即可判断是否存在
// 存在的数据不需要去数据库中去查看
$standardKeyList = $this->_redisHandle->getKeys($keyListVal['key']);
if (is_array($standardKeyList) && count($standardKeyList) > 0) {

// Redis存在
foreach ($standardKeyList as $standardKeyListVal) {

// 验证该用户在此时间段是否登录过
$redisCheckLogin = $this->_redisHandle->getUserTimeRangeLogin($standardKeyListVal, $startTime, $endTime);
if ($redisCheckLogin['hasLog']['count'] > 0) {

// 同一个用户只需记录一次
$uid = $this->_redisHandle->getLoginLogKeyInfo($standardKeyListVal, 'uid');
if (!in_array($uid, $data[$typeArrVal]['list'])) {
$data[$typeArrVal]['count']++;
$data[$typeArrVal]['list'][] = $uid;
}
$successFlg = true;
}
}

} else {

// 不存在,尝试从数据库中获取
$dbResult = $this->_dbHandle->getTimeRangeLoginSuccessUser($keyListVal['time'], $startTime, $endTime, $typeArrVal);
if (!empty($dbResult)) {
foreach ($dbResult as $dbResultVal) {
if (!in_array($dbResultVal, $data[$typeArrVal]['list'])) {
$data[$typeArrVal]['count']++;
$data[$typeArrVal]['list'][] = $dbResultVal;
}
}
$successFlg = true;
}
}
}
}
}

if ($successFlg) { return $data; }
return array();
}

/**
* 全部类型:获取某段时间内登录过的用户
* @param int  $startTime 开始时间戳
* @param int  $endTime  结束时间戳
* @return array
*/
private function getSpecifyAllTimeRangeLogin($startTime, $endTime)
{
$count   = 0;    // 有效登录用户数
$list    = array(); // 有效登录用户
$successFlg = false;  // 查询到数据标识

// 未指定类型,直接对所有数据进行检索
// 获取需要查询的Key
$keyList = $this->_redisHandle->getSpecifyAllTimeRangeRedisKey($startTime, $endTime);

if (!empty($keyList)) {
foreach ($keyList as $keyListVal) {

// 查询Kye
$standardKeyList = $this->_redisHandle->getKeys($keyListVal['key']);

if (is_array($standardKeyList) && count($standardKeyList) > 0) {

// 查询到Key,直接读取数据,记录类型
foreach ($standardKeyList as $standardKeyListVal) {

// 验证该用户在此时间段是否登录过
$redisCheckLogin = $this->_redisHandle->getUserTimeRangeLogin($standardKeyListVal, $startTime, $endTime);
if ($redisCheckLogin['hasLog']['count'] > 0) {

// 同一个用户只需记录一次
$uid = $this->_redisHandle->getLoginLogKeyInfo($standardKeyListVal, 'uid');
if (!in_array($uid, $list)) {
$count++;
$list[] = $uid;
}
$successFlg = true;
}
}
}

// 无论Redis中存在不存在都要尝试从数据库中获取一遍数据,来补充Redis获取的数据,保证检索数据完整(Redis类型缺失可能导致)
$dbResult = $this->_dbHandle->getTimeRangeLoginSuccessUser($keyListVal['time'], $startTime, $endTime);
if (!empty($dbResult)) {
foreach ($dbResult as $dbResultVal) {
if (!in_array($dbResultVal, $list)) {
$count++;
$list[] = $dbResultVal;
}
}
$successFlg = true;
}
}
}

if ($successFlg) {
return array(
'all' => array(
'count' => $count,
'list' => $list
)
);
}
return array();
}

/**
* 验证开始结束时间
* @param string $startTime 开始时间
* @param string $endTime  结束时间
* @return boolean
*/
private function checkTimeRange($startTime, $endTime)
{
return $this->_redisHandle->checkTimeRange($startTime, $endTime);
}

/**
* 批量验证用户类型
* @param array $typeArr 用户类型数组
* @return boolean
*/
private function checkTypeArr($typeArr)
{
$flg = false;
if (is_array($typeArr) && !empty($typeArr)) {
foreach ($typeArr as $val) {
if ($this->_redisHandle->checkType($val)) {
$flg = true;
} else {
$flg = false; break;
}
}
}
return $flg;
}

/**
* 定时任务每周调用一次:从Redis同步登录日志到数据库
* @param int  $existsDay 一条记录在Redis中过期时间,单位:天,必须大于31
* @return string
* 'null':  Redis中无数据
* 'fail':  同步失败
* 'success':同步成功
*/
public function cronWeeklySync($existsDay)
{

// 验证生存时间
if ($this->_redisHandle->checkExistsDay($existsDay)) {
$likeKey = 'loginLog_*';
$keyList = $this->_redisHandle->getKeys($likeKey);

if (!empty($keyList)) {
foreach ($keyList as $keyVal) {

if ($this->_redisHandle->checkLoginLogKey($keyVal)) {
$keyTime     = $this->_redisHandle->getLoginLogKeyInfo($keyVal, 'time');
$thisMonth    = date('Y-m');
$beforeMonth   = date('Y-m', strtotime('-1 month'));

// 验证是否需要进行同步:
// 1. 当前日期 >= 8号,对本月所有记录进行同步,不对本月之前的记录进行同步
// 2. 当前日期 < 8号,对本月所有记录进行同步,对本月前一个月的记录进行同步,对本月前一个月之前的所有记录不进行同步
if (date('j') >= 8) {

// 只同步本月数据
if ($thisMonth == $keyTime) {
$this->redis2db($keyVal);
}
} else {

// 同步本月或本月前一个月数据
if ($thisMonth == $keyTime || $beforeMonth == $keyTime) {
$this->redis2db($keyVal);
}
}

// 验证是否过期
$existsSecond = $existsDay * 24 * 60 * 60;
if (strtotime($keyTime) + $existsSecond < time()) {

// 过期删除
$bitMap = $this->_redisHandle->getLoginLogBitMap($keyVal);
Log::INFO('删除过期数据[' . $keyVal . ']:' . $bitMap);
$this->_redisHandle->delLoginLog($keyVal);
}
}
}
return 'success';
}
return 'null';
}
return 'fail';
}

/**
* 将记录同步到数据库
* @param string $key 记录Key
* @return boolean
*/
private function redis2db($key)
{
if ($this->_redisHandle->checkLoginLogKey($key) && $this->_redisHandle->checkRedisLogExists($key)) {
$time = $this->_redisHandle->getLoginLogKeyInfo($key, 'time');
$data['id']   = Tools::generateId();
$data['user_id'] = $this->_redisHandle->getLoginLogKeyInfo($key, 'uid');
$data['type']  = $this->_redisHandle->getLoginLogKeyInfo($key, 'type');
$data['year']  = date('Y', strtotime($time));
$data['month']  = date('n', strtotime($time));
$data['bit_log'] = $this->_redisHandle->getLoginLogBitMap($key);
return $this->_dbHandle->redis2db($data);
}
return false;
}
}

4.2 LoginLogCommon.class.php

<?php

namespace Lib\LoginLog;

use Lib\RedisData;
use Lib\Status;

/**
* 公共方法
* User: dbn
* Date: 2017/10/11
* Time: 13:11
*/
class LoginLogCommon
{
protected $_loginLog;
protected $_redis;

public function __construct(LoginLog $loginLog)
{
$this->_loginLog = $loginLog;
$this->_redis  = RedisData::getRedis();
}

/**
* 验证用户类型
* @param string $type 用户类型
* @return boolean
*/
protected function checkType($type)
{
if (in_array($type, array(
Status::LOGIN_LOG_TYPE_ADMIN,
Status::LOGIN_LOG_TYPE_CARRIER,
Status::LOGIN_LOG_TYPE_DRIVER,
Status::LOGIN_LOG_TYPE_OFFICE,
Status::LOGIN_LOG_TYPE_CLIENT,
))) {
return true;
}
$this->_loginLog->setError('未定义的日志类型:' . $type);
return false;
}

/**
* 验证唯一标识
* @param string $uid
* @return boolean
*/
protected function checkUid($uid)
{
if (is_numeric($uid) && $uid > 0) {
return true;
}
$this->_loginLog->setError('唯一标识非法:' . $uid);
return false;
}

/**
* 验证时间戳
* @param string $time
* @return boolean
*/
protected function checkTime($time)
{
if (is_numeric($time) && $time > 0) {
return true;
}
$this->_loginLog->setError('时间戳非法:' . $time);
return false;
}

/**
* 验证时间是否在当月中
* @param string $time
* @return boolean
*/
protected function checkTimeWhetherThisMonth($time)
{
if ($this->checkTime($time) && $time > strtotime(date('Y-m')) && $time < strtotime(date('Y-m') . '-' . date('t'))) {
return true;
}
$this->_loginLog->setError('时间未在当前月份中:' . $time);
return false;
}

/**
* 验证时间是否超过当前时间
* @param string $time
* @return boolean
*/
protected function checkTimeWhetherFutureTime($time)
{
if ($this->checkTime($time) && $time <= time()) {
return true;
}
return false;
}

/**
* 验证开始/结束时间
* @param string $startTime 开始时间
* @param string $endTime  结束时间
* @return boolean
*/
protected function checkTimeRange($startTime, $endTime)
{
if ($this->checkTime($startTime) &&
$this->checkTime($endTime) &&
$startTime < $endTime &&
$startTime < time()
) {
return true;
}
$this->_loginLog->setError('时间范围非法:' . $startTime . '-' . $endTime);
return false;
}

/**
* 验证时间是否在指定范围内
* @param string $time   需要检查的时间
* @param string $startTime 开始时间
* @param string $endTime  结束时间
* @return boolean
*/
protected function checkTimeWithinTimeRange($time, $startTime, $endTime)
{
if ($this->checkTime($time) &&
$this->checkTimeRange($startTime, $endTime) &&
$startTime <= $time &&
$time <= $endTime
) {
return true;
}
$this->_loginLog->setError('请求时间未在时间范围内:' . $time . '-' . $startTime . '-' . $endTime);
return false;
}

/**
* 验证Redis日志记录标准Key
* @param string $key
* @return boolean
*/
protected function checkLoginLogKey($key)
{
$pattern = '/^loginLog_\d{4}-\d{1,2}_\S+_\d+$/';
$result = preg_match($pattern, $key, $match);
if ($result > 0) {
return true;
}
$this->_loginLog->setError('RedisKey非法:' . $key);
return false;
}

/**
* 获取月份中有多少天
* @param int $time 时间戳
* @return int
*/
protected function getDaysInMonth($time)
{
return date('t', $time);
}

/**
* 对没有前导零的月份或日设置前导零
* @param int $num 月份或日
* @return string
*/
protected function setDateLeadingZero($num)
{
if (is_numeric($num) && strlen($num) <= 2) {
$num = (strlen($num) > 1 ? $num : '0' . $num);
}
return $num;
}

/**
* 验证过期时间
* @param int   $existsDay 一条记录在Redis中过期时间,单位:天,必须大于31
* @return boolean
*/
protected function checkExistsDay($existsDay)
{
if (is_numeric($existsDay) && ctype_digit(strval($existsDay)) && $existsDay > 31) {
return true;
}
$this->_loginLog->setError('过期时间非法:' . $existsDay);
return false;
}

/**
* 获取开始日期边界
* @param int $time   需要判断的时间戳
* @param int $startTime 起始时间
* @return int
*/
protected function getStartTimeBorder($time, $startTime)
{
$initDay = 1;
if ($this->checkTime($time) && $this->checkTime($startTime) &&
date('Y-m', $time) === date('Y-m', $startTime) && false !== date('Y-m', $time)) {
$initDay = date('j', $startTime);
}
return $initDay;
}

/**
* 获取结束日期边界
* @param int $time   需要判断的时间戳
* @param int $endTime  结束时间
* @return int
*/
protected function getEndTimeBorder($time, $endTime)
{
$border = $this->getDaysInMonth($time);
if ($this->checkTime($time) && $this->checkTime($endTime) &&
date('Y-m', $time) === date('Y-m', $endTime) && false !== date('Y-m', $time)) {
$border = date('j', $endTime);
}
return $border;
}
}

4.3 LoginLogDBHandle.class.php

<?php

namespace Lib\LoginLog;
use Think\Model;

/**
* 数据库登录日志处理类
* User: dbn
* Date: 2017/10/11
* Time: 13:12
*/
class LoginLogDBHandle extends LoginLogCommon
{

/**
* 从数据库中获取用户某月记录在指定时间范围内的用户信息
* @param string $type   用户类型
* @param int   $uid    唯一标识(用户ID)
* @param int   $time   需要查询月份时间戳
* @param int   $startTime 开始时间戳
* @param int   $endTime  结束时间戳
* @return array
* array(
*   'hasLog' => array(
*     'count' => n,                 // 有效登录次数,每天重复登录算一次
*     'list' => array('2017-10-1', '2017-10-15' ...) // 有效登录日期
*   ),
*   'notLog' => array(
*     'count' => n,                 // 未登录次数
*     'list' => array('2017-10-1', '2017-10-15' ...) // 未登录日期
*   )
* )
*/
public function getUserTimeRangeLogin($type, $uid, $time, $startTime, $endTime)
{
$hasCount = 0;    // 有效登录次数
$notCount = 0;    // 未登录次数
$hasList = array(); // 有效登录日期
$notList = array(); // 未登录日期

if ($this->checkType($type) && $this->checkUid($uid) && $this->checkTimeWithinTimeRange($time, $startTime, $endTime)) {

$timeYM = date('Y-m', $time);

// 设置开始时间
$initDay = $this->getStartTimeBorder($time, $startTime);

// 设置结束时间
$border = $this->getEndTimeBorder($time, $endTime);

$bitMap = $this->getBitMapFind($type, $uid, date('Y', $time), date('n', $time));
for ($i = $initDay; $i <= $border; $i++) {

if (!empty($bitMap)) {
if ($bitMap[$i-1] == '1') {
$hasCount++;
$hasList[] = $timeYM . '-' . $this->setDateLeadingZero($i);
} else {
$notCount++;
$notList[] = $timeYM . '-' . $this->setDateLeadingZero($i);
}
} else {
$notCount++;
$notList[] = $timeYM . '-' . $this->setDateLeadingZero($i);
}
}
}

return array(
'hasLog' => array(
'count' => $hasCount,
'list' => $hasList
),
'notLog' => array(
'count' => $notCount,
'list' => $notList
)
);
}

/**
* 从数据库获取用户某月日志位图
* @param string $type 用户类型
* @param int   $uid  唯一标识(用户ID)
* @param int   $year 年Y
* @param int   $month 月n
* @return string
*/
private function getBitMapFind($type, $uid, $year, $month)
{
$model = D('Home/StatLoginLog');
$map['type']  = array('EQ', $type);
$map['user_id'] = array('EQ', $uid);
$map['year']  = array('EQ', $year);
$map['month']  = array('EQ', $month);

$result = $model->field('bit_log')->where($map)->find();
if (false !== $result && isset($result['bit_log']) && !empty($result['bit_log'])) {
return $result['bit_log'];
}
return '';
}

/**
* 从数据库中判断用户在某一天是否登录过
* @param string $type 用户类型
* @param int   $uid  唯一标识(用户ID)
* @param int   $time 时间戳
* @return boolean 参数错误或未登录过返回false,登录过返回true
*/
public function dateWhetherLogin($type, $uid, $time)
{
if ($this->checkType($type) && $this->checkUid($uid) && $this->checkTime($time)) {

$timeInfo = getdate($time);
$bitMap = $this->getBitMapFind($type, $uid, $timeInfo['year'], $timeInfo['mon']);
if (!empty($bitMap)) {
if ($bitMap[$timeInfo['mday']-1] == '1') {
return true;
}
}
}
return false;
}

/**
* 从数据库中判断用户在某月是否登录过
* @param string $type 用户类型
* @param int   $uid  唯一标识(用户ID)
* @param int   $time 时间戳
* @return boolean 参数错误或未登录过返回false,登录过返回true
*/
public function dateMonthWhetherLogin($type, $uid, $time)
{
if ($this->checkType($type) && $this->checkUid($uid) && $this->checkTime($time)) {

$timeInfo = getdate($time);
$userArr = $this->getMonthLoginSuccessUser($timeInfo['year'], $timeInfo['mon'], $type);
if (!empty($userArr)) {
if (in_array($uid, $userArr)) {
return true;
}
}
}
return false;
}

/**
* 获取某月所有有效登录过的用户ID
* @param int   $year 年Y
* @param int   $month 月n
* @param string $type 用户类型,为空时获取全部类型
* @return array
*/
public function getMonthLoginSuccessUser($year, $month, $type = '')
{
$data = array();
if (is_numeric($year) && is_numeric($month)) {
$model = D('Home/StatLoginLog');
$map['year']  = array('EQ', $year);
$map['month']  = array('EQ', $month);
$map['bit_log'] = array('LIKE', '%1%');
if ($type != '' && $this->checkType($type)) {
$map['type']  = array('EQ', $type);
}
$result = $model->field('user_id')->where($map)->select();
if (false !== $result && count($result) > 0) {
foreach ($result as $val) {
if (isset($val['user_id'])) {
$data[] = $val['user_id'];
}
}
}
}
return $data;
}

/**
* 从数据库中获取某月所有记录在指定时间范围内的用户ID
* @param int   $time   查询的时间戳
* @param int   $startTime 开始时间戳
* @param int   $endTime  结束时间戳
* @param string $type 用户类型,为空时获取全部类型
* @return array
*/
public function getTimeRangeLoginSuccessUser($time, $startTime, $endTime, $type = '')
{
$data = array();
if ($this->checkTimeWithinTimeRange($time, $startTime, $endTime)) {

$timeInfo = getdate($time);

// 获取满足时间条件的记录
$model = D('Home/StatLoginLog');
$map['year']  = array('EQ', $timeInfo['year']);
$map['month']  = array('EQ', $timeInfo['mon']);
if ($type != '' && $this->checkType($type)) {
$map['type']  = array('EQ', $type);
}

$result = $model->where($map)->select();
if (false !== $result && count($result) > 0) {

// 设置开始时间
$initDay = $this->getStartTimeBorder($time, $startTime);

// 设置结束时间
$border = $this->getEndTimeBorder($time, $endTime);

foreach ($result as $val) {

$bitMap = $val['bit_log'];
for ($i = $initDay; $i <= $border; $i++) {

if ($bitMap[$i-1] == '1' && !in_array($val['user_id'], $data)) {
$data[] = $val['user_id'];
}
}
}
}
}
return $data;
}

/**
* 将数据更新到数据库
* @param array $data 单条记录的数据
* @return boolean
*/
public function redis2db($data)
{
$model = D('Home/StatLoginLog');

// 验证记录是否存在
$map['user_id'] = array('EQ', $data['user_id']);
$map['type']  = array('EQ', $data['type']);
$map['year']  = array('EQ', $data['year']);
$map['month']  = array('EQ', $data['month']);

$count = $model->where($map)->count();
if (false !== $count && $count > 0) {

// 存在记录进行更新
$saveData['bit_log'] = $data['bit_log'];

if (!$model->create($saveData, Model::MODEL_UPDATE)) {

$this->_loginLog->setError('同步登录日志-更新记录,创建数据对象失败:' . $model->getError());
logger()->error('同步登录日志-更新记录,创建数据对象失败:' . $model->getError());
return false;
} else {

$result = $model->where($map)->save();

if (false !== $result) {
return true;
} else {
$this->_loginLog->setError('同步登录日志-更新记录,更新数据失败:' . json_encode($data));
logger()->error('同步登录日志-更新记录,更新数据失败:' . json_encode($data));
return false;
}
}
} else {

// 不存在记录插入一条新的记录
if (!$model->create($data, Model::MODEL_INSERT)) {

$this->_loginLog->setError('同步登录日志-插入记录,创建数据对象失败:' . $model->getError());
logger()->error('同步登录日志-插入记录,创建数据对象失败:' . $model->getError());
return false;
} else {

$result = $model->add();

if (false !== $result) {
return true;
} else {
$this->_loginLog->setError('同步登录日志-插入记录,插入数据失败:' . json_encode($data));
logger()->error('同步登录日志-插入记录,插入数据失败:' . json_encode($data));
return false;
}
}
}
}
}

4.4 LoginLogRedisHandle.class.php

<?php

namespace Lib\LoginLog;

/**
* Redis登录日志处理类
* User: dbn
* Date: 2017/10/11
* Time: 15:53
*/
class LoginLogRedisHandle extends LoginLogCommon
{
/**
* 记录登录:每天只记录一次登录,只允许设置当月内登录记录
* @param string $key 日志记录Key
* @param int  $time 时间戳
* @return boolean
*/
public function setLogging($key, $time)
{
if ($this->checkLoginLogKey($key) && $this->checkTimeWhetherThisMonth($time)) {

// 判断用户当天是否已经登录过
$whetherLoginResult = $this->dateWhetherLogin($key, $time);
if (!$whetherLoginResult) {

// 当天未登录,记录登录
$this->_redis->setBit($key, date('d', $time), 1);
}
return true;
}
return false;
}

/**
* 从Redis中判断用户在某一天是否登录过
* @param string $key 日志记录Key
* @param int  $time 时间戳
* @return boolean 参数错误或未登录过返回false,登录过返回true
*/
public function dateWhetherLogin($key, $time)
{
if ($this->checkLoginLogKey($key) && $this->checkTime($time)) {
$result = $this->_redis->getBit($key, date('d', $time));
if ($result === 1) {
return true;
}
}
return false;
}

/**
* 从Redis中判断用户在某月是否登录过
* @param string $key 日志记录Key
* @return boolean 参数错误或未登录过返回false,登录过返回true
*/
public function dateMonthWhetherLogin($key)
{
if ($this->checkLoginLogKey($key)) {
$result = $this->_redis->bitCount($key);
if ($result > 0) {
return true;
}
}
return false;
}

/**
* 判断某月登录记录在Redis中是否存在
* @param string $key 日志记录Key
* @return boolean
*/
public function checkRedisLogExists($key)
{
if ($this->checkLoginLogKey($key)) {
if ($this->_redis->exists($key)) {
return true;
}
}
return false;
}

/**
* 从Redis中获取用户某月记录在指定时间范围内的用户信息
* @param string $key    日志记录Key
* @param int   $startTime 开始时间戳
* @param int   $endTime  结束时间戳
* @return array
* array(
*   'hasLog' => array(
*     'count' => n,                 // 有效登录次数,每天重复登录算一次
*     'list' => array('2017-10-1', '2017-10-15' ...) // 有效登录日期
*   ),
*   'notLog' => array(
*     'count' => n,                 // 未登录次数
*     'list' => array('2017-10-1', '2017-10-15' ...) // 未登录日期
*   )
* )
*/
public function getUserTimeRangeLogin($key, $startTime, $endTime)
{
$hasCount = 0;    // 有效登录次数
$notCount = 0;    // 未登录次数
$hasList = array(); // 有效登录日期
$notList = array(); // 未登录日期

if ($this->checkLoginLogKey($key) && $this->checkTimeRange($startTime, $endTime) && $this->checkRedisLogExists($key)) {

$keyTime = $this->getLoginLogKeyInfo($key, 'time');
$keyTime = strtotime($keyTime);
$timeYM = date('Y-m', $keyTime);

// 设置开始时间
$initDay = $this->getStartTimeBorder($keyTime, $startTime);

// 设置结束时间
$border = $this->getEndTimeBorder($keyTime, $endTime);

for ($i = $initDay; $i <= $border; $i++) {
$result = $this->_redis->getBit($key, $i);
if ($result === 1) {
$hasCount++;
$hasList[] = $timeYM . '-' . $this->setDateLeadingZero($i);
} else {
$notCount++;
$notList[] = $timeYM . '-' . $this->setDateLeadingZero($i);
}
}
}

return array(
'hasLog' => array(
'count' => $hasCount,
'list' => $hasList
),
'notLog' => array(
'count' => $notCount,
'list' => $notList
)
);
}

/**
* 面向用户:获取时间范围内可能需要的Key
* @param string $type   用户类型
* @param int  $uid    唯一标识(用户ID)
* @param string $startTime 开始时间
* @param string $endTime  结束时间
* @return array
*/
public function getTimeRangeRedisKey($type, $uid, $startTime, $endTime)
{
$list = array();

if ($this->checkType($type) && $this->checkUid($uid) && $this->checkTimeRange($startTime, $endTime)) {

$data = $this->getSpecifyUserKeyHandle($type, $uid, $startTime);
if (!empty($data)) { $list[] = $data; }

$temYM = strtotime('+1 month', strtotime(date('Y-m', $startTime)));

while ($temYM <= $endTime) {
$data = $this->getSpecifyUserKeyHandle($type, $uid, $temYM);
if (!empty($data)) { $list[] = $data; }

$temYM = strtotime('+1 month', $temYM);
}
}
return $list;
}
private function getSpecifyUserKeyHandle($type, $uid, $time)
{
$data = array();
$key = $this->getLoginLogKey($type, $uid, $time);
if ($this->checkLoginLogKey($key)) {
$data = array(
'key' => $key,
'time' => $time
);
}
return $data;
}

/**
* 面向类型:获取时间范围内可能需要的Key
* @param string $type   用户类型
* @param string $startTime 开始时间
* @param string $endTime  结束时间
* @return array
*/
public function getSpecifyTypeTimeRangeRedisKey($type, $startTime, $endTime)
{
$list = array();

if ($this->checkType($type) && $this->checkTimeRange($startTime, $endTime)) {

$data = $this->getSpecifyTypeKeyHandle($type, $startTime);
if (!empty($data)) { $list[] = $data; }

$temYM = strtotime('+1 month', strtotime(date('Y-m', $startTime)));

while ($temYM <= $endTime) {
$data = $this->getSpecifyTypeKeyHandle($type, $temYM);
if (!empty($data)) { $list[] = $data; }

$temYM = strtotime('+1 month', $temYM);
}
}
return $list;
}
private function getSpecifyTypeKeyHandle($type, $time)
{
$data = array();
$temUid = '11111111';

$key = $this->getLoginLogKey($type, $temUid, $time);
if ($this->checkLoginLogKey($key)) {
$arr = explode('_', $key);
$arr[count($arr)-1] = '*';
$key = implode('_', $arr);
$data = array(
'key' => $key,
'time' => $time
);
}
return $data;
}

/**
* 面向全部:获取时间范围内可能需要的Key
* @param string $startTime 开始时间
* @param string $endTime  结束时间
* @return array
*/
public function getSpecifyAllTimeRangeRedisKey($startTime, $endTime)
{
$list = array();

if ($this->checkTimeRange($startTime, $endTime)) {

$data = $this->getSpecifyAllKeyHandle($startTime);
if (!empty($data)) { $list[] = $data; }

$temYM = strtotime('+1 month', strtotime(date('Y-m', $startTime)));

while ($temYM <= $endTime) {
$data = $this->getSpecifyAllKeyHandle($temYM);
if (!empty($data)) { $list[] = $data; }

$temYM = strtotime('+1 month', $temYM);
}
}
return $list;
}
private function getSpecifyAllKeyHandle($time)
{
$data = array();
$temUid = '11111111';
$temType = 'office';

$key = $this->getLoginLogKey($temType, $temUid, $time);
if ($this->checkLoginLogKey($key)) {
$arr = explode('_', $key);
array_pop($arr);
$arr[count($arr)-1] = '*';
$key = implode('_', $arr);
$data = array(
'key' => $key,
'time' => $time
);
}
return $data;
}

/**
* 从Redis中查询满足条件的Key
* @param string $key 查询的Key
* @return array
*/
public function getKeys($key)
{
return $this->_redis->keys($key);
}

/**
* 从Redis中删除记录
* @param string $key 记录的Key
* @return boolean
*/
public function delLoginLog($key)
{
return $this->_redis->del($key);
}

/**
* 获取日志标准Key:前缀_年-月_用户类型_唯一标识
* @param string $type 用户类型
* @param int  $uid 唯一标识(用户ID)
* @param int  $time 时间戳
* @return string
*/
public function getLoginLogKey($type, $uid, $time)
{
if ($this->checkType($type) && $this->checkUid($uid) && $this->checkTime($time)) {
return 'loginLog_' . date('Y-m', $time) . '_' . $type . '_' . $uid;
}
return '';
}

/**
* 获取日志标准Key上信息
* @param string $key  key
* @param string $field 需要的参数 time,type,uid
* @return mixed 返回对应的值,没有返回null
*/
public function getLoginLogKeyInfo($key, $field)
{
$param = array();
if ($this->checkLoginLogKey($key)) {
$arr = explode('_', $key);
$param['time'] = $arr[1];
$param['type'] = $arr[2];
$param['uid'] = $arr[3];
}
return $param[$field];
}

/**
* 获取Key记录的登录位图
* @param string $key key
* @return string
*/
public function getLoginLogBitMap($key)
{
$bitMap = '';
if ($this->checkLoginLogKey($key)) {
$time = $this->getLoginLogKeyInfo($key, 'time');
$maxDay = $this->getDaysInMonth(strtotime($time));
for ($i = 1; $i <= $maxDay; $i++) {
$bitMap .= $this->_redis->getBit($key, $i);
}
}
return $bitMap;
}

/**
* 验证日志标准Key
* @param string $key
* @return boolean
*/
public function checkLoginLogKey($key)
{
return parent::checkLoginLogKey($key);
}

/**
* 验证开始/结束时间
* @param string $startTime 开始时间
* @param string $endTime  结束时间
* @return boolean
*/
public function checkTimeRange($startTime, $endTime)
{
return parent::checkTimeRange($startTime, $endTime);
}

/**
* 验证用户类型
* @param string $type
* @return boolean
*/
public function checkType($type)
{
return parent::checkType($type);
}

/**
* 验证过期时间
* @param int $existsDay 一条记录在Redis中过期时间,单位:天,必须大于31
* @return boolean
*/
public function checkExistsDay($existsDay)
{
return parent::checkExistsDay($existsDay);
}
}

5. 参考资料

  https://segmentfault.com/a/1190000008188655

  http://blog.csdn.net/rdhj5566/article/details/54313840

  http://www.redis.net.cn/tutorial/3508.html

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • PHP+Redis 消息队列 实现高并发下注册人数统计的实例

    前言 现在越来越多的网站开始注重统计和用户行为分析,作为网站经常使用的功能,如何让统计性能更加高,这也是我们需要考虑的事情.本篇通过Redis来优化统计功能(以注册人数统计为例). 传统的统计功能都是直接操作数据库把数据插入表中.这样做,对数据库的性能消耗就会比较大. 思路: 这里我们用到了redis的队列,注册的时候先添加到队列,然后在处理的时候出队,并且把人数添加redis里. 代码: <?php //register.php $redis = new Redis(); $redis->c

  • 利用Redis统计网站在线活跃用户的方法

    前言 在工作中我们经常遇到这样的需求,要对某个在线网站的活跃用户数量进行统计.这里我们以redis为例,说明一下其实现的过程. 实现方法 在Redis中存在bitmap这种数据类型,这种数据类型是建立在string数据类型之上的.这里,我们主要用到setbit.bitcount这2个命令,而使用的客户端为python的redis库. import redis r = redis.StrictRedis(host="127.0.0.1",port=6379,db=0) 这里我们引入red

  • Redis中统计各种数据大小的方法

    如果 MySQL 数据库比较大的话,我们很容易就能查出是哪些表占用的空间:不过如果 Redis 内存比较大的话,我们就不太容易查出是哪些(种)键占用的空间了. 有一些工具能够提供必要的帮助,比如 redis-rdb-tools 可以直接分析 RDB 文件来生成报告,可惜它不能百分百实现我的需求,而我也不想在它的基础上二次开发.实际上开发一个专用工具非常简单,利用 SCAN和 DEBUG等命令,没多少行代码就能实现: 复制代码 代码如下: <?php $patterns = array(    

  • 基于redis实现token验证用户是否登陆

    基于项目需求, 我们要实现一个基于redis实现token登录验证,该如何实现呢: 后端实现: 1.引入redis相关的依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupI

  • PHP使用redis实现统计缓存mysql压力的方法

    本文实例讲述了PHP使用redis实现统计缓存mysql压力的方法.分享给大家供大家参考,具体如下: <?php header("Content-Type:text/html;charset=utf-8"); include 'lib/mysql.class.php'; $mysql_obj = mysql::getConn(); //redis $redis = new Redis(); $redis->pconnect('127.0.0.1', 6379); if(is

  • redis开启和禁用登陆密码校验的方法

    1. 开启登陆密码校验 在redis-cli命令行工具中执行如下命令: config set requirepass yourpassword 2. 禁用登陆密码校验 同上,需要在redis-cli中执行如下命令: config set requirepass "" 以上这篇redis开启和禁用登陆密码校验的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

  • redis 实现登陆次数限制的思路详解

    title: redis-login-limitation  利用 redis 实现登陆次数限制, 注解 + aop, 核心代码很简单. 基本思路 比如希望达到的要求是这样: 在 1min 内登陆异常次数达到5次, 锁定该用户 1h 那么登陆请求的参数中, 会有一个参数唯一标识一个 user, 比如 邮箱/手机号/userName 用这个参数作为key存入redis, 对应的value为登陆错误的次数, string 类型, 并设置过期时间为 1min. 当获取到的 value == "4&qu

  • 基于Redis位图实现系统用户登录统计

    项目需求,试着写了一个简单登录统计,基本功能都实现了,日志数据量小.具体性能没有进行测试~ 记录下开发过程与代码,留着以后改进! 1. 需求 实现记录用户哪天进行了登录,每天只记录是否登录过,重复登录状态算已登录.不需要记录用户的操作行为,不需要记录用户上次登录时间和IP地址(这部分以后需要可以单独拿出来存储) 区分用户类型 查询数据需要精确到天 2. 分析 考虑到只是简单的记录用户是否登录,记录数据比较单一,查询需要精确到天.以百万用户量为前提,前期考虑了几个方案 2.1 使用文件 使用单文件

  • 基于Redis位图实现用户签到功能

    场景需求 适用场景如签到送积分.签到领取奖励等,大致需求如下: 签到1天送1积分,连续签到2天送2积分,3天送3积分,3天以上均送3积分等. 如果连续签到中断,则重置计数,每月初重置计数. 当月签到满3天领取奖励1,满5天领取奖励2,满7天领取奖励3--等等. 显示用户某个月的签到次数和首次签到时间. 在日历控件上展示用户每月签到情况,可以切换年月显示--等等. 设计思路 对于用户签到数据,如果每条数据都用K/V的方式存储,当用户量大的时候内存开销是非常大的.而位图(BitMap)是由一组bit

  • 基于Redis实现短信验证码登录项目示例(附源码)

    目录 Redis短信登录流程描述 短信验证码的发送 短信验证码的验证 是否登录的验证 源码分析 模拟发送短信验证码 短信验证码的验证 校验是否登录 登录验证优化 Redis短信登录流程描述 短信验证码的发送 用户提交手机号,系统验证手机号是否有效,毕竟无效手机号会消耗你的短信验证次数还会导致系统的性能下降.如果手机号为无效的话就让用户重新提交手机号,如果有效就生成验证码并将该验证码作为value保存到redis中对应的key是手机号,之所以这么做的原因是保证key的唯一性,如果使用固定字符串作为

  • Java CRM系统用户登录功能实现代码实例

    用户登录功能后台代码实现: UserMapper接口查询方法定义 /** * * @param userName * @param userPwd * @param roleName * 查询用户记录 * @return */ User queryUser(@Param("userName")String userName); <!-- 查询用户 --> <select id="queryUser" resultMap="BaseResu

  • springcloud微服务基于redis集群的单点登录实现解析

    简介 本文介绍微服务架构中如何实现单点登录功能 创建三个服务: 操作redis集群的服务,用于多个服务之间共享数据 统一认证中心服务,用于整个系统的统一登录认证 服务消费者,用于测试单点登录 大体思路:每个服务都设置一个拦截器检查cookie中是否有token,若有token,则放行,若没有token,重定向到统一认证中心服务进行登录,登录成功后返回到被拦截的服务. 搭建redis集群服务 搭建redis集群参考文档 搭建统一认证中心 主函数添加注解 /** * 单点登录既要注册到服务注册中心,

  • 浅谈mysql 系统用户最大文件打开数限制

    纸上得来终觉浅,绝知此事多宕机...记录一下自己很蠢的一次故障处理过程. 上周的时候,一个刚上线的系统又开始反映登不上了,因为最近这个系统也老是出现这个问题,开发也一直在找问题中,所以也没太在意.于是登上操作系统,mysql -uroot -p登录数据库,然后就一直没反应,登不上... 交代一下,mysql是装在mysql用户下的,装的时候虽然对数据库参数有进行调优,但是操作系统层面没做调整,所以mysql用户的最大文件打开数限制为默认的1024,用ulimit -n可以查询.然后我在用mysq

  • 使用JSP实现简单的用户登录注册页面示例代码解析

    实验要求: 将实验2中的系统用户登录和注册页面改为JSP页面,并部署自己的Web应用于Tomcat服务器中 具体要求: 完成登录JSP页面设计和注册页面设计 在登录页面表单中使用request对象获取用户信息,建立新的JSP页面完成登录验证(用户名和密码自己指定即可). 验证结果显示(如登录成功/用户名密码错误,可以使用JavaScript,也可以使用新的JSP页面). 在注册页面表单中使用request对象获取用户注册信息(注册项目由自己指定),在新的JSP页面显示用户注册信息,并提示注册成功

  • Laravel 框架基于自带的用户系统实现登录注册及错误处理功能分析

    本文实例讲述了Laravel 框架基于自带的用户系统实现登录注册及错误处理功能.分享给大家供大家参考,具体如下: 一.默认 Laravel 不会自动帮你装上用户系统, 二.但是我们可以从默认首页的代码中看到,登录注册按钮被隐藏了 三.需要手动输入命令进行安装 php artisan make:auth 安装完成后重新刷新页面就可以到登录注册按钮了 四.可能遇到的问题 1. 当在注册页面点击注册时,一般会报如下错误,如果你没动过任何配置的话, SQLSTATE[HY000] [1045] Acce

  • JavaWeb实现用户登录注册功能实例代码(基于Servlet+JSP+JavaBean模式)

    下面通过通过图文并茂的方式给大家介绍JavaWeb实现用户登录注册功能实例代码,一起看看吧. 一.Servlet+JSP+JavaBean开发模式(MVC)介绍 Servlet+JSP+JavaBean模式(MVC)适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp负责数据显示,javabean负责封装数据. Servlet+JSP+JavaBean模式程序各个模块之间层次清晰,web开发推荐采用此种模式. 这里以一个最常用的用户登录注册程序来讲解Servlet+JS

随机推荐