最新要闻
- 7斤纯铜就能压制酷睿i9 为啥还要散热风扇?原因一个字:贵
- 新能源起大早赶晚集 吉利失去的四年
- 全球新消息丨魔兽等游戏国服已停服两周 暴雪高管:寻求替代方式服务中国玩家
- 全球热点评!法拉利2022年销量破纪录 员工年终奖人均10万
- 焦点信息:官方加紧备货!红魔8 Pro系列开售一个多月供不应求:网友反映“秒没”
- 全球要闻:2.5K触屏骁龙本!小米Book 12.4二合一首销:到手2899元
- 环球微动态丨2023年来最强雨雪上线:北方下雪、南方暴雨 影响超20省份
- 单踏板不爽、刹车失灵无妨!特斯拉83%车主为新用户 客户流失低忠诚度最高
- 【环球速看料】聊天机器人ChatGPT要抢搜索引擎生意 谷歌出手:竞品Bard来了
- 【天天新要闻】安卓之光来了!曝小米13 Ultra 4月登场:支持120倍变焦
- 天天时讯:售价超过2万元 苹果MR头显即将推出:搭载全新OS 未来将取代iPhone
- 【天天新视野】我国多地取消中考男女生长跑 800米对健康不利:专家喊话体育锻炼不能放松
- 当前快报:做出《新闻联播》片头的齐东旭教授走了:中国CAD与计算机图形学痛失巨匠
- 环球观焦点:游客放孔明灯被拽下吹灭 景区回应:明文规定禁放禁售
- 打价格战底气十足:数据显示特斯拉单车利润是比亚迪6倍
- 梦回Win98!196MB内存电脑成功启动Win11:开机时间要30分钟
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
环球通讯!DataX插件二次开发指南
一、 DataX
为什么要使用插件机制?
从设计之初,DataX
就把异构数据源同步作为自身的使命,为了应对不同数据源的差异、同时提供一致的同步原语和扩展能力,DataX
自然而然地采用了框架
+ 插件
的模式:
- 插件只需关心数据的读取或者写入本身。
- 而同步的共性问题,比如:类型转换、性能、统计,则交由框架来处理。
作为插件开发人员,则需要关注两个问题:
- 数据源本身的读写数据正确性。
- 如何与框架沟通、合理正确地使用框架。
二、插件视角看框架
逻辑执行模型
插件开发者基本只需要关注特定数据源系统的读和写,以及自己的代码在逻辑上是怎样被执行的,哪一个方法是在什么时候被调用的。开发之前需要明确以下概念:
(相关资料图)
Job
:Job
是DataX用以描述从一个源头到一个目的端的同步作业,是DataX数据同步的最小业务单元。比如:从一张mysql的表同步到odps的一个表的特定分区。Task
:Task
是为最大化而把Job
拆分得到的最小执行单元。比如:读一张有1024个分表的mysql分库分表的Job
,拆分成1024个读Task
,用若干个并发执行。TaskGroup
: 描述的是一组Task
集合。在同一个TaskGroupContainer
执行下的Task
集合称之为TaskGroup
JobContainer
:Job
执行器,负责Job
全局拆分、调度、前置语句和后置语句等工作的工作单元。类似Yarn中的JobTrackerTaskGroupContainer
:TaskGroup
执行器,负责执行一组Task
的工作单元,类似Yarn中的TaskTracker。
简而言之, Job
拆分成Task
,在分别在框架提供的容器中执行,插件只需要实现Job
和Task
两部分逻辑。
物理执行模型
框架为插件提供物理上的执行能力(线程)。DataX
框架有三种运行模式:
Standalone
: 单进程运行,没有外部依赖。Local
: 单进程运行,统计信息、错误信息汇报到集中存储。Distrubuted
: 分布式多进程运行,依赖DataX Service
服务。
当然,上述三种模式对插件的编写而言没有什么区别,你只需要避开一些小错误,插件就能够在单机/分布式之间无缝切换了。当JobContainer
和TaskGroupContainer
运行在同一个进程内时,就是单机模式(Standalone
和Local
);当它们分布在不同的进程中执行时,就是分布式(Distributed
)模式。
编程接口
Job
和Task
的逻辑是怎么对应到具体的代码中的?
首先,插件的入口类必须扩展Reader
或Writer
抽象类,并且实现分别实现Job
和Task
两个内部抽象类,Job
和Task
的实现必须是 内部类的形式,原因见 加载原理一节。以Reader为例:
public class SomeReader extends Reader { public static class Job extends Reader.Job { @Override public void init() { }@Overridepublic void prepare() { } @Override public List split(int adviceNumber) { return null; } @Override public void post() { } @Override public void destroy() { } } public static class Task extends Reader.Task { @Override public void init() { }@Overridepublic void prepare() { } @Override public void startRead(RecordSender recordSender) { } @Override public void post() { } @Override public void destroy() { } }}
Job
接口功能如下:
init
: Job对象初始化工作,此时可以通过super.getPluginJobConf()
获取与本插件相关的配置。读插件获得配置中reader
部分,写插件获得writer
部分。prepare
: 全局准备工作,比如mysqlwriter在写入新数据之前执行一个truncate table的操作、读取Hive数据之前,完成Kerberos认证等。split
: 拆分Task
。参数adviceNumber
框架建议的拆分数,一般是运行时所配置的并发度。值返回的是Task
的配置列表。post
: 全局的后置工作,比如mysqlwriter同步完影子表后的rename操作。destroy
: Job对象自身的销毁工作。
Task
接口功能如下:
init
:Task对象的初始化。此时可以通过super.getPluginJobConf()
获取与本Task
相关的配置。这里的配置是Job
的split
方法返回的配置列表中的其中一个。prepare
:局部的准备工作。startRead
: 从数据源读数据,写入到RecordSender
中。RecordSender
会把数据写入连接Reader和Writer的缓存队列。startWrite
:从RecordReceiver
中读取数据,写入目标数据源。RecordReceiver
中的数据来自Reader和Writer之间的缓存队列。post
: 局部的后置工作。destroy
: Task象自身的销毁工作。
需要注意的是:
Job
和Task
之间一定不能有共享变量,因为分布式运行时不能保证共享变量会被正确初始化。两者之间只能通过配置文件进行依赖。prepare
和post
在Job
和Task
中都存在,插件需要根据实际情况确定在什么地方执行操作。
框架按照如下的顺序执行Job
和Task
的接口:
上图中,黄色表示Job
部分的执行阶段,蓝色表示Task
部分的执行阶段,绿色表示框架执行阶段。
相关类关系如下:
插件定义
代码写好了,有没有想过框架是怎么找到插件的入口类的?框架是如何加载插件的呢?
在每个插件的项目中,都有一个plugin.json
文件,这个文件定义了插件的相关信息,包括入口类。例如:
{ "name": "mysqlwriter", "class": "com.alibaba.datax.plugin.writer.mysqlwriter.MysqlWriter", "description": "Use Jdbc connect to database, execute insert sql.", "developer": "alibaba"}
name
: 插件名称,大小写敏感。框架根据用户在配置文件中指定的名称来搜寻插件。 十分重要。class
: 入口类的全限定名称,框架通过反射插件入口类的实例。十分重要。description
: 描述信息。developer
: 开发人员。
打包发布
DataX
使用assembly
打包,assembly
的使用方法请咨询谷哥或者度娘。打包命令如下:
mvn clean package -DskipTests assembly:assembly
DataX
插件需要遵循统一的目录结构:
${DATAX_HOME}|-- bin | `-- datax.py|-- conf| |-- core.json| `-- logback.xml|-- lib| `-- datax-core-dependencies.jar`-- plugin |-- reader | `-- mysqlreader | |-- libs | | `-- mysql-reader-plugin-dependencies.jar | |-- mysqlreader-0.0.1-SNAPSHOT.jar | `-- plugin.json `-- writer |-- mysqlwriter | |-- libs | | `-- mysql-writer-plugin-dependencies.jar | |-- mysqlwriter-0.0.1-SNAPSHOT.jar | `-- plugin.json |-- oceanbasewriter `-- odpswriter
${DATAX_HOME}/bin
: 可执行程序目录。${DATAX_HOME}/conf
: 框架配置目录。${DATAX_HOME}/lib
: 框架依赖库目录。${DATAX_HOME}/plugin
: 插件目录。
插件目录分为reader
和writer
子目录,读写插件分别存放。插件目录规范如下:
${PLUGIN_HOME}/libs
: 插件的依赖库。${PLUGIN_HOME}/plugin-name-version.jar
: 插件本身的jar。${PLUGIN_HOME}/plugin.json
: 插件描述文件。
尽管框架加载插件时,会把${PLUGIN_HOME}
下所有的jar放到classpath
,但还是推荐依赖库的jar和插件本身的jar分开存放。
注意:插件的目录名字必须和plugin.json
中定义的插件名称一致。
配置文件
DataX
使用json
作为配置文件的格式。一个典型的DataX
任务配置如下:
{ "job": { "content": [ { "reader": { "name": "odpsreader", "parameter": { "accessKey": "", "accessId": "", "column": [""], "isCompress": "", "odpsServer": "", "partition": [ "" ], "project": "", "table": "", "tunnelServer": "" } }, "writer": { "name": "oraclewriter", "parameter": { "username": "", "password": "", "column": ["*"], "connection": [ { "jdbcUrl": "", "table": [ "" ] } ] } } } ] }}
DataX
框架有core.json
配置文件,指定了框架的默认行为。任务的配置里头可以指定框架中已经存在的配置项,而且具有更高的优先级,会覆盖core.json
中的默认值。
配置中job.content.reader.parameter
的value部分会传给Reader.Job
;job.content.writer.parameter
的value部分会传给Writer.Job
,Reader.Job
和Writer.Job
可以通过super.getPluginJobConf()
来获取。
DataX
框架支持对特定的配置项进行RSA加密,例子中以*
开头的项目便是加密后的值。 配置项加密解密过程对插件是透明,插件仍然以不带*
的key来查询配置和操作配置项。
如何设计配置参数
配置文件的设计是插件开发的第一步!
任务配置中reader
和writer
下parameter
部分是插件的配置参数,插件的配置参数应当遵循以下原则:
驼峰命名:所有配置项采用驼峰命名法,首字母小写,单词首字母大写。
正交原则:配置项必须正交,功能没有重复,没有潜规则。
富类型:合理使用json的类型,减少无谓的处理逻辑,减少出错的可能。
- 使用正确的数据类型。比如,bool类型的值使用
true
/false
,而非"yes"
/"true"
/0
等。 - 合理使用集合类型,比如,用数组替代有分隔符的字符串。
- 使用正确的数据类型。比如,bool类型的值使用
类似通用:遵守同一类型的插件的习惯,比如关系型数据库的
connection
参数都是如下结构:{ "connection": [ { "table": [ "table_1", "table_2" ], "jdbcUrl": [ "jdbc:mysql://127.0.0.1:3306/database_1", "jdbc:mysql://127.0.0.2:3306/database_1_slave" ] }, { "table": [ "table_3", "table_4" ], "jdbcUrl": [ "jdbc:mysql://127.0.0.3:3306/database_2", "jdbc:mysql://127.0.0.4:3306/database_2_slave" ] } ]}
...
如何使用Configuration
类
为了简化对json的操作,DataX
提供了简单的DSL配合Configuration
类使用。
Configuration
提供了常见的get
, 带类型get
,带默认值get
,set
等读写配置项的操作,以及clone
, toJSON
等方法。配置项读写操作都需要传入一个path
做为参数,这个path
就是DataX
定义的DSL。语法有两条:
- 子map用
.key
表示,path
的第一个点省略。 - 数组元素用
[index]
表示。
比如操作如下json:
{ "a": { "b": { "c": 2 }, "f": [ 1, 2, { "g": true, "h": false }, 4 ] }, "x": 4}
比如调用configuration.get(path)
方法,当path为如下值的时候得到的结果为:
x
:4
a.b.c
:2
a.b.c.d
:null
a.b.f[0]
:1
a.b.f[2].g
:true
注意,因为插件看到的配置只是整个配置的一部分。使用Configuration
对象时,需要注意当前的根路径是什么。
更多Configuration
的操作请参考ConfigurationTest.java
。
插件数据传输
跟一般的生产者-消费者
模式一样,Reader
插件和Writer
插件之间也是通过channel
来实现数据的传输的。channel
可以是内存的,也可能是持久化的,插件不必关心。插件通过RecordSender
往channel
写入数据,通过RecordReceiver
从channel
读取数据。
channel
中的一条数据为一个Record
的对象,Record
中可以放多个Column
对象,这可以简单理解为数据库中的记录和列。
Record
有如下方法:
public interface Record { // 加入一个列,放在最后的位置 void addColumn(Column column); // 在指定下标处放置一个列 void setColumn(int i, final Column column); // 获取一个列 Column getColumn(int i); // 转换为json String String toString(); // 获取总列数 int getColumnNumber(); // 计算整条记录在内存中占用的字节数 int getByteSize();}
因为Record
是一个接口,Reader
插件首先调用RecordSender.createRecord()
创建一个Record
实例,然后把Column
一个个添加到Record
中。
Writer
插件调用RecordReceiver.getFromReader()
方法获取Record
,然后把Column
遍历出来,写入目标存储中。当Reader
尚未退出,传输还在进行时,如果暂时没有数据RecordReceiver.getFromReader()
方法会阻塞直到有数据。如果传输已经结束,会返回null
,Writer
插件可以据此判断是否结束startWrite
方法。
Column
的构造和操作,我们在《类型转换》一节介绍。
类型转换
为了规范源端和目的端类型转换操作,保证数据不失真,DataX支持六种内部数据类型:
Long
:定点数(Int、Short、Long、BigInteger等)。Double
:浮点数(Float、Double、BigDecimal(无限精度)等)。String
:字符串类型,底层不限长,使用通用字符集(Unicode)。Date
:日期类型。Bool
:布尔值。Bytes
:二进制,可以存放诸如MP3等非结构化数据。
对应地,有DateColumn
、LongColumn
、DoubleColumn
、BytesColumn
、StringColumn
和BoolColumn
六种Column
的实现。
Column
除了提供数据相关的方法外,还提供一系列以as
开头的数据类型转换转换方法。
DataX的内部类型在实现上会选用不同的java类型:
内部类型 | 实现类型 | 备注 |
---|---|---|
Date | java.util.Date | |
Long | java.math.BigInteger | 使用无限精度的大整数,保证不失真 |
Double | java.lang.String | 用String表示,保证不失真 |
Bytes | byte[] | |
String | java.lang.String | |
Bool | java.lang.Boolean |
类型之间相互转换的关系如下:
from\to | Date | Long | Double | Bytes | String | Bool |
---|---|---|---|---|---|---|
Date | - | 使用毫秒时间戳 | 不支持 | 不支持 | 使用系统配置的date/time/datetime格式转换 | 不支持 |
Long | 作为毫秒时间戳构造Date | - | BigInteger转为BigDecimal,然后BigDecimal.doubleValue() | 不支持 | BigInteger.toString() | 0为false,否则true |
Double | 不支持 | 内部String构造BigDecimal,然后BigDecimal.longValue() | - | 不支持 | 直接返回内部String | |
Bytes | 不支持 | 不支持 | 不支持 | - | 按照common.column.encoding 配置的编码转换为String,默认utf-8 | 不支持 |
String | 按照配置的date/time/datetime/extra格式解析 | 用String构造BigDecimal,然后取longValue() | 用String构造BigDecimal,然后取doubleValue(),会正确处理NaN /Infinity /-Infinity | 按照common.column.encoding 配置的编码转换为byte[],默认utf-8 | - | "true"为true , "false"为false ,大小写不敏感。其他字符串不支持 |
Bool | 不支持 | true 为1L ,否则0L | true 为1.0 ,否则0.0 | 不支持 | - |
脏数据处理
什么是脏数据?
目前主要有三类脏数据:
- Reader读到不支持的类型、不合法的值。
- 不支持的类型转换,比如:
Bytes
转换为Date
。 - 写入目标端失败,比如:写mysql整型长度超长。
如何处理脏数据
在Reader.Task
和Writer.Task
中,通过AbstractTaskPlugin.getTaskPluginCollector()
可以拿到一个TaskPluginCollector
,它提供了一系列collectDirtyRecord
的方法。当脏数据出现时,只需要调用合适的collectDirtyRecord
方法,把被认为是脏数据的Record
传入即可。
用户可以在任务的配置中指定脏数据限制条数或者百分比限制,当脏数据超出限制时,框架会结束同步任务,退出。插件需要保证脏数据都被收集到,其他工作交给框架就好。
加载原理
- 框架扫描
plugin/reader
和plugin/writer
目录,加载每个插件的plugin.json
文件。 - 以
plugin.json
文件中name
为key,索引所有的插件配置。如果发现重名的插件,框架会异常退出。 - 用户在插件中在
reader
/writer
配置的name
字段指定插件名字。框架根据插件的类型(reader
/writer
)和插件名称去插件的路径下扫描所有的jar,加入classpath
。 - 根据插件配置中定义的入口类,框架通过反射实例化对应的
Job
和Task
对象。
三、插件介绍文档
每个插件都必须在DataX
官方wiki中有一篇文档,文档需要包括但不限于以下内容:
- 快速介绍:介绍插件的使用场景,特点等。
- 实现原理:介绍插件实现的底层原理,比如
mysqlwriter
通过insert into
和replace into
来实现插入,tair
插件通过tair客户端实现写入。 - 配置说明
- 给出典型场景下的同步任务的json配置文件。
- 介绍每个参数的含义、是否必选、默认值、取值范围和其他约束。
- 类型转换
- 插件是如何在实际的存储类型和
DataX
的内部类型之间进行转换的。 - 以及是否存在特殊处理。
- 插件是如何在实际的存储类型和
- 性能报告
- 软硬件环境,系统版本,java版本,CPU、内存等。
- 数据特征,记录大小等。
- 测试参数集(多组),系统参数(比如并发数),插件参数(比如batchSize)
- 不同参数下同步速度(Rec/s, MB/s),机器负载(load, cpu)等,对数据源压力(load, cpu, mem等)。
- 约束限制:是否存在其他的使用限制条件。
- FAQ:用户经常会遇到的问题。
环球通讯!DataX插件二次开发指南
7斤纯铜就能压制酷睿i9 为啥还要散热风扇?原因一个字:贵
新能源起大早赶晚集 吉利失去的四年
全球新消息丨魔兽等游戏国服已停服两周 暴雪高管:寻求替代方式服务中国玩家
全球热点评!法拉利2022年销量破纪录 员工年终奖人均10万
世界新动态:读Java实战(第二版)笔记03_引入和使用流
焦点快看:必知必会的设计原则——开放封闭原则
焦点信息:官方加紧备货!红魔8 Pro系列开售一个多月供不应求:网友反映“秒没”
全球要闻:2.5K触屏骁龙本!小米Book 12.4二合一首销:到手2899元
环球微动态丨2023年来最强雨雪上线:北方下雪、南方暴雨 影响超20省份
单踏板不爽、刹车失灵无妨!特斯拉83%车主为新用户 客户流失低忠诚度最高
【环球速看料】聊天机器人ChatGPT要抢搜索引擎生意 谷歌出手:竞品Bard来了
【天天新要闻】安卓之光来了!曝小米13 Ultra 4月登场:支持120倍变焦
天天时讯:售价超过2万元 苹果MR头显即将推出:搭载全新OS 未来将取代iPhone
【天天新视野】我国多地取消中考男女生长跑 800米对健康不利:专家喊话体育锻炼不能放松
关于pacemaker-Cluster-节点的维护模式的功能介绍
SpringBoot怎么自定义一个Starter
当前快报:做出《新闻联播》片头的齐东旭教授走了:中国CAD与计算机图形学痛失巨匠
环球观焦点:游客放孔明灯被拽下吹灭 景区回应:明文规定禁放禁售
【天天新视野】《分布式技术原理与算法解析》学习笔记Day03
Spring IOC官方文档学习笔记(九)之基于注解的容器配置
每个程序员必学的10个Git命令
打价格战底气十足:数据显示特斯拉单车利润是比亚迪6倍
梦回Win98!196MB内存电脑成功启动Win11:开机时间要30分钟
环球看热讯:node中的优先从缓存中加载模块与模块的加载规则
LeetCode 239 滑动窗口最大值- Python手撕最大堆
环球新资讯:vscode编译java程序出现NoSuchMethodError的解决方法
价格又卷下来了!致态TiPlus 7100固态硬盘新史低:1TB仅589元
【天天快播报】澳大利亚一架波音737灭火飞机坠毁:全员奇迹生还
[JavaScript]实例化对象
天天热消息:【ctf权威竞赛指南笔记】(1)CTF
环球聚焦:全国城市地级市区县sql
速递!官宣续航为什么跑不到?爱玛实测电动车续航最多缩水40%
地震威力巨大 土耳其世界文化遗产古堡地震中倒塌
快资讯丨拒绝妖魔化 硬件发烧友怒买N张矿卡实测:结果大受震撼
【世界聚看点】上海一男子骑电车被劳斯莱斯撞倒后笑出了声 网友:还是大城市机会多
世界速看:Intel中国特供版i5-13490F突然开卖:1599元性价比逆天
厦门海辰储能--内部推荐
全球动态:Obsidian 插件(二):Advanced_Slides 的使用
自制肥牛卷
【快播报】【持续更新中】开源贡献记录
全球即时:USACO 2023 January Contest, Bronze Problem 3. Moo Operations
每日简讯:还有五场!饿了么免单活动又来了 一文看懂免单攻略
环球快资讯丨官方:嫦娥六号将带回2000克月球样品、嫦娥八号集大成
一天2次!土耳其再发7.8级地震:强震群 形势严峻
焦点信息:微博发布春节档电影热度报告:山东、河南网友最爱《满江红》
观点:真超薄自适应贴合!冈本超薄避孕套20片到手39.9元
世界短讯!现代农业|AIRIOT智慧农业管理解决方案
天天百事通!Ansible的部署和命令模块
携手Hello Kitty:小米Civi 2情人节特别版明日亮相
【全球快播报】国外网友:不看好网飞版《三体》 感谢腾讯!
全球通讯!10块钱可测17次!奥德中科快速检测试剂盒大促:0.59元/份清仓
世界视点!《流浪地球》引力弹弓人类真的实现过!张朝阳在线手算木星之旅
热点评!2022欧洲最畅销车型榜:国人不爱买的法系车登顶
当前快看:记录--使用Lunchbox 在 vue3 中创建一个 3D 地球
环球看点!做一个“不那么差”的程序员,有多难?
自研国产客机梦碎 消息称日本终止SpaceJet飞机 曾比我国进展都快
每日播报!8999元 科大讯飞推出T20 Pro学习机:2.5K屏、12000mAh大电池
全球微资讯!又又又融资1.35亿美元!贾跃亭喊话:FF 91将交付 百万豪车你买吗?
5499元 爱玛指挥官2023两轮电动车发布:石墨烯电池 145km长续航
《零:月蚀的假面》数字豪华版服装公布:旗袍黑丝超吸睛
天天报道:【交互式用户流程与演示设计】上海道宁与Overflow让您能更自信的展示您的设计
Helm
2023年Java面试正确姿势(1000+面试题附答案解析)
天天最新:黄光裕频繁减持国美套现10亿 把控股股东席位“减”没了
小屏幕拜拜!新款比亚迪秦PLUS DM-i内饰官图:最大短板被补足
【焦点热闻】可装进口袋!大疆新品DJI Mini 2 SE无人机2月9日发布
【全球播资讯】抛弃ARM公版 高通纯自研CPU骁龙8cx Gen4曝光:12核3.4GHz性能怪兽
焦点热讯:林志颖车祸后首谈特斯拉:称自己还在开特斯拉 没有任何阴影
天天速看:统信 UOS 重置Root账号密码 获取 Root 权限
全球快资讯:Spring:声明式事务
每日热点:Docker-harbor私有仓库
世界动态:爱奇艺用户吐槽会员跳不过广告!客服称登陆设备太多 账号异常
快播:6人网订民宿遇坐地起价:店家称有20多人订此房间 谁加价谁住
环球今日讯!索尼PS5不香了!会员数量持续流失 微软迎头赶上
10大“刮油”食物 佳节必备 转给需要的朋友!
环球视点!《流浪地球2》太空电梯能实现吗?专家:实现难度极大
全球信息:【秒杀】NTP时钟同步让秒杀成毫秒微秒纳秒杀
全球热议:满血40Gbps USB4 铭凡锐龙7000 8核准系统2549元(液金散热)
中国用户立大功!库克称iPhone很多技术灵感来自中国
广汽菲克破产 4S店已无售后!官方:我们为车主兜底
研究称调整脑电波能加速成人学习速度:至少快3倍
吾爱破解 2023 春节解题领红包之 Web 题解
开源即时通讯IM框架 MobileIMSDK v6.3 发布
MySQL执行流程
Python工具箱系列(二十四)
比眼镜蛇还毒50倍!女子买到蓝环章鱼险食用 反复辨认后扔了
信息:让ChatGPT写一篇《比亚迪能否打败特斯拉》的评论文章
Intel中国特供新神U i5-13490F首曝:频率更高、缓存更大
当前最新:(笔记)【NTP系列:06】NTP时间同步配置总结:Windows(W32Time)作为NTP时钟源服务端,Linux作为客户端
java注解与反射详解
世界快看点丨ChatGPT可能的影响与机会
当前短讯!面试官:实现异步的20种方式,你知道几个?
比亚迪海鸥实车现身 网友:买早了 8万元绝对卖爆
打破Steam Deck 12周霸榜!《霍格沃茨之遗》登顶Steam周销榜
今日精选:老人无证驾驶无牌三轮车逆行被撞还被罚 网友:建议全国推广
【世界快播报】专家称成年人有权做个废物!网友:反而更激励了我
天天亮点!00后平均期望薪资超7K 月薪高于一切:曾被痛批不应为钱选择工作
天天微头条丨drools规则动态化实践
全球微动态丨idea引入外部maven项目(非压缩)方式