人生最重要的不是努力,而是方向

0%

安装

apt install mariadb-server galera-3 galera-arbitrator-3 

修改/etc/mysql/mariadb.conf.d/50-server.cnf配置

[mysqld]
bind-address            = 0.0.0.0
[mariadb]
wsrep_provider=/usr/lib/libgalera_smm.so
wsrep_cluster_address="gcomm://192.168.6.128,192.168.6.129"
binlog_format=ROW
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
innodb_doublewrite=1
query_cache_size=0
wsrep_on=ON

坑坑坑坑坑

错误提示包含如下:

/usr/bin/galera_recovery: 71: cannot create /tmp/wsrep_recovery.HrmnwL: Permission denied

纳尼?

/tmp文件夹无权限?

使用 ll /

dr-xr-xr-x  13 root root          0 May 13 06:10 sys/
drwxrwxrwt  12 root root       4096 May 13 08:06 tmp/
drwxr-xr-x  14 root root       4096 Apr 23 07:34 usr/
drwxr-xr-x  13 root root       4096 Apr 23 07:35 var/

可已看到 /tmp 目录的权限后面多了一个 t , 什么情况?

使用 stat /tmp

jnjxmgl@imgl:/var/log/mysql$ stat /tmp/
File: /tmp/
Size: 4096            Blocks: 8          IO Block: 4096   directory
Device: 802h/2050d      Inode: 1179650     Links: 12
Access: (1777/drwxrwxrwt)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2020-05-13 08:07:24.566439125 +0000
Modify: 2020-05-13 08:06:06.304104497 +0000
Change: 2020-05-13 08:06:06.304104497 +0000
 Birth: -

1777 是啥? 不是 0777么?

以下摘自网络

怎么在777前还有一位,颠覆了我的认知啊,这时候必须翻鸟哥神书了,找到一个链接《7.4.3 文件特殊权限:SUID/SGID/Sticky Bit》,啃了一会终于明白了,整理如下:

除了传统的读r、写w、执行x以外,还有Linux的文件特殊权限,他们分别是Set UID、Set GID、Sticky Bit三种,也就是多出来的那一位,功能介绍如下:
Set UID,SUID
权值:4
符号:x --> s
特点:仅对可执行文件有效。
功能:可执行文件执行时,拥有文件所有者的权限。
案例:/usr/bin/passwd 权限为4755,普通用户可执行passwd命令时,对应的普通用户,随机秒变高富帅,获得了root权限,可以修改普通用户平常根本想都不敢想、无法修改的root拥有的/etc/shadow系统文件(如果/usr/bin/passwd 权限为755,则普通用户执行passwd的时候,会出现无权限修改root own的/etc/shadow文件的问题)
Set GID,SGID
权值:2
符号:x --> s
特点:文件、目录都可施法。
功能:可执行文件、目录执行时,相同用户组的 拥有文件所有者权限。
案例:SGID多用在特定的多人团队的项目开发上,在系统中用得较少
Sticky Bit,SBit
权值:1
符号:x --> t
特点:仅对目录有效。
功能:当目录SBit=1,权限变为rwx rwx rwt时,在此文件夹下删除、重命名、移动的操作只允许是对应创建者用户或root(如果SBit=0,则用户间创建的文件可以互相删除,互相伤害)
案例:/tmp 权限为1777,该目录下不同用户间不可互删文件,只能删自己的(如果/test 权限为777,则test目录下不同用户间可互删文件)
【Linux文件特殊权限“隐藏关卡”】
看到这里,大家应该明白了SUID SGID StickyBit均含有类似“设置后就有了创建者相应权限”的功能(可能不严谨,大家明白意思就好),那么当创建者也没有执行权限(x位为0,例如rw-rw-rw-)时,那么就会出现暗藏关卡——大写SST,即rwSrwSrwT,代表空权限。就像鸟哥书里说的那样“拥有者都无法执行了,哪里来的权限给其他人使用呢?当然就是空的”*

摘自网络结束

根据我个人的理解:

/tmp 是允许随便创建 但是不允许随便删除呀

集群启动,使用命令galera_new_cluster(需要root权限)的时候,root用去启动mysql,这一步没问提,root用户去/tmp创建/tmp/wsrep_recovery.HrmnwL(点后面的字符串是随机的)的时候没有问题,然后后面的事情,就跟root没关系了,后面的就交给了mysql用户,然后这个逼想毁尸灭迹,想把root写的东西/tmp/wsrep_recovery.HrmnwL删了,就触发了权限问题,没删除成功,然后mariadb就提示出错了,你说贱不贱,没事瞎删啥,强迫症!

解决办法

直接chmod 777 /tmp 最直接,当然应该也有别的办法,只是我还不会,哈哈😄

修改/etc/mysql/my.conf

找到bind-address = 127.0.0.1这一行
直接#掉或者改为bind-address = 0.0.0.0即可

为需要远程登录的用户赋予权限

  1. 新建用户远程连接mysql数据库
    1
    2
    3
    4

    grant all on *.* to admin@'%' identified by '123456' with grant option;

    flush privileges;
    允许任何ip地址(%表示允许任何ip地址)的电脑用admin帐户和密码(123456)来访问这个mysql server。

注意admin账户不一定要存在。

  1. 支持root用户允许远程连接mysql数据库
1
2
3
grant all privileges on *.* to 'root'@'%' identified by '123456' with grant option;

flush privileges;

颜色名 十六进制颜色值 颜色
AliceBlue #F0F8FF rgb(240, 248, 255)
AntiqueWhite #FAEBD7 rgb(250, 235, 215)
Aqua #00FFFF rgb(0, 255, 255)
Aquamarine #7FFFD4 rgb(127, 255, 212)
Azure #F0FFFF rgb(240, 255, 255)
Beige #F5F5DC rgb(245, 245, 220)
Bisque #FFE4C4 rgb(255, 228, 196)
Black #000000 rgb(0, 0, 0)
BlanchedAlmond #FFEBCD rgb(255, 235, 205)
Blue #0000FF rgb(0, 0, 255)
BlueViolet #8A2BE2 rgb(138, 43, 226)
Brown #A52A2A rgb(165, 42, 42)
BurlyWood #DEB887 rgb(222, 184, 135)
CadetBlue #5F9EA0 rgb(95, 158, 160)
Chartreuse #7FFF00 rgb(127, 255, 0)
Chocolate #D2691E rgb(210, 105, 30)
Coral #FF7F50 rgb(255, 127, 80)
CornflowerBlue #6495ED rgb(100, 149, 237)
Cornsilk #FFF8DC rgb(255, 248, 220)
Crimson #DC143C rgb(220, 20, 60)
Cyan #00FFFF rgb(0, 255, 255)
DarkBlue #00008B rgb(0, 0, 139)
DarkCyan #008B8B rgb(0, 139, 139)
DarkGoldenRod #B8860B rgb(184, 134, 11)
DarkGray #A9A9A9 rgb(169, 169, 169)
DarkGreen #006400 rgb(0, 100, 0)
DarkKhaki #BDB76B rgb(189, 183, 107)
DarkMagenta #8B008B rgb(139, 0, 139)
DarkOliveGreen #556B2F rgb(85, 107, 47)
Darkorange #FF8C00 rgb(255, 140, 0)
DarkOrchid #9932CC rgb(153, 50, 204)
DarkRed #8B0000 rgb(139, 0, 0)
DarkSalmon #E9967A rgb(233, 150, 122)
DarkSeaGreen #8FBC8F rgb(143, 188, 143)
DarkSlateBlue #483D8B rgb(72, 61, 139)
DarkSlateGray #2F4F4F rgb(47, 79, 79)
DarkTurquoise #00CED1 rgb(0, 206, 209)
DarkViolet #9400D3 rgb(148, 0, 211)
DeepPink #FF1493 rgb(255, 20, 147)
DeepSkyBlue #00BFFF rgb(0, 191, 255)
DimGray #696969 rgb(105, 105, 105)
DodgerBlue #1E90FF rgb(30, 144, 255)
Feldspar #D19275 rgb(209, 146, 117)
FireBrick #B22222 rgb(178, 34, 34)
FloralWhite #FFFAF0 rgb(255, 250, 240)
ForestGreen #228B22 rgb(34, 139, 34)
Fuchsia #FF00FF rgb(255, 0, 255)
Gainsboro #DCDCDC rgb(220, 220, 220)
GhostWhite #F8F8FF rgb(248, 248, 255)
Gold #FFD700 rgb(255, 215, 0)
GoldenRod #DAA520 rgb(218, 165, 32)
Gray #808080 rgb(128, 128, 128)
Green #008000 rgb(0, 128, 0)
GreenYellow #ADFF2F rgb(173, 255, 47)
HoneyDew #F0FFF0 rgb(240, 255, 240)
HotPink #FF69B4 rgb(255, 105, 180)
IndianRed #CD5C5C rgb(205, 92, 92)
Indigo #4B0082 rgb(75, 0, 130)
Ivory #FFFFF0 rgb(255, 255, 240)
Khaki #F0E68C rgb(240, 230, 140)
Lavender #E6E6FA rgb(230, 230, 250)
LavenderBlush #FFF0F5 rgb(255, 240, 245)
LawnGreen #7CFC00 rgb(124, 252, 0)
LemonChiffon #FFFACD rgb(255, 250, 205)
LightBlue #ADD8E6 rgb(173, 216, 230)
LightCoral #F08080 rgb(240, 128, 128)
LightCyan #E0FFFF rgb(224, 255, 255)
LightGoldenRodYellow #FAFAD2 rgb(250, 250, 210)
LightGrey #D3D3D3 rgb(211, 211, 211)
LightGreen #90EE90 rgb(144, 238, 144)
LightPink #FFB6C1 rgb(255, 182, 193)
LightSalmon #FFA07A rgb(255, 160, 122)
LightSeaGreen #20B2AA rgb(32, 178, 170)
LightSkyBlue #87CEFA rgb(135, 206, 250)
LightSlateBlue #8470FF rgb(132, 112, 255)
LightSlateGray #778899 rgb(119, 136, 153)
LightSteelBlue #B0C4DE rgb(176, 196, 222)
LightYellow #FFFFE0 rgb(255, 255, 224)
Lime #00FF00 rgb(0, 255, 0)
LimeGreen #32CD32 rgb(50, 205, 50)
Linen #FAF0E6 rgb(250, 240, 230)
Magenta #FF00FF rgb(255, 0, 255)
Maroon #800000 rgb(128, 0, 0)
MediumAquaMarine #66CDAA rgb(102, 205, 170)
MediumBlue #0000CD rgb(0, 0, 205)
MediumOrchid #BA55D3 rgb(186, 85, 211)
MediumPurple #9370D8 rgb(147, 112, 216)
MediumSeaGreen #3CB371 rgb(60, 179, 113)
MediumSlateBlue #7B68EE rgb(123, 104, 238)
MediumSpringGreen #00FA9A rgb(0, 250, 154)
MediumTurquoise #48D1CC rgb(72, 209, 204)
MediumVioletRed #C71585 rgb(199, 21, 133)
MidnightBlue #191970 rgb(25, 25, 112)
MintCream #F5FFFA rgb(245, 255, 250)
MistyRose #FFE4E1 rgb(255, 228, 225)
Moccasin #FFE4B5 rgb(255, 228, 181)
NavajoWhite #FFDEAD rgb(255, 222, 173)
Navy #000080 rgb(0, 0, 128)
OldLace #FDF5E6 rgb(253, 245, 230)
Olive #808000 rgb(128, 128, 0)
OliveDrab #6B8E23 rgb(107, 142, 35)
Orange #FFA500 rgb(255, 165, 0)
OrangeRed #FF4500 rgb(255, 69, 0)
Orchid #DA70D6 rgb(218, 112, 214)
PaleGoldenRod #EEE8AA rgb(238, 232, 170)
PaleGreen #98FB98 rgb(152, 251, 152)
PaleTurquoise #AFEEEE rgb(175, 238, 238)
PaleVioletRed #D87093 rgb(216, 112, 147)
PapayaWhip #FFEFD5 rgb(255, 239, 213)
PeachPuff #FFDAB9 rgb(255, 218, 185)
Peru #CD853F rgb(205, 133, 63)
Pink #FFC0CB rgb(255, 192, 203)
Plum #DDA0DD rgb(221, 160, 221)
PowderBlue #B0E0E6 rgb(176, 224, 230)
Purple #800080 rgb(128, 0, 128)
Red #FF0000 rgb(255, 0, 0)
RosyBrown #BC8F8F rgb(188, 143, 143)
RoyalBlue #4169E1 rgb(65, 105, 225)
SaddleBrown #8B4513 rgb(139, 69, 19)
Salmon #FA8072 rgb(250, 128, 114)
SandyBrown #F4A460 rgb(244, 164, 96)
SeaGreen #2E8B57 rgb(46, 139, 87)
SeaShell #FFF5EE rgb(255, 245, 238)
Sienna #A0522D rgb(160, 82, 45)
Silver #C0C0C0 rgb(192, 192, 192)
SkyBlue #87CEEB rgb(135, 206, 235)
SlateBlue #6A5ACD rgb(106, 90, 205)
SlateGray #708090 rgb(112, 128, 144)
Snow #FFFAFA rgb(255, 250, 250)
SpringGreen #00FF7F rgb(0, 255, 127)
SteelBlue #4682B4 rgb(70, 130, 180)
Tan #D2B48C rgb(210, 180, 140)
Teal #008080 rgb(0, 128, 128)
Thistle #D8BFD8 rgb(216, 191, 216)
Tomato #FF6347 rgb(255, 99, 71)
Turquoise #40E0D0 rgb(64, 224, 208)
Violet #EE82EE rgb(238, 130, 238)
VioletRed #D02090 rgb(208, 32, 144)
Wheat #F5DEB3 rgb(245, 222, 179)
White #FFFFFF rgb(255, 255, 255)
WhiteSmoke #F5F5F5 rgb(245, 245, 245)
Yellow #FFFF00 rgb(255, 255, 0)
YellowGreen #9ACD32 rgb(154, 205, 50)

创建用户唯一标识

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (!function_exists('create_iden')) {
/**
*创建用户唯一标识
*/
function create_iden($data = '')
{
$data .= $_SERVER['REQUEST_TIME'];
$data .= $_SERVER['HTTP_USER_AGENT'];
$data .= $_SERVER['REMOTE_ADDR'];
$data .= $_SERVER['REMOTE_PORT'];
$hash = strtoupper(hash('ripemd128', uniqid("", true) . md5($data)));
return $hash;
}
}

获取推流地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

/**
* 获取推流地址
*
*/
public function get_push_live_url()
{



try {

#配置
$video_live['aliyun']['appname'] ='zhifubao';//应用名称-支付宝
$video_live['aliyun']['push_validity'] =30;//推流鉴权有效时间(分钟)-30分钟有效
$video_live['aliyun']['push_key'] ='QNBkjiRogF';//推流鉴权key
$video_live['aliyun']['push_domain'] ='tuiliu.zhifubao.com';//推流地址

//$user_live是直播间信息集合,这里只说明直播间号
$user_live['live_num'] =100000001 ;//直播间号,一般从数据库中读取

if ($user_live['status'] != 1) {
$this->error('直播申请未通过,不能进行直播');
}
if ($user_live['flag'] == 'Y') {
$this->error('您已被管理员限制直播权限,不能进行直播');
}
//开始生成地址
$uri = "/" . $video_live['aliyun']['appname'] . "/" . $user_live['live_num'];
$timestamp = time() + 60 * $video_live['aliyun']['push_validity'];
$rand = create_iden();//创建用户唯一标识,见上面函数
$uid = 0;
$hashValue = md5($uri . "-" . $timestamp . "-" . $rand . "-" . $uid . "-" . $video_live['aliyun']['push_key']);
$server_url = "rtmp://" . $video_live['aliyun']['push_domain'] . "/" . $video_live['aliyun']['appname'] . "/";
$stream_sign = $user_live['live_num'] . "?auth_key=" . $timestamp . "-" . $rand . "-" . $uid . "-" . $hashValue;
$push_url = $server_url . $stream_sign;
/* 输出结果 */
$data = array(
'server_url' => $server_url, //推流服务器
'stream_sign' => $stream_sign, //推流串流密钥
'push_url' => $push_url,//完整的推流地址
'live_num' => $user_live['live_num'],//直播间号
);
$this->success('请求成功', $data);
} catch (Exception $th) {
save_live_logs('get_push_live_url接口异常:' . $th->getMessage());//记录错误
$this->error($th->getMessage());
}
}

根据房间号获取播流地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62


/**
* 获取播流地址
*@param string $live_num 直播房间号
*/
public function get_play_live_url()
{


try {
$live_num = $this->request->get('live_num');//前台传过来的直播间号
if (!$live_num) {

$this->error('房间号不存在');
}


#配置
$video_live['aliyun']['appname'] = 'zhifubao';
$video_live['aliyun']['play_validity'] = 30;
$video_live['aliyun']['play_key'] = 'jTOrVA3u8N';
$video_live['aliyun']['play_domain'] = 'boliu.zhifubao.com';



$user_live = //获取直播间信息集合,包括直播间号
/* 推流地址 */
if (empty($user_live)) {
$this->error('房间号不存在');
}
$timestamp = time() + 60 * $video_live['aliyun']['play_validity'];
$rand = create_iden();
$uid = 0;
/* m3u8格式播放地址,延迟最高 */
$uri = "/" . $video_live['aliyun']['appname'] . "/" . $user_live['live_num'] . ".m3u8";
$hashValue = md5($uri . "-" . $timestamp . "-" . $rand . "-" . $uid . "-" . $video_live['aliyun']['play_key']);
$play_url1 = 'http://' . $video_live['aliyun']['play_domain'] . $uri . "?auth_key=" . $timestamp . "-" . $rand . "-" . $uid . "-" . $hashValue;
/* flv播放地址 */
$uri = "/" . $video_live['aliyun']['appname'] . "/" . $user_live['live_num'] . ".flv";
$hashValue = md5($uri . "-" . $timestamp . "-" . $rand . "-" . $uid . "-" . $video_live['aliyun']['play_key']);
$play_url2 = 'http://' . $video_live['aliyun']['play_domain'] . $uri . "?auth_key=" . $timestamp . "-" . $rand . "-" . $uid . "-" . $hashValue;
/* rtmp播放地址 ,应该是延迟最低的*/
$uri = "/" . $video_live['aliyun']['appname'] . "/" . $user_live['live_num'];
$hashValue = md5($uri . "-" . $timestamp . "-" . $rand . "-" . $uid . "-" . $video_live['aliyun']['play_key']);
$play_url3 = 'rtmp://' . $video_live['aliyun']['play_domain'] . $uri . "?auth_key=" . $timestamp . "-" . $rand . "-" . $uid . "-" . $hashValue;

Db::name('user_live_list')->where('live_num', $live_num)->setInc('online_num', 1);

/* 输出结果 */
$user_live['play_url1'] = $play_url1;
$user_live['play_url2'] = $play_url2;
$user_live['play_url3'] = $play_url3;


$user_live['goods_info'] = $goods_info;//直播间展示的商品
$this->success('请求成功', $user_live);
} catch (Exception $th) {
save_live_logs('get_play_live_url接口异常:' . $th->getMessage());
$this->error($th->getMessage());
}
}

推流后回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

/**
* 推流回调
*
*@param string $time unix 时间戳
*@param string $usrargs 用户推流的参数
*@param string $action publish 表示推流,publish_done 表示断流
*@param string $app 默认为自定义的推流域名,如果未绑定推流域名即为播放域名
*@param string $appname 应用名称
*@param string $id 每页显示数量,默认10
*@param string $node CDN 接受流的节点或者机器名
*@param string $ip 推流的客户端IP,注意接收时参数名是小写
*/
public function push_notify()
{
// https: //live.aliyunlive.com/pub? action=publish & app=xc.cdnpe.com & appname=test01 & id=test01 & ip=42.120.74.183 & node=cdnvideocenter010207116011.cm3

try {

$data = input();



if (empty($data['id'])) {

$this->error('Oh,NO!');
}

if ($data['action'] == 'publish') {//开启直播
Db::name('user_live_list')->where('live_num', $data['id'])->setField('is_open', 'Y');
}
if ($data['action'] == 'publish_done') {//主动关闭或者断开
Db::name('user_live_list')->where('live_num', $data['id'])->setField('is_open', 'N');

}

Db::name('push_notify')->insert($data);//记录动态
$this->success('Yes,OK!');
} catch (Exception $th) {

save_live_logs('直播推流回调push_notify接口异常:' . $th->getMessage());
$this->error($th->getMessage());
}
}

核心代码

如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

try {

$redis = new Redis();
$redis->connect(127.0.0.1, 6379);
// $uid=$this->uid;//获取用户的id
$uid = $_GET[id];//因为我没有模拟登陆,所以用户id使用get传递以下,实际场景下可已使用上面的类似代码获取
$count = $redis->LLEN(data %};
if ($count >= 100) {//查看秒杀请求是否满了,满了提示
echo (100个了 %};
die;
}
$redis->LPUSH(data, $uid);//进来一个秒杀请求



//TODO 成功进来的请求走后续逻辑挨个出列,如创建订单
echo ($uid. 开始创建订单了 %};

} catch (Exception $e) {
echo $e->getMessage();
}

讲解

1. redis的原子性

redis之所以能够用做并发处理的中间件,很大程度上是得益于它的IO操作是原子性的,那么redis的原子性到底是什么??

原子性是数据库的事务中的特性。在数据库事务的情景下,原子性指的是:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节

对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行

Redis操作原子性的原因

Redis的操作之所以是原子性的,是因为Redis是单线程的。

由于对操作系统相关的知识不是很熟悉,从上面这句话并不能真正理解Redis操作是原子性的原因,进一步查阅进程与线程的概念及其区别。

2. 进程与线程

2.1 进程

计算机中已执行程序的实体。比如,一个启动了的php-fpm,就是一个进程。

2.2 线程

操作系统能够进行运算调度的最小单元。它被包含在进程之中,是进程的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。比如,mysql运行时,mysql启动后,该mysql服务就是一个进程,而mysql的连接、查询的操作,就是线程。

3. 进程与线程的区别

资源(如打开文件):进程间的资源相互独立,同一进程的各线程间共享资源。某进程的线程在其他进程不可见

通信:进程间通信:消息传递、同步、共享内存、远程过程调用、管道。线程间通信:直接读写进程数据段(需要进程同步和互斥手段的辅助,以保证数据的一致性)

调度和切换:线程上下文切换比进程上下文切换要快得多

线程,是操作系统最小的执行单元,在单线程程序中,任务一个一个地做,必须做完一个任务后,才会去做另一个任务。

4. 代码含义

从上面的代码中可以看出,当请求过来以后我们判断队列的请求数是否溢出,溢出的话,会提示:

秒杀商品不足,已结束

等等用户友好提示;如过人数没有溢出,就将其放入redis的队列,继续执行创建订单及付款的操作

测试脚本

下面我就用图文的方式简单的测试下我们刚刚的业务脚本,这里为了方便测试,我将总的秒杀数量改为 5 个(如下),模拟100用户同时在1s内同时发起请求的场景,因为实际场景会比我们测试的要复杂的多,所以结果仅供参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

try {

$redis = new Redis();
$redis->connect(127.0.0.1, 6379);
// $uid=$this->uid;//获取用户的id
$uid = $_GET[id];//因为我没有模拟登陆,所以用户id使用get传递以下,实际场景下可已使用上面的类似代码获取
$count = $redis->LLEN(data %};
if ($count >= 5) {//查看秒杀请求是否满了,满了提示
echo (5个,够了 %};
die;
}
$redis->LPUSH(data, $uid);//进来一个秒杀请求

//TODO 成功进来的请求走后续逻辑挨个出列,如创建订单
echo ($uid. 开始创建订单了 %};

} catch (Exception $e) {
echo $e->getMessage();
}
  1. 首先将脚本php放入网站运行根目录

  2. 我用的apache-jmeter做测试,这个基于java的测试工具个人觉得还是挺好用的;

  1. 创建测试用户数据,这里我使用excel预先处理好了模拟不同的用户id请求,如下图:
  1. 使用jmeter,操作如下图

最后点击创建并发计划图示的地方开始,附我创建计划保存的配置,直接使用jmeter打开就行

点我下载
  1. 查看结果树,如下图:

从上面我们可以看出有的正常走了下单逻辑,有的请求就提示已经够数了,我数了一下,确实发起了100个请求,也只有5个是走下面逻辑的

到此,对redis这块的讲解就完成了

第一步创建空间

登陆网站 https://cloudstudio.net/ , 单击 新建工作空间 ,如下图

进入工作空间

如上图所示,进入空间后默认给我们弹出几个模板,但是 没有 我们想要的 c 或者 c++ 环境,怎么办呢?

答:自己装

装环境

  1. 在第一步的时候我们已经知道,新创建的空间环境是linux,并且内核是3+,目测是ubuntu,我们可已用vscode调出终端,然后用如下图所示的命令进行查验
  1. 更新本地源,如下图所示
  1. 安装 gccg++ 环境 ,如下图所示, 中间一步使用y表示同意并完成安装

可以使用如下图所示的方法进行安装完成后的版本验证

  1. 使用安装后的环境创建第一个 Hello World 控制台程序

在vscode中创建test.c文件,代码如下:

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main()
{
/* 我的第一个 C 程序 */
printf("Hello, World! \n");

return 0;
}

如下图所示

然后在项目目录的终端命令行里输入

1
gcc test.c

这样自动将我们原有的.c文件,编译为一个a.out

下面我门用如下图所示的方式,在当前目录执行编译后的a.out文件,可已看到在控制台成功输出了

1
./a.out

PS: 由于我们一开始选得环境是纯净的,因此该方法适用于很多语言环境的搭建,包括GolangPHP.NET Core等语言的开发环境搭建,其操作过程类似

在宝塔上创建测试站点,如: http://pan.baidu.com ,如下图:

将下载好的cloudreve上传到宝塔,并解压完毕,如下图:

安装supervisor创建后台守护进程

启动守护进程

配置反向代理

到这一步就算配置完了,下面访问 http://pan.baidu.com 看看吧,后期可已根据自己的需求增加https的支持

简介

操作开始前介绍两家我正在使用的cdn商家:腾讯云和七牛云.

相同点:都赠送每月10G的免费cdn加速

不同点:主要表现在腾讯云免费提供https链接加速,七牛云免费提供http链接加速

七牛云的优势:自家免费提供对象存储10G,并且回源流量啥的基本也免费,总之 cdn+对象存储 加速无需考虑中间费用

注:如过你对https对Http2.0协议支持比较看好的话,并且源站不是腾讯自家的cos而是你自己的源站,推荐你使用腾讯云;因为腾讯自家的cos和cdn配合会有回源流量等费用,加速自己的源站起码流量只是源站的出流量,这个还要本着自己的源站出流量费价格要远低于cos出的价格才是合适的

根据以上的说法,我自己采用的是腾讯云的cdn,因为我是将源站设置为我的hexo博客,博客本身的静态文件总量较小,并且把博客中的图片资源等大文件托管在了七牛云,配合服务器的按流量付费出可能连一分钱都不到,如过你买的是按带宽付费的带宽的话,那就更得用cdn了,不紧出的流量不会花钱,还能解决你这小管网站cssjs等文件占用的带宽,导致访问速度慢的问题.

下面介绍两种免费的资源都用上,是如何配置的

1.云服务器一台

部署上我们的博客,使用blog.xxxx.xx域名,强烈建议使用nginx做web服务器,对静态网站支持较好,并发能力强,这一步不做演示,这都不会下面的就别看了

2.七牛云

在七牛云对象存储创建存储空间res-xxxxxxxxxx,存储区域选择国内区域,空间权限设置公开,如下图:

进入空间管理,如下图:

在七牛云CDN设置加速域名res.xxxx.xx,源站配置选择七牛云存储,其他默认,如下图:

最后将如下图操作,复制cname配置到自己的域名下,如下:
| 主机记录 | 记录类型 |线路类型|记录值|MX优先级|TTL(秒)|
| —- | —- | —- | —- | —- | —- |
| res | CNAME |默认|res-xxxx-xx-idvajuh.qiniudns.com.|-|600|

注意:表格中的记录值要改成你复制的cname

3.腾讯云

如下图创建好cdn加速站点,源站那里输入1步创建的站点域名

做好cname解析,类似七牛云的操作

至此,基本搭建完了,等cdn部署完毕,就可以访问cdn的域名看下效果了

验证ping一下www.xxxx.xx如果解析出来的ip不是你服务器的ip,就说明操作完美实现,最后可已自行研究下腾讯云cdn的https配置及其他高级配置

下面简单的说几个我目前开启的配置,可以跟风

  • 1 缓存配置->缓存过期配置->高级缓存过期设置->开启
  • 2 回源配置->Range回源配置->Range回源->开启
    • ->回源跟随301/302配置->回源跟随301/302->开启
  • 3 高级配置->带宽封顶配置->带宽封顶-> 开启
    • -> 带宽阈值 ->10Gbps
    • ->超出阈值处理 ->访问回源
  • 4 HTTPS配置 ->强制跳转HTTPS->配置好证书
  • 5 HTTP2.0配置 ->HTTP2.0->开启
  • 6 OCSP装订配置 ->OCSP装订->开启
  • 7 SEO优化配置 ->搜索引擎自动回源->开启

模拟多用户同时访问

1
2
3
4
5
6
7
8
9
10
11
<?php
//redis数据入队操作
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
for($i=0;$i<5000;$i++){
try{
$redis->LPUSH('data',$i);
}catch(Exception $e){
echo $e->getMessage();
}
}

出列操作,模拟业务,使用php index.php守护执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
//redis数据出队操作
$redis = new Redis();
$redis->pconnect('127.0.0.1',6379);
while(true){
try{
$value = $redis->LPOP('data');
if($value){
var_dump($value)."\n";
}
/*
* 利用$value进行逻辑和数据处理
*/
}catch(Exception $e){
echo $e->getMessage();
}
}

0x0000 0001首先他是个16进制的数字、

8进制的是0开头的、比如 077 他是八进制的、十进制的话就是63、7*8+7=63

0x0000 0001他表示一个32位的、

因为十六进制的一位有16种变化、四位的变化也是16种。

那么、想表示32位的数据、需要16进制的

1
2
bit  0000 0000 0000 0000 0000 0000 0000 0001
0x 0 0 0 0 0 0 0 1