实验五 消息中间件

实验目的

模拟在道路上汽车行驶的场景:
1,订单可以根据订单生成器(生成器可参考附件的文件,但可自行修改;比如根据车辆数,修改订单生成的速率)生成起点和终点。车辆以概率p(比如p=0.8)车根据订单沿着道路行走,行驶速度根据路段给定;车辆以1-p的概率随机移动(当没有订单时);(路网为DIMACS格式的文件。)
2,有些车发布消息(生产者,比如内容是一个图像),有些车接收和消费消息(消费者);当车与车之间的距离小于R时(比如R=100m),可以进行通信:生产者把消息发送给在通信范围内的消费者。当大于R时,通信停止;【不断变动消息的订阅关系】

把厦门市的道路路网进行可视化展现(基于前端html页面或者java窗体);并能展现汽车移动。
基于Spring 的 JMS编程模型,完成以上消息发布、消息接收等模拟器编程。

实验过程

  • springboot +JMS开发环境搭建
  • 路网搭建
  • 搭建订单生成服务器
  • 搭建车辆模拟服务器
  • 显示车辆移动

SpringBoot +JMS开发环境搭建

参考博文https://www.cnblogs.com/niit-soft-518/p/6957384.html

路网搭建

这里采用JGraphT搭建厦门的全联通的时间依赖的速度路网,具体过程略。

搭建订单生成服务器

订单生成器读取真实订单txt文件,将其转化成消息传递到request队列中,供车辆模拟服务器异步获取进行处理。
模拟开始时,需要同步两个服务器的时间,具体做法如下:订单生成服务器启动时会监听simulatorCommand队列,车辆模拟服务器开始模拟时会将start命令传入此队列,告诉订单生成器现在的时间,并通知其开始工作。关键代码如下:

   @JmsListener(destination = "simulatorCommand",containerFactory = "queueListenerContainer")
    public void recieve(String msg) throws Exception {
        String[] info=msg.split("#");
        //从消息中获取车辆模拟服务器传达的命令,如果是start则开始工作
        String command=info[0];
        if(command.equals("start")){
            if(thread!=null&&thread.isAlive()){
                return;
            }
            this.startTime=Integer.valueOf(info[1]);
            this.time=startTime;
            this.unitTime=Integer.valueOf(info[2]);
            this.isInterupt=false;
            this.thread=new Thread(this,"simulator");
            thread.start();
        }else if(command.equals("stop")){
            isInterupt=true;
        }else if(command.equals("terminate")){
            isInterupt=true;
            bufferedReader.close();
            bufferedReader=null;
        }
    }

在真实场景中,用户通过rest形式的http请求发出乘车请求,对应的controllor将用户的乘车信息转化为能够处理的消息格式,传入request队列中:

@RequestMapping(value = "/request",method = RequestMethod.POST)
    public void getRequest(@RequestBody Map request){
        jmsTemplate.convertAndSend("request",request.get("request"));
    }

订单生成器会读取真实订单txt文件,并模拟http请求,根据时间调用此controller,达到模拟真实场景的效果。

搭建车辆模拟服务器

服务器的每次调度过程如下:
1.向request队列中取有限个请求加到原有的请求队列中
2.获取当前的请求队列和空载车辆,选择匹配算法进行匹配(这里使用最近优先匹配)
3.根据匹配结果更新被匹配车辆状态
4.模拟车辆发布接收消息
5.随机移动空载车辆

如果将每一辆车都配置一个独立的topic,则要为每辆车配置总车辆数个监听器来监听其他车辆的topic,太麻烦。我们将所有车辆发出的信息保存的一个topic中,交给第三方——消息处理器分发信息。
每次调度,选择1辆车发布消息,选择与它最近的5辆车订阅此消息。
关键代码如下:

public void sendMessage(){
        List allCars=carExcutor.getAllCars();
        int selectId=(int)(new Random().nextDouble()*allCars.size())+1;
        for(Car car:allCars){
            selectId--;
            if(selectId==0){
                jmsTemplate.convertAndSend("carMessage",car.getId()+"#car "+car.getId()+" message");
                break;
            }
        }
    }

    @JmsListener(destination = "carMessage",containerFactory = "topicListenerContainer")
    public void getMessage(String msg){
        String[] info=msg.split("#");
        int carId=Integer.valueOf(info[0]);
        String message=info[1];
        List allCars=carExcutor.getAllCars();
        Car messageCar=allCars.get(carId-1);
        List nearCars=getNearCars(5,messageCar);

        for(Car car:nearCars){
            System.out.println("car "+car.getId()+" receive message: "+message);
        }
    }

    private List getNearCars(int num,Car center){
        List nearCars=new LinkedList<>();
        List distances=new LinkedList<>();
        for(Car car:carExcutor.getAllCars()){
            if(center==car){
                continue;
            }
            RoadNode carL=((SimulatorCar)car).getLocation();
            RoadNode centerL=((SimulatorCar)center).getLocation();
            double distance=(carL.getLat()-centerL.getLat())*(carL.getLat()-centerL.getLat())+(carL.getLon()-centerL.getLon())*(carL.getLon()-centerL.getLon());
            for(int i=0;inum){
                ((LinkedList) nearCars).removeLast();
                ((LinkedList) distances).removeLast();
            }
        }
        return nearCars;
    }

前4步做完计算服务器花费的时间,并转化为模拟时间,将空载车辆随机移动这么多的时间。

显示车辆移动

我们调用高德地图的JS API进行可视化,模拟器缓存了当前空载车辆的位置信息,我们通过提供的接口调用获取,并在图中标出相应的位置。每隔1s轮询刷新一次。

实验结果

先启动订单生成服务器,再启动车辆模拟服务器,调用/start接口开始模拟
订单生成器输出订单信息(只输出了请求的t0信息):


image.png

消息处理器输出车辆之间的消息接收信息:


image.png

显示车辆位置信息:
image.png

你可能感兴趣的:(实验五 消息中间件)