Merge branch 'master' of https://gitee.com/zzzisfunnyboy/obsidian-notes
# Conflicts: # .obsidian/workspace.json # 1Project/宠物宇宙/地图编辑器/需要做的优化.md # 1Project/宠物宇宙/版本需求.md # 1Project/构建新框架/MVVM/UI框架.md # 2Areas/编程/一些感悟/接口与抽象类区别.md # 3Resources/游戏开发/Log插件/Log插件.md # 3Resources/游戏开发/UE-CORE-002 TickTaskManager.md # InBox/网络同步.md
This commit is contained in:
113
.obsidian/workspace.json
vendored
113
.obsidian/workspace.json
vendored
@@ -4,53 +4,24 @@
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "1890c7ad64a39d65",
|
||||
"id": "023535fd6fa2e67d",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "3e6f0931816b44e6",
|
||||
"id": "c3a1fa114f02064b",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "1Project/开发文章/将开发文章阅读并分类.md",
|
||||
"file": "1Project/宠物宇宙/地图编辑器/需要支持的地图功能.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
},
|
||||
"icon": "lucide-file",
|
||||
"title": "将开发文章阅读并分类"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "e0316e2f70484fe5",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "1Project/构建新框架/MVVM/UI框架.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
},
|
||||
"icon": "lucide-file",
|
||||
"title": "UI框架"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "73d0828f3312dd71",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "2Areas/编程/一些感悟/IOC.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
},
|
||||
"icon": "lucide-file",
|
||||
"title": "IOC"
|
||||
"title": "需要支持的地图功能"
|
||||
}
|
||||
}
|
||||
],
|
||||
"currentTab": 2
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "vertical"
|
||||
@@ -82,7 +53,7 @@
|
||||
"state": {
|
||||
"type": "search",
|
||||
"state": {
|
||||
"query": "loxo",
|
||||
"query": "tag:#flashcards/政治/近代史",
|
||||
"matchingCase": false,
|
||||
"explainSearch": false,
|
||||
"collapseAll": false,
|
||||
@@ -126,7 +97,8 @@
|
||||
"title": "文件列表"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"currentTab": 4
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
@@ -242,51 +214,52 @@
|
||||
"copilot:Open Copilot Chat": false
|
||||
}
|
||||
},
|
||||
"active": "73d0828f3312dd71",
|
||||
"active": "c3a1fa114f02064b",
|
||||
"lastOpenFiles": [
|
||||
"1Project/构建新框架/MVVM/扩展其他组件.md",
|
||||
"2Areas/编程/一些感悟/IOC.md",
|
||||
"1Project/构建新框架/MVVM/UI框架.md",
|
||||
"1Project/构建新框架/MVVM/loxodon-framework Public.md",
|
||||
"3Resources/Unity/Expression.md",
|
||||
"1Project/构建新框架/MVVM",
|
||||
"1Project/构建新框架",
|
||||
"1Project/战斗",
|
||||
"1Project/开发文章/将开发文章阅读并分类.md",
|
||||
"1Project/宠物宇宙/地图编辑器/需要做的优化.md",
|
||||
"1Project/宠物宇宙/地图编辑器/地图工作流.canvas",
|
||||
"1Project/11/最近需要处理的事情.md",
|
||||
"1Project/宠物宇宙/地图编辑器/需要支持的地图功能.md",
|
||||
"1Project/宠物宇宙/版本需求.md",
|
||||
"1Project/11",
|
||||
"1Project/宠物宇宙/地图编辑器/需要做的优化.md",
|
||||
"1Project/战斗编辑器/随想/各个动作组件优劣.md",
|
||||
"1Project/战斗编辑器/随想/动作时间轴 随想.canvas",
|
||||
"1Project/战斗编辑器/参考文章/战斗系统 - 3C相关.md",
|
||||
"1Project/战斗编辑器/随想",
|
||||
"1Project/战斗编辑器/参考文章/游戏知识学习——【战斗系统】.md",
|
||||
"1Project/战斗编辑器/参考文章/如何实现一个强大的MMO技能系统——BUFF.md",
|
||||
"1Project/战斗编辑器/参考文章",
|
||||
"1Project/战斗编辑器",
|
||||
"3Projects/UE/UE-CORE-002 TickTaskManager.md",
|
||||
"3Projects/Unity/Log插件/Log插件.md",
|
||||
"3Projects/UE",
|
||||
"3Projects/Unity/Log插件",
|
||||
"1Project/战斗编辑器/参考文章/独立游戏-万字解析游戏战斗系统开发.md",
|
||||
"1Project/战斗编辑器/参考文章/还需要做的事.md",
|
||||
"2Areas/开发感悟/管道模式/管道模式.md",
|
||||
"2Areas/开发感悟/依赖环节的流程管线.md",
|
||||
"2Areas/开发感悟/接口与抽象类区别.md",
|
||||
"1Project/宠物宇宙/硬件交互/用户卡及游戏信息处理过程设计文档.md",
|
||||
"1Project/宠物宇宙/硬件交互/业务插件(RTwoGameBusiness)TCP接口设计说明.md",
|
||||
"2Areas/哲学/目的论与原因论.md",
|
||||
"2Areas/哲学/他者贡献.md",
|
||||
"2Areas/哲学",
|
||||
"InBox/loxodon-framework.md",
|
||||
"3Projects/Unity/loxodon",
|
||||
"3Projects/Unity",
|
||||
"InBox/网络同步.md",
|
||||
"InBox/obsidian右键扩展.md",
|
||||
"InBox/接口与抽象类区别.md",
|
||||
"2Areas/开发感悟/管道模式/未命名.canvas",
|
||||
"2Areas/开发感悟/管道模式",
|
||||
"1Project/网络/封装可扩展网络请求框架.md",
|
||||
"2Areas/开发感悟",
|
||||
"InBox/【Unity插件 - 图标轮廓渲染插件 SDF Image - Quality UI Outlines and Shadow-哔哩哔哩】.md",
|
||||
"InBox/资源/资源导入.md",
|
||||
"InBox/架构腐化之谜.md",
|
||||
"3Resources/游戏开发/帧同步总结.md",
|
||||
"3Resources/游戏开发/loxodon/loxodon-framework Public.md",
|
||||
"3Resources/游戏开发",
|
||||
"InBox/loxodon-framework.md",
|
||||
"InBox/obsidian右键扩展.md",
|
||||
"1Project/宠物宇宙/版本需求.md",
|
||||
"1Project/开发文章/将开发文章阅读并分类.md",
|
||||
"2Areas/哲学与心理/打球不敢往里突的人,球技很难增长,人生亦如此.md",
|
||||
"2Areas/心理/未命名.md",
|
||||
"2Areas/心理",
|
||||
"2Areas/哲学/未命名.md",
|
||||
"1Project/战斗",
|
||||
"1Project/宠物宇宙/地图编辑器/地图工作流.canvas",
|
||||
"1Project/战斗编辑器/参考文章",
|
||||
"1Project/战斗编辑器",
|
||||
"3Resources/UE",
|
||||
"InBox/编辑器/时间轴编辑器/Timeline TODO.canvas",
|
||||
"InBox/编辑器/图片/Pasted image 20221025121100.png",
|
||||
"1Project/宠物宇宙/bug.md",
|
||||
"渲染/软渲染/图片/业务插件(RTwoGameBusiness)TCP接口设计说明.md",
|
||||
"InBox/网络/封装可扩展网络请求框架.md",
|
||||
"2Areas/提升效率/未命名.md",
|
||||
"渲染/软渲染/图片/img_v3_02n6_ff40a1a4-d813-45f7-b132-6f14a74cfd7g.jpg",
|
||||
"InBox/TODO.canvas",
|
||||
"渲染/软渲染/图片/Pasted image 20250609171235.png",
|
||||
"InBox/战斗/动作时间轴 随想.canvas",
|
||||
"InBox/渲染/GPU/Pasted image 20230413163234.png",
|
||||
"InBox/Mesh/图片/Pasted image 20221021171104.png",
|
||||
"InBox/渲染/软渲染/图片/Pasted image 20230613102109.png",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
##### 流程组件
|
||||
- [ ] **构建流程组件**
|
||||
抽象成管线
|
||||
找出运行时和编辑器共有的模块
|
||||
把这些模块做成组件
|
||||
运行时和编辑器分别实例不同的组件
|
||||
@@ -19,6 +20,11 @@
|
||||
- 预览图
|
||||
- 地板数据,长宽高,
|
||||
- 地板承载的摆件怪物标志信息
|
||||
缺点:
|
||||
- SO文件会实时改动到实际的物体的配置数据
|
||||
但编辑器通常的模式是,需要dirty,不想保存的修改,不能直接修改到原有的数据
|
||||
- 对于无法序列化的东西无法保存,比如字典,参考路线配置,需要存一份文本配置来做序列化的事情
|
||||
|
||||
- Json文件
|
||||
- 一份额外信息,key为mapIndex,value为对应的额外信息
|
||||
- StreamingAssetPath中
|
||||
@@ -27,13 +33,15 @@
|
||||
|
||||
- 新的数据结构
|
||||
- 文件夹下
|
||||
-
|
||||
- 所有地板以及地板摆放的物体.json
|
||||
- 一份关于地图的数据,地图Id,地图名字
|
||||
- 预览图
|
||||
- 配置表
|
||||
- 波次数据
|
||||
- 房间从1001开头用 多主键联合的方式
|
||||
|
||||
##### 重新梳理关系和职责
|
||||
###### SpreadMap和SpreadMapLevel
|
||||
需要清晰的关系以及生命周期
|
||||
###### MapBlock和RoomBase
|
||||
二者相互引用且耦合
|
||||
|
||||
###### 先抽象工作流,在开始构建管道
|
||||
|
||||
|
||||
31
1Project/宠物宇宙/版本需求.md
Normal file
31
1Project/宠物宇宙/版本需求.md
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
> # 此版本
|
||||
|
||||
|
||||
- [x] 皮肤导入
|
||||
- [x] 上海限定 C:\Users\1\Desktop\cache\57015
|
||||
- 游戏中
|
||||
- [x] C:\Users\1\Desktop\cache\红/蓝/绿皮肤7.0
|
||||
- [x] 胳膊导入
|
||||
- 结算
|
||||
- [x] C:\Users\1\Desktop\cache\角色胜利结算
|
||||
- 卡面
|
||||
- [x] C:\Users\1\Desktop\cache\第三弹裁切输出
|
||||
- UI名字
|
||||
- [x] C:\Users\1\Desktop\cache\皮肤
|
||||
- [x] 枪械宠物UI导入
|
||||
- [x] 测试网络点数 开机检测
|
||||
- [x] 地图编辑器 [[需要支持的地图功能]]
|
||||
- [x] Boss立绘
|
||||
- [x] C:\Users\1\Desktop\cache\雷鸟血条UI
|
||||
- [ ] 积分系统
|
||||
- [ ]
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
> ## 下个版本
|
||||
|
||||
- [ ] 网络状态3\4
|
||||
[[1Project/宠物宇宙/硬件交互/业务插件(RTwoGameBusiness)TCP接口设计说明]]
|
||||
9
1Project/战斗编辑器/随想/动作时间轴 随想.canvas
Normal file
9
1Project/战斗编辑器/随想/动作时间轴 随想.canvas
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"nodes":[
|
||||
{"id":"61edc211325da480","type":"text","text":"动作时间轴","x":-1206,"y":2853,"width":906,"height":60,"color":"4"},
|
||||
{"id":"a7f14817e1497be2","type":"text","text":"不可打断","x":-1206,"y":2940,"width":586,"height":60,"color":"6"},
|
||||
{"id":"e8d1678f431e2ee0","type":"text","text":"缓存输入","x":-870,"y":3020,"width":250,"height":60,"color":"6"},
|
||||
{"id":"8fc0985df93fdde7","type":"text","text":"可切换的后摇","x":-620,"y":3020,"width":320,"height":60,"color":"6"}
|
||||
],
|
||||
"edges":[]
|
||||
}
|
||||
30
1Project/战斗编辑器/随想/各个动作组件优劣.md
Normal file
30
1Project/战斗编辑器/随想/各个动作组件优劣.md
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
|
||||
|
||||
### Animator
|
||||
- 优势
|
||||
- Unity原生支持
|
||||
- 劣势
|
||||
- 源码黑盒
|
||||
- 不可编辑帧事件
|
||||
- 意味着不可编辑打击感,不可编辑运动方向位移
|
||||
- 帧事件可能被跳过的bug
|
||||
- 混合也在黑盒里
|
||||
|
||||
|
||||
### Timeline
|
||||
- 优势
|
||||
- 自定义Track Clip Behaviour
|
||||
- Clip可以支持Inspector
|
||||
- 自定义混合
|
||||
- 劣势
|
||||
- Inspector不可编辑某一帧的事件
|
||||
- 没有Animator的状态可视化切换,需要所有切换动作都在代码中写
|
||||
|
||||
|
||||
### AnimacerComponent
|
||||
- 优势
|
||||
- 劣势
|
||||
|
||||
|
||||
### 自定义Playable动作系统
|
||||
@@ -1,19 +0,0 @@
|
||||
|
||||
单向绑定
|
||||
``` csharp
|
||||
[OnValueChanged(viewModel=>viewModel.hp, value=>string.Format("{0:D4}"))]
|
||||
CustomText text;
|
||||
|
||||
public class CustomText
|
||||
{
|
||||
public Text text;
|
||||
public void SetText(string str)
|
||||
{
|
||||
text.text = str;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
deepseek扩展:
|
||||
[DeepSeek - 探索未至之境](https://chat.deepseek.com/a/chat/s/a42c8fe7-2dd9-44a4-9854-e1ec34f3e2cf)
|
||||
6
2Areas/哲学与心理/打球不敢往里突的人,球技很难增长,人生亦如此.md
Normal file
6
2Areas/哲学与心理/打球不敢往里突的人,球技很难增长,人生亦如此.md
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
恐惧的原因
|
||||
|
||||
为何形成
|
||||
|
||||
应该怎么做
|
||||
16
2Areas/开发感悟/依赖环节的流程管线.md
Normal file
16
2Areas/开发感悟/依赖环节的流程管线.md
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
### 调试和测试的便捷性问题
|
||||
- 大部分场景美术的跑测需求是无需任何game play元素
|
||||
- A角色的B技能MotionBlur存在异常,他需要在真实的游戏场景中观察某个角色释放某个技能观察MotionBlur是否符合预期
|
||||
- 负责3C的QA希望单次测试只加载3C相关的游戏子系统,任何与之无关的子系统都是关闭状态以提高游戏加载和运行速度,以提高测试效率
|
||||
|
||||
### 在宠物宇宙中的例子F9
|
||||
- 数据端插入了测试模式的变量以及模拟了一批玩家数据
|
||||
- 下个环节根据测试模式的变量和这些玩家数据进行
|
||||
|
||||
|
||||
### 思考,如何抽象出来
|
||||
- 流程[[管道模式]]
|
||||
- 每个流程都能热插拔
|
||||
- 当获取流程所需要的数据为空的时候,在else方法中应该从IOC里面获取测试的对象
|
||||
6
2Areas/开发感悟/接口与抽象类区别.md
Normal file
6
2Areas/开发感悟/接口与抽象类区别.md
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
#### 接口不指定构造函数
|
||||
抽象类可以统一用反射来构造函数 接口不可以
|
||||
|
||||
#### 时机
|
||||
需要反射构造函数的时候需要用抽象类
|
||||
18
2Areas/开发感悟/管道模式/管道模式.md
Normal file
18
2Areas/开发感悟/管道模式/管道模式.md
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
tags:
|
||||
- 设计模式
|
||||
---
|
||||
|
||||
|
||||
``` csharp
|
||||
public class Seq<T>{
|
||||
public Command<T>[] cmds;
|
||||
public T Context;
|
||||
}
|
||||
```
|
||||
|
||||
``` csharp
|
||||
public class Command<T>{
|
||||
public void Excute(T context){}
|
||||
}
|
||||
```
|
||||
@@ -1,2 +0,0 @@
|
||||
|
||||
接口不指定构造函数
|
||||
153
3Resources/游戏开发/Log插件/Log插件.md
Normal file
153
3Resources/游戏开发/Log插件/Log插件.md
Normal file
@@ -0,0 +1,153 @@
|
||||
|
||||
source:[加Log就卡?不加Log就瞎?”——这个插件治好了我的精神内耗](https://mp.weixin.qq.com/s/Nii4fC15eKoNN8MOAzrmDQ)
|
||||
|
||||
---
|
||||
|
||||
|
||||
#
|
||||
|
||||
- 1 现有日志打印情况
|
||||
|
||||
|
||||
- 1 日志阻塞
|
||||
|
||||
- 2 从调试到生产,日志策略的抉择
|
||||
|
||||
|
||||
- 2 问题出现原因
|
||||
|
||||
|
||||
- 2.1 日志打印原理分析
|
||||
|
||||
- 2.2 log4j2 Disruptor 的初始化
|
||||
|
||||
- 2.3 队列满导致日志阻塞
|
||||
|
||||
- 2.4 产生的根本原因
|
||||
|
||||
|
||||
- 3 应对方案
|
||||
|
||||
|
||||
- 3.1 方案选择
|
||||
|
||||
- 3.2 技术选择
|
||||
|
||||
- 3.3 落地实现
|
||||
|
||||
|
||||
- 4 总结
|
||||
|
||||
|
||||
|
||||
|
||||
## 1 现有日志打印情况
|
||||
|
||||
日志作为软件工程实践中的重要基础设施,在系统监控、异常诊断及行为追溯等关键环节发挥着不可替代的作用。Apache Log4j2作为当前主流的日志框架,凭借其模块化架构和高度可扩展的特性,为开发者提供了灵活的多维度日志管理方案。然而若未能深入理解其异步日志机制、缓冲区策略等核心原理,或存在配置参数与业务场景匹配度不足等问题,则可能导致日志I/O阻塞、内存资源过度消耗等负面效应,甚至引发严重的服务性能瓶颈。因此,在实际工程实践中需遵循科学合理的使用准则,通过日志分级管理、输出格式优化、滚动策略定制等手段,方能充分发挥其技术优势,有效规避潜在风险。
|
||||
|
||||
### 1 日志阻塞
|
||||
|
||||
日志导致线程Block的问题,相信你或许已经遇到过,对此应该深有体会;或许你还没遇到过,但不代表没有问题,只是可能还没有触发而已。常见的现象是出现大量的block线程,查看jstack常常是下图现象:
|
||||
|
||||
### 2 从调试到生产,日志策略的抉择
|
||||
|
||||
在软件项目的全生命周期中,从开发阶段到生产环境的演进过程中,日志管理往往面临着微妙的平衡。开发阶段我们倾向于采用详尽的日志策略:业务接口的入参出参被完整记录,跨系统的调用链路被清晰标注,甚至非核心逻辑的辅助性信息也得以留存——这些详实的日志如同开发者的双目,为联调排障与功能验证提供了不可或缺的洞察。
|
||||
|
||||
然而当服务迈向生产环境时,过度日志带来的问题便逐渐显现。冗余的调试信息不仅会影响系统性能,更可能淹没真正关键的业务轨迹。尽管我们尝试在上线前进行日志裁剪,但总存在令人踌躇的灰色地带:某些开发期辅助日志是否暗含未来的诊断价值?那些看似非核心的流程记录会否在某个异常场景下成为关键线索?这种取舍的困境,本质上反映了我们对系统可观测性与运行效能之间永续的权衡。
|
||||
|
||||
## 2 问题出现原因
|
||||
|
||||
### 2.1 日志打印原理分析
|
||||
|
||||
在我们使用的log4j的应用中采用的是异步日志配置,简单的说明一下一条日志打印在log4j中的处理流程如下图所示: 简单点来说,就是多线程通过 log4j2 的门面类进行日志的打印,日志经过一系列的处理(过滤,包装)后放入到Disruptor的环形 buffer 中,在服务器的消费端会单启一个线程进行这些日志的消费,最终放入到我们指定的文件中。
|
||||
|
||||
### 2.2 log4j2 Disruptor 的初始化
|
||||
|
||||
当LoggerContext启动时,所有AsyncLoggerConfig会通过start()方法初始化其Disruptor: 其中Disruptor 是一个环形 buffer,官方做了很多的性能优化,这里有兴趣的可以了解其实现原理,这里不进行深入的讨论,其中在我们的应用log4j.xml配置中,没对RingBuffer进行自定义的配置,使用的是默认的大小256K。
|
||||
|
||||
### 2.3 队列满导致日志阻塞
|
||||
|
||||
Disruptor 的 RingBuffer 是一个固定大小的环形队列,其发布逻辑:
|
||||
|
||||
队列满时的默认行为:AsyncLoggerConfig.SynchronizeEnqueueWhenQueueFull=true,此时会等待着消费出下一个可以生产的环形 buffer 槽;此时所有打印日志的线程会尝试获取全局锁。此时会阻塞线程,也就是我们上述堆栈中看到的异常。
|
||||
|
||||
### 2.4 产生的根本原因
|
||||
|
||||
生产者速度 > 消费者速度:
|
||||
|
||||
AsyncAppender 的后台线程从队列中取出事件并交给实际 Appender(如 FileAppender)处理,如果实际 Appender 的写入速度慢(如磁盘 I/O 高),消费者线程无法及时清空队列,导致队列积压。其实log4j消费时会调用多次 flush,这些flush的调用根本在文件写入的 native 调用,当这种native调用太多时,系统写入不过来。
|
||||
|
||||
## 3 应对方案
|
||||
|
||||
### 3.1 方案选择
|
||||
|
||||
上述问题情况解决,大致分成两个方向:生产者方向&消费者方向,具体行为如下图简述: 在应对日志管理的挑战时,除了调整日志队列容量等基础优化(需警惕OOM风险),更核心的问题在于如何平衡日志的详实性与系统稳定性。开发者往往陷入两难:若详尽记录日志,可能引发阻塞风险;若过度精简,则排查问题时如盲人摸象,难溯根源。
|
||||
|
||||
为此,可考虑将日志划分为两类:
|
||||
|
||||
功能日志(必须):如埋点数据、核心流程记录,确保业务可观测性;
|
||||
|
||||
业务排查日志(非必须):如RPC入参/出参、调试断点等,按需动态启停;
|
||||
|
||||
通过这种分层策略,既能在高并发场景下保障核心日志的稳定输出,又能灵活控制辅助日志的打印量,使系统整体具备更强的适应性与可控性。如此,我们既能从容应对生产环境的严苛要求,又能在需要时快速激活详尽的诊断信息,实现运维效率与系统性能的兼得。
|
||||
|
||||
### 3.2 技术选择
|
||||
|
||||
在日志打印的精细化控制中,核心在于灵活性与精准度的平衡。传统的全局级别过滤(如INFO/WARN/ERROR)虽能粗放管理,却难以适配复杂多变的业务场景。理想的方案应突破层级限制,实现行级细粒度控制——无论是核心链路的关键节点,还是特定业务场景的临时调试,均可针对单行日志动态启停。
|
||||
|
||||
这种设计赋予开发者更高的自主权:业务视角:按需捕获特定模块的完整上下文;链路视角:精准聚焦某次调用的全生命周期轨迹;应急场景:即时激活深层诊断日志,无需重启或改码。
|
||||
|
||||
通过将控制粒度细化至代码行,我们既能维持生产环境的日志精简,又能随时按业务诉求“点亮”关键路径,使系统可观测性兼具严谨与弹性。
|
||||
|
||||
#### 3.2.1 区分必要日志和非必要日志打印:
|
||||
|
||||
自定义封装日志打印的方式如下图所示:
|
||||
|
||||
#### 3.2.2 如何对非必要日志进行行级别控制
|
||||
|
||||
1、自定义Appender中的filter:
|
||||
|
||||
在实现行级日志控制时,若需精确控制特定代码行(如第133行)的日志输出,采用自定义Appender过滤机制是一种可行方案。其核心思路在于:通过解析日志调用的堆栈信息,动态判断当前行号是否符合预设的打印条件,若不符合则直接过滤。
|
||||
|
||||
然而,该方案存在若干固有局限: 堆栈解析的可靠性问题:Lambda表达式中的日志调用往往难以准确获取行号信息,即使通过堆栈缓存优化,仍存在定位失准的风险;性能损耗隐患:频繁的堆栈遍历操作会引入不可忽视的性能开销,在高并发场景下可能成为新的瓶颈;分类管理缺失:该机制难以与既有的日志分级体系(必需/非必需日志)形成有机协同,增加了运维复杂度。
|
||||
|
||||
2、在打印日志前获取日志的行信息:
|
||||
|
||||
最简单的方式是人工的形式,在写日志的时候同时将日志的行信息写入进去,比如:这种方式在可扩展性和可观测性维度存在设计缺陷。
|
||||
|
||||
3、在编译的时候获取日志的行信息:
|
||||
|
||||
我们想使用LogUtils.debug(()->log.info("业务日志"));这种方式,但是我们不会在代码中明显的写入,可以在代码编译期间将行信息获取到后使用字节码修改这行代码,利用Java的重写。将它转变成 LogUtils.debug("类+行",()->log.info("业务日志")); 然后再这个方法执行中进行条件判断。
|
||||
|
||||
字节码技术选择:
|
||||
 在本次实现中需要更灵活的方式操作字节码,还要考虑性能的问题,以及对应用框架的支持, 我们选择ASM的形式。
|
||||
|
||||
4、如何随心控制开启和关闭:
|
||||
|
||||
这里我们采用的是ider插件的方式,有idea插件上报我们的对这行日志的控制行为,如下图所示:
|
||||
|
||||
### 3.3 落地实现
|
||||
|
||||
#### 3.3.1 Maven编译插件
|
||||
|
||||
目的:获取日志所在的类和行信息。
|
||||
|
||||
运行时获取的方式:在 Logger 配置中启用 includeLocation,代码从 LogEvent通过堆栈分析获取行号。这种方式存在很大的弊端,堆栈跟踪生成开销很大,每次调用 getStackTrace() 时,JVM 需要遍历当前线程的调用栈,生成完整的堆栈信息,这是一个 同步且耗时 的操作(尤其在深调用链中),如果每秒有数万次日志调用,频繁生成堆栈跟踪可能导致 CPU 使用率飙升,直接影响吞吐量。
|
||||
|
||||
Maven的process-classes阶段获取:通过Maven编译之后获取到字节码文件时,对字节码文件进行修改,存放日志的类和行信息。对运行期间无额外消耗。处理逻辑如下图所示:
|
||||
|
||||
#### 3.3.2 Idea插件
|
||||
|
||||
目的:精准的控制某一行日志是否进行打印。
|
||||
|
||||
利用Idea的插件能力,将我们对某一行日志的开启和关闭状态进行上报,整个过程不阻塞主线程,保持Idea操作流畅性。提供定时能力,保障线上我们可以更灵活的控制日志是否打印的状态。处理逻辑如下图所示;
|
||||
|
||||
#### 3.3.3 整体流程
|
||||
|
||||
使用方式:通过在项目中使用上述Maven插件对项目进行编译部署,使用Idea插件对目标日志的是否开启打印状态进行上报,存储状态采用的Apollo的能力,通过自定义打印工具类中对Apollo配置内容的分析,进一步做判断逻辑,最终将日志进行打印或者不打印。处理逻辑如下图所示:
|
||||
|
||||
## 4 总结
|
||||
|
||||
在分布式系统日益复杂的今天,日志管理已从简单的信息记录演进为系统可观测性的核心支柱。本文揭示的日志阻塞与策略困境,折射出现代化服务在稳定性与可维护性之间的深层博弈。通过剖析Log4j2异步日志机制的内在原理,我们识别出队列积压导致线程阻塞的关键症结,并由此展开对日志治理体系的深度重构。
|
||||
|
||||
本次优化方案突破传统日志分级思维的桎梏,创新性地提出双轨制日志管理体系:将日志划分为功能型与诊断型两类,前者确保核心业务脉络的持续可见,后者实现按需动态管控。通过编译期字节码增强技术,我们实现代码行级别的精准控制,配合IDE插件的可视化操作,使开发人员能够像调试断点般自由启停日志输出。这种"外科手术式"的日志管理,既避免了传统方案"一刀切"的弊端,又赋予系统在高负载场景下的弹性适应能力。
|
||||
1949
3Resources/游戏开发/UE-CORE-002 TickTaskManager.md
Normal file
1949
3Resources/游戏开发/UE-CORE-002 TickTaskManager.md
Normal file
File diff suppressed because it is too large
Load Diff
148
3Resources/游戏开发/帧同步总结.md
Normal file
148
3Resources/游戏开发/帧同步总结.md
Normal file
@@ -0,0 +1,148 @@
|
||||
## 一、逻辑与表现分离
|
||||
|
||||
①、逻辑层仅处理数据,准备好用于渲染的数据。
|
||||
|
||||
②、表现层对已有的数据,进行插值表现,且不会改动数据。
|
||||
|
||||
③、两者的帧率可以不同。
|
||||
|
||||
## 二、时间相关
|
||||
|
||||
①、客户端通过Ping包方式预测服务器的战斗时间。
|
||||
|
||||
②、RTT计算方式,发一个Ping包,包含发送时的本地时间戳,服务器接收到以后,返回这个时间,客户端接收到后,用最新的本地时间戳减去这个时间,就可以得到RTT。
|
||||
|
||||
③、RTT计算时,服务器还会返回一个服务器的战斗时间,取最低RTT的一半,加上服务器返回的战斗时间,即可以估算为战斗时间。
|
||||
|
||||
④、多次模拟,越来越接近服务器的战斗时间。
|
||||
|
||||
## 三、预输入
|
||||
|
||||
①、非锁步同步中,客户端先于服务器运行,比如快个3帧。
|
||||
|
||||
②、理想状态下,服务器因为有收包缓冲在,每帧都能收到每个客户端的输入。
|
||||
|
||||
③、但网络延迟高时,客户端发送的输入,可能超过了服务器的收包缓冲。
|
||||
|
||||
④、所以基于RTT的计算,在延迟高时:
|
||||
|
||||
客户端会增大预输入的帧数,比如原本发送超过服务器3帧的输入,改成6帧。
|
||||
|
||||
服务器也会增大收包缓冲,在收到最新的同帧号输入时,会替换之前的输入。
|
||||
|
||||
缓冲可以动态调整。
|
||||
|
||||
⑤、一般预输入只处理预测失败概率低的操作,比如持续移动。
|
||||
|
||||
⑥、客户端冗余发送
|
||||
|
||||
## 四、预测
|
||||
|
||||
①、客户端先行于服务器,所以需要预测其他客户端的行为。
|
||||
|
||||
②、一般基于连续输入原则,比如连续的移动输入等。
|
||||
|
||||
③、对于高敏感操作,一般不进行预测。
|
||||
|
||||
## 五、回滚
|
||||
|
||||
①、在本地帧输入数据与服务器发送过来的帧输入不一致时,发生回滚。
|
||||
|
||||
②、找到最近可用的快照,进行推进。
|
||||
|
||||
③、逻辑层回滚技巧:
|
||||
|
||||
基于帧索引的可序列化数据,比如随机数状态,属性等。
|
||||
|
||||
针对于大型的,序列化成本较高的数据,可记录每帧变动的指令,在回滚时,逐帧逐条回滚这些指令。
|
||||
|
||||
④、表现层回滚技巧:
|
||||
|
||||
表现层一般设计为可插值,而非多帧状态累加。
|
||||
|
||||
有一些效果,可等到逻辑帧确认后再表现。
|
||||
|
||||
插值回滚,而不是瞬间表现到位。
|
||||
|
||||
⑤、ECS回滚参考:
|
||||
|
||||
Entity/Component不增删时,拷贝整个Entity/Component的数据块。
|
||||
|
||||
Entity删除时,可标记为禁用,等确定不回滚后再删除,增加时,操作记录回退。
|
||||
|
||||
Component增删时,操作记录回退。
|
||||
|
||||
## 六、追帧
|
||||
|
||||
①、落后太多时,可选择最近目标帧的快照进行处理。
|
||||
|
||||
②、一般会设置当前帧可用于追帧的时间,避免追帧卡顿。
|
||||
|
||||
## 七、快照
|
||||
|
||||
①、较大的间隔生成全量快照,比如1秒。
|
||||
|
||||
②、较小的间隔生成增量快照,记录基于全量快照的变动。
|
||||
|
||||
③、OOP中,一般通过标签,或者内建数据结构类实现序列化、反序列化、数据变动记录。
|
||||
|
||||
④、ECS中,一般关注Entity数量、Component数量和数据即可。
|
||||
|
||||
⑤、序列化一般使用自定义二进制,且数据一般为整数,如果对大小有要求,可以参考整数压缩方案。
|
||||
|
||||
## 八、日志
|
||||
|
||||
①、常规日志,比如输入,行为,随机数,属性等。
|
||||
|
||||
②、自动日志,大致实现思路:
|
||||
|
||||
遍历所有涉及逻辑的代码文件,通过正则表达式,查找到所有的有值类型参数的方法。
|
||||
|
||||
生成记录代码,包括宏开关,记录方法ID(方法名经过映射后的ID),参数数量,每个参数大小的代码和记录值的泛型接口代码。
|
||||
|
||||
③、哈希相关:
|
||||
|
||||
每次记录可算哈希,每帧可算哈希,多帧哈希可叠加。
|
||||
|
||||
哈希计算是可能会有冲突的,尽可能的降低计算成本与哈希冲突。
|
||||
|
||||
④、不一致定位,定位哈希不同的帧和定位同帧下的不同步点。
|
||||
|
||||
## 九、不同步
|
||||
|
||||
①、基于日志定位。
|
||||
|
||||
②、可能的原因:
|
||||
|
||||
使用了浮点数相关的模块,比如内置数学API、物理模拟、动作、自定义库等。
|
||||
|
||||
使用了字典,遍历顺序不一致。
|
||||
|
||||
多线程操作数据先后,执行结果处理先后。
|
||||
|
||||
“我”的开发角度,使得逻辑倾向于其中一方。
|
||||
|
||||
工具类、单例类有未纳入序列化的数据。
|
||||
|
||||
## 十、定点数
|
||||
①、选择合适精度,这涉及计算性能与内存开销。
|
||||
|
||||
②、开方,快速近似查表代替循环。
|
||||
|
||||
③、反三角函数,查表代替泰勒展开(循环)。
|
||||
|
||||
## 十一、多世界
|
||||
|
||||
①、尽量不要使用有状态(数据)的单例类。
|
||||
|
||||
②、可以存在无状态(数据)的静态类,工具类,辅助类等。
|
||||
|
||||
## 十二、其他
|
||||
|
||||
①、帧同步中,没有秒的概念,只有帧的概念,秒的相关数据都需要转换成帧。
|
||||
|
||||
②、逻辑帧中的延迟模块,一定是由逻辑帧去轮询,比如自定义协程、延迟模块等。
|
||||
|
||||
③、客户端和服务器都有帧缓冲,客户端快照也会有缓冲,以减少内存使用。
|
||||
|
||||
④、预测帧数高时,预输入帧数可对应降低。
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"nodes":[
|
||||
{"id":"cb35f36500051eb6","x":-533,"y":-472,"width":1093,"height":60,"type":"text","text":""},
|
||||
{"id":"61edc211325da480","x":-1206,"y":2853,"width":906,"height":60,"color":"4","type":"text","text":"动作时间轴"},
|
||||
{"id":"a7f14817e1497be2","x":-1206,"y":2940,"width":586,"height":60,"color":"6","type":"text","text":"不可打断"},
|
||||
{"id":"e8d1678f431e2ee0","x":-870,"y":3020,"width":250,"height":60,"color":"6","type":"text","text":"缓存输入"},
|
||||
{"id":"8fc0985df93fdde7","x":-620,"y":3020,"width":320,"height":60,"color":"6","type":"text","text":"可切换的后摇"}
|
||||
],
|
||||
"edges":[]
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
正常流程
|
||||
1、攻击 - 扣血 - 死亡
|
||||
2、攻击附加buff
|
||||
3、技能
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
|
||||
# 祛魅游戏多人网络同步技术
|
||||
https://mp.weixin.qq.com/s/ihfnqQ6W0ziGWsZlCOjXhw
|
||||
|
||||
Reference in New Issue
Block a user