一:環境
1.一臺阿里雲服務器
2.雲服務器需要安裝redis、kafka、mysql、zookeeper環境
3.一臺本地電腦需要安裝postman
注:kafka與zookeeper的安裝和配置請參考:https://blog.csdn.net/wushichao0325/article/details/84993081 或者使用docker直接拉去鏡像安裝。
二:工具說明
1.postman:用於使用其中的(Run)實現併發測試
2.redis:用於存放商品信息,這裏只創建一個counter字段,使用redis的命令set設置個初始值。100,即商品總量。模擬數據爲:counter=100
3.kafka:結合zookeeper使用,將秒殺成功的結果生成一個producer,待consumer去消費及同步數據到mysql。模擬數據爲:創建一個topic名爲PRODUCT_NUMBER。
4.mysql:存放最終的秒殺結果。模擬數據爲:創建一個seckill數據庫,再創建一個seckill表,表字段爲id自增和date存放秒殺時間和uid存放用戶id。
三:代碼實現
1.代碼目錄
2.node依賴包
3.seckillService.js
接收秒殺請求,並將秒殺結果及用戶信息發送個kafka的producer。
var express=require("express"),
redis=require("redis"),
kafka=require('kafka-node'),
Producer=kafka.Producer,
kafkaClient=new kafka.Client(),
producer=new Producer(kafkaClient);
count=0;
app=express();
config=require("./config/redis.json");
var bodyParser=require('body-parser');
app.use(express.json());
app.use(bodyParser.json({limit: '1mb'})); //body-parser 解析json格式數據
app.use(bodyParser.urlencoded({ //此項必須在 bodyParser.json 下面,爲參數編碼
extended: true
}));
app.all("*", function (req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.setHeader("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
res.setHeader("X-Powered-By",' 3.2.1');
next();
});
app.post('/seckill',function(req,res){
console.log('count='+count++);
let fn=function(optionalClient){
let client;
if(optionalClient=='undefined'||optionalClient==null){
client=redis.createClient(config);
}else{
client=optionalClient;
}
client.on('error',function(er){
console.error(er.stack);
client.end(true);
});
client.watch("counter");//監聽counter字段
client.get("counter",function(err,reply){
if(parseInt(reply)>0){
let multi=client.multi();
multi.decr('counter');//更新redis的counter數量減一。
multi.exec(function(err,replies){
if(replies==null){//counter字段正在操作中,等待counter被其他釋放
console.log("counter被使用");
fn(client);
}else{
var args = {
openid: 'b05NZ2Y1WjbE9fRV9MZTBWWQ==',
seckillTime: '2018-12-12 00:00:01',
}
let payload=[{
topic:'PROUDCT_NUMBER',
messages:[JSON.stringify(args)],
key:"seckill",
partition:0
}];
console.log("payload:",payload);
producer.send(payload,function(err,data){
console.log(data);
});
res.send(replies);
client.end(true);
}
})
}else{
console.log("已經賣光了");
res.send("已經賣光了");
client.end(true);
}
});
}
fn(null);
});
app.listen(8888, "0.0.0.0",function () {
console.log(`Please link connectorServer http://localhost:8888`);
});
4.seckill_kafka_consumer.js
消費者消費生產者的數據,並將數據同步到mysql中。
var kafka=require('kafka-node'),
Consumer=kafka.Consumer,
client=new kafka.Client(),
consumer=new Consumer(
client,
[
{topic:'CAR_NUMBER',partition:0}
],
{
autoCommit:false,
fetchMaxWaitMs: 1000,
fetchMaxBytes: 1024 * 1024,
fromOffset: true
}
);
var mysql=require('mysql');
var connection=mysql.createConnection({
"host":"**.***.***.**",
"user":"root",
"password":"password",
"database":"seckill"
});
connection.connect();
consumer.on('message',function(message){
console.log("得到的生產者的數據爲:",message);
let value=JSON.parse(message.value);
connection.query('insert into seckill set ?',{date:new Date(),uid:value.openid},function(error,results,fields){
if(error){
console.error(error);
}
console.log("插入數據庫成功");
});
})
四:啓動項目
1.啓動雲服務器的redis
2.啓動雲服務器的zookeeper
3.啓動雲服務器的kafka
4.將項目部署到雲服務器中。
5.進入項目根目錄,啓動seckillService.js和seckill_kafka_consumer.js,我是使用pm2去管理這兩個服務。
五:測試
打開postman如圖:
先將這個鏈接保存一個文件,然後點擊Runner按鈕。
如圖:
可以使用post請求:http://seckill.ykplay.com/seckill 測試。
服務器打印信息如下:
seckillService.js
seckill_kafka_consumer.js
有這些信息證明秒殺功能成功實現了。