zoukankan      html  css  js  c++  java
  • 游戏服务器之Java热更新

    对于运行良好的游戏来说,停服一分就会损失很多收益。因为有些小bug就停服就划不来了。在使用Java开游戏服务器时,JVM给我们提供了一些接口,可以简单做一些热更新。修复一些小Bug而不用重启服务。

    JVM可以给运行中的服务器绑定一个代理,在这个代理中可以拿到Instrumentation 这个类的实例,它可以让用户手动修改jvm中的class类,对它进行热更新,但是有一点,用于热更新的新类和老的类方法签名必须一样,即不能修改方法的名字,参数类型,还有修改声明的字段。只能修改方法体里面的代码。一般的小bug都是方法体内的逻辑漏洞,不会做很多大的修改,所以这种方式还是能满足我们的需要求的。

    现在我们分三个项目:

    1,GameServer 即我们正常的游戏服务器。

    2,LoadAgent  这个是热更新的代理项目,热更新的操作就在这里面执行。

    3,GameServerHotBoot    这个项目是用来把LoadAget代理和GameServer进行绑定的。

     

    JDK代理的两种方式:

    1.premain方式是Java SE5开始就提供的代理方式,但其必须在命令行指定代理jar,并且代理类必须在main方法前启动,它要求开发者在应用启动前就必须确认代理的处理逻辑和参数内容等等

    2.agentmain方式是JavaSE6开始提供,它可以在应用程序的VM启动后再动态添加代理的方式

    应用场景:

    premain这种方式必须在jar包启动的时候进行指定,它是运行在项目的main方法之前的,即项目启动时:

    1
    java – javaagent:LoadAgent.jar -jar GameServer.Jar

     

    但是正常的生产环境下,一般不会开启代理功能,但是在发生问题时,我们不希望停止应用就能够动态的去修改一些类的行为,以帮助排查问题,这在应用启动前是无法确定的。这时agentmain就可以做到了。所以我们采用agentmain这种方式。

    1,LoadAgent实现

    这个实现也比较简单,就像我们的程序入口有main方法一样,它需要一个agemtmain方法

    复制代码
    public class GameServerAgent {
    
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> agentmain(String args, Instrumentation inst) <span style="color: #0000ff;">throws</span><span style="color: #000000;"> Exception {
        
        System.out.println(</span>"agent 启动成功,开发重定义对象...."<span style="color: #000000;">);
    
        Class</span>&lt;?&gt;[] allClass =<span style="color: #000000;"> inst.getAllLoadedClasses();
        </span><span style="color: #0000ff;">for</span> (Class&lt;?&gt;<span style="color: #000000;"> c : allClass) {
            </span><span style="color: #0000ff;">if</span> (c.getName().endsWith("TestHot"<span style="color: #000000;">)) {
                String pathname </span>= "config\TestHot.class"<span style="color: #000000;">;
                File file </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> File(pathname);
                </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
                    </span><span style="color: #0000ff;">byte</span>[] bytes =<span style="color: #000000;"> fileToBytes(file);
                    System.out.println(</span>"文件大小:" +<span style="color: #000000;"> bytes.length);
                    ClassDefinition classDefinition </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> ClassDefinition(c, bytes);
                    inst.redefineClasses(classDefinition);
                } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (IOException e) {
                    e.printStackTrace();
                }
                System.out.println(</span>"转换代码。。。"<span style="color: #000000;">);
            }
        }
        System.out.println(</span>"热更新成功...."<span style="color: #000000;">);
    
    }
    
    </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">byte</span>[] fileToBytes(File file) <span style="color: #0000ff;">throws</span><span style="color: #000000;"> IOException {
        FileInputStream in </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> FileInputStream(file);
        </span><span style="color: #0000ff;">byte</span>[] bytes = <span style="color: #0000ff;">new</span> <span style="color: #0000ff;">byte</span><span style="color: #000000;">[in.available()];
        in.read(bytes);
        in.close();
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> bytes;
    }
    

    }

    复制代码

    对某个class的替换有两种方式

    1,使用ClassFileTransformer

    2,使用ClassDefinition

    由于ClassDefinition比较方便,所以我们使用ClassDefinition对类进行更新。

    项目源码地址:https://github.com/youxijishu/game-hot-update

    热更新步骤:

    1,打包LoadAgent

     在使用LoadAgent的时候,需要在MANNIFEST.MF添加一些属性

    1
    2
    3
    Agent-Class: com.xinyue.hot.agent.GameServerAgent
    Can-Redefine-Classes: true
    Can-Retransform-Classes: true

     这个在可以在打包的pom.xml中配置

    复制代码
     <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>2.3.1</version>
                    <configuration>
                        <archive>
                            <manifest>
                                <addClasspath>true</addClasspath>
    
                        &lt;/manifest&gt;
                        &lt;manifestEntries&gt;
                            &lt;Agent-Class&gt;<span style="color: #000000;">
                                com.xinyue.hot.agent.GameServerAgent
                            </span>&lt;/Agent-Class&gt;
                            &lt;Can-Redefine-Classes&gt;<span style="color: #0000ff;">true</span>&lt;/Can-Redefine-Classes&gt;
                            &lt;Can-Retransform-Classes&gt;<span style="color: #0000ff;">true</span>&lt;/Can-Retransform-Classes&gt;
                        &lt;/manifestEntries&gt;
                    &lt;/archive&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;</span></pre>
    
    复制代码

    然后使用mvn install命令即可,在target中找到生成的jar,LoadAgent-0.0.1-SNAPSHOT.jar,把它放到GameServer下的config目录下面,这里是测试,用的相对路径。

    2,在GameServer中创建一个测试的类,叫TestHop.java,使用里面有一个输出方法,打印1,然后使用mvn install进行编译,在target/classes中找到这个类,把它也复制到GameServer的config路径下面。

    3,把TestHop类中的输出修改为2,运行GameServer,这时会输出pid和2

    4,把pid复制到GameServerHotUpdate的HotUpdateMain类中,当然,在实际应用中也可以通过args传进来。然后运行HotUpdateMain,等会儿就会输出结果

    这时我们发现类的输出变化了,没有热更之前输出是的2,热更新之后,输出的是1.说明,热更新成功了。

    项目源码地址:https://github.com/youxijishu/game-hot-update


  • 相关阅读:
    pat00-自测5. Shuffling Machine (20)
    Spiral Matrix
    Search in Rotated Sorted Array II
    Search in Rotated Sorted Array
    Best Time to Buy and Sell Stock II
    4Sum
    3Sum Closest
    3Sum
    MySQL存储过程、函数和游标
    Word Ladder
  • 原文地址:https://www.cnblogs.com/jpfss/p/10943695.html
Copyright © 2011-2022 走看看