一键热更表数据
目录
- 我们项目的热更流程
- instrument
- 杂项需求简介
- 几种打包jar的方式?
- postman发起get和post格式
- jar包动态读取dict.properties配置文件
- bat发起curl请求,同时接受返回的参数进行解析
- 待解决为什么premain函数操作超级耗时?
- classloder加载对象不能出现重复名
我们项目的热更流程
1.我们项目,热更表是自己实现了一个类似安全点的机制,原理如上图。
热更表不需要用到instrument,只是从数据库从新load出最新的数据然后替换到对应对象。
2.热更类,用到的是instrument,不过用instrument热更类有很多限制。
instrument
1.什么是instrument?
简单理解可以认为就是为java语言编写插装服务提供支持,
我们经常用它来替换和修改jvm运行时的类。
2.instrument重定义类的限制
我们在用instrument更新类时,一般会用到如下方法:
void redefineClasses(ClassDefinition... definitions)
throws ClassNotFoundException,
UnmodifiableClassException
上述方法有如下限制:
The redefinition may change method bodies, the constant pool and attributes.
The redefinition must not add, remove or rename fields or methods, change the signatures of methods,
or change inheritance.
These restrictions maybe be lifted in future versions.
重定义可以改变方法体,常量池和属性。但不能移除和重命名字段或方法,不能改变方法签名,不能更改继承关系。
这些限制也许在未来会开发。
为什么会有这些限制?
我在stackoverflow上搜索该问题,其中有一条回答可以适当参考,但也不是绝对:
At present it is impossible to redefine a class at runtime such that the redefinition will result in new methods or fields.
This is due to the complexity involved in scanning the heap for all existing instances and transforming them + their references
+ potential Unsafe field offset base updaters (like AtomicFieldUpdater).
大体就是说:
由于在堆中扫描所有现有实例并转换它们+它们的引用+潜在的不安全的字段偏移量基更新器所涉及的复杂性,所以目前没有实现改功能。
https://stackoverflow.com/questions/16986206/how-can-i-modify-a-java-lang-class-on-the-fly/17162577#17162577
3.底层实现
这篇讲instrument原理还可以,可以适当参考:
http://lovestblog.cn/blog/2015/09/14/javaagent/
从源码角度上看重定类限制:重定类时会对比新老类,父类必须是同一个,实现的接口数也要相同,,并且是相同的接口...
但是为什么会加这些校验,本质原因是什么?我们还是没搞清楚。
杂项需求简介
bat发起一键热更请求,策划点击bat脚本,将策划本地修改的表压进数据库,同时请求热更接口。
服务器接受到热更请求,进行表数据热更。
策划达到不用重启服务器,修改表数据效果,方便测试。
几种打包jar的方式?
1.直接用命令行(javaAgent为例)
1.InstrumentationAgent工具类放在自己项目需要用的目录下
例如:我放在项目的utils下
package com.game2sky.application.utils;
2.然后,我们在同目录下,自己写一个MANIFEST.MF文件
文件名就是为:MANIFEST.MF
内容如下:
Manifest-Version: 1.0
Premain-class: com.game2sky.application.utils.InstrumentationAgent
Main-Class: com.game2sky.application.utils.InstrumentationAgent
其中要特别注意的是Premain-class和Main-Class的路径都要写对。
3.我们用cmd直接进入该目录下
package com.game2sky.application.utils;
用cmd命令打jar包,命令如下:
javac InstrumentationAgent.java
jar cmf MANIFEST.MF InstrumentationAgent.jar InstrumentationAgent.class
4.使用
打包好的jar放入本地,在idea的vm启动参数里加入本地存放的路径,或者可以将jar包集成到自己的项目里
-javaagent:F:\badperson3\InstrumentationAgent.jar
2.maven
maven这里先不多讲,主要就是在pom里配置。
3.idea的buid打包jar
1.创建一个工件
点击idea的File -> Project Settings -> Artifacts
2.添加一个jar
“+” 号 -> Add -> JAR -> From modules with dependencies
3.配置参数,Main Class 和 MANIFEST.MF路径
4.打包
Apply -> OK
Build -> Build Artifacts -> Action选择Build
5.另一个工程引用刚才导出的jar包
File -> Project Settings -> Modules
Dependencies -> 点击 “+” 号 -> JARs or directories
Apply -> OK
postman发起get和post格式
1.post请求格式
1.点击post
2.请求的地址
3.body
4.我这里选这text格式
5.text格式参数
2.get请求格式
1.点击get
2.请求的地址
3.Params
4.key -- value 设置值
jar包动态读取dict.properties配置文件
1.dict.properties文件,数据库地址,用户名,密码
目录:
内容如下:
jdbc=jdbc:mysql://141.156.16:3306/dict_154?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
user=xx
password=qqq
2.读取dict.properties文件内容到内存
1.配置文件操作类
public static Properties getProperties(String fileName)
{
Properties properties = new Properties();
ClassLoader classLoader = PropertiesUtils.class.getClassLoader();
try
{
InputStream in = classLoader.getResourceAsStream(fileName);
if (in == null)
{
fileName = System.getProperty("user.dir") + "/" + fileName;
in = new BufferedInputStream(new FileInputStream(fileName));
// in = classLoader.getResourceAsStream(System.getProperty("user.dir") + "/" + fileName);
if (in == null)
{
BPLog.BP_LOGIC.error("class loader get resource failure, fileName:{}", fileName);
return null;
}
}
properties.load(in);
}
catch (IOException e)
{
e.printStackTrace();
return null;
}
return properties;
}
2.读取文件内容
// 从本地获取
Properties properties = PropertiesUtils.getProperties("dict.properties");
if (properties == null)
{
BPLog.BP_LOGIC.error("file:{} is not existed", "dict.properties");
return;
}
ConfigConstants.jdbc = PropertiesUtils.getString(properties, "jdbc");
ConfigConstants.user = PropertiesUtils.getString(properties, "user");
ConfigConstants.password = PropertiesUtils.getString(properties, "password");
3.执行jar包时动态读取dict.properties文件
1.maven打包时过滤dict.properties文件
不包括:
2.bat执行jar包时动态传入dict.properties文件
将dict.properties放进resources文件夹下
bat发起curl请求,同时接受返回的参数进行解析
set par="serverId=%serverId%"
set getAddress=curl http://xxxxxxx?
for /f "tokens=12 delims=," %%i in ('%getAddress%%par%') do set result=%%i
发起curl请求后接受返回的参数并进行字符串切割
待解决为什么premain函数操作超级耗时?
classloder加载对象不能出现重复名
地址:http://zakirrizvi.blogspot.com/2017/11/exception-in-thread-main.html