使用nodejs+socket.io+redis订阅发布完成竞拍的过程

42 篇文章 0 订阅
37 篇文章 0 订阅

模拟用户竞拍出价的过程,其中涉及到soket.io实时消息以及拍单处理过程

具体的时序图业务流程整理如下:



服务端nodejs代码: node socket_server.js

var RedisPort = 6379;
var RedisHost = "127.0.0.1";
var redis      = require("redis");
var rclient   = redis.createClient(RedisPort,RedisHost);
rclient.on("error", function (err) {
    console.log("Error(redis): " + err);
})

var io = require('socket.io').listen(8006);
var socketUsers = {};
io.sockets.on('connection', function(socket) {
    socket.emit('connect_success', {msg: socket.id});
    socket.on('login',function(data, fn){
        console.log("arrive here,login()"+data.order_id);
        socketUsers[socket.id] = {'order_id':data.order_id, 'socket':socket};

        //订阅
        rclient.subscribe("orderchannel:"+data.order_id); 
    });

    //收到消息后执行回调,message是redis发布的消息
    rclient.on('message', function(channel, price){
        console.log("我在频道【"+channel+"】接收到了消息:"+price);
        console.log(socketUsers);
        console.log("in message()\n");

        //channel名称:orderSubUsers:orderId
        var orderId = channel.substr(13);
        console.log("orderId=["+orderId+"]\n");

        console.log("aaaaaaa");
        var currSock = socketUsers[socket.id];
        console.log(currSock);
        for(var item in currSock) {
            if (item == 'order_id') {
                if(currSock[item] == orderId) {
                    socket.emit('receive', {'nowprice':price});
                }
            }
        }
        console.log("bbbbbbb");        
    });

    //监听断线
    socket.on('disconnect',function() {
        console.log('-链接断开['+socket.id+']-');   
        delete socketUsers[socket.id];
    });  
});


客户端php代码  bid_price.php

<?php
    session_start(); 
    if(empty($_SESSION['user_id'])) {
        header('Location:http://'.$_SERVER['HTTP_HOST'].'/bid_login.php');
        return;
    } 
    ?>
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
    <title>测试nodejs socket.io的客户端</title>    
</head>
<body>
<select name="order" id="selectOrders">
    <option value="">请选择一个拍单</option>
    <option value="1000" selected="selected">奥迪A4</option>
    <option value="1001">宝马Q3</option>
</select><br/>
<label>买车出价</label><br/>
<input type="text" id="txtPrice" /><br/>
<input type="hidden" id="socketId" />
<button οnclick="sendprice()">竞拍出价</button>
<br/>
<label>实时出价信息</label><br/>
<textarea rows="50" cols="60" id="areaInfo"></textarea>
</body>
    <script type="text/javascript" src="./jquery.min.js"></script>
    <script type="text/javascript" src="./socket.io.js"></script>
<script>    
    var currentOrderId = "<?php echo intval($_GET['order_id']);?>";
    $("#selectOrders").val(currentOrderId);
    socket = io.connect('http://192.168.3.125:8006');
    var eleArea = document.getElementById("areaInfo");
    
    //todo:连接成功后获取当前socket.id,连同当前用户Id发给用户
    socket.on('connect_success',function(data){
        $("#socketId").val(data.msg);  //获取到当前客户端的socketId 
        var postdata = {order_id:$("#selectOrders").find("option:selected").val()};
        socket.emit('login',postdata,function(result){
            console.log("登录成功");
        });
    }); 
    socket.on("receive", function(data){  
        console.log(data);             
        eleArea.innerHTML = '当前价格:'+data.nowprice+"\n";
    });
    var sendprice = function() {
        var post ={order_id:$("#selectOrders").find("option:selected").val(), price:$("#txtPrice").val(), socketId:$("#socketId").val()};
        //todo:向php页面提交拍单请求,参数:价格、拍单Id
        $.post('bid_operate.php?action=bidding',post,function(data){
            if (data.status) {
                // socket.emit("postprice",{order_id:post.order_id, price:post.price});
                alert("拍单提交成功");  
            }else{
                alert("拍单失败:"+data.msg);
            }
        },'json');
    }
</script>
</html>

fom提交后的代码: bid_operate.php

<?php
session_start();
switch($_GET['action']) {
    case 'login':
        checkLogin();
        break;
    case 'bidding':
        biddingCar();
        break;
    default:
        echo "参数非法!";
        break;
}
session_write_close();

function checkLogin() {
    $userList = array(['id'=>1,'username'=>'wangxiao','password'=>'secret'],
            ['id'=>2,'username'=>'shiyf','password'=>'secret'],['id'=>3,'username'=>'liming','password'=>'secret']);
    $username = trim(htmlentities($_POST['username']));
    $userpwd = trim(htmlentities($_POST['password']));
    $isSuccess = false;
    foreach($userList as $item) {
        if($username == $item['username'] && $userpwd == $item['password']) {
            $isSuccess = true;
            $_SESSION['username'] = $item['username'];
            $_SESSION['user_id'] = $item['id']; 
            header('Location:http://'.$_SERVER['HTTP_HOST'].'/bid_price.php');
        }
    }

    if (!$isSuccess) {
        echo "用户名或密码错误<script>window.location.href='./bid_login.php'</script>";
        return;
    }
}

function biddingCar() {
    //将出价信息写入数据库
    //考虑是从拍单主表和拍单出价表取最新价格合适?可以考虑在竞拍期间不写入最终价格,到竞拍结束的时候再写入价格。

    //先从redis中读取出该拍单的价格信息,拍单的信息从redis的集合中来找
    //判断价格是否合法:拍单的价格要比原来至少高出三百元
    //判断拍单状态

    $output = ['status'=>false, 'msg'=>''];

    $user_id  = $_SESSION['user_id'];
    $order_id = $_POST['order_id'];
    $price    = $_POST['price'];
    $socketId = $_POST['socketId'];
    $createtime = microtime(TRUE);


    //判断价格是否有变动 multi->watch()->exec();
    //判断拍单 然后监控拍单的价格如果价格有变化停止

    //todo:将用户的出价信息写入到bidding_log表
    //todo:将最新的价格写入到bidding_car表
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    $watchKey = 'bidOrder:'.$order_id;
    $redis->watch($watchKey);

    $pdo = new PDO('mysql:host=192.168.3.125:3306;dbname=test','root','secret');
    try{
        $pdo->beginTransaction();
        //插入出价表
        $sth = $pdo->prepare('insert into order_bidding_log(order_id,user_id,price,createtime,status) values(?,?,?,?,1);');
        $result1 = $sth->execute([$order_id, $user_id, $price, $createtime]);

        $myinfo =  'in '.__METHOD__.',result1:'.var_export($result1,TRUE)."\n";
        file_put_contents('/tmp/shiyf.log', $myinfo, FILE_APPEND);

        if ($result1) {
            $sth = $pdo->prepare('update orders set user_id=?, price=? where id=?');
            $result2 = $sth->execute([$user_id, $price, $order_id]);//更改主表
            
            $myinfo =  'in '.__METHOD__.',result2:'.var_export($result2,TRUE)."\n";
            file_put_contents('/tmp/shiyf.log', $myinfo, FILE_APPEND);
            
            $ret = $redis->multi()->hset($watchKey,'price', $price)->hset($watchKey,'user_id',$user_id)->exec();

            $myinfo =  'in '.__METHOD__.',ret:'.var_export($ret,TRUE)."\n";
            file_put_contents('/tmp/shiyf.log', $myinfo, FILE_APPEND);

            if (is_array($ret)) {
                $count = count($ret);
                if ($ret[$count-1] === 0) {
                    $pdo->commit();
                    //将user_id与socketId关联起来
                    //$redis->sAdd('socketids:'.$user_id, $socketId);

                    //将user_id记录到一个订阅列表中,后续会查找其中的socketId,找到后会发消息
                    $redis->sAdd('orderSubUsers:'.$order_id, $socketId);

                    //发布消息
                    $redis->publish('orderchannel:'.$order_id, $price);

                    $output['status'] = true;
                } else {
                    $pdo->rollback();
                }
            } else {
                $pdo->rollback();
            }
        } else {
            $pdo->rollback();
            $output['msg'] = "插入order_bidding_log错误";
        }

        

    } catch(Exception $e) {
        $myinfo =  'in '.__METHOD__.','.$e->getMessage();
        file_put_contents('/tmp/shiyf.log', $myinfo, FILE_APPEND);
        $output['msg'] = "数据库操作异常:".$e->getMessage();
        $pdo->rollback();
    }

    echo json_encode($output);
    return;
    

}

    



?>

可以针对多个拍单进行出价,多个客户端会同时收到最新的出价信息的。

具体访问地址:127.0.0.1/bid_price.php?order_id=1001


  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Typora是一款方便易用的Markdown编辑器,可以用来记录笔记并画流程图、时序图和甘特图。关于如何使用Typora画时序图,可以参考以下资源: 1)《使用Typora之画图》:这篇文章介绍了使用Typora画图的方法,包括如何画流程图、时序图等。 2)《快速学习时序图:时序图简介、画法及实例》:这篇文章详细介绍了时序图的概念、画法和实例,对于初学者来说非常有帮助。 3)《UML时序图(Sequence Diagram)学习笔记》:这篇文章详细介绍了UML时序图的基本概念和画法,可以作为进一步学习时序图的参考资料。 总结来说,你可以使用Typora来画时序图,可以参考上述资源以了解详细的画法和示例。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [typora-setup-x64](https://download.csdn.net/download/hujincai_55/13122188)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [使用Typora画 流程图、时序图、顺序图、甘特图、等图详解](https://blog.csdn.net/qq_36075612/article/details/118161100)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值