<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    I want to fly higher
    programming Explorer
    posts - 114,comments - 263,trackbacks - 0
    1.概述
       本篇隨筆主要講述了在線程序通過腳本或者代碼進行更新的一個例子.
           a.在線程序通常會有更改內存數據或者修復錯誤邏輯的需求.
           b.更改內存數據則通常是找到要修改的對象,然后直接通過加載更新腳本更新對象數據.
           c.更改錯誤邏輯,通常是新寫一個class,繼承出錯的類并覆寫出錯的邏輯方法,然后將所有class的實例對象重新替換一下,一定要注意數據的拷貝,即從舊對象復制到新對象.
           d.本篇通過兩種方式,一種更新代碼就是.java,在外部編譯好.class,然后通知在線程序進行更新,在線程序利用classloader加載更新代碼,然后執行更新操作(所有的腳本都會實現一個更新接口).
           e.第二種方式就是直接使用更新腳本groovy,groovy相比java來說,寫起來更簡單(太tm方便了),然后在線程序這邊直接通過GroovyScriptEngine直接運行groovy更新腳本.
    _____________________________________________________________________________________________________________________
    2.例子
      本文的例子是有一個Player對象,該對象有幾個屬性和一個方法。更新腳本則是在線修改玩家對象數據以及修改錯誤的方法.

    3.代碼.

    package com.mavsplus.example.java.compile;

    /**
     * 測試的玩家對象
     * 
     * @author landon
     * @since 1.8.0_25
     
    */
    public class Player implements Cloneable {

        // ---三個測試屬性,需要有動態修改的需求,如線上玩家等級出錯,需要腳本將內存中的值直接修改為正確的值 --//
        public long id;
        public String name;
        public int lv;
        public int vipLv;

        // -- 測試方法,需要有動態修改的需求,如線上的時候發現該方法實現有問題,需要將方法邏輯修正為正確的實現 --//
        public boolean isVip() {
            return vipLv > 0;
        }

        @Override
        public String toString() {
            return "Player [id=" + id + ", name=" + name + ", lv=" + lv + ", vipLv=" + vipLv + "]";
        }

        @Override
        public Object clone() {
            try {
                Player clonePlayer = (Player) super.clone();

                return clonePlayer;
            } catch (CloneNotSupportedException e) {
                throw new InternalError();
            }
        }
    }


    package com.mavsplus.example.java.compile;

    /**
     * 腳本執行接口
     * 
     * @author landon
     * @since 1.8.0_25
     
    */
    public interface IScriptExecute {

        public void execute();
    }


    package com.mavsplus.example.java.compile.script;

    import com.mavsplus.example.java.compile.IScriptExecute;
    import com.mavsplus.example.java.compile.OnlineServer;
    import com.mavsplus.example.java.compile.Player;
    import com.mavsplus.example.java.compile.PlayerService;

    /**
     * 修改Player內存數據腳本
     * 
     * @author landon
     * @since 1.8.0_25
     
    */
    public class ModifyPlayerFieldScript implements IScriptExecute {

        public void execute() {
            System.out.println("Execute Script:ModifyPlayerFieldScript");

            PlayerService playerService = OnlineServer.playerService;

            if (playerService != null) {
                // 注意這里是5L,如果5的話則返回null.5默認為int,而Key的參數是Long,切記
                Player errPlayer = playerService.playerMap.get(5L);

                if (errPlayer != null) {
                    errPlayer.lv = 10086;
                    errPlayer.vipLv = 11;
                }
            }
        }
    }


    package com.mavsplus.example.java.compile.script;

    import java.util.ArrayList;
    import java.util.List;

    import com.mavsplus.example.java.compile.IScriptExecute;
    import com.mavsplus.example.java.compile.OnlineServer;
    import com.mavsplus.example.java.compile.Player;

    /**
     * 修改Player線上業務邏輯
     * 
     * @author landon
     * @since 1.8.0_25
     
    */
    public class ModifyPlayerLogicScript implements IScriptExecute {

        @Override
        public void execute() {
            System.out.println("Execute Script:ModifyPlayerLogicScript");

            List<Player> newPlayers = new ArrayList<>();

            // 這樣更新的一個很大的問題在于得找到所有的Player對象,如果Player不在線怎么辦?
            // 改進:線上其實可以在獲取Player對象的方法中進行統一進行處理(均都根據id查詢Player,用腳本覆寫改方法返回ModifiedPlayer且重新加入cache).-->這樣就不care是否是在線玩家還是離線玩家了
            for (Player oldPlayer : OnlineServer.playerService.playerMap.values()) {
                // 如果是多線程處理的這里也可以直接進行克隆
                ModifiedPlayer newPlayer = new ModifiedPlayer(oldPlayer);
                newPlayers.add(newPlayer);
            }

            for (Player newPlayer : newPlayers) {
                OnlineServer.playerService.playerMap.put(newPlayer.id, newPlayer);
            }
        }

        // 修改的Player對象,修正了方法邏輯實現,需要將線上的對象給替換掉,注意數據的克隆
        private static class ModifiedPlayer extends Player {

            public ModifiedPlayer(Player player) {
                this.id = player.id;
                this.name = player.name;
                this.vipLv = player.vipLv;
                this.lv = player.lv;
            }

            // 覆寫錯誤的數據邏輯
            @Override
            public boolean isVip() {
                return false;
            }
        }
    }



    package com.mavsplus.example.java.compile.groovy

    import com.mavsplus.example.java.compile.OnlineServer;
    import com.mavsplus.example.java.compile.Player;
    import com.mavsplus.example.java.compile.PlayerService;

    // 同ModifyPlayerFieldScript,不過由groovy實現
    // 生成:target\classes\com\mavsplus\example\java\compile\groovy\ModifyPlayerField.class
    def execute() {
        println "execute groovy:ModifyPlayerField";

        PlayerService playerService = OnlineServer.playerService;

        if (playerService != null) {
            Player errPlayer = playerService.playerMap.get(7L);

            if (errPlayer != null) {
                errPlayer.lv = 99999;
                errPlayer.vipLv = 15;
            }
        }
    }

    execute();


    package com.mavsplus.example.java.compile.groovy

    import java.util.ArrayList;
    import java.util.List;

    import com.mavsplus.example.java.compile.OnlineServer;
    import com.mavsplus.example.java.compile.Player;

    // 生成:target\classes\com\mavsplus\example\java\compile\groovy\ModifiedPlayer.class    ModifyPlayerLogic.class
    class ModifiedPlayer extends Player {
        ModifiedPlayer(Player player) {
            this.id = player.id;
            this.name = player.name;
            this.vipLv = player.vipLv;
            this.lv = player.lv;
        }

        // 覆寫錯誤的數據邏輯
        @Override
        boolean isVip() {
            return vipLv > 5;
        }
    }

    def execute(){
        println "Execute groovy:ModifyPlayerLogic"

        List<Player> newPlayers = new ArrayList<>();

        for (Player oldPlayer : OnlineServer.playerService.playerMap.values()) {
            ModifiedPlayer newPlayer = new ModifiedPlayer(oldPlayer);
            newPlayers.add(newPlayer);
        }

        for (Player newPlayer : newPlayers) {
            OnlineServer.playerService.playerMap.put(newPlayer.id, newPlayer);
        }
    }

    execute();


    package com.mavsplus.example.java.compile;

    import groovy.util.GroovyScriptEngine;

    import java.io.File;

    /**
     * 
     * 腳本更新服務,用來加載script目錄下腳本class
     * 
     * <p>
     * 線上更新的時候可以將腳本打成包,并將包放在classpath下
     * 
     * <p>
     * 當然也可以放到一個一個專門的目錄,用自定義classloader進行加載
     * 
     * <p>
     * start0方法用來執行groovy
     * 
     * @author landon
     * @since 1.8.0_25
     
    */
    public class ScriptUpdateService {

        public void loadScriptAndExecute(String scriptClassName) throws Exception {
            String fullName = "com.mavsplus.example.java.compile.script." + scriptClassName;

            Class<IScriptExecute> clazz = (Class<IScriptExecute>) ClassLoader.getSystemClassLoader().loadClass(fullName);
            IScriptExecute instance = clazz.newInstance();

            instance.execute();
        }

        public void start() {
            try {
                // 掃描script目錄
                String scriptPath = getClass().getResource("").getPath() + "\\script";

                File file = new File(scriptPath);
                File[] subFiles = file.listFiles();

                for (File subFile : subFiles) {
                    String fileClassName = subFile.getName();
                    int dotCharIndex = fileClassName.indexOf('.');

                    String loadClassName = fileClassName.substring(0, dotCharIndex);

                    if (loadClassName.contains("$")) {
                        continue;
                    }

                    loadScriptAndExecute(loadClassName);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 加載groovy
        public void start0() {
            try {
                String groovyPath = "E:\\github\\mavsplus-all\\mavsplus-examples\\src\\main\\java\\com\\mavsplus\\example\\java\\compile\\groovy";
                GroovyScriptEngine engine = new GroovyScriptEngine(groovyPath);

                engine.run("ModifyPlayerField.groovy""");
                engine.run("ModifyPlayerLogic.groovy""");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    package com.mavsplus.example.java.compile;

    import java.util.HashMap;
    import java.util.Map;

    /**
     * 玩家服務
     * 
     * @author landon
     * @since 1.8.0_25
     
    */
    public class PlayerService {

        // -- 玩家map --//
        public Map<Long, Player> playerMap = new HashMap<>();

        public void start() {
            for (Player player : playerMap.values()) {
                System.out.println(player);
                System.out.println("isVip:" + player.isVip());
            }
        }
    }


    package com.mavsplus.example.java.compile;

    import java.util.concurrent.ThreadLocalRandom;
    import java.util.concurrent.TimeUnit;

    /**
     * 線上運行的服務程序
     * 
     * <p>
     * 該example的主要目的在于測試在程序運行過程中,需要改變一些內存中的一些數據或者操作,需要動態修改.目前初步的實現方式是服務程序可以接收一個.java
     * , 這個可以理解為補丁或者更新腳本,程序收到該腳本后,在內存中動態編譯執行,進而修改一些數據或者操作
     * 
     * <p>
     * 當然也可以直接將補丁腳本編譯好,然后將編譯后的class放到classpath下,通知服務程序用classloader進行加載并更新.
     * 
     * <p>
     * 后續可以使用jvm腳本如groovy做對比分析
     * 
     * @author landon
     * @since 1.8.0_25
     
    */
    public class OnlineServer {

        public static PlayerService playerService;
        public static ScriptUpdateService scriptUpdateService;

        public static void init() {
            playerService = new PlayerService();
            scriptUpdateService = new ScriptUpdateService();
        }

        public static void initData() {
            // 初始化部分測試數據

            for (int i = 0; i < 10; i++) {
                Player player = new Player();

                player.id = i + 1;
                player.lv = ThreadLocalRandom.current().nextInt(100+ 1;
                player.name = "landon" + i;
                player.vipLv = ThreadLocalRandom.current().nextInt(10+ 1;

                playerService.playerMap.put(player.id, player);
            }
        }

        // 模擬服務啟動
        public static void start() throws Exception {
            while (true) {
                playerService.start();

                System.out.println("----------------------------------");

                TimeUnit.SECONDS.sleep(5);

                // 目前測試這樣操作,實際線上通過網絡消息通知更新服務器更新具體的某個腳本即可
                scriptUpdateService.start();
                playerService.start();

                System.out.println("----------------------------------");

                TimeUnit.SECONDS.sleep(5);

                // 加載groovy腳本
                scriptUpdateService.start0();
                playerService.start();

                System.out.println("----------------------------------");
            }
        }

        public static void main(String[] args) throws Exception {
            init();
            initData();

            start();
        }
    }


    4.代碼部分解釋
        1.OnlineServer是一個在線運行的程序,它包括一個玩家服務。目前程序只是簡單打印所有的玩家信息.
        2.程序運行的時候,比如我們發現線上某一個Player的數據有問題,我們需要進行在線進行修正,所以我們會寫一個更新腳本ModifyPlayerFieldScript.java,然后將其編譯.然后通知在線程序利用classloader加載更新腳本并執行(執行默認的execute方法)
        3.作為對比,同時寫了功能一樣的groovy更新腳本,然后通知在線程序利用GroovyScriptEngine直接運行指定的groovy腳本.
        4.同時程序運行的時候,會發現Player類的一個方法實現有bug,我們需要在線進行修正,所以我們會寫一個更新腳本ModifyPlayerLogicScript.java(主要原理在于繼承Player并覆寫出錯方法),然后將其編譯,然后通知在在線程序更新.
        5.作為對比,也同樣寫了一個功能一樣的groovy更新腳本實現.


    5.輸出:

    Player [id=1, name=landon0, lv=93, vipLv=6]
    isVip:true
    Player [id=2, name=landon1, lv=37, vipLv=9]
    isVip:true
    Player [id=3, name=landon2, lv=25, vipLv=2]
    isVip:true
    Player [id=4, name=landon3, lv=78, vipLv=4]
    isVip:true
    Player [id=5, name=landon4, lv=27, vipLv=8]
    isVip:true
    Player [id=6, name=landon5, lv=84, vipLv=1]
    isVip:true
    Player [id=7, name=landon6, lv=95, vipLv=4]
    isVip:true
    Player [id=8, name=landon7, lv=100, vipLv=8]
    isVip:true
    Player [id=9, name=landon8, lv=41, vipLv=9]
    isVip:true
    Player [id=10, name=landon9, lv=100, vipLv=5]
    isVip:true
    ----------------------------------
    Execute Script:ModifyPlayerFieldScript
    Execute Script:ModifyPlayerLogicScript
    Player [id=1, name=landon0, lv=93, vipLv=6]
    isVip:false
    Player [id=2, name=landon1, lv=37, vipLv=9]
    isVip:false
    Player [id=3, name=landon2, lv=25, vipLv=2]
    isVip:false
    Player [id=4, name=landon3, lv=78, vipLv=4]
    isVip:false
    Player [id=5, name=landon4, lv=10086, vipLv=11]
    isVip:false
    Player [id=6, name=landon5, lv=84, vipLv=1]
    isVip:false
    Player [id=7, name=landon6, lv=95, vipLv=4]
    isVip:false
    Player [id=8, name=landon7, lv=100, vipLv=8]
    isVip:false
    Player [id=9, name=landon8, lv=41, vipLv=9]
    isVip:false
    Player [id=10, name=landon9, lv=100, vipLv=5]
    isVip:false
    ----------------------------------
    execute groovy:ModifyPlayerField
    Execute groovy:ModifyPlayerLogic
    Player [id=1, name=landon0, lv=93, vipLv=6]
    isVip:true
    Player [id=2, name=landon1, lv=37, vipLv=9]
    isVip:true
    Player [id=3, name=landon2, lv=25, vipLv=2]
    isVip:false
    Player [id=4, name=landon3, lv=78, vipLv=4]
    isVip:false
    Player [id=5, name=landon4, lv=10086, vipLv=11]
    isVip:true
    Player [id=6, name=landon5, lv=84, vipLv=1]
    isVip:false
    Player [id=7, name=landon6, lv=99999, vipLv=15]
    isVip:true
    Player [id=8, name=landon7, lv=100, vipLv=8]
    isVip:true
    Player [id=9, name=landon8, lv=41, vipLv=9]
    isVip:true
    Player [id=10, name=landon9, lv=100, vipLv=5]
    isVip:false
    ----------------------------------
    posted on 2015-08-07 21:59 landon 閱讀(1210) 評論(0)  編輯  收藏 所屬分類: ScriptHotSwapClassLoader

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 一区二区免费在线观看| jzzjzz免费观看大片免费| 免费观看国产小粉嫩喷水| 久久久久久噜噜精品免费直播 | 青青青国产色视频在线观看国产亚洲欧洲国产综合| 老牛精品亚洲成av人片| 亚洲日韩精品A∨片无码| 最近中文字幕国语免费完整| 亚洲AV色无码乱码在线观看| 亚洲AV无码一区东京热久久| 欧洲美熟女乱又伦免费视频 | 中文字幕日本人妻久久久免费| 亚洲另类视频在线观看| 亚洲成aⅴ人片久青草影院| 97青青草原国产免费观看| 另类图片亚洲校园小说区| 久久精品国产亚洲AV无码偷窥| 亚洲精品岛国片在线观看| 免费人成在线观看69式小视频| 好猛好深好爽好硬免费视频| 亚洲娇小性xxxx色| 久久亚洲高清观看| 亚洲av午夜成人片精品电影| 91免费播放人人爽人人快乐| 中文字幕在线免费观看视频| 亚洲精品无码久久久久牙蜜区| 亚洲好看的理论片电影| 深夜国产福利99亚洲视频| 4399好看日本在线电影免费| 国产一区二区三区免费观看在线| 国产偷国产偷亚洲高清人| 亚洲人成在线播放| 亚洲国产女人aaa毛片在线| vvvv99日韩精品亚洲| 成人免费男女视频网站慢动作| 99精品在线免费观看| 久久久久免费视频| 无人视频免费观看免费视频 | 四虎最新永久免费视频| 三级黄色在线免费观看| 另类图片亚洲校园小说区|