Spring Boot仅发送已更改的数据

时间:2017-07-24 22:30:52

标签: spring spring-boot

我正在Spring Boot上的server和后端的经典Javascript上构建游戏。

现在我有这个:

...
@Autowired
private SimpMessagingTemplate template;
...
@Scheduled(fixedRate = 1000 / Constants.FPS)
public void renderClients() {
  for(Game g : games) {
    template.convertAndSend("/game/render/" + g.getId(), g);
  }
}
...

基本上我有多个Games正在运行,我将每个id发送给客户。

然而,我发送的数据(或大部分数据)是静态的(不会改变)......

如果我不想发送整个数据但只发送已更改的部分,该怎么办?

响应JSON看起来像这样:

{"id":"862b1dd8-48d5-4562-802a-7d669a5a5ed5","players":[{"id":"da8dcbec-7028-4a39-9547-a4e2dc321c3c","name":"John Doe","position":{"x":100.0,"y":100.0},"rotation":0.0,"hero":{"maxHealth":1300.0,"movementSpeed":4.5,"attackDamage":32.75,"width":68,"height":71,"heroName":"drowRanger","radius":34.0},"stats":{"kills":0,"lastHits":0},"lastClick":null}],"duration":380107.12}

并且唯一改变的是持续时间,有时是玩家移动时的x和y ......

甚至可能吗? 我可以编写一些中间件,在对象转换为JSON时会这样做吗?

2 个答案:

答案 0 :(得分:0)

维护数据结构存储您更改的值,并将其附加到您的游戏对象。 当发送时,将地图转换为json,并清除它。

使用这种方式可能会占用比以前更多的内存,但不会花费太多时间。

答案 1 :(得分:0)

我知道!!

在我的GameController我这样做:

@Scheduled(fixedRate = 1000 / Constants.FPS)
public void renderClients() throws Exception {
    for(Game g : games) {
        template.convertAndSend("/game/render/" + g.getId(), g.formatToSend());
    }
}

注意g.formatToSend()方法 这是Game类的样子:

public class Game {

    private BandWidthOptimizer optimizer = new BandWidthOptimizer();
    ...
    ...
    public String formatToSend() throws Exception {
        return optimizer.optimize(this);
    }
}

这就是BandWidthOptimizer:

package com.iddqd.doto.optimization;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.tools.classfile.Opcode;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

import java.util.Iterator;
import java.util.Set;
import java.util.function.BiConsumer;

public class BandWidthOptimizer {

    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.json.simple.JSONArray;
    import org.json.simple.JSONObject;
    import org.json.simple.parser.JSONParser;

    private String[] preserveKeys;

    public BandWidthOptimizer() {
        this.preserveKeys = new String[0];
    }

    public BandWidthOptimizer(String[] preserveKeys) {
        this.preserveKeys = preserveKeys;
    }

    public String optimize(Object obj) throws Exception {
        String json = mapper.writeValueAsString(obj);
        Object nobj = parser.parse(json);
        Object oobj = parser.parse(lastJSON);

        JSONObject newJsonObj = (JSONObject)nobj;
        JSONObject oldJsonObj = (JSONObject)oobj;

        JSONObject res = getJSONObjectDiff(newJsonObj, oldJsonObj);

        lastJSON = json;

        return res.toJSONString();
    }

    private JSONObject getJSONObjectDiff(JSONObject obj1, JSONObject obj2) {
        JSONObject res = new JSONObject();
        Set set = obj1.keySet();

        for (Object key : set) {
            // If doesn't exist put it in the diff
            if (!obj2.containsKey(key)) {
                res.put(key, obj1.get(key));
            } else {
                // Get the values from both objects
                Object val1 = obj1.get(key);
                Object val2 = obj2.get(key);
                // If their instances are of the same type
                if(val1 == null) {
                    continue;
                }
                if(val2 == null) {
                    res.put(key, val1);
                    continue;
                }
                if (val1.getClass().equals(val2.getClass())) {
                    // If they are JSONObject
                    if (val1 instanceof JSONObject) {
                        // Recursively parse JSONObject with all of it's properties
                        JSONObject nested = getJSONObjectDiff((JSONObject) obj1.get(key), (JSONObject) obj2.get(key));
                        // If it contains any keys
                        if(nested.keySet().size() > 0) {
                            // Store the diff into final diff
                            res.put(key, nested);
                        }
                    // If they are JSONArrays
                    } else if (val1 instanceof JSONArray) {
                        // If val1 contains some values (is not empty)
                        if(((JSONArray) val1).size() > 0) {
                            // Get their diff
                            JSONArray arr = getJSONArrayDiff((JSONArray) val1, (JSONArray) val2);
                            // If array is not empty
                            if (arr.size() > 0) {
                                // put it into the diff
                                res.put(key, arr);
                            }
                        }
                    // If they are just a pure values
                    } else {
                        // Compare them - If they're not equal
                        if(!val1.equals(val2)) {
                            // put the val1 into diff
                            res.put(key, val1);
                        }
                    }
                } else {
                    res.put(key, val1);
                }
            }
        }
        return res;
    }

    private JSONArray getJSONArrayDiff(JSONArray arr1, JSONArray arr2) {

        JSONArray res = new JSONArray();

        // For every element
        for(int i = 0; i < arr1.size(); i++) {
            Object val1 = arr1.get(i);
            // If i is out of arr2 bounds
            if(i > arr2.size()) {
                // put the arr1 item into the diff
                res.add(val1);
            }

            Object val2 = arr2.get(i);

            if(val1 == null) {
                continue;
            }

            if(val2 == null) {
                res.add(val1);
                continue;
            }

            // If their types are equal
            if(val1.getClass().equals(val2.getClass())) {
                // If they are JSONObjects
                if(val1 instanceof JSONObject) {
                    // Get their diff
                    JSONObject obj = getJSONObjectDiff((JSONObject) val1, (JSONObject) val2);
                    // If it contains any keys
                    if(obj.keySet().size() > 0) {
                        // Store the diff into final diff
                        res.add(obj);
                    }
                // If they are JSONArrays
                } else if (val1 instanceof JSONArray) {
                    // Get their diff
                    JSONArray arr = getJSONArrayDiff((JSONArray) val1, (JSONArray) val2);
                    // If array is not empty
                    if(arr.size() > 0) {
                        // put it into the diff
                        res.add(arr);
                    }
                // If they are just a pure values
                } else {
                    // Compare them - If they're not equal
                    if(val1 != val2) {
                        // add the val1 into diff
                        res.add(val1);
                    }
                }
            } else {
                res.add(val1);
            }
        }
        return res;
    }
}

就是这样,现在如果地图上没有任何移动,结果JSON看起来像这样:

{"duration":282964.56}

因为只有duration更改

但是当我的Player在地图上移动时会看到会发生什么:

{"duration":386676.06,"players":[{"position":{"x":556.5914801003707,"y":153.55964799554002}}]}

<强> TODO 我必须实现一个preserveKeys函数,因为我总是想发送一些像id这样的键等等......