3

strongant的个人博客

 3 years ago
source link: https://baiwenhui.com/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
strongant的个人博客
  1. 什么时间
  2. 什么地点
  3. 做什么事

一个简单的任务

早期的 cron

V7,1979

  1. 在Version 7 Unix里是一个系统服务
  2. 只用 root 运行任务
  3. 算法简单直接

更多详情请参考: https://en.wikipedia.org/wiki/Cron

早期的 cron 运行逻辑

  1. 读 /usr/lib/crontab 文件
  2. 如果有命令要在当前时间执行,就用 root 用户去执行命令
  3. Sleep 1 minute
  4. 重新从步骤 1 开始

支持多用户的 cron

Unix System V,1983

  1. 启动的时候读取所有用户下的 .crontab 文件
  2. 计算出每个 crontab 文件里需要执行的命令的下一次执行时间
  3. 把这些命令按下一次执行时间排序后放入队列里
  4. 进入主循环
    ① 计算队列里第一个任务的执行时间与当前的时间差
    ② Sleep 直到第一个任务执行时间
    ③ 后台执行任务
    ④ 计算这个任务的下一次执行时间,放回队列,排序

近代的 cron

Linux,1991

  1. Vixie cron(Paul Vixie 1987)
  2. Version 3 Vixie cron(1993)
  3. Version 4.1 ISC Cron(2004)
  4. anacron, dcron, fcron

cron 的局限性

  1. 功能比较简单
  2. 多机器的情况下任务维护成本较高

分布式任务系统

分布式系统的特点

  1. 对等性(没有控制系统的主机, 也没有被控制的从机)
  2. 缺乏全局时钟(缺乏全局时钟序列控制)
  3. 故障总是会发生

分布式 cron

我只是不爱动,不是懒

分布式系统对任务调度的几点要求

1、平台:快速开发、业务复用、自维护和扩展;
2、HA/集群:避免单点故障,发挥集群优势
3、弹性扩缩:适应业务快速发展
4、故障处理:Failover、失败告警
5、阻塞处理:耗时任务阻塞
5、⾼性能:调度和任务解耦,全异步化
6、自运维:自助维护、实时监控、快速了解任务进展

我眼里的“西施”

  1. 可替代 cron
  2. 分布式、高可用
  3. 支持多种任务属性

为什么选择使用 XXL-JOB

项目中有这样的需求:需要使用定时器从 A 和 B 系统中同步不同的表中的数据,并且对数据进行一定的加工处理,最后将最终的数据写入到数据仓库DB,需要实现针对不同的数据源进行配置(这块也就是多数据源的支持),任务配置可以根据任务量的大小使用不同的资源。对数据同步的时效性和性能要求非常高,并且这些定时任务可以通过页面配置的方式做到灵活,可控制。

通过以上的这些需求,我们对目前业界比较流行的任务调度框架进行了技术选型,通过研究发现,目前使用比较广泛的任务调度框架有 XXL-JOB 和 Elastic-JOB ,因此,参考 XXL-JOB 官方对不同任务调度平台的比对,如下图所示:

针对以上对比,我们选择了 XXL-JOB 任务调度框架,因为它相比于其他任务调度框架来说,具有更好的用户体验、有良好的运维支持、任务依赖、任务路由原生支持,通过调研发现,框架自带的任务路由策略已经满足我们的业务场景,并且支持分片任务,当任务延迟较高时,可以利用分片的方案加速任务的处理。

XXL-JOB 介绍

XXL-JOB是⼀个轻量级分布式任务调度框架。拥有“HA、弹性扩缩、故障处理、阻塞处
理、⾼性能、自运维”等特点。
其核⼼部分包括:
1、调度模块(调度中⼼):负责管理任务信息,触发任务执⾏,自身不承担业务逻辑;
同时提供“日志、报表、告警、GLUE、注册中⼼”等功能;
2、执⾏模块(执⾏器): 负责接收调度中⼼请求,进⾏任务逻辑执⾏、终⽌、日志加载
等操作;专注于任务执⾏相关操作;

XXL-JOB 发展

• 2015年中,着⼿设计XXL-JOB,提交第⼀个Commit
• 2015-11月,发布首个Release版本,同期当选为开源中国月度热门项目
• 2016-01月,我司展开定制和接⼊,⾄今调度近百万次
• 2017-05-03,应邀参加“第62期开源中国源创会”现场分享XXL-JOB
• 2017-10月,当选为开源中国首批GVP项目
• ……
• ⾄今,XXL-JOB登记接⼊公司50+(点评、移动、平安、海尔、优信),
社区群8个,群成员约3000 ⼈

XXL-JOB 架构图

XXL-JOB 特性全览

1、简单
2、动态
3、调度中心HA(中心式)
4、执行器HA(分布式)
5、任务Failover
6、一致性
7、自定义任务参数
8、调度线程池
9、弹性扩容缩容
10、邮件报警
11、状态监控
12、Rolling执行日志
13、GLUE:提供Web IDE
14、数据加密
15、任务依赖
16、推送maven中央仓库
17、任务注册
18、路由策略
19、运行报表
20、脚本任务
21、阻塞处理策略
22、失败处理策略
23、分片广播任务
24、动态分片
25、事件触发

HA/集群

执行器路由策略

故障转移 & 忙碌转移

分片任务 & 动态分片

阻塞策略 & 失败处理策略

###触发规则

  • Cron 表达式
    如:每日凌晨 2 点,进行“PV 数据计算”的任务
  • 任务依赖触发
    如:父任务“PV 数据计算”执行结束后,触发多业务线的“PV 报表生成”的多个子任务
  • 事件触发/API 服务
    如:类 MQ 场景,短信验证码、订单创建等。

1、BEAN 模式:JobHandler、Spring Bean
2、GLUE 模式(Java ):Web IDE、在线开发、版本回溯、Java 语言、动态编译(IOC)、实时生效、Rolling

Log
3、GLUE 模式(Shell):Web IDE、在线开发、版本回溯、实时生效、Rolling Log
4、GLUE 模式(Python)
5、GLUE 模式(NodeJS)

(其中“GLUE 模式(NodeJS)”来源于 “icyblazek@github”同学提交的 PR,XXL-JOB 理论上支持任何类型脚本

语言的任务模式扩展)

GLUE 模式

执行日志 & Rolling Log

XXL-JOB 搭建

1、安装 Mysql,初始化 XXL-JOB 底层库表

• 2、编译部署“调度中心”

• 3、编译部署“执行器”(参考多个 Sample 项目)

》》》开发第一个 JobHandler
入门文档 (可参考文档 “快速入门”章节:): http://www.xuxueli.com/xxl-job/

日志处理方案演进

1.1 什么是日志

日志是带时间戳的基于时间序列的机器数据,包括IT系统信息(服务器、网络设备、操作系统、应 用软件)、物联网各种传感器信息。日志反映用户行为,是事实数据,也是系统运维、故障诊断、性能分析的重要来源。对于任何系统,日志都是极其重要的组成部分。

1.2 日志处理的背景

随着大数据时代的来临,系统日志量也呈指数级增加。随着日志格式复杂度的增加、日志规模的扩大以及应用节点的增多,传统的日志量过大分析耗时耗力、效率低下、无法胜任复杂的统计分析等。并且传统的日志分析无法满足实时化分析的业务需求,实时化分析也是当今大数据技术的发展趋势之一。

1.3 日志处理方案演进

业界一般对日志的处理方案演进如下:

  • 日志处理古老版

    日志没有集中式处理,一般情况下手动到服务器上使用 Linux 命令排查日志;日志的唯一作用就是做故障之后的排查;使用数据库存储日志,无法胜任复杂的实时统计分析需求。

  • 日志处理离线版

    一般使用 Hadoop 平台实现日志的离线批量处理,缺点便是实时性太差;

    也有使用 Storm 流处理框架、Spark 内存计算框架处理日志, Hadoop/Storm/Spark 都是编程框架,并不是拿来就可以使用的平台。

  • 日志处理实时版

    一般使用日志实时搜索引擎分析日志,其作为代表的解决方案主要有 Splunk(Splunk是一个分析计算机系统产生的机器数据,并在广泛的场景中提供数据收集、分析、可视化分布式的数据计算平台。)、ELK、EFK(EFK由ElasticSearch、Fluentd和Kiabana三个开源工具组成。Fluentd也是一个实时开源的数据收集器,)。

优点有什么?

  1. 快速,日志从产生到搜索分析出结果只有数秒延时;
  2. 可以处理大量数据,每天可以处理 TB 级日志量;
  3. 很灵活,可以分析任何的实时日志。

其中,ELK 在业界最受欢迎,目前我们也使用的是ELK日志处理方案。ELK 是 Elastic 公司出品的开源实时日志处理与分析的解决方案,ELK 分别代表分布式搜索引擎 Elasticsearch、日志采集与解析工具 Logstash、日志可视化分析工具 Kibana,ELK日志处理方案具有配置灵活、集群可线性扩展、日志实时导入、检索性能高效、可视化分析方便等优点,已经成为业界日志处理方案的不二选择。

ELK 架构解读

2.1 架构选择

完整的日志分析系统主要包括日志采集系统、日志解析系统、日志存储系统和可视化分析系统四部分组成。典型的 ELK 架构图如下所示:

ELK 架构中,由于 Logstash 既作为日志搜集器又作为日志解析器,本身会消耗服务器较多的CPU 和 内存,如果服务器计算资源不够丰富,则会造成服务器性能下降无法正常工作。为了解决Logstash 占用系统资源高的问题,Elastic 公司推出了轻量级的日志采集器 Beats,在数据收集方面取代 Logstash,引入 Beats 后的系统架构如如下:

Beats 是一系列采集器的总称,对于不同的日志源和日志格式可以使用不同的Beats,截止目前为止,Beats 家族包括以下 7 个成员:

  • Filebeat:轻量级的日志采集器,可用于收集文件数据。
  • Metricbeat:5.0版本之前名为Topbeat,收集系统、进程和文件系统级别的 CPU 和内存使用情况等数据。
  • Packetbeat:收集网络流数据,可以实时监控系统应用和服务,可以将延迟时间、错误、响应时间、SLA性能等信息发送到Logstash或Elasticsearch。
  • Winlogbeat:Windows 事件日志
  • Heartbeat:运行时间监控。
  • Auditbeat: 审计日志数据采集。
  • Functionbeat:无需服务器的采集器。

目前对于日志规模较大的场景,还需要引入消息队列,Logstash 支持常用的消息队列有 Kafka、RabbitMQ、Redis等。引入消息队列后的架构如下:

2.2 日志采集模块分析

日志采集是对日志进行分析的基础,我们对于不同的日志类型可以选择不同类型的 beats。在我们的日常使用中,基本大多数情况以文件类型的日志居多,这里我们就以filebeat 采集文件类型的日志进行探索日志采集的原理。

FileBeat 是采用Go 语言由 Elastic 官方开发的轻量级日志采集器,在应用服务器上以代理的形式安装。当这个 FileBeat 进行启动时,它会启动一个 prospector 监控日志路径或者日志文件,每一个日志文件都会有一个对应的 harvester, harvester 按行读取日志内容并转发至后台程序。FileBeat 维护了一个记录文件读取信息的注册文件,记录每个 harvester 最后读取位置的偏移量。日志按行读取以后,转发至 logstash 做解析。

2.3 日志解析模块分析

Logstash是分布式数据收集引擎,在日志处理中担任搬运工的角色,它支持动态的从各种数据源搜集数据,并对数据进行过滤、分析、统一格式等操作,然后输出到用户指定的位置。事件流通过Logstash中的Input、Filter和Output插件处理和转换,Input用于配置日志的输入源,Filter用来解析日志,Output指定日志的输出去向。

Grok是Logstash的一个正则解析插件,它能对日志流进行解析,内置了120多种的正则表达式库,对日志解析时需要针对日志格式定义相应的正则表达式。结合Grok提供的便利,总结出按行读取文件格式日志的解析步骤:

(1)首先对日志进行分析,明确每一个字段的含义,确定日志的切分规则,也就是一条日志切分成哪几个字段,这些字段是以后做日志分析的关键,对原始日志的解读和分析是非常关键的一步。

(2)根据步骤(1)的切分原则确定提取每一个字段的正则表达式,如果Grok中的正则库满足需求直接使用即可,否则采用自定义模式,两者可以组合使用。

(3)在Grok Debugger调试工具中调试、验证解析日志的正则表达式是否正确。

举一个例子,下面是一条半结构化日志:

2017-12-16 00:00:01,570 133382978 [  HandleConsumer.java:445:INFO ]  车辆号牌为空

对应的Grok表达式如下:

%{TIMESTAMP_ISO8601:time}\s*%{NUMBER:bytes}\s*\[\s*%{JAVAFILE:class}\:%{NUMBER:lineNumber}\s*\:%{LOGLEVEL:level}\s*\]\s*(?<info>([\s\S]*))

格式化后的日志如下:

{
"time": "2017-12-16 00:00:01,570",
"bytes":"133382978",
"class": "HandleConsumer.java",
"lineNumber": "445",
"level": "INFO",
"info":"车辆号牌为空。"
}

Grok Debugger调试正则表达式如图4所示:

enter image description here

2.4 日志存储模块分析

Elasticsearch是存储日志的中央系统,实际项目中,根据需求搭建Elasticsearch集群即可。

2.5 日志可视化模块分析

Kibana是一个开源日志分析及可视化平台,使用它可以对存储在Elasticsearch索引中的数据进行高效的搜索、可视化、分析等各种操作。Kibana的愿景是让海量数据更容易理解,通过浏览器的用户界面可以创建各种高级图表进行数据分析和展示,它的仪表盘功能可以汇总多个单操作图表,并且可以实时显示查询动态。

临下班前,楼主接到了一个需求,由于基础镜像标准发生变更,需要按照最新的Docker 镜像标准构建自己应用的自定义镜像。目前的标准是这样的:基础架构组只提供所有项目必须接入的3个公共镜像,这3个公共基础镜像包含了:JDK8、Skywalking、Arthas。对于各自业务组的应用如果还需要加入其它镜像,则由各个业务组自己基于基础架构组提供的公共镜像之上,再添加自定义的镜像,结构图如下:

image.png

编写Dockerfile

基于最新的规范来看,我们需要编写一个Dockerfile,然后引用基础架构组提供的基础镜像,再加入应用需要的其他镜像。因此最终的 Dockerfile 文件如下:

FROM 基础镜像地址
RUN apk add 需要添加的自定义镜像
...

在Centos7下安装Docker环境

卸载旧版本

较旧的 Docker 版本称为 docker 或 docker-engine 。如果已安装这些程序,请卸载它们以及相关的依赖项。

$ sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

安装 Docker Engine-Community

使用 Docker 仓库进行安装

在新主机上首次安装 Docker Engine-Community 之前,需要设置 Docker 仓库。之后,您可以从仓库安装和更新 Docker。

设置仓库

安装所需的软件包。yum-utils 提供了 yum-config-manager ,并且 device mapper 存储驱动程序需要 device-mapper-persistent-data 和 lvm2。

$ sudo yum install -y yum-utils \
device-mapper-persistent-data \
lvm2

使用以下命令来设置稳定的仓库。

$ sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
安装 Docker Engine-Community

安装最新版本的 Docker Engine-Community 和 containerd,或者转到下一步安装特定版本:

$ sudo yum install docker-ce docker-ce-cli containerd.io

如果提示您接受 GPG 密钥,请选是。

有多个 Docker 仓库吗?

如果启用了多个 Docker 仓库,则在未在 yum install 或 yum update 命令中指定版本的情况下,进行的安装或更新将始终安装最高版本,这可能不适合您的稳定性需求。

Docker 安装完默认未启动。并且已经创建好 docker 用户组,但该用户组下没有用户。

要安装特定版本的 Docker Engine-Community,请在存储库中列出可用版本,然后选择并安装:

1、列出并排序您存储库中可用的版本。此示例按版本号(从高到低)对结果进行排序。

$ yum list docker-ce –showduplicates | sort -r

3:18.09.1-3.el7 docker-ce-stable
docker-ce.x86_64  3:18.09.0-3.el7           docker-ce-stable
docker-ce.x86_64 18.06.1.ce-3.el7 docker-ce-stable
docker-ce.x86_64 18.06.0.ce-3.el7 docker-ce-stable

2、通过其完整的软件包名称安装特定版本,该软件包名称是软件包名称(docker-ce)加上版本字符串(第二列),从第一个冒号(:)一直到第一个连字符,并用连字符(-)分隔。例如:docker-ce-18.09.1。

$ sudo yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io

启动 Docker。

$ sudo systemctl start docker

通过运行 hello-world 映像来验证是否正确安装了 Docker Engine-Community 。

$ sudo docker run hello-world

以上安装过程参考自: https://www.runoob.com/docker/centos-docker-install.html

开始构建应用自定义镜像

根据 Dockerfile 文件进行自定义镜像的构建

在Dockerfile 文件所在的目录下执行如下命令进行自定义镜像的构建:

sudo docker build -f Dockerfile -t 你的自定义镜像名称 .

推送到企业私有的镜像harbor之前进行登录

docker login 企业私有的harbor地址
输入用户名
输入密码
完成登录

将构建完成的自定义镜像推送到企业私有的harbor

sudo docker push 你的自定义镜像名称

通过 1.编写自定义构建镜像的Dockerfile 2.安装Docker环境 3.构建自定义镜像 4.上传自定义镜像到harbor 以上4个步骤,我们便完成了应用自定义镜像的构建,后续我们自己的应用中直接使用自定义镜像即可,这样做的好处就是基于基础的镜像,我们可以随意组合,构建出满足自己应用的镜像,更灵活、镜像分层管理、可扩展。

什么是MinIO ?

根据官方定义:

  1. MinIO 是在 Apache License v2.0 下发布的对象存储服务器。 它与 Amazon S3 云存储服务兼容。 它最适合存储非结构化数据,如照片,视频,日志文件,备份和容器/ VM 映像。 对象的大小可以从几 KB 到最大 5TB。
  1. MinIO 服务器足够轻,可以与应用程序堆栈捆绑在一起,类似于 NodeJS,Redis 和 MySQL。
  2. 一种高性能的分布式对象存储服务器,用于大型数据基础设施。它是机器学习和其他大数
    据工作负载下 Hadoop HDFS 的理想 s3 兼容替代品。

为什么需要MinIO?

  1. Minio 有良好的存储机制
  2. Minio 有很好纠删码的算法与擦除编码算法
  3. 拥有RS code 编码数据恢复原理
  4. 公司做强做大时,数据的拥有重要性,对数据治理与大数据分析做准备。
  5. 搭建自己的一套文件系统服务,对文件数据进行安全保护。
  6. 拥有自己的平台,不限于其他方限制。

MinIO 和其他OSS存储解决方案各有什么优缺点?

这里主要针对Ceph、Minio、FastDFS 热门的存储解决方案进行比较。

优点

  • 红帽继子,ceph创始人已经加入红帽
  • 国内有所谓的ceph中国社区,私人机构,不活跃,文档有滞后,而且没有更新的迹象。
  • 从git上提交者来看,中国有几家公司的程序员在提交代码,星辰天合,easystack, 腾讯、阿里基于ceph在做云存储,但是在开源社区中不活跃,阿里一位叫liupan的有参与
  • 支持数千节点
  • 支持动态增加节点,自动平衡数据分布。(TODO,需要多长时间,add node时是否可以不间断运行)
  • 可配置性强,可针对不同场景进行调优

缺点
学习成本高,安装运维复杂。

Minio

优点

  • 学习成本低,安装运维简单,开箱即用
  • 目前minio论坛推广给力,有问必答
  • 有java客户端、js客户端
  • 数据保护:分布式Minio采用 纠删码来防范多个节点宕机和位衰减bit rot。分布式Minio至少需要4个硬盘,使用分布式Minio自动引入了纠删码功能。
  • 一致性:Minio在分布式和单机模式下,所有读写操作都严格遵守read-after-write一致性模型。

缺点

  • 社区不够成熟,业界参考资料较少
  • 不支持动态增加节点,minio创始人的设计理念就是动态增加节点太复杂,后续会采用其它方案来支持扩容。

    FastDFS

    fastdfs是阿里余庆做的一个个人项目,在一些互联网创业公司中有应用,没有官网,不活跃,6个contributors。

如何安装使用MinIO?

基于 Docker 容器使用

  • docker pull minio/minio
    docker run -p 9000:9000 minio/minio server /data
  • docker pull minio/minio:edge
    docker run -p 9000:9000 minio/minio:edge server /data

基于 Mac Homebrew 使用

brew install minio/stable/minio
minio server /data

下载二进制文件安装使用

操作系统 CPU架构 地址 Apple macOS 64-bit Intel https://dl.min.io/server/minio/release/darwin-amd64/minio
chmod 755 minio
./minio server /data

GNU/Linux

下载二进制文件

操作系统 CPU架构 地址 GNU/Linux 64-bit Intel https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
./minio server /data

微软Windows系统

下载二进制文件

操作系统 CPU架构 地址 微软Windows系统 64位 https://dl.min.io/server/minio/release/windows-amd64/minio.exe
minio.exe server D:\Photos

FreeBSD

使用 pkg进行安装。

pkg install minio
sysrc minio_enable=yes
sysrc minio_disks=/home/user/Photos
service minio start

使用源码安装

采用源码安装仅供开发人员和高级用户使用,如果你还没有Golang环境, 请参考 How to install Golang.

go get -u github.com/minio/minio

使用MinIO浏览器进行验证

安装后使用浏览器访问http://127.0.0.1:9000,如果可以访问,则表示minio已经安装成功。

使用MinIO客户端 mc进行验证

mc 提供了一些UNIX常用命令的替代品,像ls, cat, cp, mirror, diff这些。 它支持文件系统和亚马逊S3云存储服务。 更多信息请参考 mc快速入门 - https://docs.min.io/docs/minio-client-quickstart-guide

已经存在的数据

当在单块磁盘上部署MinIO server,MinIO server允许客户端访问数据目录下已经存在的数据。比如,如果MinIO使用minio server /mnt/data启动,那么所有已经在/mnt/data目录下的数据都可以被客户端访问到。

上述描述对所有网关后端同样有效。

Java 14包含比前两个发行版更多的新功能-其中大多数旨在简化编码。

劳尔·加布里埃尔·乌尔玛(Raoul-Gabriel Urma)

2020年2月27日

下载本文的PDF

https://app.compendium.com/api/post_attachments/07a7a6fa-9588-4206-aa38-cf507df7a1ed/view

Java 14计划于3月17日发布。版本14包含的JEP(Java增强建议)比Java 12和13的总和还多。那么,对于每天编写和维护代码的Java开发人员来说,最重要的是什么呢?

https://openjdk.java.net/projects/jdk/14/

在本文中,我研究了以下主要方面:

  • 改进的 switch 表达式,该表达式最初在Java 12和Java 13中作为预览出现,现在已完全成为Java 14的一部分
  • 模式匹配instanceof(一种语言功能)
  • 有用NullPointerException的(JVM功能)

如果您阅读本文并尝试在代码库中使用其中的某些功能,建议您通过向Java团队提供反馈来分享您的经验。这样,您就有机会为Java的发展做出贡献。

switch表达式

在Java 14中,switch表达式变为永久性的。如果您需要回顾一下什么是switch表达式,那么在前 两篇文章中已广泛讨论了它们。

https://blogs.oracle.com/javamagazine/new-switch-expressions-in-java-12

https://blogs.oracle.com/javamagazine/inside-java-13s-switch-expressions-and-reimplemented-socket-api

在早期版本中,switch 表达式是“预览”功能。提醒一下,将指定为“预览”版本以收集反馈,并且根据反馈可能会更改甚至删除这些功能。但预计大多数最终将在Java中永久化。

新的switch表达式的优点包括由于没有中断行为,穷举以及由于表达式和复合形式而易于编写,从而减小了错误的范围。作为一个示例,switch表达式现在可以利用箭头语法,例如在以下示例中:

var log = switch (event) {
case PLAY -> "User has triggered the play button";
case STOP, PAUSE -> "User needs a break";
default -> {
String message = event.toString();
LocalDateTime now = LocalDateTime.now();
yield "Unknown event " + message +
" logged on " + now;
}
};

Java 13引入了文本块作为预览功能。文本块使使用多行字符串文字更加容易。此功能将通过Java 14进行第二轮预览,并进行了一些调整。作为编写带有许多字符串连接和转义序列的代码以提供适当的多行文本格式非常普遍。下面的代码显示了HTML格式的示例:

String html = "<HTML>" +
"\n\t" + "<BODY>" +
"\n\t\t" + "<H1>\"Java 14 is here!\"</H1>" +
"\n\t" + "</BODY>" +
"\n" + "</HTML>";

使用文本块,您可以简化此过程并使用界定文本块开头和结尾的三个引号来编写更优雅的代码:

String html = """
<HTML>
<BODY>
<H1>"Java 14 is here!"</H1>
</BODY>
</HTML>""";

与普通字符串文字相比,文本块还提供了更高的表达能力。您可以在较早的文章中了解有关此内容的更多信息。

https://blogs.oracle.com/javamagazine/text-blocks-come-to-java

Java 14中添加了两个新的转义序列。首先,您可以使用新的\s转义序列表示单个空格。其次,您可以使用反斜线\来禁止在行尾插入新行字符。当您想分隔很长的行以简化文本块内的可读性时,这很有用。

例如,当前处理多行字符串的方法是

String literal = 
"Lorem ipsum dolor sit amet, consectetur adipiscing " +
"elit, sed do eiusmod tempor incididunt ut labore " +
"et dolore magna aliqua.";

使用\文本块中的转义序列,可以将其表示为:

String text = """
Lorem ipsum dolor sit amet, consectetur adipiscing \
elit, sed do eiusmod tempor incididunt ut labore \
et dolore magna aliqua.\
""";

instanceof的模式匹配

Java 14引入了预览功能,该功能有助于消除对在进行条件instanceof检查之前进行显式强制转换的需求。例如,考虑以下代码:

if (obj instanceof Group) {
Group group = (Group) obj;

// use group specific methods
var entries = group.getEntries();
}

可以使用预览功能将其重构为:

if (obj instanceof Group group) {
var entries = group.getEntries();
}

因为条件检查断言obj是type Group,所以为什么要在第一个代码段中用条件块再次说该obj类型Group?这种需求可能会增加错误的范围。

较短的语法将删除典型Java程序中的许多强制转换。(A 研究论文从2011年提出了一个相关的语言特性报告说,所有铸件约24%遵循instanceof的条件语句。)

http://www.cs.williams.edu/FTfJP2011/6-Winther.pdf

JEP 305涵盖了此更改,并从Joshua Bloch 的Effective Java书中指出了一个示例,该示例通过以下相等方法进行说明:

https://openjdk.java.net/jeps/305

@Override public boolean equals(Object o) { 
return (o instanceof CaseInsensitiveString) &&
((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}

通过删除冗余的显式强制转换为,可以将前面的代码简化为以下形式CaseInsensitiveString

@Override public boolean equals(Object o) { 
return (o instanceof CaseInsensitiveString cis) &&
cis.s.equalsIgnoreCase(s);
}

这是一个有趣的预览功能,因为它为更广泛的模式匹配打开了大门。模式匹配的思想是为语言功能提供方便的语法,以便根据某些条件提取对象的成分。instanceof运算符就是这种情况,因为条件是类型检查,并且提取操作正在调用适当的方法或访问特定字段。

换句话说,此预览功能仅仅是个开始,您可以期待一种语言功能,它可以帮助进一步减少冗长性,从而减少错误的可能性。

还有另一种预览语言功能:records。像到目前为止提出的其他想法一样,此功能遵循减少Java冗长并帮助开发人员编写更简洁的代码的趋势。记录集中在某些域类上,这些类仅用于将数据存储在字段中,并且不声明任何自定义行为。

为了直接解决该问题,请使用一个简单的域类,该类BankTransaction通过三个字段对交易进行建模:日期,金额和描述。声明类时,您需要担心多个组件:

  • equals方法
  • toString()
  • hashCode()equals()

此类组件的代码通常由IDE自动生成,并占用大量空间。这是BankTransaction该类的完整生成的实现:

public class BankTransaction {
private final LocalDate date;
private final double amount;
private final String description;


public BankTransaction(final LocalDate date,
final double amount,
final String description) {
this.date = date;
this.amount = amount;
this.description = description;
}

public LocalDate date() {
return date;
}

public double amount() {
return amount;
}

public String description() {
return description;
}

@Override
public String toString() {
return "BankTransaction{" +
"date=" + date +
", amount=" + amount +
", description='" + description + '\'' +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BankTransaction that = (BankTransaction) o;
return Double.compare(that.amount, amount) == 0 &&
date.equals(that.date) &&
description.equals(that.description);
}

@Override
public int hashCode() {
return Objects.hash(date, amount, description);
}
}

Java的14提供了一种方法来去除冗长,使意图明确指出,所有你想要的是,只有拥有的实现数据聚合在一起的一类equalshashCodetoString方法。您可以BankTransaction按以下方式重构:

public record BankTransaction(Date date,
double amount,
String description) {}

有了记录,你“自动”得到的实现equalshashCode以及toString除了构造函数和getter。

要尝试该示例,请记住您需要使用预览标志来编译文件:

javac --enable-preview --release 14 BankTransaction.java

记录的字段是隐式的final。这意味着您无法重新分配它们。但是请注意,这并不意味着整个记录都是不变的。存储在字段中的对象本身可以是可变的。

如果您对有关记录的更详细的文章感兴趣,请参阅Java杂志上 Ben Evans的最新文章

https://blogs.oracle.com/javamagazine/records-come-to-java

敬请关注。从教育的角度来看,唱片还为下一代Java开发人员提出了有趣的问题。例如,如果您指导初级开发人员,那么什么时候应该在课程中引入记录:在引入OOP和类之前还是之后?

有用的NullPointerExceptions

有人说throw NullPointerException应该是Java中新的“ Hello world”,因为您无法逃避它们。除了笑话,它们还导致挫败感,因为当代码在生产环境中运行时,它们经常出现在应用程序日志中,这可能使调试变得困难,因为原始代码不容易获得。例如,考虑以下代码:

var name = user.getLocation().getCity().getName();

在Java 14之前,您可能会收到以下错误:

Exception in thread "main" java.lang.NullPointerException
at NullPointerExample.main(NullPointerExample.java:5)

不幸的是,如果在第5行,则有一个具有多个方法调用的赋值- getLocation()和- getCity()两者都可能返回null。实际上,变量user也可以为null。因此,目前尚不清楚是什么原因引起的NullPointerException

现在,使用Java 14,有一个新的JVM功能,通过它您可以收到更多信息的诊断信息:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Location.getCity()" because the return value of "User.getLocation()" is null
at NullPointerExample.main(NullPointerExample.java:5)

该消息现在具有两个明确的组成部分:

  • 结果: Location.getCity()无法调用。
  • 原因:的返回值为User.getLocation()null。

仅当您使用以下标志运行Java时,增强型诊断才有效:

-XX:+ShowCodeDetailsInExceptionMessages

这是一个例子:

java -XX:+ShowCodeDetailsInExceptionMessages NullPointerExample

在Java的未来版本中,这项功能可能会被默认启用,据报道在这里

https://bugs.openjdk.java.net/browse/JDK-8233014

此增强功能不仅可用于方法调用,而且还可在可能导致的其他地方使用NullPointerException,包括字段访问,数组访问和赋值。

在Java 14中,有新的预览语言功能和更新可帮助开发人员进行日常工作。例如,Java 14引入了instanceof模式匹配,这是减少显式转换的一种方法。而且,Java 14引入了记录,这是一种新的结构,用于简洁地声明仅用于聚合数据的类。此外,NullPointerException消息已通过更好的诊断得到了增强,并且开关表达式现在已成为Java 14的一部分。文本块是一种可帮助您处理多行字符串值的功能,在引入了两个新的转义序列后,将进行另一轮预览。Java操作的一部分技术人员可能会感兴趣的另一项更改是JDK Flight Recorder中的事件流。该选项在本埃文斯的这篇文章

https://openjdk.java.net/jeps/349

https://blogs.oracle.com/javamagazine/java-flight-recorder-and-jfr-event-streaming-in-java-14

如您所见,Java 14带来了很多创新。您绝对应该使用一下,并将有关预览功能的反馈发送给Java团队。

什么是 MyCAT ?

根据 MyCAT 官网 - http://mycat.io/ 的描述可以知道, MyCAT 是如下的一个东东:

  • 一个彻底开源的,面向企业应用开发的大数据库集

  • 支持事务、ACID、可以替代MySQL的加强版数据库

  • 一个可以视为MySQL集群的企业级数据库,用来替代昂贵的Oracle集群

  • 一个融合内存缓存技术、NoSQL技术、HDFS大数据的新型SQL Server

  • 结合传统数据库和新型分布式数据仓库的新一代企业级数据库产品

  • 一个新颖的数据库中间件产品

总结一下就是: MyCAT 是一款数据库中间件,类似于Tomcat 容器或者相关的 Web 中间件。它主要用于解决数据库相关的问题。

MyCAT 能干什么?为什么要使用它?使用它可以解决什么问题?

  • 用于支持海量数据存储,对海量数据进行分库分表

  • 支持分库分表场景下的分布式事务

  • 对多个数据源进行统一整合

  • 高并发应用场景下,降低请求对单个数据库节点带来的灾难性压力

  • 可以通过数据库中间间层面实现数据库读写分离,使其Java程序与数据库访问解耦

具体更多的特性可以参考 http://mycat.io/ 对MyCAT 特性的介绍。总结下来的一般常用的用途有3个,分别如下:

  • 数据读写分离
  • 多数据源整合

MyCAT 是唯一的数据库分库分表的解决方案吗?与其它的数据库中间件有什么区别?

我们来看如下图,图片来源于网络:

具体介绍如下:

  1. Cobar 属于阿里 B2B 事业群,始于 2008 年,在阿里服役 3 年多,接管 3000+ 个 MySQL 数据库的 schema,

集 群日处理在线 SQL 请求 50 亿次以上。由于 Cobar 发 起人的离职, Cobar 停止维护。

  1. My cat 是开源社区在阿里 cobar 基础上进行二次开发,解决了 cobar 存在的问题,并且加入了许多新

的功能在其中。青出于蓝而胜于蓝。

  1. OneProxy 基于 MySQL 官方的 proxy 思想利用 c 进行开发的, OneProxy 是一款商业收费的中间件。舍

弃了一些功能,专注在性能和稳定性上。

  1. kingshard 由小团队用 go 语言开发,还需要发展,需要不断完善 。

  2. Vite ss 是 Youtube 生产在使用 架构很复杂。不支持 MySQL 原生协议,使用需要大量改造成本 。

  3. Atlas 是 3 60 团队基于 mysql proxy 改写 ,功能还需完善 ,高并发下不稳定 。

  4. MaxScale 是 mariadb MySQL 原作者维护的一个版本 研发的中间件

  5. MySQLRout e 是 MySQL 官方 Oracle 公司发布的中间件

除了这些之外,我们去github 搜罗了一下,还有如下的一些数据库中间件:

  1. Oceanus - 58同城数据库中间件,github star 数 500+;
  2. SOHU-DBProxy - 是由 搜狐 数据库团队开发维护的一个基于MySQL协议的数据中间层项目。它在MySQL官方推出的MySQL-Proxy 0.8.3版本的基础上, 修改了大量bug,添加了很多功能特性。现在已经在sohu的多个业务线上使用, github star 数 700+;
  3. Cetus是由C语言开发的关系型数据库MySQL的中间件,主要提供了一个全面的数据库访问代理功能。Cetus连接方式与MySQL基本兼容,应用程序几乎不用修改即可通过Cetus访问数据库,实现了数据库层的水平扩展和高可用。 , github star 数 1000+;
  4. Zebra是一个基于JDBC API协议上开发出的高可用、高性能的数据库访问层解决方案,是美团点评内部使用的数据库访问层中间件。github star 数 1500+;

MyCAT作为分库分表中间件的原理是什么?

Mycat收到一条SQL语句时, 首先解析SQL语句涉及的表, 接着查看此表的定义, 如果该表存在分片规则, 则获取SQL语句里分片字段的值, 并匹配分片函数, 得到该SQL语旬对应的分片列表, 然后将SQL语句发送到相应的分片去执行, 最后处理所有分片返回的数据并返回给客户端。 以 select* from Orders where prov=? 语句为例, 查找prov=wuhan, 按照分片函数, wuhan值存放在dnl上, 于是SQL语句被发送到Mysql l , 把DBI上的查询结果返回给用户。
这里使用过阿里巴巴数据源 Druid 的同学不知道有没有发现,在Druid 中有一个叫做 SQLParser 的东东,这个玩意其实就是可以用来对原始的SQL语句,根据SQL语法树,对SQL进行加强改造的一个解析器,如果你们项目中有做过表数据的行权限或者列权限,则用这个Druid 的SQLParser 对所有的SQL 进行拦截改写是一个不错的方案,我之前在做数据权限的时候,参考相关实现时,也发现了MyCAT 中对于SQL的解析和改写也使用的这个,具体可以参考:https://github.com/MyCATApache/Mycat-Server/blob/f929f96a16852869bc9dc63f4c0f192ee02818e0/src/main/java/io/mycat/statistic/stat/UserSqlHighStat.java

MyCAT 入门

对于一款开箱即用的数据库分库分表中间件来说,MyCAT的入门相对简单,只需要按照官方的入门指导一步步往下走即可。

安装之前我们首先准备环境,这里我们以企业中生产使用最多的Centos 7 为示例,进行下面的实操,本次CentOS 7 的内核版本如下:

Linux VM_0_14_centos 3.10.0-862.el7.x86_64

由于 MyCAT 源码是通过 Java 语言进行开发的,因此在我们使用之前,需要先检查自己的CentOS 7 主机具备不具备Java 环境,使用java -version 进行验证,如果没有安装JDK,则首先需要安装JDK并且配置Java环境变量才阔以,Centos 7 安装Java8 比较容易,只需要如下一行命令即可:

yum -y install java-1.8.0-openjdk

JDK8 安装完毕之后,进行如下验证,没有错误即可:

java -version


openjdk version "1.8.0_242"
OpenJDK Runtime Environment (build 1.8.0_242-b08)
OpenJDK 64-Bit Server VM (build 25.242-b08, mixed mode)

接下来我们根据官方的入门指导,下载mycat server 包,下载地址为:http://dl.mycat.io/ , 这里我们实验时使用MyCAT 1.6.7.3 release 版本即可,下载 1.6.7.3 release 安装包到自己主机的 /opt 目录下 , 操作命令如下:

cd /opt/ && wget  /usr/local/ http://dl.mycat.io/1.6.7.3/20190927161129/Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz

下载完成之后,我们对mycat 安装包进行解压即可,命令如下:

tar -zxvf Mycat-server-1.6.7.3-release-20190927161129-linux.tar.gz

解压完成之后,我们看到 /opt 目录下有了一个 mycat 目录,我们进入到该目录下,发现它的子目录主要有如下:

.
├── bin
├── catlet
├── conf
├── lib
├── logs
├── tmlogs
└── version.txt

这里我们进入到conf 目录下,只关注如下的几个配置文件即可,其它的配置文件先不用关心:

server.xml  定义逻辑库,表、分片节点等内容
schema.xml 定义用户以及系统相关变量,如端口等

修改相关配置

为了对用户进行区分,此时,我们先修改 server.xml 中的 root 用户为 mycat,当使用 mycat 用户连接时,就代表我们直连的是 mycat,其它的配置不动,具体修改如下:

...
<!-- 修改之前的 root 用户为 mycat -->
<user name="mycat" defaultAccount="true">
<property name="password">123456</property>
<property name="schemas">TESTDB</property>

<!-- 表级 DML 权限设置 -->
<!--
<privileges check="false">
<schema name="TESTDB" dml="0110" >
<table name="tb01" dml="0000"></table>
<table name="tb02" dml="1111"></table>
</schema>
</privileges>
-->
</user>
...

</mycat:server>

接下来我们修改 schema.xml

删除 标签间的 表信息 dataNode 标签只留一个 dataHost 标签只留一个 writeHost

<readHost 只留一 对,如下:
<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">

<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1" />
<dataNode name="dn1" dataHost="host1" database="testdb" />
<dataHost name="host1" maxCon="1000" minCon="10" balance="3"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()</heartbeat>
<!-- can have multi write hosts -->
<writeHost host="hostM1" url="118.25.102.189:3306" user="mycat"
password="123456"></writeHost>
</dataHost>
</mycat:schema>

其中 <writeHost host=”hostM1” 表示 MySQL 的主数据库,等会我们启动 MyCAT 之后进行验证读写否可以正常进行操作。

启动 MyCAT 前验证数据库远程访问情况

在验证之前由于我们的主数据库目前并没有创建mycat 这个用户,我们需要登录到 118.25.102.189:3306 数据库实例进行创建用户,脚本如下:

# 在 118.25.102.189 主机上登录 mysql 服务器
mysql -uroot
# 登录成功后进行创建用户并授权
CREATE USER 'mycat'@'%' IDENTIFIED BY '123456';

开始登陆验证MyCAT登陆

在我们配置并创建完毕MyCAT 用户之后,就可以尝试登陆MyCAT 了, 通过以下方式登陆,进行验证,登陆方式如下:

# 完事之后我们开始尝试登陆MyCAT , mycat 默认的端口为 8066
mysql -umycat -h118.26.102.189 -p123456 -P8066

通过上述方式登陆成功后,如下图所示:

ES 客户端读取数据的流程

客户端 -> shard -> filesystem cache -> 磁盘文件

海量数据检索查询性能优化思路

如果内存足够大, filesystem cache 会缓存,如果查询走filesystem cache 则速度耗时在毫秒级别,如果查询请求走磁盘文件,则最少查询耗时都在秒级别。

如果整个磁盘上索引数据文件在3台机器上,一共占用了1T的磁盘容量,ES数据量是1T,每台机器的数据量是300G。ES性能最佳情况,你的机器内存至少可以容纳总数据量的一半。

生产环境试验,最好用ES存储少量的数据,用来搜索的那些索引,内存留给filesystem cache , 100G。数据量控制在100G以内,相当于查询的数据几乎全部走内存来搜索,性能非常高,几乎搜索结果在1秒以内就可以出结果。

另外还有注意的一点,就是在ES中真正存储的记录字段都应该是你需要查询的字段,不应该把整条记录中的所有字段都放在ES中,如果全部字段都放到ES中,则会导致你机器的filesystem chche 占据空间很大,很多记录其实查询都要走硬盘文件,这样会导致查询性能会很低。

后台系统自动搜索一下热数据,提前让数据加载到filesystem cache 中,当客户端查询时,直接从 filesystem cache中找到,性能非常高。

类似于MySQL的冷热分离,将大量访问不频繁的数据放到单独的一个索引。将查询频繁的放到一个索引,提高查询的性能。

写入索引的时候,就将关联的数据直接写入进去,不要在搜索的时候进行join,因为ES中的复杂查询都很耗费性能。

分布式的,查100页的10条数据,必须从每个shard,都查询一批数据过来,然后拿过来在内存里面分页,页翻得越深,基本查询性能很差。优化策略:1.不允许深度分页 2.类似于下拉分页的话,可以使用 scroll api 进行查询。它的分页原理,会一次性生成快照,然后通过游标一次一次往下翻,无论翻多少页,性能就是毫秒级的,scroll 智能一页一页往后翻,天然适合微博,往下拉的时候。

1.简介

在本文中,我们将探讨Oracle Java Development KitOpenJDK之间的差异。我们先快速浏览一下,然后进行比较。之后,我们将看到其他JDK实现的列表。

2. Oracle JDK和Java SE历史

JDK(Java Development Kit)是Java平台编程中使用的软件开发环境。它包含一个完整的Java运行时环境,即所谓的私有运行时。该名称来自于它包含的工具多于独立的JRE以及开发Java应用程序所需的其他组件。

Oracle强烈建议使用术语JDK来引用Java SE(标准版)开发工具包(还有Enterprise Edition和Micro Edition平台)。

我们来看看Java SE的历史:

  • JDK Beta - 1995
  • JDK 1.0 - 1996年1月
  • JDK 1.1 - 1997年2月
  • J2SE 1.2 - 1998年12月
  • J2SE 1.3 - 2000年5月
  • J2SE 1.4 - 2002年2月
  • J2SE 5.0 - 2004年9月
  • Java SE 6 - 2006年12月
  • Java SE 7 - 2011年7月
  • Java SE 8(LTS) - 2014年3月
  • Java SE 9 - 2017年9月
  • Java SE 10(18。3) - 2018年3月
  • Java SE 11(18.9 LTS) - 2018年9月
  • Java SE 12(19。3) - 2019年3月

注意:不再支持斜体版本。

我们可以看到Java SE的主要版本大约每两年发布一次,直到Java SE 7.从Java SE 6开始花了五年时间,之后又花了三年时间升级到Java SE 8。

自Java SE 10以来,我们可以期待每六个月发布一次新版本。但是,并非所有版本都是长期支持(LTS)版本。由于Oracle的发布计划,LTS产品发布仅每三年发布一次。

Java SE 11是最新的LTS版本,Java SE 8将在2020年12月之前获得免费的公共更新,用于非商业用途。

在2010年Oracle收购Sun Microsystems之后,这个开发工具包得到了它的当前名称。在此之前,它的名字是SUN JDK,它是Java编程语言的官方实现。

3. OpenJDK

OpenJDK是Java SE 平台版本的免费开源实现。它最初于2007年发布,是Sun Microsystems于2006年开始开发的结果。

当然,我们应该强调 OpenJDK是自SE 7版以来Java标准版的官方参考实现

最初,它仅基于JDK 7.但是,从Java 10开始,Java SE平台的开源参考实现是JDK项目http://openjdk.java.net/projects/jdk/的责任。而且,就像Oracle一样,JDK项目也将每六个月发布一次新功能。

我们应该注意到,在这个长期运行的项目之前,JDK Release Projects发布了一个功能,然后停止了。

现在让我们看看OpenJDK版本:

  • OpenJDK 6项目 - 基于JDK 7,但经过修改后提供了Java 6的开源版本
  • OpenJDK 7项目 - 2011年7月28日
  • OpenJDK 7u项目 - 该项目开发Java Development Kit 7的更新
  • OpenJDK 8项目 - 2014年3月18日
  • OpenJDK 8u项目 - 该项目开发Java Development Kit 8的更新
  • OpenJDK 9项目 - 2017年9月21日
  • JDK10项目于2018年3月10日至20日发布
  • JDK11项目于2018年9月11日至25日发布
  • JDK12项目发布 - 稳定阶段

4. Oracle JDK与OpenJDK

在本节中,我们将重点介绍Oracle JDK和OpenJDK之间的主要区别。

4.1. 发布时间表

正如我们所提到的,Oracle将每三年发布一次,OpenJDK将每六个月发布一次

Oracle为其版本提供长期支持。另一方面,OpenJDK仅支持对发布的更改,直到下一个版本发布。

4.2. 许可证

Oracle JDK根据Oracle二进制代码许可协议获得许可,而OpenJDK具有GNU通用公共许可证(GNU GPL)版本2,使用了一个修正版本

使用Oracle平台时会产生一些许可影响。如Oracle 宣布的那样,在没有商业许可的情况下,在2019年1月之后发布的Oracle Java SE 8的公开更新将无法用于商业,商业或生产用途。但是,OpenJDK是完全开源的,可以自由使用。

4.3. 性能

两者之间没有真正的技术差别,因为针对Oracle JDK构建过程是基于OpenJDK的的

在性能方面,Oracle在响应能力和JVM性能方面要好得多。由于其对企业客户的重要性,它更加关注稳定性。

相比之下,OpenJDK将更频繁地发布版本。结果,我们可能遇到不稳定的问题。根据社区反馈,我们知道一些OpenJDK用户遇到了性能问题。

4.4. 功能

如果我们比较功能和选项,我们将看到 Oracle产品具有Flight Recorder,Java Mission Control和Application Class-Data Sharing 功能,而OpenJDK具有Font Renderer功能

此外,Oracle有更多的垃圾收集选项和更好的渲染器,我们可以在另一个比较中看到。

4.5. 发展与人气

Oracle JDK由Oracle Corporation完全开发,OpenJDK由Oracle,OpenJDK和Java Community开发。然而,红帽,Azul Systems,IBM,Apple Inc.,SAP AG等顶级公司也积极参与其开发。

正如我们从前一小节的链接中看到的那样,当涉及到在其工具中使用Java开发工具包的顶级公司(例如Android Studio或IntelliJ IDEA)的流行时Oracle JDK是更**受欢迎的**。

另一方面,主要的Linux发行版(Fedora,Ubuntu,Red Hat Enterprise Linux)提供OpenJDK作为默认的Java SE实现。

5.自Java 11以来的变化

正如我们在Oracle博客文章中看到的那样 ,从Java 11开始有一些重要的变化。

首先,Oracle将使用Oracle JDK作为Oracle产品的一部分,将开源**GNU通用公共许可证v2与Classpath Exception(GPLv2 + CPE)**和商业许可证结合使用,或者更改其历史“ BCL ”许可证, 或者服务,或不欢迎开源软件。

每个许可证都有不同的版本,但这些版本在功能上只与一些装饰和包装差异相同。

此外,OpenJDK现在提供传统的“商业功能”,如Flight Recorder,Java Mission Control和Application Class-Data Sharing,以及Z Garbage Collector。因此,Oracle JDK和OpenJDK构建从Java 11开始基本相同

让我们看看主要的区别:

  • 使用-XX:+ UnlockCommercialFeatures选项时,Oracle的Java 11工具包会发出警告,而在OpenJDK版本中,此选项会导致错误
  • Oracle JDK提供了一种配置,可将使用日志数据提供给“高级管理控制台”工具
  • Oracle一直要求第三方加密提供程序由已知证书签名,而OpenJDK中的加密框架具有开放加密接口,这意味着可以使用哪些提供程序没有限制
  • Oracle JDK 11将继续包括安装程序,品牌和JRE打包,而OpenJDK构建目前可用作ziptar.gz文件
  • javac的-释放命令行为有所不同了Java 9和Java 10个目标由于一些额外的模块在Oracle的版本存在
  • java -versionjava -fullversion命令的输出将Oracle的构建与OpenJDK构建区分开来

6.其他JDK实现

现在让我们快速浏览一下其他活动的Java Development Kit实现。

6.1. 自由开源

按字母顺序列出的以下实现是开源的,可以免费使用:

  • Amazon Corretto
  • Azul Zulu
  • Bck2Brwsr
  • CACAO
  • Codename One
  • DoppioJVM
  • Eclipse OpenJ9
  • GraalVM CE
  • HaikuVM
  • HotSpot
  • Jamiga
  • JamVM
  • Jelatine JVM
  • Jikes RVM (Jikes Research Virtual Machine)
  • JVM.go
  • leJOS
  • Maxine
  • Multi-OS Engine
  • RopeVM

6.2. 专有实现

还有受版权保护的实施:

  • Azul Zing JVM
  • CEE-J
  • Excelsior JET
  • GraalVM EE
  • Imsys AB
  • JamaicaVM(aicas)
  • JBlend(Aplix)
  • MicroJvm(IS2T - 工业智能软件技术)
  • PTC Perc
  • SAP JVM
  • Waratek CloudVM for Java

与上面列出的有效实现一起,我们可以看到非有效实现的列表 以及每个实现的简短描述。

在本文中,我们专注于当今最流行的两个Java开发工具包。

我们首先描述了它们中的每一个,然后强调了它们之间最显着的差异。然后,我们特别关注自Java 11以来的变化和差异。最后,我们列出了今天可用的其他有效实现。

2000年7月,加州大学伯克利分校的Eric Brewer教授在ACM PODC会议上提出CAP猜想。2年后,麻省理工学院的Seth Gilbert和Nancy Lynch从理论上证明了CAP。之后,CAP理论正式成为分布式计算领域的公认定理。

一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。

Consistency 一致性

一致性指“all nodes see the same data at the same time”,即更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致。分布式的一致性

对于一致性,可以分为从客户端和服务端两个不同的视角。从客户端来看,一致性主要指的是多并发访问时更新过的数据如何获取的问题。从服务端来看,则是更新如何复制分布到整个系统,以保证数据最终一致。一致性是因为有并发读写才有的问题,因此在理解一致性的问题时,一定要注意结合考虑并发读写的场景。

从客户端角度,多进程并发访问时,更新过的数据在不同进程如何获取的不同策略,决定了不同的一致性。对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性。如果能容忍后续的部分或者全部访问不到,则是弱一致性。如果经过一段时间后要求能访问到更新后的数据,则是最终一致性。

Availability 可用性
可用性指“Reads and writes always succeed”,即服务一直可用,而且是正常响应时间。
对于一个可用性的分布式系统,每一个非故障的节点必须对每一个请求作出响应。也就是,该系统使用的任何算法必须最终终止。当同时要求分区容忍性时,这是一个很强的定义:即使是严重的网络错误,每个请求必须终止。
好的可用性主要是指系统能够很好的为用户服务,不出现用户操作失败或者访问超时等用户体验不好的情况。可用性通常情况下可用性和分布式数据冗余,负载均衡等有着很大的关联。

Partition Tolerance分区容错性
分区容错性指“the system continues to operate despite arbitrary message loss or failure of part of the system”,即分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务。
分区容错性和扩展性紧密相关。在分布式应用中,可能因为一些分布式的原因导致系统无法正常运转。好的分区容错性要求能够使应用虽然是一个分布式系统,而看上去却好像是在一个可以运转正常的整体。比如现在的分布式系统中有某一个或者几个机器宕掉了,其他剩下的机器还能够正常运转满足系统需求,或者是机器之间有网络异常,将分布式系统分隔未独立的几个部分,各个部分还能维持分布式系统的运作,这样就具有好的分区容错性。

CA without P:如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。但其实分区不是你想不想的问题,而是始终会存在,因此CA的系统更多的是允许分区后各子系统依然保持CA。

CP without A:如果不要求A(可用),相当于每个请求都需要在Server之间强一致,而P(分区)会导致同步时间无限延长,如此CP也是可以保证的。很多传统的数据库分布式事务都属于这种模式。

AP wihtout C:要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。现在众多的NoSQL都属于此类。

对于多数大型互联网应用的场景,主机众多、部署分散,而且现在的集群规模越来越大,所以节点故障、网络故障是常态,而且要保证服务可用性达到N个9,即保证P和A,舍弃C(退而求其次保证最终一致性)。虽然某些地方会影响客户体验,但没达到造成用户流程的严重程度。

整理自:http://www.hollischuang.com/archives/666

对于 Java 程序员来说,在我们平时的编码过程中,并不会手动的去分配内存和回收内存,因为对于内存这块的处理都是依赖于 JVM 的,这样会导致一个问题:就是如果我们对 JVM 的内存管理机制不是特别熟悉的前提下,如果程序在内存这块出现了莫名的问题,此时处理这些问题就是一个灾难,因此,对于 Java 内存区域这块需要我们有一个清晰的认知,这样才可以在出现问题是根据 JVM 内存管理这块有好的解决方案。
在我们学习 运行时数据区域之前,我们先概览一下 JVM 的整体架构,这样对于我们下面内容的理解是有很大的帮助的,先将 JVM 整体的架构图展示如下:
简版的图如下:

详细点儿的图如下:

运行时数据区域

一般情况下,运行时数据区域主要包含:程序计数器Java 虚拟机栈本地方法栈Java 堆方法区这几个大的部分。对于此结构,通过图的方式进行如下展示:

由图示意可知:其中Method Area (方法区) Heap(堆) 是线程共享的,VM Stack (虚拟机栈)Native Method Area (本地方法栈)Programmer Computer Register(程序计数器) 是线程私有的。当我们分析到这边之后,会有一个疑问?为什么 JVM 要将内存划分为线程共享和非线程共享呢?

我们可以回顾下 Java 程序执行的流程。Java 源文件 – 字节码文件(使用 Javac 编译为 *.class) – 每个 Java 程序都需要运行在 自己的 JVM 之上,JVM 会找到程序运行的入口,JVM 通过字节码解释器加载字节码运行。程序运行后JVM 如何对内存进行划分呢?其实 JVM 在初始化的时候,会分配好方法区(Method Area) 和 堆(Heap),当 JVM 遇到一个线程的时候,便会为其分配一个程序计数器(Programmer Register)JVM虚拟机栈(VM Stack)本地方法栈(Native Method Stack),当线程死亡的时候,虚拟机栈本地方法栈程序计数器所占用的内存会被释放掉。通过这里的分析,便可以了解到,JVM 将内存区域分为线程共享和非线程共享两个部分,非线程共享的区域与所属的线程的生命周期相同,而线程共享的区域与 JVM 虚拟机的生命周期相同。这样也可以说明,其实 JVM 大多数的垃圾回收是发生在线程共享的区域(实际上更多位于堆上)。

对于运行时数据区域总体的理解,可以参考下图:

程序计数器

程序计数器(Program Counter Register)是一块较小的内存空间,可看做是当前线程所执行字节码行号的指示器。Java 虚拟机中多线程的执行原理是通过线程轮流切换并分配执行时间的方式来实现的,一般情况下在单核的处理器中,处理器在某一时刻只能处理一条线程中的指令。为了线程切换后能恢复到上次执行的位置,每个线程需要一个独立的计数器,每个线程之间的计数器是互不影响的。因此计程序计数器区域为“线程私有” 的内存。

如果线程执行的是一个 Java 方法,则程序计数器记录的是正在执行的虚拟机字节码指令的地址,如果执行的是 Native 方法,则计数器为Undefined。

Java 虚拟机栈

Java 虚拟机栈和程序计数器一样属于线程私有。虚拟机栈主要描述 Java 执行时的内存模型:每个方法在执行的同时会创建一个栈帧(Stack Frame) 用于存储变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用到执行完成,对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

虚拟机栈中的局部变量表存放在编译期间可知的基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型)和 returnAddress 类型(指向一条字节码指令的地址)。

其中 64 位长度的 long 和 double 会占用 2 个局部变量空间,其它的只占用 1 个局部变量空间。一般局部变量所需在帧中分配多大的局部变量空间在程序编译期间完成分配,程序运行期间不会改变局部变量表的大小。

Java 虚拟机规范中指出,当线程请求的栈深度大于虚拟机所允许的深度时,会抛出 StackOverflowError 异常。

本地方法栈

本地方法栈(Native Method Stack)与虚拟机栈所发挥的作用非常相似,它们之间的区别在于虚拟机栈为虚拟机执行 Java 方法(执行字节码)服务,而 本地方法栈为虚拟机使用的 Native 方法服务。虚拟机规范中没有对本地方法栈做强制的规定,由具体的虚拟机自己实现,Sun HotSpot 虚拟机字节将本地房发展和虚拟机栈合二为一。通虚拟机栈类似,本地房发展也会抛出 StackOverflowError 和 OutOfMemoryError 异常。

Java 堆

Java 堆是Java 虚拟机所管理内存中最大的一块。Java 堆被所有线程共享,此区域的唯一目的便是存放对象的实例。 Java 虚拟机规范规定:所有的对象实例以及数组都要在堆上进行分配,随着 JIT 编译器的发展与内存逃逸技术的成熟,栈上分配、标量替换优化技术使所有对象分配在堆上的结论不是那么“绝对”。

Java 堆是垃圾回收的主要区域,可将 Java 堆划分为:新生代和老年代;更细的划分新生代包含 Eden 空间、From Survivor、To Survivor 空间等。

Java 虚拟机规范规定,Java 堆可以处于物理机器上不连续的内存空间中。当堆空间无法分配扩展时,将会抛出 OutOfMemoryError 异常。

对于堆的加深理解可以参考此图:

方法去(Method Area)和 Java 堆类似,也属于各个线程共享的内存区域,主要用来存放被 JVM 虚拟机加载的类信息、常量、静态变量。即时编译器编译后的代码等数据。方法区又称为非堆。方法区本质上与“永久代”(Permanent Generation)不等价,主要原因是 HotSpot 虚拟机的设计团队把 GC 分代收集扩展到了方法区,换句话说就是使用永久代实现了方法区。在 JDK1.7 的 HotSpot中,由于永久代区域是一个类的静态区域,主要存储类的加载信息,常量池,方法代码等内容,默认大小只有 4 m,如果在常量池中大量的比如使用 String.intern() 方法会直接导致发生 java.lang.OutOfMemoryError: PermGen space,所以在 jdk7 的版本中,字符串常量池已经从 Perm 区移动到 Java Heap 区域了。主要是原因是:Perm 区域太小,在 Java 8 中已经取消了永久代进而代替的是元区域(Meta Space)。根据 Java 虚拟机规范规定,当方法区无法满足内存分配需求时,会产生 OutOfMemoryError 异常,也称为 OOM 。

运行时常量池

运行时常量池(Runtime Constant Pool) 属于方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项便是常量池(Runtime Constant Pool),常量池主要作用为:存放编译期间生成的各种字面量和符号引用,这些内容将在 Java 类被加载后进入方法区的运行时常量池中存放。运行时常量另外一个重要特征便是具备动态性,具体的案例便是使用 String 的 intern 方法,不要求常量只在类编译期间才能产生,在运行期间也可以将新的常量放入池中。运行时常量池属于方法区的一部分,当常量池无法进行内存分配时,也会抛出 OutOfMemoryError 异常。

直接内存(Direct Memory) 不是虚拟机运行时数据区域的一部分,也不是 Java 虚拟机规范中定义的内存区域。这部分的内存也会被频繁使用,当内存分配空间不足时,也会抛出 OutOfMemoryError 异常。对于直接内存使用的典型案例便是,在 JDK 1.4 之后,新加入了 NIO(New Input/Output) 类,其核心原理是基于通道 (Channel) 与 缓冲区 (Buffer) 的 I/O 方式,可以通过 Native 函数库直接分配堆外内存,然后通过存储在 Java 堆中的 DirectByteBuffer 对象作为内存的引用进行操作。 这样的优点就是在一些场景中提高性能,避免了 Java 堆和 Native 来回赋值数据的开销。
本地内存的分配不会受到 JVM 堆大小的限制,但是会受到系统本身内存大小的限制,当系统本身内存空间不够用时,也会出现 OutOfMemory 异常。

« Prev 1235Next »

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK