Victor's Code Journey
Victor's Code Journey

Java NIO: Buffer

Java 传统 IO 是面向流的,流的处理是单向「只能从输入流中读取数据,或是向输出流中写入数据」且阻塞的。通常都是从输入流中边读取数据边处理数据,这样 IO 处理效率就会很低,基于上述原因,JDK1.4 引入了 NIO,而 NIO 是面向 Buffer 的,在处理 IO 操作的时候,会一次性将 Channel 中的数据读取到 Buffer 中然后在做后续处理,向 Channel 中写入数据也是一样,也是需要一个 Buffer 做中转,然后将 Buffer 中的数据批量写入 Channel 中。通过增加Buffer支持了数据的灵活处理。

除此之外,Nio Buffer 还提供了堆外的直接内存和内存映射相关的访问方式,来避免内存之间的来回拷贝,所以即使在传统 IO 中用到了 BufferedInputStream 也还是没办法和 Nio Buffer 相匹敌。本文将结合源码介绍 ByteBuffer^[本文JDK源码基于Java8]。

响应式宣言

在不同领域中深耕的组织都在不约而同地尝试发现相似的软件构建模式。 希望这些系统会更健壮、更具回弹性 、更灵活,也能更好地满足现代化的需求。

近年来,应用程序的需求已经发生了戏剧性的更改,模式变化也随之而来。仅在几年前, 一个大型应用程序通常拥有数十台服务器、 秒级的响应时间、 数小时的维护时间以及GB级的数据。 而今,应用程序被部署到了形态各异的载体上, 从移动设备到运行着数以千计的多核心处理器的云端集群。 用户期望着毫秒级的响应时间,以及服务100%正常运行(随时可用)。 而数据则以PB计量。 昨日的软件架构已经根本无法满足今天的需求。

我们相信大家需要一套贯通整个系统的架构设计方案, 而设计中必需要关注的各个角度也已被理清, 我们需要系统具备以下特质:即时响应性(Responsive)、回弹性(Resilient)、弹性(Elastic)以及消息驱动(Message Driven)。 我们称这样的系统为反应式系统(Reactive System)。

版本 2.0,2014 年 9 月 16 日发布

网络 IO 模型

在《UNIX网络编程》中介绍了5种I/O模型:阻塞I/O、非阻塞I/O、I/O复用、SIGIO、异步I/O; Unix的I/O模型,一个输入操作通常包括两个不同的阶段:

  • 等待数据准备好;
  • 从内核向进程复制数据。

对于一个套接字的输入操作,第一步通常涉及等待数据从网络到达,当所等待分组到达时,被复制到内核的某个缓冲区;第二步把数据从内核缓冲区复制到应用进程缓冲区。

网络应用需要处理的无非就是两大类问题,网络IO,数据计算。相对于后者,网络IO的延迟,给应用带来的性能瓶颈大于后者。网络IO的模型大致有如下几种:

  • 阻塞IO(bloking IO)
  • 非阻塞IO(non-blocking IO)
  • 多路复用IO(multiplexing IO)
  • 信号驱动式IO(signal-driven IO)
  • 异步IO(asynchronous IO)

RAID技术

RAID ( Redundant Array of Independent Disks )即独立磁盘冗余阵列,通常简称为磁盘阵列。简单地说, RAID 是由多个独立的高性能磁盘驱动器组成的磁盘子系统,从而提供比单个磁盘更高的存储性能和数据冗余的技术。

操作系统-IO

先介绍下系统的IO原型架构。CPU 通过内存总线(memory bus)连接到系统内存。显卡或者其他高性能 I/O 设备通过常规的I/O 总线(I/O bus)连接到系统,在许多现代系统中会是 PCI 或它的衍生形式。最后,更下面是外围总线(peripheral bus),比如 SCSI、SATA 或者 USB。它们将最慢的设备连接到系统,包括磁盘、鼠标及其他类似设备。

IO原型架构

考虑物理布局及造价成本。越快的I/O 设备的总线越短,因此高性能的内存总线没有足够的空间连接太多设备。另外,在工程上高性能总线的造价非常高。所以,系统的设计采用了这种分层的方式,这样可以让要求高性能的设 备(比如显卡)离 CPU 更近一些,低性能的设备离 CPU 远一些。将磁盘和其他低速设备连到外围总线的好处很多,其中较为突出的好处就是你可以在外围总线上连接大量的设备。

web安全之XSS漏洞

Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。为了和 CSS 区分,这里把攻击的第一个字母改成了 X,于是叫做 XSS。XSS 的本质是:恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。

而由于直接在用户的终端执行,恶意代码能够直接获取用户的信息,或者利用这些信息冒充用户向网站发起攻击者定义的请求。在部分情况下,由于输入的限制,注入的恶意脚本比较短。但可以通过引入外部的脚本,并由浏览器执行,来完成比较复杂的攻击策略。

Linux 环境编译openjdk

  • Linux 环境: ubuntu 20.04
  • jdk 版本: openjdk-8

openjdk的源码可以从github中获取:

git clone https://github.com/openjdk/jdk.git
git checkout jdk8-b120
git checkout -b tag-jdk8-b120

国内码云上有同步的镜像仓库:

Ubuntu切换GCC版本

首先在ubuntu上安装多版本的GCC:

$ sudo apt install build-essential
$ sudo apt -y install gcc-7 g++-7 gcc-8 g++-8 gcc-9 g++-9

然后使用update-alternatives注册不同版本的GCC:

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 7
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-7 7
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 8
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 8
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 9
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 9

选择想使用的GCC:

$ sudo update-alternatives --config gcc
There are 3 choices for the alternative gcc (providing /usr/bin/gcc).

  Selection    Path            Priority   Status
------------------------------------------------------------
  0            /usr/bin/gcc-9   9         auto mode
  1            /usr/bin/gcc-7   7         manual mode
* 2            /usr/bin/gcc-8   8         manual mode
  3            /usr/bin/gcc-9   9         manual mode
Press  to keep the current choice[*], or type selection number: 

Kerberos协议

什么是Kerberos?

Kerberos是一种网络身份验证协议,提供了网络上的身份验证和强大的加密工具,旨在通过使用密钥加密为客户端/服务器应用程序提供强身份验证。

互联网是一个不安全的地方。Internet中使用的许多协​​议都不提供任何安全性。一些站点尝试使用防火墙来解决其网络安全问题。不幸的是,防火墙假设“坏人”在外面,这通常是一个非常糟糕的假设。大多数真正破坏性的计算机犯罪事件都是由内部人员实施的。防火墙还有一个很大的缺点,那就是它限制了用户使用Internet的方式。(毕竟,防火墙一个极端的例子,就是没有比没有连接到网络并关闭电源的计算机更安全的说法了。)

Kerberos是由MIT创建的,用于解决这些网络安全问题。Kerberos协议使用强大的加密技术,以便客户端可以通过不安全的网络连接向服务器证明其身份(反之亦然)。客户端和服务器使用Kerberos证明其身份后,他们还可以加密所有通信,以确保在开展业务时的私密性和数据完整性。

缓存的一些思考

缓存常被用于处理高并发,高性能问题,在现今的系统中被广泛使用。缓存模式,简单来说就是利用时间局限性原理,通过空间换时间来达到加速数据获取的目的。

缓存的读写性能很高,预热快,在数据访问存在性能瓶颈或遇到突发流量,系统读写压力大增时,可以快速部署上线,同时在流量稳定后,也可以随时下线,从而使系统的可扩展性大大增强。

但是,在系统中引入缓存后,会增加系统的复杂度。

分布式锁简介

在分布式场景中分布式锁是一种很常见的需求。实现一个分布式锁要注意以下几点:

  • 安全: 独享(相互排斥)。在任意一个时刻,只有一个客户端持有锁。
  • 锁失效保护: 无死锁。即便持有锁的客户端崩溃(crashed)或者网络被分裂(gets partitioned),锁仍然可以被获取。
  • 集群容错。 只要大部分节点都活着,客户端就可以获取和释放锁。
  • 原子性:获取释放锁最好是原子操作,获取释放锁的性能要好。
  • 可重入(optional): 同一个线程在没有释放锁之前,如果想再次操作,可以直接获得锁。
  • 阻塞/非阻塞(optional):若没有获取到锁,返回获取失败。

契约式编程

契约式设计(Design by Contract),也被称为契约式编程,契约优先式开发或代码合约等,是一种设计软件的方法。这种方法要求软件设计者为软件组件定义正式的,精确的并且可验证的接口,这样,为传统的抽象数据类型又增加了先验条件、后验条件和不变式。这种方法的名字里用到的“契约”或者说“契约”是一种比喻,因为它和商业契约的情况有点类似。

DbC的核心思想是对软件系统中的元素之间相互合作以及"责任"与"权利"的比喻。这种比喻从商业活动中"客户"与"供应商"达成"契约"而得来。例如:

  • 供应商必须提供某种产品(责任),并且他有权期望客户已经付款(权利)。
  • 客户必须付款(责任),并且有权得到产品(权利)。
  • 契约双方必须履行那些对所有契约都有效的责任,如法律和规定等。

同样的,如果在面向对象程序设计中一个类的函数提供了某种功能,那么它要:

  • 期望所有调用它的客户模块都保证一定的进入条件:这就是函数的先验条件—客户的义务和供应商的权利,这样它就不用去处理不满足先验条件的情况。
  • 保证退出时给出特定的属性:这就是函数的后验条件—供应商的义务,显然也是客户的权利。
  • 在进入时假定,并在退出时保持一些特定的属性:不变条件。

Java版本管理

使用Java时会接触到不同的版本。一般情况下是配置JAVA_HOME,指定不同的Java版本,但是这需要人为手动的输入。如果又要选择其他版本,就需要对JAVA_HOME重新进行设置。