-
Notifications
You must be signed in to change notification settings - Fork 1
/
api.php
executable file
·216 lines (193 loc) · 7.21 KB
/
api.php
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
<?php
include_once 'base.php';
class Api extends Base
{
//共享信息存储在redis中,一hash表形式存储,%s变量代表的是商品id
static $userId;
static $productId;
static $REDIS_REMOTE_HT_KEY = 'product_%s'; //共享信息key
static $REDIS_REMOTE_TOTAL_COUNT = 'total_count'; //商品总库存
static $REDIS_REMOTE_USE_COUNT = 'used_count'; //已售库存
static $REDIS_REMOTE_QUEUE = 'c_order_queue'; //创建订单队列
static $APCU_LOCAL_STOCK = 'apcu_stock_%s'; //总共剩余库存
static $APC_LOCAL_USE = 'apcu_stock_use_%s'; //本地已售库存
static $APC_LOCAL_COUNT = 'apcu_stock_count_%s'; //本地分库分摊总数
public function __construct($productId, $userId)
{
self::$REDIS_REMOTE_HT_KEY = sprintf(self::$REDIS_REMOTE_HT_KEY, $productId);
self::$APCU_LOCAL_STOCK = sprintf(self::$APCU_LOCAL_STOCK, $productId);
}
//清空缓存
public function clear()
{
//var_export(apcu_cache_info()); //运行时缓存
apcu_clear_cache();
self::output();
}
//数据同步
public function sync()
{
$res = 0;
$localUse = apcu_fetch(self::$APC_LOCAL_USE);
//与redis同步,redis库的存量和已售减去本地缓存预售的量,去本地化
if($localUse > 0){
showInfo();
$script = <<<eof
local key = KEYS[1]
local field1 = KEYS[2]
local field2 = KEYS[3]
local field1_res = redis.call('HINCRBY', key, field1, 0 - ARGV[1])
if(field1_res) then
return redis.call('HINCRBY', key, field2, 0 - ARGV[1])
end
return 0
eof;
$res = self::conRedis()->eval($script, [
self::$REDIS_REMOTE_HT_KEY,
self::$REDIS_REMOTE_TOTAL_COUNT,
self::$REDIS_REMOTE_USE_COUNT,
$localUse
], 3);
apcu_store(self::$APC_LOCAL_USE, 0);
apcu_dec(self::$APCU_LOCAL_STOCK, $localUse); //本地剩余库存
}
self::output([$res,$localUse], 0, '同步成功');
}
//查询库存
public function getStock()
{
$stockNum = apcu_fetch(self::$APCU_LOCAL_STOCK);
var_dump(self::$APCU_LOCAL_STOCK. '-2', $stockNum);
if ($stockNum === false){
$stockNum = self::initStock();
}
self::output(['stock_num'=>$stockNum]);
}
// 抢购-减库存
public function buy()
{
$localStockNum = apcu_fetch(self::$APCU_LOCAL_STOCK);
if ($localStockNum === false){
$localStockNum = self::init();
}
$localUse = apcu_inc(self::$APC_LOCAL_USE); //已卖 +1
if($localUse > $localStockNum){ //抢购失败,大部分流量分流拦截在此
self::output([1], -1, '该商品已售完');
}
//同步已售库存 +1
if(!$this->incUseCount()){ //如果改失败,返回已售完
self::output(['*'], -1, '该商品已售完');
}
//写入创建订单队列
self::conRedis()->lPush(self::$REDIS_REMOTE_QUEUE, json_encode([
'user_id' => self::$userId,
'product_id' => self::$productId
]));
//返回抢购成功
self::output([], 0, '抢购成功,请从订单中心查看订单');
}
//初始化本地数据
static function init()
{
apcu_add(self::$APC_LOCAL_USE, 0);
return 0;
}
static function initStock()
{
$data = self::conRedis()->hMGet(self::$REDIS_REMOTE_HT_KEY,
[self::$REDIS_REMOTE_TOTAL_COUNT, self::$REDIS_REMOTE_USE_COUNT, 'server_num']
);
$num = $data[self::$REDIS_REMOTE_TOTAL_COUNT] - $data[self::$REDIS_REMOTE_USE_COUNT];
$stock = round($num / ($data['server_num'] - 1));
apcu_add(self::$REDIS_REMOTE_TOTAL_COUNT, $data[self::$REDIS_REMOTE_TOTAL_COUNT]); //总剩余库存
apcu_add(self::$APCU_LOCAL_STOCK, $num); //总剩余库存分布到本地额度
var_dump(self::$APCU_LOCAL_STOCK .'-1', $stock);
return $stock;
}
//私有方法,库存同步: 给总预售数 +1
private function incUseCount()
{
$script = <<<eof
local key = KEYS[1]
local field1 = KEYS[2]
local field2 = KEYS[3]
local field1_val = redis.call('hget', key, field1) + 0
local field2_val = redis.call('hget', key, field2) + 0
if(field1_val > field2_val) then
return redis.call('HINCRBY', key, field2, 1)
end
return 0
eof;
/*var_dump(self::$REDIS_REMOTE_HT_KEY,
self::conRedis()->eval("return redis.call('hgetall','product_1')"),
self::conRedis()->eval($script, [
self::$REDIS_REMOTE_HT_KEY,
self::$REDIS_REMOTE_TOTAL_COUNT,
self::$REDIS_REMOTE_USE_COUNT
], 3)
);exit;*/
return self::conRedis()->eval($script, [
self::$REDIS_REMOTE_HT_KEY,
self::$REDIS_REMOTE_TOTAL_COUNT,
self::$REDIS_REMOTE_USE_COUNT
], 3);
}
}
echo '<pre>';
parse_str($_SERVER['QUERY_STRING'], $req);
if($req['act'] && $req['product_id'] && $req['user_id']){
$method = $req['act'];
print_r($req);
$rm = new \ReflectionMethod('Api',$method);
if($rm->isStatic()){
(new Api($req['product_id'], $req['user_id']))::$method();
}else{
(new Api($req['product_id'], $req['user_id']))->$method();
}
}else{
echo '初始化---------<br/>';
apcu_clear_cache();
showInfo();
$data = [$req['product_id'] ?? 1, $req['user_id'] ?? 1];
$key = sprintf(Api::$REDIS_REMOTE_HT_KEY, $data[1]);
$res = Base::conRedis()->hMset($key, [Api::$REDIS_REMOTE_TOTAL_COUNT=>1000, Api::$REDIS_REMOTE_USE_COUNT=>0, 'server_num'=>3]);
var_dump($res);
(new Api($data[0], $data[1]))::initStock();
Base::output();
}
echo '<pre>';
parse_str($_SERVER['QUERY_STRING'], $req);
showInfo();
if($req['act'] && $req['product_id'] && $req['user_id']){
$method = $req['act'];
print_r($req);
$rm = new \ReflectionMethod('Api',$method);
if($rm->isStatic()){
(new Api($req['product_id'], $req['user_id']))::$method();
}else{
(new Api($req['product_id'], $req['user_id']))->$method();
}
}else{
echo '初始化---------<br/>';
apcu_clear_cache();
$data = [$req['product_id'] ?? 1, $req['user_id'] ?? 1];
$key = sprintf(Api::$REDIS_REMOTE_HT_KEY, $data[1]);
$res = Base::conRedis()->hMset($key, [Api::$REDIS_REMOTE_TOTAL_COUNT=>1000, Api::$REDIS_REMOTE_USE_COUNT=>0, 'server_num'=>3]);
var_dump($res);
(new Api($data[0], $data[1]))::initStock();
Base::output();
}
function showInfo(){
$serverInfo = [
'gethostbyname'=>gethostbyname(gethostname().'.'),
'REQUEST_TIME'=>date("Y-m-d H:i:s", $_SERVER['REQUEST_TIME']),
'HTTP_COOKIE'=>$_SERVER['HTTP_COOKIE'],
'HTTP_HOST'=>$_SERVER['HTTP_HOST'],
'SERVER_SOFTWARE'=>$_SERVER['SERVER_SOFTWARE'],
'SERVER_ADDR'=>$_SERVER['SERVER_ADDR'],
'SERVER_PORT'=>$_SERVER['SERVER_PORT'],
'REMOTE_ADDR'=>$_SERVER['REMOTE_ADDR'],
'REMOTE_PORT'=>$_SERVER['REMOTE_PORT'],
];
var_export(array_merge_recursive(['session_id'=>session_id()], $serverInfo));
}