1.JSF源码分析(一)
2.Hermes源码分析(二)——解析字节码
3.nginx源码分析--master和worker进程模型
4.Android Adb 源码分析(一)
5.MySQL XA事务源码分析
JSF源码分析(一)
在深入分析 JSF 框架的源码源码源码时,我们首先关注的分析是核心的功能模块,以帮助我们理解其工作原理。源码源码通常,分析我们从常见的源码源码项目 XML 配置文件入手,这些文件包含了 JSF 框架的分析购买源码不可用基本设置。让我们以地址服务的源码源码 jsf-provider.xml 文件为例,进行详细的分析解析。
在 JSF 的源码源码配置文件中,虽然没有直接显示注册中心的分析内容,但作为自研的源码源码高性能 RPC 调用框架,高可用的分析注册中心是其核心功能之一。因此,源码源码我们接下来将探索如何在没有提供注册中心地址的分析情况下,这些标签是源码源码如何完成服务的注册和订阅的。
### 配置解析
首先,我们发现配置文件中自定义的 xsd 文件,通过 NamespaceUri 链接到 jsf.jd.com/schema/jsf/j...。随后,基于 SPI(Service Provider Interface)机制,我们在 META-INF 中找到了定义好的 Spring.handlers 文件和 Spring.schemas 文件,这两个文件分别用于配置解析器和 xsd 文件的具体路径。
进一步地,我们查询了继承自 NamespaceHandlerSupport 或实现 NamespaceHandler 接口的类。在 JSF 框架中,JSFNamespaceHandler 通过继承 NamespaceHandlerSupport 实现了对自定义命名空间的标点云盗版源码解析功能。NamespaceHandler 的主要作用是解析我们自定义的 JSF 命名空间,通过 BeanDefinitionParser 对特定标签进行处理,完成对 XML 中配置信息的具体处理。
### 服务暴露
最终,通过 JSFBeanDefinitionParser 实现了 org.springframework.beans.factory.xml.BeanDefinitionParser,完成 XML 配置的解析。解析的结果会注册到 BeanDefinitionRegistry 对象中,进而触发 Bean 的初始化过程。最终,ProviderBean 实例监听上下文事件,在容器初始化完毕后,调用 export() 方法进行服务的暴露。
### 服务注册与暴露
服务暴露的实现逻辑集中在 ProviderConfig#doExport 方法中。首先,方法会对配置进行基本校验和拦截。随后,获取所有 RegistryConfig,如果获取不到注册中心地址,将使用默认的注册中心地址:“i.jsf.jd.com”。接着,根据 Provider 配置中的 server 相关信息启动 server,并使用默认序列化方式(如 msgpack)进行服务编码。然后,通过 ServerFactory 初始化并启动 Server,调用 ServerTransportFactory 生成对应的ubuntu右键菜单源码传输层,实现与注册中心的通信。最后,服务注册通过 JSFRegistry 类完成,该类连接注册中心,如果没有可用的中心,则使用本地文件并开启守护线程,使用两个线程池进行心跳检测、重试机制和连接状态监控。至此,服务从配置装配到服务暴露的过程完成。
### 消费者配置与初始化
对于消费者端(jsf-consumer.xml),注册中心地址(如“i.jsf.jd.com”)被配置在其中,而 Provider 的配置则在 jsf-provider.xml 中。配置解析过程与 Provider 类似,最终解析为 ConsumerConfig 和 RegistryConfig。通过 ConsumerBean 类实现 FactoryBean 接口,以便通过 getObject() 方法获取代理对象,完成客户端的初始化。在这个过程中,消费者会根据配置订阅相关的 Provider 服务。核心代码在 ConsumerConfig#refer 方法中,该方法通过调用子类的 subscribe() 方法开始订阅过程,连接 Provider 服务。
### 框架流程概述
综上所述,JSF 框架通过 Provider、docker官方源码编译Consumer 和注册中心(Registry)之间的协同工作,实现了高效的服务注册、订阅和通信。具体流程包括:
1. **Provider 端**:启动服务向注册中心注册,并根据配置初始化相关组件。
2. **Consumer 端**:首次获取实体信息时,通过 FactoryBean 接口获取代理对象,完成初始化并订阅 Provider 服务。
3. **注册中心**:提供异步通知机制,监控服务状态变化。
4. **服务调用**:直接调用服务方法。
5. **监控与治理**:框架内置监控机制,支持服务治理和降级容灾策略。
了解这一过程对于深入理解 JSF 框架的内部机制至关重要,也为后续的模块分析和系统优化提供了基础。
Hermes源码分析(二)——解析字节码
前面一节 讲到字节码序列化为二进制是有固定的格式的,这里我们分析一下源码里面是怎么处理的这里可以看到首先写入的是魔数,他的值为
对应的二进制见下图,注意是小端字节序
第二项是字节码的版本,笔者的版本是,也即 上图中的4a
第三项是源码的hash,这里采用的是SHA1算法,生成的哈希值是位,因此占用了个字节
第四项是文件长度,这个字段是vscode查看库源码位的,也就是下图中的为0aa,转换成十进制就是,实际文件大小也是这么多
后面的字段类似,就不一一分析了,头部所有字段的类型都可以在BytecodeFileHeader.h中看到,Hermes按照既定的内存布局把字段写入后再序列化,就得到了我们看到的字节码文件。
这里写入的数据很多,以函数头的写入为例,我们调用了visitFunctionHeader方法,并通过byteCodeModule拿到函数的签名,将其写入函数表(存疑,在实际的文件中并没有看到这一部分)。注意这些数据必须按顺序写入,因为读出的时候也是按对应顺序来的。
我们知道react-native 在加载字节码的时候需要调用hermes的prepareJavaScript方法, 那这个方法做了些什么事呢?
这里做了两件事情:
1. 判断是否是字节码,如果是则调用createBCProviderFromBuffer,否则调用createBCProviderFromSrc,我们这里只关注createBCProviderFromBuffer
2.通过BCProviderFromBuffer的构造方法得到文件头和函数头的信息(populateFromBuffer方法),下面是这个方法的实现。
BytecodeFileFields的populateFromBuffer方法也是一个模版方法,注意这里调用populateFromBuffer方法的是一个 ConstBytecodeFileFields对象,他代表的是不可变的字节码字段。
细心的读者会发现这里也有visitFunctionHeaders方法, 这里主要为了复用visitBytecodeSegmentsInOrder的逻辑,把populator当作一个visitor来按顺序读取buffer的内容,并提前加载到BytecodeFileFields里面,以减少后面执行字节码时解析的时间。
Hermes引擎在读取了字节码之后会通过解析BytecodeFileHeader这个结构体中的字段来获取一些关键信息,例如bundle是否是字节码格式,是否包含了函数,字节码的版本是否匹配等。注意这里我们只是解析了头部,没有解析整个字节码,后面执行字节码时才会解析剩余的部分。
evaluatePreparedJavaScript这个方法,主要是调用了HermesRuntime的 runBytecode方法,这里hermesPrep时上一步解析头部时获取的BCProviderFromBuffer实例。
runBytecode这个方法比较长,主要做了几件事情:
这里说明一下,Domain是用于垃圾回收的运行时模块的代理, Domain被创建时是空的,并跟随着运行时模块进行传播, 在运行时模块的整个生命周期内都一直存在。在某个Domain下创建的所有函数都会保持着对这个Domain的强引用。当Domain被回收的时候,这个Domain下的所有函数都不能使用。
未完待续。。。
nginx源码分析--master和worker进程模型
一、Nginx整体架构
正常执行中的nginx会有多个进程,其中最基本的是master process(主进程)和worker process(工作进程),还可能包括cache相关进程。
二、核心进程模型
启动nginx的主进程将充当监控进程,主进程通过fork()产生的子进程则充当工作进程。
Nginx也支持单进程模型,此时主进程即是工作进程,不包含监控进程。
核心进程模型框图如下:
master进程
监控进程作为整个进程组与用户的交互接口,负责监护进程,不处理网络事件,不负责业务执行,仅通过管理worker进程实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能。
master进程通过sigsuspend()函数调用大部分时间处于挂起状态,直到接收到信号。
master进程通过检查7个标志位来决定ngx_master_process_cycle方法的运行:
sig_atomic_t ngx_reap;
sig_atomic_t ngx_terminate;
sig_atomic_t ngx_quit;
sig_atomic_t ngx_reconfigure;
sig_atomic_t ngx_reopen;
sig_atomic_t ngx_change_binary;
sig_atomic_t ngx_noaccept;
进程中接收到的信号对Nginx框架的意义:
还有一个标志位:ngx_restart,仅在master工作流程中作为标志位使用,与信号无关。
核心代码(ngx_process_cycle.c):
ngx_start_worker_processes函数:
worker进程
worker进程主要负责具体任务逻辑,主要关注与客户端或后端真实服务器之间的数据可读/可写等I/O交互事件,因此工作进程的阻塞点在select()、epoll_wait()等I/O多路复用函数调用处,等待数据可读/写事件。也可能被新收到的进程信号中断。
master进程如何通知worker进程进行某些工作?采用的是信号。
当收到信号时,信号处理函数ngx_signal_handler()会执行。
对于worker进程的工作方法ngx_worker_process_cycle,它主要关注4个全局标志位:
sig_atomic_t ngx_terminate;//强制关闭进程
sig_atomic_t ngx_quit;//优雅地关闭进程(有唯一一段代码会设置它,就是接受到QUIT信号。ngx_quit只有在首次设置为1时,才会将ngx_exiting置为1)
ngx_uint_t ngx_exiting;//退出进程标志位
sig_atomic_t ngx_reopen;//重新打开所有文件
其中ngx_terminate、ngx_quit、ngx_reopen都将由ngx_signal_handler根据接收到的信号来设置。ngx_exiting标志位仅由ngx_worker_cycle方法在退出时作为标志位使用。
核心代码(ngx_process_cycle.c):
Android Adb 源码分析(一)
面对Android项目的调试困境,我们的团队在项目临近量产阶段,将userdebug版本切换为了user版本,并对selinux权限进行了调整。然而,这一转变却带来了大量的bug,日志文件在/data/logs/目录下,因为权限问题无法正常pull出来,导致问题定位变得异常困难。面对这一挑战,我们尝试了两种解决方案。
首先,我们尝试修改data目录的权限,使之成为system用户,以期绕过权限限制,然而数据目录下的logs文件仍保留了root权限,因此获取日志依然需要root权限,这并未解决问题。随后,我们找到了一个相对安全的解决办法——通过adb命令的后门机制,将获取root权限的命令修改为adb aaa.bbb.ccc.root。这一做法在一定程度上增加了后门的隐蔽性,避免了被窃取,同时对日常开发的影响也降至最低。
在解决这一问题的过程中,我们对Android ADB的相关知识有了更深入的理解。ADB是Android系统中用于调试的工具,它主要由三部分构成:adb client、adb service和adb daemon。其中,adb client运行于主机端,提供了命令接口;adb service作为一个后台进程,位于主机端;adb daemon则是运行于设备端(实际机器或模拟器)的守护进程。这三个组件共同构成了ADB工具的完整框架,且它们的代码主要来源于system/core/adb目录,用户可以在此目录下找到adb及adbd的源代码。
为了实现解决方案二,我们对adb的代码进行了修改,并通过Android SDK进行编译。具体步骤包括在Windows环境下编译生成adb.exe,以及在设备端编译adbd服务。需要注意的是,在进行编译前,需要先建立Android的编译环境。经过对ADB各部分关系及源代码结构的梳理,我们对ADB有了更深入的理解。
在后续的开发过程中,我们将继续深入研究ADB代码,尤其是关于如何实现root权限的功能。如果大家觉得我们的分享有价值,欢迎关注我们的微信公众号“嵌入式Linux”,一起探索更多关于Android调试的技巧与知识。
MySQL XA事务源码分析
事务类型外部 XA PREPARE 流程
省流版:
详细版:
外部 XA COMMIT 过程
省流版:
详细版:
外部 XA 2PC 阶段 Log 落盘顺序
------------------- XA PREPARE START -------------------------
------------------- XA PREPARE END -------------------------
.
.
.
.
.
.
------------------- XA COMMIT START -------------------------
------------------- XA COMMIT END -------------------------
本地事务 commit 流程
省流版
与外部 XA PREPARE 2PC 的不同
与外部 XA COMMIT 的不同
详细版:
------------------- PREPARE START -------------------------
------------------- PREPARE END -------------------------
------------------- COMMIT START -------------------------
------------------- COMMIT END -------------------------
外部 XA ROLLBACK 流程
省流版(Not Prepared Rollback 和 Prepared Rollback 的不同之处)
详细版
Not Prepared Rollback(在 end - prepare 之间 rollback)
Prepared Rollback(在 prepare 之后 rollback)
外部 XA RECOVERY 流程
省流版
详细版
本地事务 RECOVERY 流程
省流版
详细版
为什么只遍历最后一个binlog文件:
rotate 到新的 binlog 文件前,redo log 强制落盘,因此redo commit记录会落盘,保证老的binlog文件没有正在提交的事务