游戏服务器线上出bug,怎么办?急,在线等!

简介: 一直在写基础篇,本来想按顺序来,但是想想无所谓了,只要是计划内的就好,今天聊一下热更新。

一直在写基础篇,本来想按顺序来,但是想想无所谓了,只要是计划内的就好,今天聊一下热更新。



1、热更新的存在合理性


由于游戏开发的节奏很快,加上策划的美其名调整,整天瞎鸡儿改,又经常熬夜加班,所以代码质量相对来说就会低一些,程序经常出现一些bug。尤其是线上问题,如果出现刷资源,刷道具的bug,将会对游戏的平衡性是严重的打击,所以所有出现影响游戏流水的bug都是S级的bug。但是出现问题了就得解决问题,众所周知客户端可以发布补丁,游戏再次启动就好了,客户端的重启只是影响一个玩家,但是服务端重启是影响一个服的所有玩家,问题来了:服务端如何在不停服的情况下解决问题?热更新!!!


2、热更新的局限性


热更新不是万能的,因为程序没办法重启,所以有一些事情做不了,比如:重启应用,哈哈,主要有下面几个限制。

  1. 函数签名不能修改,只能修改函数内部的逻辑。


  1. 不能增加或者减少类的函数或变量。


  1. 函数必须能够退出,如果有函数在死循环中,无法执行更新类(笔者实验发现,死循环跳出之后,再执行类的时候,才会是更新类)


简单来说:只能修改函数逻辑。


3、Java提供了的Instrumentation


Instrumentation 的最大作用 替换和修改某些类的定义


  • 有两种获取Instrumentation接口实例的方法:


  1. 当以指定JavaAgent的方式启动JVM时。在这种情况下, Instrumentation实例被传递给代理类的premain方法。


  1. 当JVM在JVM启动后的某个时间提供启动JavaAgent时。在这种情况下, Instrumentation实例将传递给代理代码的agentmain方法。


如何定义JavaAgent?项目结构如下:

480c005ffd8c494595663b8821faadad~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg

首先看清单文件:


Manifest-Version: 1.0
Premain-Class: com.pdool.GamWatcherPreMain
Agent-Class: com.pdool.GamWatcherAgentMain
Can-Retransform-Classes: true
Can-Redefine-Classes: true
复制代码


Agent-Class是通过attach的方式调用的类。


package com.pdool;
import java.lang.instrument.Instrumentation;
public class GamWatcherAgentMain {
  public static void agentmain(String options, Instrumentation ins) {
      Spy.spy = ins;
      System.out.println("attach成功");
  }
}
复制代码


Premain-Class是通过启动参数的方式调用的类


package com.pdool;
import java.lang.instrument.Instrumentation;
public class GamWatcherPreMain {
  public static void premain(String options, Instrumentation ins) {
      Spy.spy = ins;
      System.out.println("启动 成功");
  }
}
复制代码


生成自己的jar,然后在vm 参数后增加


-javaagent:D:\wechat\GamWatcherAgent\out\artifacts\GamWatcherAgent_jar\GamWatcherAgent.jar
复制代码

 

下面模拟线上环境,首先是主线程:


public class MainThread extends Thread {
  @SneakyThrows
  @Override
  public void run() {
      while (true){
          TestHotSwap.print();
          Thread.sleep(3*1000);
      }
  }
}
复制代码


然后是热更新线程,热更新线程会在控制台输入“1” 的时候进行加载更新类。


redefineClasses(classDefinition); 需要一个参数ClassDefinition,


ClassDefinition 的构造需要class 和二进制数组,前者表示原来的class,后者表示新的class 二进制。


import com.pdool.Spy;
import lombok.SneakyThrows;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.lang.instrument.ClassDefinition;
import java.util.Scanner;
public class RedefineThread extends Thread {
  Scanner scanner;
  public RedefineThread() {
      scanner = new Scanner(System.in);
  }
  @SneakyThrows
  @Override
  public void run() {
      while ( true){
          int i = scanner.nextInt();
          if (i == 1){
              Class<?> testHotSwap = Class.forName("TestHotSwap");
              BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\wechat\\arthas\\demo\\target\\classes\\TestHotSwap.class"));
              int len = bis.available();
              byte []b = new byte[len];
              bis.read(b, 0, len);
              ClassDefinition classDefinition = new ClassDefinition(testHotSwap, b);
              Spy.spy.redefineClasses(classDefinition);
          }
      }
  }
}
复制代码


想要热更的类:热更前


public class TestHotSwap {
  public static void print(){
      System.out.println("before");
  }
}
复制代码


热更后:


public class TestHotSwap {
  public static void print(){
      System.out.println("after");
  }
}
复制代码


启动测试:


/**
* 微信公众号:gamWatcher
*/
public class Aain {
  public static void main(String[] args) throws Exception {
      MainThread mainThread = new MainThread();
      RedefineThread redefineThread = new RedefineThread();
      mainThread.start();
      redefineThread.start();
  }
}
复制代码

 

f759f08a354a4751ba865895d899a4fd~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg

可以看到,代码已经被更新成新的代码。代码热更新完成。


总结:


热更新并不难,难的是没有思路,看了上面的代码,应该有所觉悟,哪有那么高深,不过是使用现成的接口而已。拿去装逼,不谢。


只能更新函数逻辑,需要启动Javaagent注入Instrumentation。


  有一个问题大家可以思考一下,为什么我在宿主程序内可以使用javaagent内对象的属性?大家可以留言给我。

目录
相关文章
|
1月前
|
网络协议 Java API
【网络】TCP回显服务器和客户端的构造,以及相关bug解决方法
【网络】TCP回显服务器和客户端的构造,以及相关bug解决方法
61 2
|
存储 C语言
浪潮NF8480 M5服务器HBA卡BUG处理
浪潮NF8480 M5服务器HBA卡BUG处理
230 0
|
网络协议 Java
《bug记录》在利用TCP协议创建【服务器-客户端交互程序】中出现的一些问题
《bug记录》在利用TCP协议创建【服务器-客户端交互程序】中出现的一些问题
|
Arthas NoSQL Java
线上服务器CPU100%的真相排查【Bug利器Arthas】
这起CPU100%的事故,由某个客户演示的bug暴露出来,气氛比较尴尬....
750 0
线上服务器CPU100%的真相排查【Bug利器Arthas】
|
弹性计算 数据中心
新加坡ap-southeast-1创建ECS与查询时region不一致的BUG
实习用terraform做锤子时偶然碰见,简单来说是:创建ECS时region需要写为ap-southeast-1;而查询时region应该写为all并指定region ID,或将region改为ap-southeast,否则会返回无效信息。 目前,初步认为是IP限制导致的
新加坡ap-southeast-1创建ECS与查询时region不一致的BUG
|
Java 应用服务中间件 Maven
将maven项目部署到tomcat服务器(可能遇到的bug: 启动tomcat后,Maven项目做的ava Web时无WEB-INF/classes)
将maven项目部署到tomcat服务器(可能遇到的bug: 启动tomcat后,Maven项目做的ava Web时无WEB-INF/classes)
171 0
将maven项目部署到tomcat服务器(可能遇到的bug: 启动tomcat后,Maven项目做的ava Web时无WEB-INF/classes)
|
监控 应用服务中间件 nginx
Nginx 服务器性能Bug和性能优化方案(真实经历)
一、遇到的问题 1、问题:本应该是3个ffmpeg ,但是怎么会有5个ffmpeg出现? 2、Lua脚本问题,一直写入日志,导致有大量的日志,这里的错误日志是直接写进nginx的error.log 日志文件的 (1)日志文件 total 1.
1363 0
|
4天前
|
机器学习/深度学习 人工智能 弹性计算
什么是阿里云GPU云服务器?GPU服务器优势、使用和租赁费用整理
阿里云GPU云服务器提供强大的GPU算力,适用于深度学习、科学计算、图形可视化和视频处理等多种场景。作为亚太领先的云服务提供商,阿里云的GPU云服务器具备灵活的资源配置、高安全性和易用性,支持多种计费模式,帮助企业高效应对计算密集型任务。