面包屑思维模型实战模型错题集结构手册流程手册自我检测专题模块

  • 微信小程序前端
    微信小程序前端易错点收集
    查看
  • css/less/sass样式控制
    在开发过程中的一些样式控制bug规避
    查看
  • tp5开发小程序
    tp5开发小程序时错误积累
    查看
  • PHP错题集
    PHP在实际开发过程中遇到的问题
    查看
  • MySql数据库
    使用MySql在实际开发中遇到的错误总结
    查看
  • TP5错题集
    积累tp5框架在实际开发过程中遇到的问题
    查看
  • uni-app爬坑
    主要用于uni-app项目中遇到的一些问题
    查看
  • Vue.js易错收集
    vue.js项目常见错误收集整理
    查看
  • uni-app开发微信小程序
    uni-app开发微信小程序的一些爬坑积累
    查看
  • Linux
    Linux在部署、开发、运维时遇见的错误积累
    查看
  • 安全设计
    常见安全设计
    查看
  • Redis
    项目中使用redis的相关错误积累
    查看
  • 前端特效
    前端特效相关错题集
    查看
more

最新博文

  • uniapp使用socket

    socket

    uniapp做客户端,基本上大多数端都可以使用官方的webSocket和socketTask来实现。

    主要注意几个问题:

    (1)连接socket的ip及端口;

    (2)socket对象;

    (3)实际项目中的全局调用


    一.注意连接socket的协议:// ip:端口写法

    服务端的协议是socket,完整监听地址一般是socket://127.0.0.0.0:2345这样的写法,但客户端就是ws://127.0.0.0.0:2345这样的写法。

    另外注意,本地调试时,h5可以调ws://127.0.0.0.0:2345,但App端的调试只能调线上地址,如ws://129.128.45.32:2345。


    二.socket对象

    项目中,消息发送/接收消息可以多次,但一般连接只一次,所以需要socket对象。

    注意点是uni.connectSocket()方法,如果不传入success / fail /complate等参数,会返回一个promise对象。那么该对象就不能使用send、onMessage、close等方法。

    正确写法是:

    var socketObj=uni.connectSocket({
    url:"ws://127.0.0.0:2345",
    complete: ()=> {}
     });


    三.实际项目的全局调用

    实际项目中,消息长连接一般都是APP运行进程内全局进行。

    (1)uniapp中实现长连接,尽量在App.vue中进行,确切的说是onLaunch中;

    (2)socketTask对象绑定到app全局变量中,其他页面也可调用

    onLaunch: function() {
    //#ifdef APP-PLUS
    var app = getApp();
    app.globalData.bgAudioManager=uni.getBackgroundAudioManager();
    app.globalData.bgAudioManager.src='https://zxchannel.com/takeout/public/orderNotice.mp3';
    app.globalData.socketObj=uni.connectSocket({
    url:"ws://129.28.180.149:2346",
    complete: ()=> {}
    });
    uni.onSocketOpen(()=>{
    console.log('连接成功');
    uni.onSocketMessage((res)=>{
    console.log(res.data);
    if(res.data>app.globalData.orderNum){
    app.globalData.bgAudioManager.play();
    setTimeout(()=>{
    app.globalData.bgAudioManager.play();
    },7000);
    }
    app.globalData.orderNum=res.data;
    console.log(app.globalData.orderNum);
    });
    setInterval(()=>{
        app.globalData.socketObj.send({data:'peng'});
    console.log('peng一次');
    },55000);
    });
    //#endif  

    注意,全局变量是定义在App.vue的script中





    风口下的猪2022-04-14socket

    阅读更多
  • socket心跳

    socket

    只要做长连接,就一定存在心跳,如果没有心跳就不能保证长连接和适时断开。


    心跳的主要逻辑是

    (1)设定心跳周期,一般一分钟内;

    (2)客户端定时(心跳周期)向服务端发送一次信息,服务端收到信息后设置该连接线程最后一次发送信息的时间;

    (3)服务端定时(小于客户端的定时周期)遍历查看各连接线程的最后一次发送信息的时间,和当前时间进行比对 。如果间隔时间大于心跳周期,说明客户端已经断开,则服务端需要断开该连接线程,否则继续保持连接。


    a.客户端发送心跳

    var app = getApp();
    app.globalData.socketObj=uni.connectSocket({
    url:"ws://127.0.0.0:2345",
    complete: ()=> {}
    });
    uni.onSocketOpen(()=>{
    setInterval(()=>{
    app.globalData.socketObj.send({data:'peng'});
    console.log('peng一次');
    },55000);
     });


    b.服务端收到客户端心跳消息后,设置该连接线程的最后一次发送消息的时间

    public function onMessage($connection, $data)
        {
            global $workerObj;
            foreach($workerObj->connections as $item){
                $item->lastMessageTime=time();
            }
        }


    C.服务端定时遍历各线程,的最后一次发送信息的时间,和当前时间进行比对 。如果间隔时间大于心跳周期,说明客户端已经断开,则服务端需要断开该连接线程,否则继续保持连接。

    // 心跳间隔55秒
    define('HEARTBEAT_TIME', 55);


    /**
         * 每个进程启动
         * @param $worker
         */
        public function onWorkerStart($worker)
        {
            global $workerObj;
            $workerObj=$worker;
            //心跳控制
            Timer::add(3,function()use($workerObj){
                $time_now=time();
                $redis=RedisDo::instance();
                $orderNum=$redis->get('socketTest');
                foreach($workerObj->connections as $connection){
                    if(empty($connection->lastMessageTime)){
                        $connection->lastMessageTime=$time_now;
                        continue;
                    }
                    if($time_now-$connection->lastMessageTime>HEARTBEAT_TIME){
                        $connection->close();
                    }else{
                        if($orderNum){
                            $connection->send($orderNum);
                        }
                    }
                }
            });
        }





    风口下的猪2022-04-14socket

    阅读更多
  • socket全发和分发

    socket

    socket全发和分发,主要是指针对所有连接线程,还是针对特定的连接线程 发送信息

    广播消息/新订单提醒等,可以针对所有连接线程发送信息,大家都看得到。即时通讯/一对一聊天/指向信息派发等,就需要针对特定连接线程发送信息。


    一.为不同线程绑定用户信息,区分线程

    主要考虑在连接时设置用户属性,在断开时清除用户属性

    $worker->onConnect=function ($connection){
           global $worker;
           if(!isset($connection->uid)){
                  $uid=(getUserInfo($data['param']['token']))->uid;
                  $connection->uid=$uid;
                  array_push($worker->uidConnections,$uid);  //为$worker绑定一个数组,存放不同连接线程的uid
           }
    }
    $worker->onClose=function ($connection){
           global $worker;
           if(isset($connection->uid)){
                  unset($worker->uidConnections[$connection->uid]);
           }
    }


    二.全发消息

    function sendAll($message){
           global $worker;
           foreach($worker->connections as $connection){
                   $connection->send($message);
           }
    }


    三.分发信息

    function sendByUid($uid,$message){
            global $worker;
            if(isset($worker->UidConnections[$uid])){
                  $connection=$worker->UidConnections[$uid];
                  $connection->send($message);
            }
    }



    风口下的猪2022-04-14socket

    阅读更多
  • worker类实例和connection

    socket

    每一个worker类实例都是一个进程,每一个worker实例下的connection都是一个连接线程

    (1)进程通过onWorkerStart($worker)获得,实际项目中要通过global全局变量对其进行捕获;

    (2)$worker->connections存放着所有当前服务端和客户端的连接线程,可以通过foreach遍历出$connection单独执行send、close等;

    (3)$worker->id区别不同进程,自带;

    (4)区别不同线程,因为往往实际项目中,不同线程关联不同用户。所以区别不同线程,通过给不同线程设置不同用户信息来实现。例如给不同线程设置uid属性

    public function onConnect($connection){
          $uid=$userInfo['uid'];
          $connection->uid=$uid;
    }


    注意,通过cmd或ssh操作worker类得到的实例,和项目中其他控制器方法通过new Worker()得到的实例是不一样的,前者是真正的进程,后者不过是Worker的一个实例对象。后者即不能实现连接,也不能收发消息



    风口下的猪2022-04-14socket

    阅读更多
  • global全局变量在workerman中的作用

    socket

    用workerman做socket一定会使用到global全局变量


    一.必须使用global全局变量的原因


    worker类每一个实例,都代表一个socket进程,而每个woker实例下的connection则代表具体的每一个socket连接线程

    worker/server.php中的onMessage、onConnect、onClose、onError等进程内方法没有$worker(具体进程)做参数,捕捉不到具体socket进程。

    // onConnect
        'onConnect'      => function ($connection) {
           
        },
        // onMessage
        'onMessage'      => function ($connection, $data) {
           
        },
        // onClose
        'onClose'        => function ($connection) {

        },
        // onError
        'onError'        => function ($connection, $code, $msg) {
            echo "error [ $code ] $msg\n";
        },


    worker/server.php中的onWorkerStart主要是实例一个新的socket进程,其却捕捉不到除该进程外的其他进程中的连接线程,不能实施$connection->send()等线程的方法

    // onWorkerStart
        'onWorkerStart'  => function ($worker) {

        },
        // onWorkerReload
        'onWorkerReload' => function ($worker) {

        },



    二.global全局变量主要用于存$worker进程

    public function onWorkerStart($worker)
        {
            global $workerObj;
            $workerObj=$worker;


    foreach($workerObj->connections as $connection){
                    if(empty($connection->lastMessageTime)){
                        $connection->lastMessageTime=$time_now;
                        continue;
                    }
                    if($time_now-$connection->lastMessageTime>HEARTBEAT_TIME){
                        $connection->close();
                    }else{
                        if($orderNum){
                            $connection->send($orderNum);
                        }
                    }
                }



    public function onMessage($connection, $data)
        {
            global $workerObj;
            $num=count($workerObj->connections);
            foreach($workerObj->connections as $item){
                $connection->lastMessageTime=time();
                $item->send($num);
            }
        }






    风口下的猪2022-04-14socket

    阅读更多
  • TP6安装/配置workerman

    socket

    TP6后端安装/配置workerman主要包括

    (1)composer workerman的安装包;

    (2)配置workerman相关参数;

    (3)封装workerman项目类worker;

    (4)测试运行


    一.composer workerman的安装包执行安装

    执行

    composer require topthink/think-worker

    即可进行安装,是否安装完毕,需要看项目文件中是否在config中出现worker.php和worker_server.php,另外在vendor中出现workerman和topthink/think-worker的类库


    二.配置workerman相关参数

    workerman配置参数主要是 协议、监听地址、监听端口、socket完整地址、支持最大进程数、自定义workerman项目类等

    // 扩展自身需要的配置
        'protocol'       => 'websocket', // 协议 支持 tcp udp unix http websocket text
        'host'           => '0.0.0.0', // 监听地址
        'port'           => 2346, // 监听端口
        'socket'         => 'http://127.0.0.1:2346', // 完整监听地址
        'context'        => [], // socket 上下文选项
        'worker_class'   => 'app\worker\Worker', // 自定义Workerman服务类名 支持数组定义多个服务

        // 支持workerman的所有配置参数
        'name'           => 'thinkphp',
        'count'          => 1,


    参数的配置地主要是config下的worker.php 和 worker_server.php

    (1)协议的注意点

    protocal可设置为tcp、udp、unix、http、websocket等协议,但无论何种协议,一是要看项目具体需求,二是服务端和客户端的协议一定要一致

    如果服务端设置protocal为websocket,则客户端连接则为ws://ip:端口号(或wss);如果服务端设置protocal为http,则客户端连接则为http://ip:端口号。

    (2)监听地址注意点

    host无论是线上项目还是本地测试,都建议设置为0.0.0.0

    (3)端口号

    port的设置注意注意两点

    a.与常用端口号不冲突,即避免设置为20、21、80、8000等ssh、ftp、宝塔面板等使用的端口号;

    b.如果是线上项目要设置安全组,出入站规则,对该端口进行放行

    (4)完整监听地址

    socket完整监听地址的注意点是格式正确,如果项目封装了处理workerman的类,如上文的app\worker\Worker,则要注意Worker.php中$socket设置为严格的 “协议:// IP:端口” 格式。如果没封装workerman的类,则要在worker_server中socket采用严格的格式

    (5)项目封装的worker类

    当worker_server.php中'worker_class'设置了参数,指明了封装的worker类,则workerman一旦运行,则走worker_class,workerman的socket参数将会采用封装的workerman类里定义的参数


    三.封装workerman项目类worker

    实际项目中,往往需要单独封装一个worker类来处理socket业务,上文配置即是app\worker\Worker类

    worker类封装的注意点是:

    (1)一定要引用think\worker\Server类

    use think\worker\Server;


    class Worker extends Server


    不然无法使用onMessage、onConnect、onWorkerStart等方法

    (2)socket参数设置正确

    protected $socket = 'websocket://127.0.0.1:2346';



    四.测试运行

    cmd或ssh启动socket-server即可

    php think worker:server

    安装TP6完全手册上先执行php think worker再执行php think worker:server会导致客户端无法发送消息,因为workerman作为http-server和socket-server是不一样的

    成功后会出现以下结果




    风口下的猪2022-04-14socket

    阅读更多
  • TP6+uniapp使用workerman

    socket

    TP6服务端和uniapp客户端的workerman socket长连接实现较简单

    主要是

    (1).TP6服务端 socket-server的搭建/配置

    (2).TP6服务端 socket-server的进程守护

    (3).uniapp端的适合h5/app/特定小程序的socket连接方案

    (4).服务端与客户端的心跳,实现长连接与未在线连接的断开管理




    风口下的猪2022-04-14socket

    阅读更多
  • Composer安装遇到fileinfo问题,提示Install or enable PHP's fileinfo extension

    PHP

    (1)在面板中软件商店项目php中按装fileinfo扩展

    (2)打开其配置文件php.ini找到extension=fileinfo将前面的分号注释掉,即开启

    (3)如果再运行composer仍然报这个错,直接在宝塔面板composer执行表单中选择执行用户为root来执行



    风口下的猪2022-04-13PHP

    阅读更多
  • Redis命令时间复杂度查询表

    Redis

    String类型

    命令 时间复杂度

    set 0(1)

    get 0(1)

    del 0(k),k是键的个数

    mset 0(k),k是键的个数

    mget 0(k),k是键的个数

    incr 0(1)

    decr 0(1)

    incryby 0(1)

    decryby 0(1)

    incrybyfloat 0(1)

    append 0(1)

    strlen 0(1)

    setrange 0(n),n为更改字符串长度

    getrange 0(n),n为获取字符串长度

    ————————————————


    hash类型

    命令 时间复杂度

    hset 0(1)

    hget 0(1)

    hdel 0(k),k是键的个数

    hlen O(1)

    hgetall 0(k),k是field的个数

    hmget 0(k),k是field的个数

    hmset 0(k),k是field的个数

    hexists O(1)

    hkeys 0(k),k是field的个数

    hvals 0(k),k是field的个数

    hsetnx O(1)

    hincrby O(1)

    hincrbyfloat O(1)

    hstrlen O(1)

    ————————————————


    list类型

    命令 时间复杂度

    rpush 0(k),k是field的个数

    lpush 0(k),k是field的个数

    linsert 0(n),n是插入位置距离表头或表尾的距离

    lrange O(s+n),s是start的偏移量,n是start到end的范围

    lindex O(n),n是索引的偏移量

    llen O(1)

    lpop O(1)

    rpop O(1)

    lrem O(n),n是列表的长度

    ltrim O(n),n是要裁剪的元素总数

    lset O(n),n是索引的偏移量

    blpop O(1)

    ————————————————


    set类型

    命令 时间复杂度

    sadd O(k),k为元素个数

    srem O(k),k为元素个数

    scard O(1)

    sismember O(1)

    srandmember O(count)

    spop O(1)

    smembers O(n),n为元素总数

    sinter O(m*k),k为多个集合中元素较少的个数,m是键个数

    suinon O(k),k为多个集合元素个数和

    sdiff O(k),k为多个集合元素个数和

    ————————————————


    zset类型

    命令 时间复杂度

    zadd O(k*log(n)),k为添加 成员个数,n为当前成员个数

    zcard O(1)

    zscore O(1)

    zrank  / zrevrank       O(log(n)),n为当前成员个数

    zrem O(k*log(n)),k为删除成员个数,n为当前成员个数

    zincrby O(log(n)),n为当前成员个数

    zrange / zrevrange      O(log(n)+k),k为要获取成员个数,n为当前成员个数

    zrangebyscore / zrevrangebyscore       O(log(n)+k),k为要获取成员个数,n为当前成员个数

    zcount O(log(n)+k),k为要获取成员个数,n为当前成员个数

    zremrangebyrank O(log(n)+k),k为要删除成员个数,n为当前成员个数

    zremrangebyscore O(log(n)+k),k为要删除成员个数,n为当前成员个数

    zinterstore O(n*k) + O(m*log(m)),n是成员数最小的有序集合的成员个数,k是有序集合的个数,m是结果集中成员个数

    zunionstore O(n) + O(m*log(m)),n是所有有序集合成员个数和,m是结果集中成员个数



    风口下的猪2022-03-29Redis

    阅读更多
  • 快速开发--数据分析(3)之redis缓存设计

    软件开发

    redis设计的主要内容是

    (1)选用元素的redis类型;

    (2)选用描述元素间关系的redis类型;

    选择的主要参考是时间复杂度内存性能


    一.元素选用redis类型

    依据:

    (1)数据量大小

    如果数据量小,尽量选择读取数据方法是O(1)时间复杂度的redis类型。例如string的get;

    如果数据量大,则选择list、sortset、list这样的redis类型。

    (2)是否存在单项查询

    如果不存在单项查询,则仍然坚持使用读取方法是O(1)的string类型;

    如果存在单项查询(用户查询单项/管理端或服务器任何需要依据主键查询单项),此时就要摒弃string类型了,选择遍历性能好的数据类型,例如sortset的zscan配合关键字匹配实现迭代查询。

    (3)是否有严格的排序需求

    如果要求数据有明确的排序需求,例如队列和定时任务,那么最好选择list。


    二.关系选用类型

    关系主要指元素之间的一对一,一对多,多对多关系。确定关系,是对元素选择的一个补充,使元素的选择和使用达到最优。


    1.数据量不大时,元素仍然选择string

    数据所有遍历到前端,再通过数据遍历来分类显示;

    (1)可在string元素中增加分类属性和排序属性,则不再需要关系描述;

    (2)也可加上关系描述,即每个分类建一个key,来存string元素的索引,这时string元素就不需要加分类属性了。如果cate key是list的话,还可用来排序,这时string元素也不需要加排序属性了。

    元素搜索在数据量不大时,元素仍然选择string,数据遍历到前端交给前端js去查找


    2.数据量大时,元素的选择就要侧重考虑时间复杂度的问题了,避免阻塞

    数据量大的话,分类中的数据一定显示不完,实际上就成了分类分页的问题。

    (1)如果对分类要求无序且单页数据量没有严格要求,建议直接上sortset,不外加关系描述,直接在元素里增加分类属性。然后通过zscan+count来实现分类分页;

    (2)如果对分类要求有序,则有两种方案

    A:每个类建一个list key,里面存元素,分类分页就是在具体的类list中$redis->lrange();

    B:所有元素还是一个sortset,每个类建一个list key,里面存元素的索引,分类分页就是在具体的类list中$redis->lrange,然后再在结果中遍历执行zRangByScore limit 1。

    如果有元素关键字搜索需求,则只能摒弃元素是list的方案



    风口下的猪2022-03-20软件开发

    阅读更多
  • 快速开发--数据分析(2)之冷热区分

    软件开发

    数据表及字段设计后,就需要依据应用使用场景,对数据进行一个冷热分析。其目的是为并非发/性能考虑。

    应用场景主要考虑:

    (1)用户端;

    (2)管理端;

    (3)服务器任务/事务;

    侧重考虑优先级:用户端>管理端>服务器任务/事务


    一.分类

    大致将数据分为 冷数据次冷数据次热数据热数据。冷热主要以并发及时性做参考


    1.冷数据

    特征:

    (1)用户无权改变;

    例子:

    公告、企业信息、规则信息等


    2.次冷数据

    特征:

    (1)用户有权改变;

    (2)数据改变结果对其他用户或管理端影响不大,数据改变偏向于“自嗨”;

    (3)数据改变配合前端,使用户自己及时看到变化

    例子:

    不限额活动报名、投票、个人资料编辑等


    3.次热数据

    特征:

    (1)用户有权改变;

    (2)数据改变结果对其他用户或管理端影响不大;

    (3)数据改变不需要及时反馈给用户或其他用户,配合服务器定时任务和队列,定期对数据进行改变;

    例子:

    商品月销量统计、日订单统计、营销及营收统计等


    4.热数据

    特征:

    (1)用户有权改变;

    (2)数据改变结果对其他用户或管理端影响大,影响使用或显示;

    例子:

    点赞、领取限额优惠券、限额活动报名、下订单、即时通讯等




    二.处理措施

    数据冷热分析后,依据类型采用对应的后端处理结果。这样能有条理地进行开发,并且相同类型的处理方式进行片层封装,大大提高开发时间可靠度。并且便于后期维护


    1.冷数据

    冷数据完全不用考虑并发,db操作后紧接redis操作。

    所有冷数据的处理可以使用统一封装的DB  CURD操作和redis CURD操作,

    统一方法的参数主要是:(1)传入数据/主键/数据表名称;(2)DB CURD类型;(3)redis操作的key/处理的数据或数据字段(从传入数据或DB数据隐射过来);(4) redis CURD类型


    2.次冷数据

    次冷数据也不用考虑并发,仅考虑用户瞬时行为。其实次冷数据某种意义上就是冷数据,只是冷数据是管理端在操作,用户在看;

    次冷数据是用户端在操作,用户自己和管理端看。

    所有次冷数据同样采用冷数据封装的DB CURD操作和redis CURD操作,对数据进行增删改查;


    3.次热数据

    次热数据也不用考虑并发,用户瞬时行为强调新增而不是修改;

    次热数据的处理,分为瞬时行为后期行为两个部分。即通过queue进行解耦,使程序运行负担下降,操作更具可靠性。

    瞬时行为处理:

    (1)同样可以采用冷数据封装的DB CURD操作和redis CURD操作,对数据进行插入;

    后期行为处理:

    (1)统计数据的redis结构,尽量采用list或者setsort,将新增的数据记录下来即可;

    (2)定时任务遍历队列,逐一将数据写入目标数据表和redis中,并以此改变一些统计字段;


    4.热数据

    热数据就要考虑两个方面了,并发处理的独立性、原子性、排他性,还有就是数据库和redis的数据一致问题

    并发处理:

    (1)采用乐观锁,即数据表和redis数据中都存有版本号,且两者一致;

    (2)单次事务的参照版本号,是事务开始前,从redis中读取。数据表的回滚及redis的watch都参照这个版本号;

    数据一致:

    (1)一定采用先数据表操作后redis操作,且redis在watch的执行下错误时,要带数据表的回滚。也就是说数据表操作回滚在数据表自身比对版本号时错误回滚、在redis操作比对版本号时错误回滚;

    (2)延时双删,在redis操作之前,删除redis,在redis操作之后,再一次删除redis




    风口下的猪2022-03-19软件开发

    阅读更多
  • 软件开发
  • 素质要求
  • 计算机基础
  • 架构
  • 安全
  • 性能
  • 运维
  • 尾页
  • 数据库
  • 开发终端
  • 语言基础
  • 项目管理
  • 产品设计
  • 系统
  • 工作规范
  • 计算机网络
  • 前端技术栈
  • 数据结构
  • 计算机组成原理
  • 后端技术栈
  • 性能优化
  • 安全设计
  • 常见模块
  • 计算机操作系统
  • 服务器
  • python
  • MySQL
  • thinkphp
  • PHP
  • Java
  • JavaScript
  • Windows
  • Linux
  • 特效
  • indexedDB
  • vue
  • 淘宝联盟
  • Ionic
  • Angular
  • 微信小程序
  • 支付宝小程序
  • uni-app
  • css/sass/less
  • 支付
  • socket
  • 爬虫
  • web性能优化
  • 消息推送
  • CVM
  • sqlite
  • Redis
  • 前端基础
  • 基础
  • element
  • Nginx
  • yii2
  • /ponder/index/catelist/catelist/cateid/10.html

    我的名片

    网名:风口下的猪

    职业:软件开发、广告传媒

    现居:重庆渝北

    Email:kamoneyte@qq.com

    标签云

    站点信息

    • 文章统计:528篇
    • 移动端访问:扫码进入SQ3R