使用消息队列+js实现分布式服务器热切换业务处理功能

本文中的想法源自于最近对erlang的学习以及公司最近新业务需求的技术讨论,具体就是:假设分布式服务器原来用于处理阶乘运算,当需要更改为进行矩形面积计算或者圆形面积计算时,常规作法是停机更新服务器,然后重启,而我的想法是实现类似erlang的热切换功能,即不需要停机即可完成业务功能的切换。

该想法实现的demo中包含,主服务器和分布式服务器两部分。主服务器使用java实现,包含:分布式服务器注册和消息队列两个功能。分布式服务器使用html+js实现,主要实现业务功能的热切换和具体业务处理(阶乘,矩形面积计算,圆形面积计算),demo的流程图如下:


下面是主服务器的核心代码:

if(pathInfo.startsWith(path + "/msg")){  
    String receid = req.getParameter("receiver");  
    Receiver receiver = db.find(receid);  
    String msg = null;  
    //默认返回空消息  
    Message message = new Message();  
    message.setType(Message.TYPE_EMPTY);  
    msg = JSONObject.fromObject(message).toString();  
    if(receiver != null){   //如果该服务器已经注册,则看消息队列中是否有该分布式服务器的消息  
        String strId = req.getParameter("id");  //上次处理的消息id  
        System.out.println("last msg id:["+strId+"]");  
        if(StringUtils.isNotBlank(strId)){    
            //更新消息状态为完成,避免循环推送  
            long id = Long.parseLong(strId);  
            String status = req.getParameter("status");  
            Message lastMsg = msgQueue.get(id);  
            if(lastMsg != null){  
                System.out.println("更新消息状态:["+receid+","+id+","+status+"]");  
                lastMsg.setStatus(status);  
                if(lastMsg.getType().equals(Message.TYPE_BUSINESS) && status.equals(Message.STATUS_COMPLETE)){  
                    //如果为切换业务处理器消息,则需要更新服务器记录中的业务处理器,以及将状态置为切换完成  
                    //#########################################  
                	receiver.setBusiness(((BusinessBody)lastMsg.getMessageBody()).getBusiness());  
                    receiver.setStatus(Receiver.STATUS_COMPLETE);  
                    //############################################  
                }  
            }  
        }  
        //当服务器的业务处理器处理完成时才能推送消息,避免因为分布式系统的异步请求,造成消息处理异常  
        if(receiver.getStatus().equals(Receiver.STATUS_COMPLETE)){  
            Message sendMsg = msgQueue.pull(receiver);  
            if(sendMsg != null){  
                if(sendMsg.getType().equals(Message.TYPE_BUSINESS)){  
                	//#################################################  
                    receiver.setStatus(Receiver.STATUS_UNCOMPLETE); //如果为切换业务处理器消息,则将服务器记录中的状态置为未完成切换,避免异步处理异常  
                    //####################################################  
}  
                msg = JSONObject.fromObject(sendMsg).toString();  
                System.out.println("分发新消息:["+receid + "," + msg + "]");  
            }  
        }  
    }  
    resp.setContentType("application/json");  
    resp.getWriter().write(msg);  
} 
该部分代码主要实现向分布式服务器推送消息,#号注释包围的代码部分是为了解决,分布式服务器采用异步ajax在切换业务功能时所带来的同步问题,即当需要切换业务功能时,可能网络延迟等原因导致,在下次交互周期时,业务功能没有完成切换,这时候,主服务器需要保证不能让分布式服务器获取到任何消息,避免分布式服务器的处理异常,具体实现采用状态标记,即当向分布式服务器推送切换业务功能的消息时,将数据库中服务器的状态标记为正在切换业务功能,每次取消息时,判断服务器是否已经完成业务功能切换,保证切换业务功能的同步。

下面是分布式服务器代码的核心部分:

SlaveUtil.send(slave.msgUrl, {"receiver":tmpResult.receiver, "id":tmpResult.id, "status": tmpResult.status}, true, "json", "get", function(data){  
    if(data.type == "Business"){    //如果为切换业务处理器消息  
        var orginHandler = slave.callbackName;  
        //##############################  
        slave.callbackName = data.messageBody.business; //修改本地系统的业务处理器名称  
        if($("#js_"+slave.callbackName).length == 0){   //如果新的业务处理器的js未加载,则先加载js文件  
            var path = slave.baseDir + "/" + slave.callbackName + ".js";  
            $("#js").append("<script id='js_"+slave.callbackName+"' type='text/javascript' src='"+path+"'>");  
        }  
        slave.callback = eval(data.messageBody.business);   //字符串转函数,切换业务处理器  
        //#################################  
        var now = new Date();  
        $("#result").append("<p>"+now.toLocaleString() + ":[切换本地业务处理器完成:"+ orginHandler +" TO "+ slave.callbackName +"]</p>");  
        slave.result = new Result(slave.id, data.id, "2");  
    }else if(data.type == "Empty"){  
        //空消息不处理  
    }else{  
        var cb = slave.callback;  
        slave.result = cb(data);    //处理消息  
        slave.result.receiver = slave.id;  
    }  
}, function(xhr, status, err){  
    var now = new Date();  
    $("#result").append("<p>"+now.toLocaleString() + ":[请求数据错误:"+status+",错误:"+err+"]</p>");  
}); 
上述代码中的#号注释包围部分就是最重要的热切换功能,主要就是从消息中取出新的业务处理名称,然后加载对应的js文件,然后切换业务功能,唯一需要注意的地方就是必须先加载文件才能切换业务功能。

整个想法的实现都很简单,最大受限的地方就是分布式系统采用js,需要浏览器,因此难以和操作系统进行交互,具体的实用性和性能还有待验证,在此只是分享一下我对于分布式服务器业务处理功能热切换的思路,整个demo的工程文件放在:http://download.csdn.net/detail/kliwin/8351723,具体用法参考工程中doc目录的readme.txt文件,如有任何疑问或者想法,欢迎随时跟我交流(E-mail:kliwin@163.com),代码写的不咋样,只是保证功能和思路是正确的,希望不要介意,谢谢。最后贴一张demo的执行效果图:

 
 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值