年終結尾最後一次更新,在不寫一次估計年前就沒機會再寫了(保證一月一次)。突然想起之前一個朋友問起實時訂單推送如何實現。在年尾沒啥事自己也比較感興趣,簡單實現了一下。
1.開始
準備工作就不寫了哈~,直接梭。安裝websocket依賴庫。
//安裝 websocket依賴
npm install websocket
//安裝 hashMap依賴
npm install hashmap
2.實現服務端
此處代碼是簡單實現一下業務場景而已,其中使用很多for循環,真實業務場景千萬不要這麼使用,可自行找其他代替方案。如Redis或MongoDB都可以很完美實現。(有興趣或看到的朋友,千萬別複製粘貼一把梭了。一定要看註釋。在結合自己的想法去優化,取長補短。個人覺得有更好的方式去實現不妨留言探討一下。謝謝!共同進步!)
const http = require('http');
const WebSocketServer = require("websocket").server
const hashMap = require('hashmap');
const httpServer = http.createServer((request, response) => {
console.log('[' + new Date + '] Received request for ' + request.url);
response.writable(404);
response.end();
})
const wsServer = new WebSocketServer({
httpServer,
autoAcceptConnections: true
})
const driverMap = new hashMap();//使用hashMap,保持鍵唯一,查詢快
const passengerMap = new hashMap();//使用hashMap,保持鍵唯一,查詢快
const driverContent = [];
wsServer.on('connect', function (connection) {
connection.on('message', message => {
console.log(message);
if (message.type == "utf8") {
//轉換一下JSON格式。
var ConnectMessage = JSON.parse(message.utf8Data)
if (ConnectMessage.type == "driver") {
console.log("司機連接socket!");
//將司機連接頭信息裝進hashMap形成一個隊列,以openID爲Key保證唯一。此爲測試用,實際線上環境使用redis。
//(如:使用hashMap在線上環境遍歷時導致服務器掛壁,獨自負責哈~!)
driverMap.set(ConnectMessage.openId, connection);
//此處將司機信息保存起來。此信息也可以用redis記錄,因爲3.2版本之後redis也支持地理操作(如距離查詢等等)。
//當然使用mongodb也是可以的。redis優先級最高,因爲可以緩存,查詢起來起碼會快很多。
driverContent.push(ConnectMessage)
//司機連接成功給一個提示!
connection.send(JSON.stringify({ code: 200, status: true, msg: "連接成功!等待系統爲您指派任務!" }));
}
if (ConnectMessage.type == "passenger") {
console.log("乘客連接socket!");
//將乘客連接頭信息裝進hashMap形成一個隊列。
passengerMap.set(ConnectMessage.openId, connection);
//這裏是代替redis或mongodb 經緯度距離查詢操作。
for (var i = 0; i < driverContent.length; i++) {
//我這裏實現的比較簡單,正常是拿到客戶經緯度去redis或mongodb查詢匹配的距離,如多個結果直接拿最近距離的結果。
var dist = GetDistance(driverContent[i].lng, driverContent[i].lat, ConnectMessage.lng, ConnectMessage.lat)
if (dist < 20) {
console.log(dist);
console.log("訂單匹配成功");
driverMap.forEach(function (value, key) {
if (driverContent[i].openId == key) {
//此處當訂單派發成功後,司機端給一個提示,是否接單,如接單就將訂單持久化放入數據庫,如不接單將訂單迴歸socket訂單池。
value.send(JSON.stringify({ code: 200, status: true, data: ConnectMessage, msg: "接單成功!" }));
//訂單發佈成功後,通知客戶端
connection.send(JSON.stringify({ code: 200, status: true, data: driverContent, msg: "附近司機成功接攬您的訂單!" }));
}
}, driverMap)
}
}
}
}
}).on("close", (reasonCode, description) => {
console.log('[' + new Date() + '] Peer ' + connection.remoteAddress + ' disconnected.')//關閉
// 由於司機 連接socket 就加入的序列,所有用戶退出socket需要移除用戶序列
driverMap.forEach(function (value, key) {
// 判斷關閉socket的連接 是否與序列中的連接一樣。如果一樣移除
if (connection === value) {
driverMap.remove(key)
//刪除司機信息。
for (var i = 1; i < driverContent.length; i++) {
if (driverContent[i].openId == key) {
driverContent.splice(driverContent[i], 1)
}
}
}
}, driverMap)
//乘客
passengerMap.forEach(function (value, key) {
if (connection === value) {
passengerMap.remove(key)
}
}, passengerMap)
})
})
httpServer.listen(666, () => {
console.log((new Date()) + ' Server is listening on port 666');
})
//代替redis 和 Mongodb中的 經緯度地理計算。此方法爲其他博客找的,距離查詢跟redis mongodb 有很大出入,跟手機APP定位的距離也有出入。
function GetDistance(lat1, lng1, lat2, lng2) {
var radLat1 = lat1 * Math.PI / 180.0;
var radLat2 = lat2 * Math.PI / 180.0;
var a = radLat1 - radLat2;
var b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
var s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
s = s * 6378.137;// EARTH_RADIUS;
s = Math.round(s * 10000) / 10000;
return s;
}
3.實現客戶端(包括客戶端,司機端,只有簡單JS實現,具體UI實現個人有想法可以自己梭一把。)
司機端:
<template>
<div>
<a-button @click="close">斷開</a-button>
</div>
</template>
<script>
export default{
name:"socketTest",
data(){
return{
socket:""
}
},
created(){
this.initWebSocket()
},
mounted(){},
methods:{
initWebSocket(){
var url = "ws:192.168.16.90:666";
this.socket = new WebSocket(url);
this.socket.onopen = this.onOpen;
this.socket.onmessage = this.getMessage;
this.socket.onerror = this.error
this.socket.onclose = this.close
},
onOpen(e){
console.log("socket連接成功!",e);
var data = {
openId:"driver_2d14d3f0d84a11e9b5c7a1dd69b6481f",
type:"driver",
name:'chenbd',
lng:"22.5595796300",
lat:"113.8894646400"
}
this.send(JSON.stringify(data));
},
error(err){
console.log("socket連接失敗",err);
},
getMessage(message){
console.log("消息接收成功!")
console.log(message.data)
console.log(JSON.parse(message.data))
},
send(params){
this.socket.send(params)
},
close(){
console.log("socket關閉!")
},
},
components:{}
}
</script>
<style lang="scss" scoped>
</style>
客戶端:
<template>
<div>
</div>
</template>
<script>
export default{
name:"socketTest2",
data(){
return{
socket:""
}
},
created(){
this.initWebSocket()
},
mounted(){
},
methods:{
initWebSocket(){
var url = "ws:192.168.16.90:666";
this.socket = new WebSocket(url);
this.socket.onopen = this.onOpen;
this.socket.onmessage = this.getMessage;
this.socket.onerror = this.error
this.socket.onclose = this.close
},
onOpen(e){
console.log("socket連接成功!",e);
var data = {
openId:"passenger_2d14d3f0d84a11e9b5c7a1dd69b6481f",
type:"passenger",
name:'波波先生',
lng:"22.6980270000",
lat:"113.9989310000",
address:"我要去深圳北站,我需要30個小時後趕到!"
}
this.send(JSON.stringify(data));
},
error(err){
console.log("socket連接失敗",err);
},
getMessage(message){
console.log("消息接收成功!")
console.log(message.data)
console.log(JSON.parse(message.data))
},
send(params){
this.socket.send(params)
},
close(){
console.log("socket關閉!")
},
},
components:{
}
}
</script>
<style lang="scss" scoped>
</style>
4.實現效果圖。
1.先讓司機端連線。打開頁面,自動連接socket服務器。等待系統自動匹配訂單
2.客戶端發佈訂單消息!打開頁面自動發佈訂單消息,等待系統交付司機訂單消息,等待通知!
5.結尾
做到這就結束了。以上有什麼錯誤或更好的處理方式,請留言我及時更正,避免誤人子弟!
同時年尾最後幾天了。祝大家明年更好,工作順心。新年快樂!