低延迟文章引用

Linux获取纳秒时间戳的正确方式 - 知乎

Linux低延迟服务器系统调优 - 知乎

  最近做了一些系统和网络调优相关的测试,达到了期望的效果,有些感悟。同时,我也发现知乎上对Linux服务器低延迟技术的讨论比较欠缺(满嘴高并发现象);或者对现今cpu + 网卡的低延迟潜力认识不足(动辄FPGA现象),比如一篇知乎高赞的介绍FPGA的文章写到“从延迟上讲,网卡把数据包收到 CPU,CPU 再发给网卡,即使使用 DPDK 这样高性能的数据包处理框架,延迟也有 4~5 微秒。更严重的问题是,通用 CPU 的延迟不够稳定。例如当负载较高时,转发延迟可能升到几十微秒甚至更高”,刚好我前几天做过类似的性能测试,发现一个tcp或udp的echo server可以把网卡到网卡的延迟稳定在1微秒以内,不会比FPGA方案慢很多吧?

因此,我觉得有必要分享下自己的见解。总的来说,我打算分两篇文章讨论相关低延迟技术:

1)系统调优(本文):一些低延迟相关的Linux系统设置,和一些原则。

2)网络调优:使用solarflare网卡降低网络IO延迟

这里不打算介绍用户空间的延迟优化,因为太广泛了,另外我之前的文章也分享一些解决某类问题的低延迟类库。

说到低延迟,关键点不在低,而在稳定,稳定即可预期,可掌控,其对于诸如高频交易领域来说尤为重要。 而说到Linux的低延迟技术,一个不能不提的词是"kernel bypass",也就是绕过内核,为什么呢?因为内核处理不仅慢而且延迟不稳定。可以把操作系统想象成一个庞大的框架,它和其他软件框架并没有什么本质的不同,只不过更加底层更加复杂而已。既然是框架,就要考虑到通用性,需要满足各种对类型用户的需求,有时你只需要20%的功能,却只能take all。

因此我认为一个延迟要求很高(比如个位数微秒级延迟)的实时任务是不能触碰内核的,(当然在程序的启动初始化和停止阶段没有个要求,That's how linux works)。 这里的避免触碰是一个比bypass更高的要求:不能以任何方式进入内核,不能直接或间接的执行系统调用(trap),不能出现page fault(exception),不能被中断(interrupt)。trap和exception是主动进入内核的方式,可以在用户程序中避免,这里不深入讨论(比如在程序初始化阶段分配好所有需要的内存并keep的物理内存中;让其他非实时线程写日志文件等)。本文的关键点在于避免关键线程被中断,这是个比较难达到的要求,但是gain却不小,因为它是延迟稳定的关键点。即使中断发生时线程是空闲的,但重新回到用户态后cpu缓存被污染了,下一次处理请求的延迟也会变得不稳定。

不幸的是Linux并没有提供一个简单的选项让用户完全关闭中断,也不可能这么做(That's how linux works),我们只能想法设法避免让关键任务收到中断。我们知道,中断是cpu core收到的,我们可以让关键线程绑定在某个core上,然后避免各种中断源(IRQ)向这个core发送中断。绑定可以通过taskset或 sched_setaffinity实现,这里不赘述。 避免IRQ向某个core发中断可以通过改写/proc/irq/*/smp_affinity来实现。例如整个系统有一块cpu共8个核,我们想对core 4~7屏蔽中断,只需把屏蔽中断的core(0 ~ 3)的mask "f"写入smp_affinity文件。这个操作对硬件中断(比如硬盘和网卡)都是有效的,但对软中断无效(比如local timer interrupt和work queue),对于work queue的屏蔽可以通过改写/sys/devices/virtual/workqueue/*/cpumask来实现,本例中还是写入"f"。

那么剩下的主要就是local timer interrupt(LOC in /proc/interrupts)了。Linux的scheduler time slice是通过LOC实现的,如果我们让线程独占一个core,就不需要scheduler在这个core上切换线程了,这是可以做到的:通过isolcpus系统启动选项隔离一些核,让它们只能被绑定的线程使用,同时,为了减少独占线程收到的LOC频率,我们还需要使用"adaptive-ticks"模式,这可以通过nohz_fullrcu_nocbs启动选项实现。本例中需要在系统启动选项加入isolcpus=4,5,6,7 nohz_full=4,5,6,7 rcu_nocbs=4,5,6,7 来使得4~7核变成adaptive-ticks。adaptive-ticks的效果是:如果core上的running task只有一个时,系统向其发送LOC的频率会降低成每秒一次,内核文档解释了不能完全屏蔽LOC的原因:"Some process-handling operations still require the occasional scheduling-clock tick. These operations include calculating CPU load, maintaining sched average, computing CFS entity vruntime, computing avenrun, and carrying out load balancing. They are currently accommodated by scheduling-clock tick every second or so. On-going work will eliminate the need even for these infrequent scheduling-clock ticks."。

至此,通过修改系统设置,我们能够把中断频率降低成每秒一次,这已经不错了。如果想做的更完美些,让关键线程长时间(比如几个小时)不收到任何中断,只能修改内核延长中断的发送周期。不同kernel版本相关代码有所差异,这里就不深入讨论。不过大家可能会顾虑:这样改变系统运行方式会不会导致什么问题呢?我的经验是,这有可能会影响某些功能的正常运转(如内核文档提到的那些),但我尚未发现程序和系统发生任何异常,说明这项内核修改至少不会影响我需要的功能,我会继续使用。

两个原则:

1)如果一件事情可以被delay一段时间,那它往往能够被delay的更久,因为它没那么重要。

2)不要为不使用的东西付费,对于性能优化来说尤为如此。

如何检测中断屏蔽的效果呢?可以watch/proc/interrupts文件的变化 。更好的方法是用简单的测试程序来验证延迟的稳定性:

#include <iostream>

uint64_t now() {
   return __builtin_ia32_rdtsc();
}

int main() {
  uint64_t last = now();
  while (true) {
    uint64_t cur = now();
    uint64_t diff = cur - last;
    if (diff > 300) {
      std::cout << "latency: " << diff << " cycles" << std::endl;
      cur = now();
    }
    last = cur;
  }
  return 0;
}

通过taskset绑定一个核运行程序,每进入一次内核会打印一条信息。

最后,除了进入内核以外,影响延迟稳定性的因素还有cache misstlb miss

对于减少cache miss,一方面需要优化程序,minimize memory footprint,或者说减少一个操作访问cache line的个数,一个缓存友好例子是一种能高速查找的自适应哈希表文章中的哈希表的实现方式。另一方面,可以通过分(lang)配(fei)硬件资源让关键线程占有更多的缓存,比如系统有两块CPU,每块8核,我们可以把第二块CPU的所有核都隔离掉,然后把关键线程绑定到其中的部分核上,可能系统只有一两个关键线程,但它们却能拥有整块CPU的L3 cache。

对于减少tlb miss,可以使用huge pages。

使用solarflare网卡降低网络IO延迟 - 知乎

这篇文章承接上篇Linux低延迟服务器系统调优,主要谈谈Linux网络IO的低延迟方案。由于本人经验所限,只使用过solarflare的软硬件方案,没用过其他的kernel bypass框架(如DPDK)或网卡,所以本文只局限于solarflare相关的使用经验。

首先声明下,本人和solarflare公司没有利益关系,本文尽可能客观的分享个人的使用感受。另外我也不是solarflare产品的专家,如有不准确之处还望指正。

solarflare的拳头产品是它的高性能网卡。我对solareflare网卡的第一感觉是“贵”,和一块高端服务器CPU的价格差不多了。因此如果只把它当成一块支持10G/25G网络的普通网卡来用是暴殄天物了,这里说的当成普通网卡指的是不使用它的软件方案,而是使用Linux内核实现网络编程,这样的话它并不会比普通网卡快多少。如上篇文章所言“一个对延迟要求很高(比如个位数微秒级延迟)的实时任务是不能触碰内核的”,solarflare网卡提供了配套的kernel bypass软件解决方案,还不止一个:Onload, ef_vi和Tcpdirect(关于这三个stack的用法这里就不细说了,官方文档说的很详细)。首先我们关心的是它们的性能怎么样?下图是官方文档中的一个测试结果:

Latency test results

测试使用了两台直接连接的软硬件配置相同的服务器进行pingpong测试,这样RTT的一半可以认为是一台机器从收到发的网卡到网卡的延迟。

从测试结果看来这个数字还挺吸引人的:10G网络可以低至800多ns。不过我认为这个测试方法有些脱离实际应用:首先测试中每次发送的数据都是完全一样的(payload全0),在ef_vi udp的测试代码中甚至出现了这种优化:初始化阶段就准备好了要发送的frame数据(包括ethernet头,ip头,udp头和payload),只要程序一收到数据就发送这个相同的frame出去,有点作弊的成分。另外,pingpong测试是不间断的收发,一秒钟会处理几十万个包,实际场景一般不会出现这么高频的情况。

因此,我决定实现一个更贴合实际的测试:echo测试,echo client和echo server程序分别在两台服务器上,echo client每次发送不同的数据,echo server收到后原样转发给client,这里echo server是under test的机器。echo server的收和发使用的是不同的协议:收udp multicast(模仿接收行情),发送tcp数据(模仿发送订单),因此echo server会先tcp连接到echo client。echo client控制发送频率:每秒1000个包,5秒结束。

通过测试不同的收发stack组合,以及各种配置选项,我所能达到的最佳延迟是960ns +/- 90ns(这里除去了第一个echo的延迟,因为这个简单的测试没做cache warmup,第一个延迟会高出5000ns左右)。最佳方案使用了最新的X2系列网卡(支持ctpio,比非ctpio快200ns),ef_vi接收udp,tcpdirect发送tcp,以及上篇文章提到的屏蔽中断的内核。这个测试主要使用16字节payload的小包进行测试,对于更长的包可以用1 byte = 1.5ns来近似(亲测)。下面简单谈谈我对3个stack的使用感受。

onload

这是solarflare最经典的kernel bypass stack,属于transparent kernel bypass,因为它提供了兼容socket网络编程的接口,用户不需要修改自己的代码,只需preload libonload.so即可使用(类似使用tcmalloc)。由于其特别的易用性,十分适合新人入坑(先要买块网卡),是运动手表界的iwatch。

关于性能,在我的测试中onload比使用kernel的传统方法快了6000多ns,比ef_vi/tcpdirect慢300多ns,算是不错了。

另外提一下onload的配置,onload提供了非常多的配置选项,我在测试中测试了大部分相关的配置,最后发现对绝大多数配置来说使用默认值就好了,例外是设置EF_TCP_FASTSTART_INIT=0 EF_TCP_FASTSTART_IDLE=0 会稍微降低些延迟,这也和推荐的latency profile一致。

ef_vi

ef_vi是一个level 2的底层API,可以收发原始的ethernet frame,但没有提供上层协议的支持(不过能支持基于IP,proto,port的接收端filter)。除此之外ef_vi最大的特点是zero-copy:它要求用户预先分配一些recv buffer提供给ef_vi使用,网卡收到包后会直接写入这些buffer,用户通过eventq poll接口获得已填充数据的buffer id即可开始处理接收数据,处理完后再把buffer交还ef_vi循环使用。相比而言,onload无法做到这样的zero-copy,因为socket API只在用户开始接收数据时才提供buffer。

ef_vi API使用起来比较复杂,且缺乏上层协议支持,但能提供最好的性能,是专业用户的选择。我建议只使用ef_vi做udp的接收端,因为经filter后用户对包头的解析工作不多,这还有一个好处是,可以在用户程序中抓包(比如用于记录行情,后面会提到通用的本地抓包方法并不合适):通过一个ring buffer和一个写pcap文件的非关键线程,可以做到处理网络数据和抓包同时进行,而且抓包对处理数据的关键线程几乎零影响,这充分利用了ef_vi底层和zero-copy的特性。

tcpdirect

tcpdirect是基于ef_vi并实现了上层协议的stack,提供了类似socket的API:zocket,能让用户读写tcp/udp的payload数据,同时也继承了ef_vi zero-copy的特性。另外,tcpdirct要求使用huge pages。

我的建议是,对于udp的发送端和整个tcp使用tcpdirect。

最后,谈一个比较重要的话题:如何测量网卡到网卡的延迟?在这个echo测试中,由于echo server和echo client是不对等的的测试者(从收发的协议上),而且由于条件所限,两台机器的硬件配置有一定差异,也没有通过网线直连(中间经过了交换机),所以光看RTT不可靠,只能使用通用的网络延迟测试方法:

1)让交换机把echo server的收发链路数据镜像到另一个抓包设备。这种方法比较依赖交换机性能,精确度不高,比如抓包结果会出现因果乱序的现象。

2)通过tap或分光设备把echo server的网口数据分发到另一个抓包设备。由于条件所限,暂时无法使用...

3)在echo server本地抓包。这个比较依赖抓包工具,我进行过尝试,最后发现目前已有的工具都有其局限性,后面会详细讨论。

4)在程序中使用onload/ef_vi/tcpdirect提供的API获得包收发的硬件时间戳(网卡时间戳),这是我主要使用的方法,同时也参考了从echo client获得的RTT,用于double check前者的结果,另外可以测量各种抓包工具或者获取硬件时间戳造成的额外延迟。

本人经验有限,如果有更好的方法欢迎在评论区探讨。最后谈谈我使用过的本地记录网络包时间戳的方法:

tcpdump

这是大家用过的工具,通过kernel抓包,但对kernel bypass的方案无效,而且记录的是软件时间(系统时间戳),不等同网卡时间。tcpdump带来的额外延迟约为2000ns。

onload_tcpdump

顾名思义,可以对使用onload方案的网络数据进行抓包,但对其他无效,也无法获得硬件时间戳。onload_tcpdump带来的额外延迟约为1500ns。

solar_capture

solarflare自己软件抓包工具,需要额外购买license,不便宜,和网卡本身的价格差不多了,而且license绑定网卡。官方宣传solar_capture性能很高,可以记录收发包的网卡时间戳。我们曾经寄予厚望买了一个license试用,但发现局限性很大,基本个玩具。

首先,solar_capture不支持solarflare自己的最先进的X2系列网卡,只支持较老的7000/8000系列。关于这个问题我前几天有机会问了solarflare的一个system architect,他说更新solar_capture是一个较低优先级的事情...看来这工具本身在公司内部就是个边缘产品(官网上找solarCapture看到的都是硬件设备,这个软件工具的信息很难找到)。

另外,solar_capture不支持"ultra-low-latency"的网卡模式。这又是个硬伤,因为"ultra-low-latency"比其他模式快100ns左右(亲测),是我们默认会使用的模式。

最后,solar_capture对于一个网口的收和发的数据是通过两个任务来记录的,pcap文件中可能出现因果乱序现象(但时间戳是准确的),需要分析结果的用户自己处理。

solar_capture带来的额外延迟约为50ns。

通过ef_vi/tcpdirect API获取网卡时间戳

现在ef_vi和tcpdirect提供了获取接收和发送时间戳的API(获取发送时间戳是OpenOnload 201811新增加的功能),这样就可以在用户程序中直接测量网卡到网卡的延时了。获取两个硬件时间戳带来的额外延迟约为50ns,看上去和获取系统时间戳的开销差不多(相关文章:Linux获取纳秒时间戳的正确方式)。

sfptpd

这其实是solarflare提供的一个同步网卡时间源的工具,可以和网络上的其他设备同步,不过我主要用来和本地系统时间同步,这样可以测量网卡到用户程序,和用户程序到网卡的分段延迟。使用sfptpd的一个不方便的地方是这个服务必须一直运行才能保证同步的比较准确。

原则:所有的时间测量方式都会造成额外延迟,尽量避免在生产环境中过多使用。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值