• 线上bug的解决方案--带来的全新架构设计


    缘由

    本人从事游戏开发很多年一直都是游戏服务器端开发。

    因为个人原因吧,一直在小型公司,或者叫创业型团队工作吧。这样的环境下不得不逼迫我需要什么都会,什么做。

    但是自我感觉好像什么都不精通。。。。。

    好了好像偏题了!

    回归正题

    现在公司是做手游开发,java服务器 + U3D做客户端的mmo手游,仿魔兽世界!

    做软件的都知道一个蛋疼的事情,那就是线上Bug。但是做服务的人更清楚另一个蛋疼的事情,就是在线更新,不停服更新。

    知道的人,想想都蛋疼,当然不知道的,那就只能自己想象了。

    线上经常出问题大家都是能理解的。bug肯定是有的。做服务的同僚应该都知道,不到维护的时间是不能停止服务区更新的除非是非常重大的bug。

    这里面游戏服务器更为严格,因为你无故维护,维护一次可能就会流失一批玩家。所以这一直是一个梗。

    本人经过三个星期的研究和测试,最终发现一个可以解决这种一出现bug,却能在线更新,且不影响性能的触发方式。

    通过这样一个方式,主程序,加数据库,通过脚本实现逻辑模块;

    这样就有一个脚本的实现形式,脚本如果实现的不好,那么性能消耗就很大了。

    我的做法是把逻辑部分的方法抽象出来。放到脚本去实现。通过脚本把实现脚本逻辑代码在注册会逻辑模块。

    这样的做法能保证我们有逻辑模块有bug的时候及时的修改逻辑,再次注册脚本已到达热更新效果。保证服务器运行的稳定性。

    我们开始吧。

    首先我要说一下我这里使用的是C#语言作为讲解。java也是一样的道理。
    不在赘述,如果需要代码实现的可以留言!
    这里可能牵涉到我之前的一些文章包含的类容,比如脚本加载器,日志模块,http监听模块。

    我们先创建一个控制台项目叫TestApp

    在新建一个类库项目 TestApp.Scripts

    基本工作已经完成了。

    接下来,我们在TestApp下面新建一个类IMainManager
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 
     8 /**
     9  * 
    10  * @author 失足程序员
    11  * @Blog http://www.cnblogs.com/ty408/
    12  * @mail 492794628@qq.com
    13  * @phone 13882122019
    14  * 
    15  */
    16 namespace TestApp
    17 {
    18     /// <summary>
    19     /// 
    20     /// </summary>
    21     public interface IMainManagerScript
    22     {
    23 
    24          void Test();
    25 
    26     }
    27 }
    我们在TestApp下面新建一个类MainManager
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 
     8 /**
     9  * 
    10  * @author 失足程序员
    11  * @Blog http://www.cnblogs.com/ty408/
    12  * @mail 492794628@qq.com
    13  * @phone 13882122019
    14  * 
    15  */
    16 namespace TestApp
    17 {
    18     /// <summary>
    19     /// 
    20     /// </summary>
    21     public class MainManager : IMainManagerScript
    22     {
    23 
    24         public static MainManager instance = new MainManager();
    25 
    26         public static MainManager GetInstance()
    27         {
    28             return instance;
    29         }
    30 
    31         public IMainManagerScript IScript;
    32 
    33         public void Test()
    34         {
    35             IScript.Test();
    36         }
    37 
    38     }
    39 }
    接下来我们在TestApp.Scripts 新建一个文件
     1 using Net.Sz.Framework.Log;
     2 using Net.Sz.Framework.Script;
     3 using System;
     4 using System.Collections.Generic;
     5 using System.Linq;
     6 using System.Text;
     7 using System.Threading.Tasks;
     8 
     9 
    10 /**
    11  * 
    12  * @author 失足程序员
    13  * @Blog http://www.cnblogs.com/ty408/
    14  * @mail 492794628@qq.com
    15  * @phone 13882122019
    16  * 
    17  */
    18 namespace TestApp.Scripts
    19 {
    20     /// <summary>
    21     /// 
    22     /// </summary>
    23     public class MainManagerScript : BaseScript, IMainManagerScript
    24     {
    25 
    26         public void Test()
    27         {
    28             Logger.Info("我是test");
    29         }
    30 
    31         public override void InitScript()
    32         {
    33             MainManager.GetInstance().IScript = this;
    34             Logger.Info("MainManagerScripttest  InitScript");
    35         }
    36 
    37     }
    38 }
     1 using Net.Sz.Framework.Log;
     2 using System;
     3 using System.Collections.Generic;
     4 using System.Linq;
     5 using System.Text;
     6 using System.Threading.Tasks;
     7 
     8 namespace TestApp
     9 {
    10     class Program
    11     {
    12         static void Main(string[] args)
    13         {
    14             //Logger.LOGCONSOLE = true;//不在控制台打印信息
    15             //Logger.LOGLEVEL = Logger.ENUM_LOGLEVEL.INFO;//不显示debug消息
    16             Logger.Info("==================Start============================");
    17 
    18             Net.Sz.Framework.Script.ScriptManager.GetInstance().LoadCSharpFile(new string[] { "../../../TestApp.Scripts/" });
    19 
    20             MainManager.GetInstance().Test();
    21 
    22             Logger.Info("==================End============================");
    23             Console.ReadLine();
    24         }
    25     }
    26 }
    View Code

    我们先尝试运行一次

    也许上面的打印太乱,我们看看简洁版本的

    上面我们看样看出来,功能是没有问题的。

    如果你稍微注意了,或者看过我之前的文章的话,会发现我的这个脚本没有实现IBaseScript接口,而只继承了BaseScript抽象类。
    因为这样的脚本不需要放到脚本存储器里面,只需要执行init函数,把脚本自身反注册到主程序函数体里面!

    现在就是牵涉更新的事情。

    更新有一个问题,当服务一旦部署后需要更新那就是的再次执行加载脚本方法。如果你使用windows服务运行的,更新势必是一个问题。

    这里我只介绍一种方式,http监听方式。

    要开启http监听方式,需要一个Net.Sz.Framework.Netty.Http.IHttpHandler的实现类

     1 using Net.Sz.Framework.Log;
     2 using System;
     3 using System.Collections.Generic;
     4 using System.Linq;
     5 using System.Text;
     6 using System.Threading.Tasks;
     7 
     8 
     9 /**
    10  * 
    11  * @author 失足程序员
    12  * @Blog http://www.cnblogs.com/ty408/
    13  * @mail 492794628@qq.com
    14  * @phone 13882122019
    15  * 
    16  */
    17 namespace TestApp
    18 {
    19     /// <summary>
    20     /// 
    21     /// </summary>
    22     public class ReloadScriptHandler : Net.Sz.Framework.Netty.Http.IHttpHandler
    23     {
    24         public void Action(Net.Sz.Framework.Netty.Http.HttpSession session, string method, string bindpath, Dictionary<string, string> parms)
    25         {
    26             Logger.Info("收到请求");
    27 
    28             Net.Sz.Framework.Script.ScriptManager.GetInstance().LoadCSharpFile(new string[] { "../../../TestApp.Scripts/" });
    29             MainManager.GetInstance().Test();
    30 
    31             session.writeSuccess();
    32             session.AddBodyLine("OK");
    33             session.WriteFlush();
    34         }
    35     }
    36 }

    接下来我们在主函数加入下面代码

                Dictionary<string, Net.Sz.Framework.Netty.Http.IHttpHandler> handlers = new Dictionary<string, IHttpHandler>();
    
                handlers.Add("ReloadScript", new ReloadScriptHandler());//开启对http监听,监听模块为  ReloadScript
    
                Net.Sz.Framework.Netty.NettyPool.GetInstance().AddHttpBind("127.0.0.1", 9527, handlers);

    运行起来,看看,

    可以看出,开启了http监听,reloadscript

    然后我们修改一下MainManagerScript文件的Test方法

            public void Test()
            {
                Logger.Info("我是test  修改过的版本");
            }

    在浏览器输入:http://127.0.0.1:9527/reloadscript

    运行时成功的。这样的架构方式,很适合的是游戏开发,某些功能块很容易出bug,如果用脚本存储器访问比较耗费性能。

    这样反向注册功能块脚本到功能块,既能实现bug的热更新,也不会影响性能消耗。

    如果有疑问可以留言哦。如果你觉得这纯粹是扯淡,也欢迎留言。

    不要问我,逻辑部分都是源码方服务器上安全吗?
    难道你打包就安全了?
    反编译,脱壳反混淆的工具一大堆。

    上面是C#版本的,如果想要源码,

    或者是java源码的,可以留言哦~!

     

  • 相关阅读:
    (感受)新人生的三种境界
    (学)如何在打印时对横向页面重复左端标题
    (原)解决.NET 32位程序运行在64位操作系统下的兼容性问题
    (原)儿子上小学了
    OSG学习过程中的笔记
    从c++角度学习JAVA、Android的总结
    Android Studio利用cmakelists.txt编译OSG的方法总结
    android studio 利用gradle和cmakelist生成c++静态库.a的方法总结
    Android Studio使用c++静态库的方法总结(hello-libs为例)
    Android.mk、CMake、Gradle简介 NDK和JNI的关系
  • 原文地址:https://www.cnblogs.com/shizuchengxuyuan/p/5151742.html
Copyright © 2020-2023  润新知