作者: houlijiang

  • 正确地使用Redis的SETNX实现锁机制

    setNX,是set if not exists 的缩写,也就是只有不存在的时候才设置, 设置成功时返回 1 , 设置失败时返回 0 。可以利用它来实现锁的效果,但是很多人在使用的过程中都有一些问题没有考虑到。
    例如某个查询数据库的接口因为请求量比较大所以加了缓存,并设定缓存过期后刷新。当并发量比较大并且缓存过期的瞬间,大量并发请求会直接查询数据库导致雪崩。如果使用锁机制来控制只有一个请求去更新缓存就能避免雪崩的问题。下面是很多人下意识想到的加锁方法

    $rs = $redis->setNX($key, $value);
    if ($rs) {//处理更新缓存逻辑
    // ......
    //删除锁
    $redis->del($key);
    }

      通过 setNX 获取锁,如果成功了则更新缓存然后删除锁。其实这里有一个严重的问题:如果更新缓存的时候因为某些原因意外退出了,那么这个锁就不会被删除而一直存在,以至于缓存再也得不到更新。为了解决这个问题有人可能会想到给锁设置一个过期时间,如下

    1234 $redis->multi();$redis->setNX($key, $value);$redis->expire($key, $ttl);$redis->exec();

      因为 setNX 不具备设置过期时间的功能,所以要借助 Expire 来设置,同时需要使用 Multi/Exec 来确保请求的原子性,以免 setNX 成功了 Expire 却失败了。这样还有问题:当多个请求到达时,虽然只有一个请求的 setNX 可以成功,但是任何一个请求的 Expire 却都可以成功,这就意味着即便获取不到锁也可以刷新过期时间,导致锁一直有效,还是解决不了上面的问题。显然 setNX 满足不了需求,Redis从 2.6.12 起,SET 涵盖了 SETEX 的功能, SET 本身又包含了设置过期时间的功能,所以使用 SET 就可以解决上面遇到的问题

    1234567 $rs  $redis ->set( $key $value array ( 'nx' 'ex' =>  $ttl )); if ( $rs ) { //处理更新缓存逻辑 // ...... //删除锁 $redis ->del( $key ); }

      到这一步其实还是有问题的,如果一个请求更新缓存的时间比锁的有效期还要长,导致在缓存更新过程中锁就失效了,此时另一个请求就会获取到锁,但前一个请求在缓存更新完毕的时候,直接删除锁的话就会出现误删其它请求创建的锁的情况。所以要避免这种问题,可以在创建锁的时候需要引入一个随机值并在删除锁的时候加以判断

    123456789 $rs  $redis ->set( $key $random array ( 'nx' 'ex' =>  $ttl )); if ( $rs ) { //处理更新缓存逻辑 // ...... //先判断随机数,是同一个则删除锁 if ( $redis ->get( $key ) ==  $random ) { $redis ->del( $key ); } }
  • mysql 备份

    备份所有数据库:
    
    mysqldump -uroot -p --all-databases > /backup/mysqldump/all.db
    备份指定数据库:
    
    mysqldump -uroot -p test > /backup/mysqldump/test.db
    备份指定数据库指定表(多个表以空格间隔)
    
    mysqldump -uroot -p  mysql db event > /backup/mysqldump/2table.db
    备份指定数据库排除某些表
    
    mysqldump -uroot -p test --ignore-table=test.t1 --ignore-table=test.t2 > /backup/mysqldump/test2.db
    
     授权:
     grant all privileges on *.* to 'root'@'%' identified by '6NbAFQBE';
     grant all privileges on *.* to 'dbmanager'@'%' identified by '6NbAFQBE';
     FLUSH PRIVILEGES;
    
    导出全部数据库
    mysqldump -uroot -p --all-databases > sqlfile.sql

    三、还原命令

    3.1 系统行命令

    mysqladmin -uroot -p create db_name 
    mysql -uroot -p  db_name < /backup/mysqldump/db_name.db
    
    注:在导入备份数据库前,db_name如果没有,是需要创建的; 而且与db_name.db中数据库名是一样的才可以导入。
    

    3.2 soure 方法

    mysql > use db_name
    mysql > source /backup/mysqldump/db_name.db
  • think-swoole应用-微服务之RPC远程调用通信实战

    RPC(Remote Procedure Call):远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的思想。

    解决问题:
    解决分布式系统中,服务之间的调用问题。
    远程调用时,要能够像本地调用一样方便,让调用者感知不到远程调用的逻辑。

    Think-Swoole中已经实现了的基于TCP的PRC,这样我们使用传统型框架也可以做简单的分布式架构应用了。

    一、配置服务端
    1,修改 config/swoole.php

        'rpc'        => [
            'server' => [
                'enable'   => true,
                'port'     => 9000,
                'services' => [
                    \app\rpc\service\UserService::class            ],
            ],
            'client' => [
            ],
        ],

    2,新增rpc目录结构如下:

    3,定义接口抽象方法

    <?php// +----------------------------------------------------------------------// | najing [ 通用后台管理系统 ]// +----------------------------------------------------------------------// | Copyright (c) 2020 http://www.najingquan.com ;All rights reserved.// +----------------------------------------------------------------------// | Author: 救火队队长namespace app\rpc\interfaces;interface UserInterface{
        public function add($name);}

    4,实现接口方法

    <?php// +----------------------------------------------------------------------// | najing [ 通用后台管理系统 ]// +----------------------------------------------------------------------// | Copyright (c) 2020 http://www.najingquan.com ;All rights reserved.// +----------------------------------------------------------------------// | Author: 救火队队长namespace app\rpc\service;use app\rpc\interfaces\UserInterface;class UserService implements UserInterface{
        public function add($name)
        {
            return "您要新增的用户名是:{$name}";
        }}

    服务端的操作就已经完成。 可以开启swoole 命令 php think swoole start

    二、配置客户端
    1,修改 config/swoole.php

    
    
    
    
    'rpc'        => [
            'server' => [
                'enable'   => false,
                'port'     => 9000,
                'services' => [  
                ],
            ],
            'client' => [
                'userservice'=>[
                     //RPC服务端的ip地址
                    'host' => '127.0.0.1',
                     //RPC服务端的端口
                    'port' => 9000
    
                ]
            ],
        ],

    2,生成RPC服务接口

    php think rpc:interface

    会在app目录下生成一个rpc.php的文件,它就是RPC服务接口文件
    文件内容大致:

    
    
    
    
    <?php/**
     * This file is auto-generated.
     */declare(strict_types=1);namespace rpc\contract\userservice;interface UserInterface{
        public function add($name);}return ['userservice' => ['rpc\contract\userservice\UserInterface']];

    注意return中就是每个服务接口的命名空间;在控制器中使用该命名空间实例对象就可以调用

    3,Controller调用

    
    
    
    
    <?php// +----------------------------------------------------------------------// | najing [ 通用后台管理系统 ]// +----------------------------------------------------------------------// | Copyright (c) 2020 http://www.najingquan.com ;All rights reserved.// +----------------------------------------------------------------------// | Author: 救火队队长namespace app\controller;use app\BaseController;use rpc\contract\userservice\UserInterface;class User extends BaseController{
        public function add(UserInterface $user)
        {
           echo $user->add("救火队队长");
        }}

    4,浏览器访问客户端Controller
    6 收藏 点击回复

  • php替换html里面的图片相对路径地址为绝对路径

    /**
     * 替换html里面的图片相对路径地址为绝对路径
     * @param  string $content 请求html内容
     * @param  array  $prefix 绝对url地址
     */
    function pregReplaceImg2($content,$prefix)
    {
        $contentAlter = preg_replace_callback('/(<[img|IMG].*?src=[\'\"])([\s\S]*?)([\'\"])[\s\S]*?/i', function($match)use($prefix){
            if(strstr($match[2], 'http://') == false && strstr($match[1], 'https://') == false)
                return $match[1].$prefix.$match[2].$match[3];
            else
                return $match[1].$match[2].$match[3];
        } , $content);
        return $contentAlter;
    }
  • java中逗号分隔的字符串和List相互转换

    1、将逗号分隔的字符串转换为List

    String str = "a,b,c"; 
    
    List<String> result = Arrays.asList(str.split(","));

    2、将List转换为逗号分隔的字符串

    (1) 利用Guava的Joiner

    List<String> list = new ArrayList<String>(); 
    list.add("a"); 
    list.add("b"); 
    list.add("c"); 
    
    String str = Joiner.on(",").join(list); 

    (2)利用Apache Commons的StringUtils

    List<String> list = new ArrayList<String>(); 
    list.add("a"); 
    list.add("b"); 
    list.add("c"); 
    
    String str = StringUtils.join(list.toArray(), ","); 
  • docker logs 命令

    • docker logs 容器名
    • docker logs -f 容器名
    • docker logs -tail=100 -f 容器名 最新100条
  • windows 端口转发命令

    把windws 最为代理服务器,解决只有本机才能访问的有特殊ip限制的服务。

    设置端口转发:

    netsh interface portproxy add v4tov4  listenaddress=172.16.8.212 listenport=33060 connectaddress=192.168.0.5 connectport=3306

    查看端口转发:

    netsh interface portproxy show v4tov4
  • redis/mysql 开机自启/关闭

    redis开机启动:

    systemctl disable redis.service
    systemctl enable redis.service

    mysql开机自动启动控制:

    chkconfig mysql on
    chkconfig mysql off

    docker 开启启动:

    chkconfig docker on
    chkconfig docker off
    
  • docker开机自动启动

    方法一:

    1 chkconfig docker on

    方法二:

    1.1是用systemctl:

    1 systemctl enable docker

    1.2将Docker的docker.service服务移动到系统服务中

    123 # cp /usr/lib/systemd/system/docker.service /etc/systemd/system/ # systemctl daemon-reload # systemctl restart docker

    1.3查看

    1 ll /etc/systemd/system/multi-user.target.wants/docker.service
    1 lrwxrwxrwx 1 root root 38 Mar 26 14:45 /etc/systemd/system/multi-user.target.wants/docker.service -> /usr/lib/systemd/system/docker.service
  • Docker update命令使用

    描述

    更新一个或多个容器的配置。

    语法

    1 docker update [OPTIONS] CONTAINER [CONTAINER…]

    OPTIONS 说明

    名称 描述
    --blkio-weight 阻塞IO (相对权重),介于10到1000之间,0表示禁用(默认禁止)
    --cpu-period 限制CPU CFS(完全公平的调度程序)期限
    --cpu-quota 限制CPU CFS(完全公平的调度程序)配额
    --cpu-rt-period API 1.25+,将CPU实时时间限制为微秒
    --cpu-rt-runtime API 1.25+,将CPU实时运行时间限制为微秒
    --cpu-shares-c CPU份额(相对权重)
    --cpus API 1.29+,CPU数量
    --cpuset-cpus 允许执行的CPU(0-3,0,1)
    --cpuset-mem 允许执行的MEM(0-3,0,1)
    --kernel-memory 内核内存限制
    --memory-swap 交换限制等于内存加交换,“-1”以启用无限交换
    --memory-reservatio 内存软限制
    --memory-m 内存限制
    --pids-limit API 1.40+,调节容器pids限制(-1表示无限制)
    --restart 容器退出时重新启动策略以应用

    实例

    1. 更新 CPU 共享数量
    1 docker update –cpu-shares 512 f361b7d8465
    1. 更新容器的重启策略
    1 docker update –restart=always f361b7d8465
    1. 更新容器内存
    1 docker update -m 500M f361b7d8465

    更多用法请查看:https://docs.docker.com/engine/reference/commandline/update/