2024年3月

一:背景

1. 讲故事

先说一下题外话,一个监控别人系统运行状态的程序,结果自己出问题了,有时候想一想还是挺讽刺的,哈哈,开个玩笑,我们回到正题,前些天有位朋友找到我,说他们的系统会偶发性CPU爆高,CPU上去了就下不来了,让我帮忙看一下怎么回事,而且自己也分析过了,没找到哪里有问题,写监控的都是高手,给我的第一感觉就是这个dump可能解决起来不容易,不管怎么说,有了dump就开干吧!

二:WinDbg 分析

1. CPU真的爆高吗

作为调试人,第一准则就是不要轻信任何人透露给你的信息,因为人家在这块是一个小白,往往他的信息会把你带偏,我们只相信数据即可,切记!!! 所以我们先用
!tp
观察下CPU使用率。


0:198> !tp
CPU utilization: 100%
Worker Thread: Total: 197 Running: 42 Idle: 154 MaxLimit: 32767 MinLimit: 8
Work Request in Queue: 0
--------------------------------------
Number of Timers: 0
--------------------------------------
Completion Port Thread:Total: 10 Free: 5 MaxFree: 16 CurrentLimit: 10 MaxLimit: 1000 MinLimit: 8

从卦中信息看当前
CPU=100%
,还是蛮惨的,那到底谁在吃CPU资源呢?根据经验先查一下是不是触发了2代GC,接下来用
!t
观察下是否有GC标记。


0:198> !t
ThreadCount:      214
UnstartedThread:  0
BackgroundThread: 211
PendingThread:    0
DeadThread:       1
Hosted Runtime:   no
                                                                                                        Lock  
       ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
   0    1 276f0 000002789526b5f0    2a020 Preemptive  0000000000000000:0000000000000000 000002789525e840 0     MTA 
   2    2 25e5c 0000027895296d00    2b220 Preemptive  0000000000000000:0000000000000000 000002789525e840 0     MTA (Finalizer) 
   3    3 260e8 00000278ae35f0c0  202b020 Preemptive  0000000000000000:0000000000000000 000002789525e840 0     MTA 
   ...
 169 2113 10c20 00000278c26766c0  1029220 Preemptive  00000278B5D7D188:00000278B5D7D188 000002789525e840 1     MTA (GC) (Threadpool Worker) xxxException 00000278b5d46ce0
 ...

尼玛从卦中的
(GC)
来看,还真的触发了GC,接下来的研究方向就是洞察下是不是CPU爆高的祸首。

2. GC触发导致的吗

要寻找这个问题的答案,首先就是看下这次GC是不是
FullGC
即可,可以切到 169 号线程,观察下线程栈。


0:169> k 10
 # Child-SP          RetAddr               Call Site
00 000000c4`36ffb798 00007ffc`d5f14313     ntdll!NtWaitForSingleObject+0x14
01 000000c4`36ffb7a0 00007ffc`c927cb27     KERNELBASE!WaitForSingleObjectEx+0x93
02 000000c4`36ffb840 00007ffc`c927cadf     clr!CLREventWaitHelper2+0x3c
03 000000c4`36ffb880 00007ffc`c927ca5c     clr!CLREventWaitHelper+0x1f
04 000000c4`36ffb8e0 00007ffc`c926bd32     clr!CLREventBase::WaitEx+0x7c
05 000000c4`36ffb970 00007ffc`c9269bc4     clr!ThreadSuspend::SuspendRuntime+0x32c
06 000000c4`36ffba60 00007ffc`c91814e3     clr!ThreadSuspend::SuspendEE+0x128
07 000000c4`36ffbb60 00007ffc`c9185f51     clr!WKS::GCHeap::GarbageCollectGeneration+0xb7
08 000000c4`36ffbbc0 00007ffc`c9260f56     clr!WKS::gc_heap::trigger_gc_for_alloc+0x2d
09 000000c4`36ffbc00 00007ffc`c6b0f7e7     clr!JIT_NewArr1+0xa97
0a 000000c4`36ffc030 00007ffc`6a388270     mscorlib_ni!System.String.ToCharArray+0x27 [f:\dd\ndp\clr\src\BCL\system\string.cs @ 758] 
0b 000000c4`36ffc080 00007ffc`6a3880ed     0x00007ffc`6a388270
0c 000000c4`36ffc100 00007ffc`6a56056d     0x00007ffc`6a3880ed
0d 000000c4`36ffc150 00007ffc`6a3cd749     0x00007ffc`6a56056d
0e 000000c4`36ffc1b0 00007ffc`c911989d     0x00007ffc`6a3cd749
0f 000000c4`36ffc220 00007ffc`c9119764     clr!ExceptionTracker::CallHandler+0xfd

从卦中看此时的GC还处于早期的
SuspendEE
阶段,无法获取内部的
settings
结构,这就比较麻烦了,那怎么办呢?只能看看 GarbageCollectGeneration 的第一个参数有没有保存在栈中,要是没有就惨了。。。方法签名如下:


size_t
GCHeap::GarbageCollectGeneration (unsigned int gen, gc_reason reason)
{

}

根据 x64调用协定,gen是保存在 rdx 寄存器里,接下来观察汇编代码。


0:000> uf 00007ffc`c91814e3
clr!WKS::GCHeap::GarbageCollectGeneration:
00007ffc`c918142c 48895c2418      mov     qword ptr [rsp+18h],rbx
00007ffc`c9181431 89542410        mov     dword ptr [rsp+10h],edx
00007ffc`c9181435 48894c2408      mov     qword ptr [rsp+8],rcx
00007ffc`c918143a 55              push    rbp
00007ffc`c918143b 56              push    rsi
00007ffc`c918143c 57              push    rdi
00007ffc`c918143d 4154            push    r12
00007ffc`c918143f 4155            push    r13
00007ffc`c9181441 4156            push    r14
00007ffc`c9181443 4157            push    r15
...

0:169> dd 000000c4`36ffbbc0-0x8+0x10 L1
000000c4`36ffbbc8  00000000

从卦中看,谢天谢地,edx保存在
rsp+10h
的位置,通过dp观察内存地址的值发现是0,也就表示当前是 0 代GC,这种smallgc 经常触发是很正常的,并不是我们CPU爆高的诱因,接下来就陷入迷茫了。。。

3. 路在何方

撞了南墙之后得要看看其他路子,其实刚才用
!t
观察线程列表的时候我就注意到一个特征,那就是很多线程上挂了异常,截图如下:

从卦中看此时有19个线程在抛
xxxResultException
异常,做过开发的朋友都知道,如果频繁的抛异常是很耗CPU资源的,因为它要设计到
用户态

内核态
的切换,如果有 19 个线程一起抛异常,那绝对是一个灾难。。。

有些朋友说我cpu猛一点是不是就可以了,哈哈,理论上是可以的,可以用
!cpuid
观察下这台机器的cpu核心数。


0:169> !cpuid
CP  F/M/S  Manufacturer     MHz
 0  6,167,1  <unavailable>   3408
 1  6,167,1  <unavailable>   3408
 2  6,167,1  <unavailable>   3408
 3  6,167,1  <unavailable>   3408
 4  6,167,1  <unavailable>   3408
 5  6,167,1  <unavailable>   3408
 6  6,167,1  <unavailable>   3408
 7  6,167,1  <unavailable>   3408

从证据链的完整性上来说,其实这里还需要再做一个验证,就是19个线程抛异常不代表他们的并发性,言外之意就是能不能再找一些其他证据,怎么找其他证据呢?

做C#开发的朋友应该知道,Exception 属于引用类型,如果密集抛了很多异常,那托管堆上自然就有很多,直到GC回收,所以我们观察下这个时间差即可,使用
!wdae
命令,这里为了隐私性我就模糊了哈。


0:169> !wdae
     384 of Type: xxxResultException 000002789fdb6478 000002789fdb69b0 000002789fdb9848
Message: xxxFailed
Inner Exception: (none)
Stack:
IP               Function
00007ffc6a269861 xxx.ChannelAsyncOperation`1[[System.Int32, mscorlib]].End(Int32, Boolean)
...
     411 of Type: xxxResultException 000002789fdb6e90 000002789fdb7090 000002789fdb72a8
Message: xxxClosed
Inner Exception: (none)
Stack:
IP               Function
00007ffc6a269861 xxx.ChannelAsyncOperation`1[[System.Int32, mscorlib]].End(Int32, Boolean)
...
808 Exceptions in 12 unique type/stack combinations (duplicate types in similar stacks may be rethrows)

从卦中看当前抛了808个异常,大多是和channel通信有关,结合16个线程并发抛,这就稳了,看样子cpu爆高期间就是由于高频的抛异常所致,分析出这些信息之后,就是告诉朋友把这些异常给解决掉即可。

三:总结

CPU爆高的诱因非常多,高频的抛异常就属于其中一例,其实这种通信时发生了突发异常正是 Polly 这种
弹性和瞬态故障处理库
大显身手的地方。

图片名称

pandas
中的
cut
函数可将一维数据按照给定的区间进行分组,并为每个值分配对应的标签。
其主要功能是将连续的数值数据转化为离散的分组数据,方便进行分析和统计。

1. 数据准备

下面的示例中使用的数据采集自王者荣耀比赛的统计数据。
数据下载地址:
https://databook.top/

导入数据:

# 2023年世冠比赛选手的数据
fp = r"D:\data\player-2023世冠.csv"

df = pd.read_csv(fp)

# 这里只保留了下面示例中需要的列
df = df.loc[:, ["排名", "选手", "场均经济", "场均伤害"]]
df

image.png

2. 使用示例

每个选手的
“场均经济”

“场均伤害”
是连续分布的数据,为了整体了解所有选手的情况,
可以使用下面的方法将
“场均经济”

“场均伤害”
分类。

2.1. 查看数据分布

首先,可以使用直方图的方式看看数据连续分布的情况:

import matplotlib.pyplot as plt

df.loc[:, ["场均经济", "场均伤害"]].hist()
plt.show()

image.png
图中的
横轴
是“经济”和“伤害”的数值,
纵轴
是选手的数量。

2.2. 定制分布参数

从默认的直方图中可以看出大部分选手的
“场均经济”

“场均伤害”
大致在什么范围,
不过,为了更精细的分析,我们可以进一步定义自己的分类范围,看看各个分类范围内的选手数量情况。

比如,我们将
“场均经济”
分为3块,分别为


0~5000
),


5000~10000
),


10000~20000
)。
同样,对于
“场均伤害”
,也分为3块,分别为


0~50000
),


50000~100000
),


100000~200000
)。

bins1 = [0, 5000, 10000, 20000]
bins2 = [0, 50000, 100000, 200000]

labels = ["低", "中", "高"]
s1 = "场均经济"
s2 = "场均伤害"
df[f"{s1}-分类"] = pd.cut(df[s1], bins=bins1, labels=labels)
df[f"{s2}-分类"] = pd.cut(df[s2], bins=bins2, labels=labels)

df

image.png
分类之后,选手被分到3个类别之中,然后再绘制直方图。

df.loc[:, f"{s1}-分类"].hist()
plt.title(f"{s1}-分类")
plt.show()

image.png
从这个图看出,大部分选手都是
“中”

“高”
的经济,说明职业选手很重视英雄发育。

df.loc[:, f"{s2}-分类"].hist()
plt.title(f"{s2}-分类")
plt.show()

image.png
从图中可以看出,打出高伤害的选手比例并不高,可能职业比赛中,更多的是团队作战。

3. 总结

总的来说,
cut
函数的主要作用是将输入的数值数据(可以是一维数组、Series或DataFrame的列)按照指定的间隔或自定义的区间边界进行
划分
,并为每个划分后的区间分配一个
标签

这样,原始的连续数据就被转化为了离散的分组数据,每个数据点都被分配到了一个特定的组中,从而方便后续进行分析和统计。

这几天,突然想写写这些年的工作总结,毕业至今快20年的回顾。

想到20年前,在做毕业设计的时候,当时是学的机械工程类专业,因为带毕业设计的老师兼职企业有个门户网站的需求,而我又会做点网站设计,带的老师破天荒给个企业做门户网站的毕业设计。

珍惜心:该网站可能此生自己能做唯一一个大型网站,以后不会有

当时一时兴趣一个人接下来,开始做了一段时间后,发现要做好,要费太多精力了,有次想想是否随便做做,应付一下得了,何必这么认真。这时内心突然出现一种
念头:"人生第一次做这样大的门户网站,这样的机会可能这辈子不会再有。现在不认真做, 不珍惜,以后就没有机会了。"

在这个念头下,当时真是用心努力去做这个网站,丝毫没考虑是否有什么收获,收获多少问题,的确这么多年过去了,该门户网站至今是自己做的唯一的一个大型门户网站,至今也没有其他的。

后面工作有时自己有懈怠,
经常提醒自己,现在做的工作和项目,也许是自己此生做的最后一个该类型工作和项目,要倍加珍惜和用心!

做好当前事:新机会才会有

毕业找工作一直想转网站程序编程方面工作,但技术水平太一般,而且不是学计算机专业,毕业学校也一般,就是没机会。但凭这个给做的网站,在导师帮助下,去了导师兼职做的公司做了网管,从机械类转到计算机类相关的工作,去后,工作都只能靠一个人,
没有别人培养,只能自己培养自己
,在工作中用心,给公司做个公司网站,也是用尽心力,当时有个
念头:做好这么多事,应该会有更好工作机会
。 所以工作尽量多为公司做事,写在简历上就很丰富。第二年4月底,凭这些做的网站和事情,去上海找工作,就在一家大型物流公司应聘上了程序员的工作,记得面试时,IT部经理,让我上机用VS.NET工具做个表的增删改,结果删除还是更新做不出来,还是他让我在网上查一下原因才搞定,就这样差的技术水平,但看我做的网站估计还不错,刚好他们公司有这个做网站的需求,这样情况下放水以程序员的名义进去的。

做别人不愿意做的事:只要用心做事,就会学到真本领

在这个全国的物流公司做程序员,因公司有个VB6.0+SQL Server 2000开发的物流ERP系统,去公司2年多,虽然自己也努力学习,但没有做大的项目, 也没法接触其真正核心和业务,做的都是一些边边脚脚的东东,技术和水平进步很慢,后来公司老板决定开发新一代的物流ERP系统,新系统用JAVA RCP+ SQL Server2005来做,做了不到几个月,旧系统有个货号升级的项目要做,最后分配给我,让我一个来完成,现在都用新技术JAVA,做好了将来跳槽也会找到更好的工作,这个VB都落后了,做这个没前途,没办法,既然让我做,只能接了,但是为了做这个货号升级,以前的IT部经理,不得不把全部的系统源代码和数据库开放给我,让我来研究和开发,这才结合实践,学到点真东西,比如数据库SQL优化,复杂报表的存储过程编写等等,也是在做该项目的几个月迅速成长起来,几个月就学到了真本领。

在新工作项目优化中就用到, 如报表复杂存储过程的开发:

1,
经典存储过程计算报表

写完该存储过程, 这么逻辑复杂的报表都能实现了,给了自己很大的信心,以后再有复杂SQL,也难不倒我。

做别人不愿意做的事,并不一定没有收获,在他们看来没有多大价值,但只要你用心,实际对自己会有很大帮助。
就是靠这些,后来换工作就找到一份java程序员,但去了主要做java程序数据库优化的项目,给后来转DBA积累大量经验和技术储备。

多总结:  多写技术blog助力工作转型,并领悟核心(道)

虽然在新公司,优化了几个项目,技术积累不少,因程序员有30岁的坎,后面不好再找好工作,而这时网上鼓吹DBA的职业生涯比较长,就决心转型DBA岗位,但又没有做DBA的经验,DBA工作有点不好找,后来出现一个
念头: 在
博客园
的上写技术文章和工作总结,面试的时候可以给面试官看,看自己写技术博客,展现自己的技术水平。

的确自己的技术博客,在从JAVA程序员转SQL Server DBA,再从SQL Server DBA转MySQL DBA,2次应聘上都起到了一定的助力,这2次转型的工作都很累,都是2家公司业务急速发展阶段,期间学到不少,自己总结做了这么多年DBA,得出DBA的工作核心:

1,
数据库运维核心--可控

做难事: 必有所得

正如中国军事战略家
金一南
将军说过

做难事,必有所得!

相关视频:
金一南:做难事必有所得,一定要干自己没有干过的,你的生命才能真正绽放

其实这些年,做DBA,真正难忘不是什么SQL优化,高可用等,而是解决工作难题:

1、
SSIS数据同步系统

2,
2011年2月--2011年7月数据库性能优化过程

3、
核心只读数据库实例故障应急解决方案

4 、
虚IP解决AlWaysON读库服务器过保替换

5、
用ELK分析每天4亿多条腾讯云MySQL审计日志(1)--解决过程

6、
数据抽取平台pydatax介绍--实现和项目使用

技术升华:从解决工作难题到解决生活难题,并领悟生活

在解决技术难题,从发现其规律,生活中有次自己病痛折磨,从这些解决过程得到灵感和感触,应用于生活病痛的难题解决:

1,
从数据库优化到治病(1)---做超越自己能力的事情

在工作中帮助其他人,最终被别人帮忙治好心悸,
因果不虚
,如俗语所说:

所做之恶,留在身边, 所做之善,回到身边

2,
从数据库优化到治病(2)---治好心悸过程

工作的高度:

今日头条刷视频,看到字节跳动的
张一鸣
说他对优秀人才的定义:
相关视频:
张一鸣如何寻找优秀人才视频

1,对某个领域有提纲挈领,高屋建瓴的总结

2,在以前的工作中做出优秀和出类拔萃的成绩

特斯拉的
埃隆·马斯克
,他挑选优秀人才,就看有没有解决过什么难题:
埃隆·马斯克的用人观

看这些世界级顶级老板,他们心中什么是优秀人才,就知道我们在工作应该怎么奋斗,怎么努力,才和这些大老板的高度一样。

珍惜心和认真心:

这些年技术,经验和能力等大量积累, 比以前有了很大进步,但是和以前第一次做门户网站,优化
计算报表
相比,
就明显感觉
越来越缺少以前那份认真的心和珍惜的心,以前的心更真一些!

工作感悟:

1,看了那么多本
鸾书善书
,明白了善恶因果等,同时不要贪小便宜(包括公司的,如多报销一点等)

2,工作上的同事,也不都是不靠谱的,现在的工作就是他们推荐的和给的机会

3,同事和朋友等,技术要帮忙的,尽量去做,不要太在意回报,以后会体会到其中好处

......

最近1,2年有时做梦,梦到这些年过得太快,好像提示很多事情和心愿还没完成!

本文分享自华为云社区《
Sermant 的整体流程学习梳理
》,作者:用友汽车信息科技(上海)有限公司 刘亚洲 Java研发工程师。

一、sermant架构

Sermant整体架构包括Sermant Agent、Sermant Backend、Sermant Injector、动态配置中心等组件。其中Sermant Agent是提供字节码增强基础能力及各类服务治理能力的核心组件,Sermant Backend、Sermant Injector、动态配置中心为Sermant提供其他能力的配套组件。

二、java agent和bytebuddy组合使用场景

比较典型的就是skywalking、sermant、arthas、mockito。如果说java agent开了一扇门,那么bytebuddy在开的这扇门中打开了一片新的天地。

三、Sermant的入口

前面我们说AgentLauncher是java agent的入口,为什么这么说呢?

<manifestEntries>

    <Premain-Class>com.huaweicloud.sermant.premain.AgentLauncher</Premain-Class>

    <Agent-Class>com.huaweicloud.sermant.premain.AgentLauncher</Agent-Class>

    <Can-Redefine-Classes>true</Can-Redefine-Classes>

    <Can-Retransform-Classes>true</Can-Retransform-Classes>

</manifestEntries>

答案可以从pom.xml中找到答案,这里可以看到基于Premain-Class和Agent-Class的两个类都指向了AgentLauncher这个类。因此我们可以非常确认的肯定它就是javaagent入口类。类似于java程序有一个main的执行入口,而java agent有一个自己的入口类premain。

因此可以看到它的入口执行main:

    /**
* premain
*
* @param agentArgs premain启动时携带的参数
* @param instrumentation 本次启动使用的instrumentation
*/ public static voidpremain(String agentArgs, Instrumentation instrumentation) {
launchAgent(agentArgs, instrumentation,
false);
}
/**
* agentmain
*
* @param agentArgs agentmain启动时携带的参数
* @param instrumentation 本次启动使用的instrumentation
*/ public static voidagentmain(String agentArgs, Instrumentation instrumentation) {
launchAgent(agentArgs, instrumentation,
true);
}

基于premain模式的和基于agent模式,区别在于是否为isDynamic。从这里我们可以看到这里提出了两个类值得我们去关注:AgentCoreEntrance、CommandProcessor,也即sermant这个项目的两个重点类。

更多需要了解的,可以参考byte-buddy这个开源项目。

四、入口方法执行的全流程

五、spi的加载过程

启动核心服务的过程是spi的加载过程,此时会初始化所有的服务。也即我们看到的所有服务会在此时会做一个启动的操作,同时还会启动事件:

service.start();
collectServiceStartEvent(startServiceArray.toString());

其实这个两个方法也做了很多事情。

启动服务做的事情:

collectServiceStartEvent则调用netty客户端向netty服务端发送数据。到服务端后,服务端进行数据处理,其收集的信息提供给backend模块方便后台展示查看。

六、以标签路由为例PluginService中扩展插件初始化

除此之外,还有一批实现了BaseService接口的,也即PluginService扩展插件服务基类,以标签路由为例,可以看你的其初始化的整个过程。

七、install的过程

同时我们可以看到install对应的process方法也是执行它的方法:

  publicResettableClassFileTransformer install(Instrumentation instrumentation) {
AgentBuilder builder
= newDefault().disableClassFormatChanges();//遍历actions for(BuilderAction action : actions) {
builder
=action.process(builder);
}
//执行安装操作,此时交给bytebuddy returnbuilder.installOn(instrumentation);
}

从入参中的Instrumentation,我们往回看:ByteEnhanceManager.
init
(instrumentation)

这个方法里面定义了action的顺序。

 public static voidinit(Instrumentation instrumentation) {
instrumentationCache
=instrumentation;
builder
=BufferedAgentBuilder.build();//初始化完成后,新增Action用于添加框架直接引入的字节码增强 enhanceForFramework();
}

执行下面的过程:

我们根据上面的添加顺序,来看初始化插件的顺序:

public static voidenhanceDynamicPlugin(Plugin plugin) {if (!plugin.isDynamic()) {return;
}
//获取描述信息 List<PluginDescription> plugins =PluginCollector.getDescriptions(plugin);//添加插件,然后执行安装操作 ResettableClassFileTransformer resettableClassFileTransformer =BufferedAgentBuilder.build()
.addPlugins(plugins).install(instrumentationCache);
plugin.setClassFileTransformer(resettableClassFileTransformer);
}

从引用上看,PluginSystemEntrance.
initialize
(isDynamic)中引用了这个方法。

可以看到这里的添加插件,可以理解为自定义的插件。

从sermant官网,我们可以知道:定义自定义插件,需要实现PluginDeclarer这个接口。也即从这里可以看到也即自定义的插件:

 /**
* 从插件收集器中获取所有插件声明器
*
* @param classLoader 类加载器
* @return 插件声明器集
*/ private static List<? extends PluginDeclarer>getDeclarers(ClassLoader classLoader) {
final List
<PluginDeclarer> declares = new ArrayList<>();for(PluginDeclarer declarer : loadDeclarers(classLoader)) {if(declarer.isEnabled()) {
declares.add(declarer);
}
}
returndeclares;
}

有了插件,就可以进行安装操作。

按照这个顺序,可以看到对应的action.process(builder)里面也执行了对应的构建方法。完成构建后,执行installOn方法。

完成安装工作后,根据安装前spi的增强实现,然后执行下游服务拦截增强,从而实现精准筛选工作。

八、以标签路由下游拦截处理为例

可以看到标签路由对应的几个代表性的Declarer:

NopInstanceFilterDeclarer、LoadBalancerDeclarer、BaseLoadBalancerDeclarer、ServiceInstanceListSupplierDeclarer等。

对应的拦截器Interceptor:

NopInstanceFilterInterceptor、LoadBalancerInterceptor、BaseLoadBalancerInterceptor、ServiceInstanceListSupplierInterceptor。

两者相互照应。

LaneServiceImpl和LoadBalancerServiceImpl是基于sermant框架的插件服务spi做的实现。

LaneServiceImpl和RouteRequestTagHandler是和路由能力相关的,LaneServiceImpl和LaneRequestTagHandler是和染色能力相关的。
RouteRequestTagHandler用来拦截并存储调用过程中的标签,FlowRouteHandler和TagRouteHandler是在路由选择下游实例时做的筛选过程。

下游拦截方法会经过BaseLoadBalancerInterceptor到loadBalancerService.getTargetInstances(serviceId, instances, requestData),最终到 HandlerChainEntry.
INSTANCE
.process(targetName, instances, requestData),基于责任链模式执行处理。目前主要有两种方式:FlowRouteHandler和TagRouteHandler。

这里面只是简单的介绍了整体的流程,具体细节的内容,还需要自己多实践。同时sermant大量使用了java agent的内容。

由于本人的局限性,有不妥的地方,还望批评指正!

参考:

  • sermant官网: https://sermant.io/zh/
  • sermant开源地址:https://github.com/huaweicloud/Sermant
  • byte-buddy开源地址:https://github.com/raphw/byte-buddy

点击关注,第一时间了解华为云新鲜技术~

DaMeng 达梦数据库介绍:

达梦数据库(DMDB)是中国自主研发的关系型数据库管理系统,由达梦科技股份有限公司开发。

达梦数据库提供了企业级的数据库解决方案,广泛应用于金融、电信、政府、制造等行业领域。

达梦数据库具有以下特点和优势:

  1. 高性能:具备高性能的并发处理能力,能够支持大容量和高并发的数据访问需求。
  2. 高可靠性:采用了多种数据保护机制,包括事务管理、数据备份恢复等,保障数据安全和稳定性。
  3. 高可扩展性:支持集群部署和分布式架构,能够灵活扩展以满足不同规模的数据存储需求。
  4. 兼容性强:兼容SQL标准,支持PL/SQL存储过程和触发器,同时也提供了丰富的工具和接口。
  5. 自主创新:在安全、性能、高可用性等方面不断进行技术创新和研发,满足用户不断变化的需求。

总体来说,达梦数据库作为国产数据库管理系统,在国内市场具有一定的地位和影响力,受到一些企业用户的青睐和选择。

CYQ.Data 框架简介:

好几年没写 CYQ.Data 框架的文章了,一直都在低调更新版本和源码,这里就简单引用一下 GTP4 的介绍:

CYQ.Data 是一个高性能且功能强大的ORM(对象关系映射)框架,支持包括.NET Core在内的各种.NET版本。

它设计用于与多种数据库如MSSQL、MySQL、Oracle、Sybase、PostgreSQL、DB2、FireBird、SQLite、DaMeng、KingBaseES等,以及格式如Txt、Xml、Access、Excel和FoxPro等工作。

该框架旨在通过提供写日志、操作JSON和分布式缓存等功能,简化数据层操作,无需额外的库如Log4net.dll、newtonjson.dll或Memcached.ClientLibrary.dll。

该框架自豪于其低调但持续15年的更新,强调其长期可靠性和对开发人员寻找稳定且多功能ORM解决方案的支持。

前言:

去年有群友问我,CYQ.Data 支不支国产数据库,那时候,没支持,只是给了些提示,让其自行通过开源代码支持了。

后来问的人多了,就开始支持了。

CYQ.Data 从去年年底开始支持国产数据库,下面就开始介绍相关内容。

1、开源地址:

https://github.com/cyq1162/cyqdata

2、 Nuget 引用

可以通过 nuget 管理器,直接搜 cyq.data,找到对应的 DaMeng 版本,引入即可。

之前仅是发布了 cyq.data 原始版本,没有提供对应集成驱动的单独版本,今年刚添加的,一添加就十来个,多了不少工作量。

cyq.data 原始版本和 cyq.data.dameng 版本的区别:

cyq.data 原始版本:

  不包含其它数据库驱动,需要自行引用对应数据的驱动:比如使用mysql数据库时,需要再引用 mysql.data.dll 驱动。如果引用 cyq.data.mysql,则里面包含了 mysql.data.dll 驱动。

cyq.data.dameng 版本:

  同样,里面集成了对应的数据库驱动,不用再单独引用。

3、支持的版本:

从下图可以看如,一如即往,从.net 2.0 一路支持到 .net 8 及以上。

4、数据库链接语句:

随便 CYQ.Data 支持的数据库越多,有些数据库的语句都一样,无法再根据关键字信息来识别,于是在语句上支持了provider:

链接语句示例:

provider=dameng;user id=SYSDBA;password=123456789;data source=127.0.0.1;port number=3050;schema=test;

链接语句配置:

{"ConnectionStrings": {"Conn": "provider=dameng;user id=SYSDBA;password=123456789;data source=127.0.0.1;port number=3050;schema=test;"}
}

5、使用示例:

框架默认会引用配置中 Conn 的链接,所以在代码中无需指定。

1、无实体:

使用 MAction 操作表、视图,带分页:

using (MAction action = new MAction("表名、视图名、sql查询语句"))
{
MDataTable dt
= action.Select(1, 10, "id>10");
}

使用 MProc 操作原始sql语句或存储过程:

using (MProc proc=new MProc("sql语句、存储过程名"))
{
MDataTable dt
=proc.ExeMDataTable();
}

2、有实体:

A、纯实体

public classUsers
{
public int ID { get; set; }public string Name { get; set; }
}

使用:

List
<Users> users=DBFast.Select<Users>(1,10,"id>10");

B、实体继承自 CYQ.Data.Orm

public class Users:CYQ.Data.Orm.SimpleOrmBase<Users>{publicUsers()
{
base.SetInit(this, "表名");
}
public int ID { get; set; }public string Name { get; set; }
}

使用:
using(Users user = newUsers())
{
List
<Users> users = user.Select(1, 10, "id>10");
}

以上仅展示查询功能,CYQ.Data 的操作,对十多种数据库,操作都是一致的。

如果还没有学过,可以看 CYQ.Data 相关教程。

1、V4 系列:
https://www.cnblogs.com/cyq1162/category/216965.html

2、V5 系列:
https://www.cnblogs.com/cyq1162/category/852300.html

6、DaMeng 数据库的基础说明:

在安装或使用 DaMeng 达梦数据库时,可以通过安装后的DM管理工具,来管理数据库

启动,输入账号进入界面后:

可以看到,默认都是大写标识,语句的关键字,是通过双引号包括起来。

可以看到,达梦和常规的数据库比较不一样,你可以这样理解它:

1、单数据库:

一个实例只有一个数据库,一个进程或服务,只管理一个数据库。

【对标以往其它数据库,都是一个实例对应多个数据库。】

因此,打开管理工具时,就没能再看到新建数据库这种常规操作,初入时,会有点MengBiBi。

2、多模式:

一对一的关系,让实例即数据库,可省掉了数据库这个中间概念。

同时支持多种模式,通过新建模式,区分库的概念,可理解为新建数据库。

【对标有些数据库:支持多数据库和多模式,所以多模式又不完全是多数据库的替代概念。】

基于它这种实例即数据库的模式,因此数据库链接中,不再需要指定数据库名称,但要指定模式名称:

重新看一下这个示例的数据库链接语句:

provider=dameng;user id=SYSDBA;password=123456789;data source=127.0.0.1;port number=3050;schema=test;

3、创建多个数据库,需要创建新的实例:

通过DB数据库配置助手,可以创建新的实例:

在创建实例过程中,还是可以看到数据库名称和实例名称可以命名编写的:

默认字符串查询比较是区分大小写的,如果不想区分,可以取消这个选项:

做为一篇介绍框架支持数据库的文章,就不过多的介绍介绍数据库本身了,有用到的,可以上官方了解更多。

总结:

CYQ.Data 是一个用于操作数据库的框架,可以方便地连接和管理各种类型的数据库。在操作达梦数据库时,使用 CYQ.Data 框架可以提供以下功能和优势:

  1. 连接数据库:CYQ.Data 可以轻松地建立与达梦数据库的连接,通过简单的配置即可实现连接功能,节省了开发人员的时间和精力。

  2. 执行 SQL 查询:使用 CYQ.Data 可以方便地执行各种 SQL 查询操作,包括查询数据、更新数据、插入数据等,同时还支持事务处理,确保数据操作的准确性和完整性。

  3. 参数化查询:CYQ.Data 支持参数化查询,可以有效防止 SQL 注入攻击,提高数据库操作的安全性。

  4. 数据库事务:通过 CYQ.Data 框架可以轻松管理数据库事务,确保多个操作的原子性,避免数据不一致的情况发生。

总的来说,使用 CYQ.Data 框架操作达梦数据库可以简化开发流程,提高开发效率,同时也增强了系统的稳定性和安全性。