Drools kie 中的 Assets

Drools Workbench 中有很多的 Assets (资源)类型,每一种类型的 asset 都意味着一种类型的规则模型,下面就记录下学习的过程。

Model

这个是最好理解的概念了,和 Java 的对象一样。可以通过基础类型定义一些抽象的概念。

Data enumerations

枚举,和常见的枚举也没有太大差别,不过在 Drools 中会被下拉菜单用到。

Fact Field Context
Applicant age [20, 25, 30]

然后会生成这样的代码

'Applicant.age' : [20,25,30]

如果想要缩写可以使用等号,比如

'Person.gender' : ['M=Male','F=Female']

guided rules

向导型规则,通过 WHEN ,THEN 语句快速建立规则,相对比较简单的一种。在规则设计器中可以轻松的添加条件和结果规则。

Guided decision tables

向导型决策表是一种以表格形式表现规则的工具,非常适合描述条件判断很多,条件又可以相互组合,有很多决策方案的情况。决策表可以将这些复杂的逻辑以一种精确而简单的表格形式整理出来,通过 Workbench 中直观的表格形式非常清晰。

Drools 中的决策表可以非常轻松的引导用户制作一个基于 UI 的规则,可以定义规则 attributes, metadata, conditions 和 actions。一旦通过 UI 形式定义好规则,那么所有的规则都会编译为 Drools Rule Language(DRL) 规则。

guided rule templates

规则模板,可以使用占位符来生成模板来给其他使用

Spreadsheet decision tables

由用户上传一张 excel 表

Test Scenario

Test Scenario 用来验证规则是否符合预期,当规则发生改变,可以使用 Test Scenario 来回归测试。

reference


2018-10-31 drools , rule-engine , kie

使用 hub 命令来操作 GitHub

hub 命令是 git 命令的扩展,利用 GitHub 的 API 可以轻松的扩展 Git 的能力,比如常见的 pull requests 可以通过命令行来实现。

安装

在官网的文档上,Mac 有一键安装,Fedora 有一键安装,唯独 Ubuntu/Mint 系列没有一键安装的,其实用 hub 的二进制也非常容易,不过没有一键安装,比如 apt install hub 这样的命令还是有些麻烦。

所以有了这个很简单的脚本

VERSION="2.5.1"
wget https://github.com/github/hub/releases/download/v$VERSION/hub-linux-amd64-$VERSION.tgz
tar xzvf hub-linux-amd64-$VERSION.tgz
sudo ./hub-linux-amd64-$VERSION/install

对于 bash,zsh 的自动补全可以参考文末的链接。

Mac 或者 Go 安装可以参考这里

当第一次和 GitHub 有交互时会弹出用户名和密码用来生成 OAuth token,token 保存在 ~/.config/hub 文件中。或者可以提供 GITHUB_TOKEN 环境变量,值是拥有 repo 权限的 access token。

使用

贡献者

如果是开源项目贡献者,hub 可以使用命令来拉取代码,浏览页面,fork repos,甚至提交 pull requests 等等。

这里为了和 git 命令区别开,还是使用 hub 命令,如果熟悉之后可以设置一个别名直接用 hub 替换 git 命令。

hub clone dotfiles      # clone own repo
hub clone github/hub    # clone others
hub browse -- issues    # Open browser and navigate to issue page

贡献者工作流

hub clone others/repo
cd repo
git checkout -b feature
git commit -m "done with feature"
hub fork    # fork repo , hub command will add a remote
hub push YOUR_USER feature
hub pull-request

维护者工作流目前还没有用到先略过。

常用命令介绍

hub push

将本地 branch push 到 remote,和 git 命令类似

hub push REMOTE[,REMOTE2...] [REF]

比如

hub push origin,staging,qa branch_name

hub create

在 GitHub 创建 repo 并且添加 remote.

hub create [-poc] [-d DESC] [-h HOMEPAGE] [[ORGANIZATION/]NAME]

reference


2018-10-30 hub , github , git , version-control

通过 microk8s 使用 Kubernetes

看 Kubernetes 相关书籍的时候都推荐使用 minikube 来在本地安装 Kubernetes 调试环境,但是发现 minikube 安装和使用,都需要使用虚拟化工具,比较麻烦,搜索一下之后发现了 microk8s , microk8s 安装非常简单

snap install microk8s --classic

只需要本地有 snap 环境就可以非常快速一行命令安装成功。

为了不和已经安装的 kubectl 产生冲突,microk8s 有自己的 microk8s.kubectl 命令 o

microk8s.kubectl get services

如果本地没有 kubectl 命令可以增加一个别名

snap alias microk8s.kubectl kubectl

或者取消

snap unalias kubectl

API 服务监听 8080 端口

microk8s.kubectl config view

查看。

Kubernetes Addons

microk8s 只是最精简的安装,所以只有 api-server, controller-manager, scheduler, kubelet, cni, kube-proxy 被安装运行。额外的服务比如 kube-dns, dashboard 可以通过 microk8s.enable 启动

microk8s.enable dns dashboard

禁用

microk8s.disable dns dashboard

可用的扩展

  • dns
  • dashboard
  • storage
  • ingress
  • gpu
  • istio
  • registry
  • metrics-server

停止或重启 microk8s

snap disable microk8s   # 停止
snap enable microk8s    # 重启

移除

microk8s.reset
snap remove microk8s

更多配置参考官网

reference


2018-10-29 kubernetes , microk8s , container , docker

Kubernetes

免责声明:这篇文章只是在了解 Kubernetes 时的一些笔记记录,非常不全面,如果需要全面了解 Kubernetes 那么还请看书或者文档。

Kubernetes 是什么

Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications.

Kubernetes 是 Google 开源的容器集群管理系统,为容器化应用提供资源调度,部署运行,服务发现,扩容和缩容等一系列服务。

能够做什么

Kubernetes 最重要的功能就是容器编排(container orchestration),也就是确保所有的容器能够按照预先的设定在物理机或者虚拟机上执行不同的工作流程。容器必须按照约定打包, Kubernetes 会监控所有正在运行的容器,如果容器”死亡“,或者”无响应“,都会被 Kubernetes 处理。

Kubernetes

图片来自 Architecture 101

  • Cluster, A cluster is a collection of hosts storage and networking resources that Kubernetes uses to run the various workloads that comprise your system.
  • Node(除去 Master 的其他机器), single host, a physical or virtual machine. job is to run pods,可以通过 kubectl get nodes 查看 node,或者 kubectl describe node <node_name> 查看详细信息

    • kubelet 负责 Pod 对应容器创建、启动停止等
    • kube-proxy 实现 Kubernetes Service 通信和负载均衡
    • Docker 引擎,负责本机容器创建和管理
  • Master (集群控制节点), is the control plane of Kubernetes, consists of several components, like API server, a scheduler, and a controller manager.

    • kube-apiserver 提供 HTTP Rest 接口
    • kube-controller-manager 资源对象自动化控制中心
    • kube-scheduler 负责资源调度 Pod 调度
  • Pod is the unit of work in Kubernetes. Each pod contains one or more containers.
  • Label (标签),Label 可以附加到各种资源上,node,pod,service,RC 等。
  • Replication Controller(RC),核心概念之一,定义一个期望的场景,声明某种 Pod 的副本数量在任意时刻都符合某个期望值

    • Pod 期待的副本数 replicas
    • 筛选目标 Pod 的 Label Selector
    • 当 Pod 副本数小于预期时,创建新 Pod 的 Pod 模板
  • Replica Sets 和 RC 的区别,在于支持集合的 Label selector,而 RC 只支持基于等式的 Label Selector
  • Horizontal Pod Autoscaler(HPA) 实现 Pod 扩容和缩容。
  • Service 可以理解为一个微服务

为什么要用 Kubernetes

  • 单机走向集群已是必然
  • 设计实现分布式系统
  • Kubernetes 全面拥抱微服务架构
  • Kubernetes 架构有超强的横向扩容能力

分布式系统设计模式

Sidecar Pattern

The Sidecar pattern 是在 pod 中除去 main application 容器之外额外附加的一个容器模式。主要的应用感知不到 sidecar 容器。最好的例子就是中心化的日志收集器,主要的应用容器可以将日志打到 stdout,然后 sidecar 容器收集,然后将所有的日志发送到中心日志服务。

这种方式和在主应用中加入日志系统带来的优势是巨大的,首先,应用可以不被中心日志服务拖累,其次如果想要升级或者改变日志服务,只需要更新 sidecar 容器即可,而不需要修改主应用。

关于更加详细的分析可以参考这篇文章

Ambassador pattern

The Ambassador pattern 可以理解为一个代理层,对于一个远程服务,可以表现为一个本地服务加一些规则来代替提供相同的服务。最常见的使用场景就是,有一个 Redis 集群,master 用来写,而其他 replicas 用来读。

一个 local Ambassador 容器通过代理提供服务,然后暴露 Redis 给主应用容器。主应用容器通过 localhost:6379 来连接 Redis,但是实际上是连接到了同一个 pod 中的 ambassador ,这个代理层会过滤请求,发送写请求给真正的 Redis master,然后读请求会随机的发送给从服务器(replicas)。和 Sidecar 模式中主应用容器一样,主应用是不感知这样的模式的。

这种模式的优点是当 Redis 集群配置发生改变时,只需要 ambassador 做相应的修改即可,主应用不用任何改动。

Adapter pattern

The Adapter pattern 可以理解成将输出标准化。考虑一种模式,一个服务是逐渐发布的,他产生的报告格式可能和之前的格式不相同,但是其他接收输出报告的服务或者应用还没有升级。那么一个 Adapter 容器可以被部署到同一个 pod,将主应用输出的内容转换成老的格式,直到所有的报告消费者都升级完成。Adapter 容器和主应用容器共享一个文件系统,他可以监控本地文件系统,一旦新的应用写入内容,立即将其修改。

Multi-node pattern

单节点 patterns 被 Kubernetes 通过 pod 直接支持。Multi-node 模式,比如 leader election, work queues, 和 scatter-gather 并没有直接支持,但是通过标准接口组合 pods 可以实现。

下载安装

使用 Minikube 快速搭建单节点集群

具体的教程参考官网

使用 microk8s 安装

microk8s 是另外一个用以提供 Kubernetes 快速安装的工具,参考这里

使用

创建 rc 配置

样例文件:

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

解释:

  • kind 资源类型
  • spec.replicas Pod 副本期待数量
  • spec.template 基于此模板创建 pod 实例
  • template 下 spec Pod 中容器定义

发布到 Kubernetes

kubectl create -f nginx-rc.yaml

用命令查看

kubectl get rc
kubectl get pods

reference


2018-10-29 kubernetes , container , open-source , automating , scaling , management , docker

Drools Kie 简单使用

Drools 是一个 Java 实现的开源规则引擎 (Rule Engine),或者又被称为 Business Rules Management System(BRMS) 。Drools workbench 被叫做 Drools-WB,KIE-WB(或者也叫 KIE Drools workbench) 组合了 Guvnor, Drools 和 jBPM 插件。1

Kie Server 是一个模块化的,独立的组件,可以用来演示和执行规则和流程。

KIE 全称是 Knowledge Is Everything 2

为什么会产生规则引擎

在企业复杂项目演进过程中随着外部条件复杂化会造成不断变化的业务逻辑,在系统实现时需要考虑将应用开发实现和商业业务决策逻辑剥离。这些规则可以集中管理,也可以在运行时动态管理修改,规则引擎正是基于上面的背景诞生的解决方案。

规则引擎用来处理什么问题

Drools 用来解决复杂规则的问题。现实问题往往会有很多逻辑判断,而如果将这些逻辑判断都编码写死在代码逻辑中,不仅实现混乱,而不易于维护。Drools 可以让应用逻辑和数据逻辑分离,通过直观的规则编排将数据逻辑单独处理。

通常来讲,如果一个系统需要接受一系列的参数,根据这些参数做一些决策,那么 Drools 应该都能够处理。

规则引擎(BRMS)的特点:

  • 业务规则可以嵌入应用程序任何位置
  • 可持久化
  • 依据市场变化,业务规则需要能够快速,低成本更新
  • 测试场景可视化
  • 版本控制
  • 类人语言

可以这么理解规则引擎,是一种在应用程序中课嵌入的组件,将业务逻辑从应用代码中分离,使用行业特定的规则模块编写业务逻辑,接受数据输入,解释业务规则,并根据规则做出业务决策。

规则引擎适用场景

规则引擎并不万能,在业务中使用规则引擎需要预先分析业务的使用场景,规则引擎适用于下面的场景:

  • 业务规则数量有限,如果有成百条规则就明显不太适合使用规则引擎
  • 规则经常发生变动

声明式编程

规则引擎描述做什么,而不是如何去做。规则可以对复杂问题进行简化,规则比编码更易读。

逻辑与数据分离

数据保存在系统对象,逻辑保存在规则,打破了面向对象编程系统中数据和逻辑耦合的问题。

当逻辑跨领域时更为有用,通过将逻辑规则集中在一起维护,取代了分散在代码中的问题。

速度和可测量

Rete 算法,Leaps 算法,提供了系统数据对象有效的匹配。RETE 算法来自 Dr. Charles Forgy 在 1979 年的 《专家系统原理和编程》中 CIS587:The RETE Algorithm

集中化规则

通过规则,可以建立一个可执行的规则库,规则库代表着现实业务策略,理想情况下可读性高的规则还可以作为文档。

类自然语言的规则

通过 DSL 领域特定语言,可以让编码者通过接近自然语言的方式来编写规则。这让非技术人员和领域专家可以使用自己的逻辑来理解和编写规则。

如何使用规则引擎

规则引擎至少应该包括:

  • 加载卸载规则集 API
  • 数据操作 API
  • 引擎执行 API

使用规则引擎遵循五个典型步骤:

  • 创建规则
  • 向引擎添加或者更换规则
  • 向引擎提交数据
  • 引擎执行
  • 导出引擎执行结果,获取数据

一个开放的规则引擎可以被嵌在程序任何位置。

Docker 启动 drools workbench

Google 搜索之后发现 drools-workbench 有下面两个版本,不带 showcase 的版本是设计用来扩展,可以增加自己的的配置的镜像,而如果想要直接使用,那么可以使用 drools-workbench-showcase:latest 这个镜像,这个镜像包含了一些默认的配置。

docker pull jboss/drools-workbench
docker pull jboss/drools-workbench-showcase

拉取镜像后

docker run -p 8080:8080 -p 8001:8001 -d --name drools-wb jboss/drools-workbench-showcase:latest

当应用启动后,可以访问 http://localhost:8080/drools-wb 来体验 workbench 功能。

下面是镜像中默认包含的用户和角色:

USER        PASSWORD    ROLE
*********************************************
admin       admin       admin,analyst,kiemgmt
krisv       krisv       admin,analyst
john        john        analyst,Accounting,PM
sales-rep   sales-rep   analyst,sales
katy        katy        analyst,HR
jack        jack        analyst,IT

如果想要自己扩展用户,那么可以尝试使用不带 showcase 的版本。

Docker 启动 Kie Server

拉取镜像

docker pull jboss/kie-server-showcase

拉取完成后,如下启动:

docker run -p 8180:8080 -d --name kie-server --link drools-wb:kie_wb jboss/kie-server-showcase:latest

Drools

Drools 大体可以分为两个部分:Authoring 构建 和 Runtime 运行。

Authoring

构建过程涉及到 .drl 规则文件创建,通过上面的 workbench 可以使用界面来创建规则。

Runtime

运行时则是在执行规则的服务,kie 提供了 server 可以用来执行规则。

reference


2018-10-25 drools , rules , java , rule-engine

Netty 简单实用

Netty 是异步、事件驱动的网络框架,可以用于开发高性能的网络服务器程序。

传统的多线程服务端程序是 Blocking (阻塞的),也就是接受客户端连接,读数据,发送数据是阻塞的,线程必须处理完才能继续下一个请求。而 Netty 的 NIO 采用事件机制,将连接,读,写分开,使用很少的线程就能够异步 IO。Netty 是在 Java NIO 的基础上的一层封装。

Netty 的官方文档和入门手册已经非常详细了,几乎是手把手的实现了 DISCARD ,ECHO 和 TIMESERVER 的例子,把官方的例子实现一遍对 Netty 就会有一点的了解了。

使用 LineBasedFrameDecoder 解决 TCP 粘包问题

TCP 粘包拆包

首先要了解 TCP 的粘包和拆包,TCP 是一个流协议,是一串没有边界的数据,TCP 并不了解上层业务数据含义,他会根据 TCP 缓冲区实际情况进行包划分,所以业务上,一个完整的包可能被 TCP 拆分为多个包发送,也可能把多个小包封装为一个大数据包发送。

业界对 TCP 粘包和拆包的解决方案:

  • 消息定长,固定长度,不够补位
  • 包尾增加回车换行符进行切割,FTP
  • 将消息分为消息头和消息体,在消息头中包含消息总长度,通常设计一个字段用 int32 来表示消息长度
  • 其他应用层协议

Netty 提供了半包解码器来解决 TCP 粘包拆包问题。

private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
  @Override
  protected void initChannel(SocketChannel arg0) throws Exception {
    arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));
    arg0.pipeline().addLast(new StringDecoder());
    arg0.pipeline().addLast(new TimeServerHandler());
  }
}

对于使用者,只需要将支持半包解码的 Handler 添加到 ChannelPipeline 即可。

LineBasedFrameDecoder 原理是依次遍历 ByteBuf 中可读字节,判断是否有 \n\r\n ,有则以此为结束,组成一行。

StringDecoder 是将接受到的对象转成字符串,然后调用后面的 Handler,LineBasedFrameDecoder 和 StringDecoder 组合就是按行切换的文本解码器。

分隔符和定长解码器

就像上文说的 TCP 以流进行传输,上层应用对消息进行区分,采用的方式:

  • 固定长度
  • 回车换行作为结束符
  • 特殊分隔符作为结束
  • 定义消息头,包含消息总长度

Netty 对这四种方式做了抽象,提供四种解码器来解决对应的问题。上面使用了 LineBasedFrameDecoder 解决了 TCP 的粘包问题,另外还有两个比较常用的 DelimiterBaseFrameDecoder 和 FixedLengthFrameDecoder。

DelimiterBaseFrameDecoder 是分隔符解码器,而 FixedLengthFrameDecoder 是固定长度解码器。

ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
ch.pipeline().addLast(new FixedLengthFrameDecoder(20));

对应的源代码可以参考这里

Netty 实际用途

Netty 在 RPC 框架中有大量的使用,提到 RPC 就不得不提 Java 的编解码。Java 序列化的主要目的:

  • 对象持久化
  • 网络传输

但是 Java 序列化也有缺陷:

  • 无法跨语言使用
  • 序列化后码流太大
  • 序列化性能不行

代码库:https://gitlab.com/einverne/netty-guide-book

reference


2018-10-24 netty , nio , java , jdk , network , programming

由 libevent 库开始学习 Linux IO 模型

在看 Java 的 Netty 的时候,了解到了 NIO,从 NIO 了解到了 C 语言实现的 libevent 。我们为什么需要这样一个库,他的出现是为了解决什么问题。对于熟悉网络编程,或者多线程的人来说,都会知道一个普遍存在的问题,CPU 要远远快过 IO。所以如果我们要同时处理多个任务,而当前的任务阻塞了 IO,那么理想的状态应该是让 CPU 执行其他任务,而让阻塞 IO 的任务放到后台执行。

libevent 库提供了一种事件响应机制,当事件发生在用户关心的文件描述符上时通知用户,并且隐藏了真正后台使用的方法(select,epoll,kqueue) ,这避免了让用户为各个平台书写不同代码的问题。

Linux 网络 IO 模型

Linux 内核将所有外部设备看做一个文件来操作,对一个文件的读写操作会调用内核提供的命令,返回 file descriptor。对一个 socket 的读写也有相应的描述符,socketfd,描述符是一个数字,指向内核中一个结构体。

UNIX 网络编程对 IO 模型划分了 5 类:

  • 阻塞 I/O 模型:默认情况下所有文件操作都是阻塞的。以套接字为例,进程中调用 recvfrom,系统调用直到数据包到达且被复制到应用进程缓冲区或者发生错误时才返回,期间一直等待,进程从调用 recvfrom 开始到返回整个过程是被阻塞的
  • 非阻塞 I/O 模型:recvfrom 从应用层到内核,如果该缓冲区没有数据,直接返回一个 EWOULDBLOCK 错误,一般轮讯检查该状态,看内核是否有数据
  • I/O 复用模型:Linux 提供 select/poll ,进程通过一个或者多个文件描述符传递给 select 或者 poll 系统调用,阻塞在 select 操作,select/poll 可以侦测多个文件描述符是否处于就绪状态。Linux 还提供 epoll 系统调用,epoll 使用基于事件驱动方式代替顺序扫描
  • 信号驱动 I/O 模型,开启套接口信号驱动 IO 功能,通过系统调用 sigaction 执行信号处理函数(非阻塞),当数据准备好,进程生成 SIGIO 信号,通过信号回调通知应用程序调用 recvfrom 来读取数据,并通知主循环函数处理
  • 异步 IO:告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核复制到用户缓冲区)通知用户。这种模型区别于信号驱动主要区别是:信号驱动 IO 由内核通知何时开始 IO 操作;异步 IO 由内核通知 IO 何时已经完成

更多的可以参考《UNIX 网络编程》这本书。

NIO 类库

NIO 在 JDK 1.4 引入,弥补了 JAVA 原来的同步阻塞 IO 的不足。

缓冲区 Buffer

Buffer 是一个对象,包含一些要写入或者要读的数据。在面向流的 IO 中,数据可以直接写入或者读取到 Stream 对象中,在 NIO 库中,所有的数据都是用缓冲区处理。

缓冲区实质上是数组,通常是字节数组 ByteBuffer,缓冲区也不仅是一个数组,缓冲区提供了数据结构化访问以及维护读写位置等信息。

最常用的是 ByteBuffer ,但是每一种 Java 基本类型都对应一个缓冲区。

通道 Channel

网络数据通过 Channel 读写,通道和流不同的是通道是双向的,流只是一个方向的移动,通道可以同时用于读、写或者同时进行。

Channel 可以分为两类:

  • 网络读写的 SelectableChannel
  • 文件 FileChannel

多路复用器 Selector

Selector 会不断轮询注册在上面的 Channel,如果某 Channel 发生读写时间,Channel 处于就绪状态,被 Selector 轮询出来,通过 SelectionKey 获取就绪 Channel 集合,进行后续 IO。

reference


2018-10-23 libevent , c , nio , non-blocking

Java 查漏补缺之 jvm

JVM 设计者将 JVM 内存结构划分为多个区域,每个内存区域有各自的用途,负责存储各自的数据类型。有些内存区生命周期和 JVM 一致,也有些和线程生命周期一致,伴随着诞生,伴随着消亡。

Java 源代码文件会被编译为字节码(.class),然后由 JVM 中类加载器加载类字节码,加载完毕后,交给 JVM 执行引擎,整个程序郭晨中 JVM 会使用一段内存空间来存储执行过程中需要用到的数据和信息,这段空间一般被称为 Runtime Data Area,也就是 JVM 内存。

线程共享内存区

允许被所有线程共享访问的内存区,包括堆,方法区,运行时常量池三个内存区。

Java 堆区在 JVM 启动时被创建,在实际内存空间可以是不连续的。Java 堆用于存储对象实例,GC 执行垃圾回收重点区域。JVM 一些优化会将生命周期长的 Java 对象移动到堆外。所以 Java 堆不再是 Java 对象内存分配唯一的选择。

JVM 中的对象分为,生命周期比较短的瞬时对象,和长时间的对象。针对不同的 Java 对象,采取不同的垃圾手机策略,分代收集。GC 分代手机,新生代 和 老年代。

-Xms-Xmx 参数分别可以设置 JVM 启动时起始内存和最大内存。

方法区

方法区存储了每一个 Java 类结构信息,包括运行时常量池,字段和方法数据,构造函数,普通方法字节码内容以及类,实例,接口初始化需要用到的特殊方法等数据。

-XX:MaxPermSize 设置方法区内存大小,方法区内存不会被 GC 频繁回收,又称“永久代”。

运行时常量池 Runtime Constant Pool

运行时常量池属于方法区中一部分。有效的字节码文件包含类的版本信息、字段、方法和接口等描述信息之外,还包含常量池表(Constant Pool Table),运行时常量池就是字节码文件中常量池表的运行时表现形式。

线程私有内存区

和共享内存区不同,私有内存区是不允许被所有线程共享访问的。线程私有内存区是只允许被所属的独立线程进行访问的一类内存区域,包括 PC 寄存器,Java 栈,本地方法栈三个。

PC 计数器 Program Counter Register

JVM 中的 PC 计数器(又被称为 PC 寄存器,不同于物理的寄存器,这里只是代称),JVM 中的 PC 寄存器是对物理 PC 寄存器的抽象,线程私有,生命周期和线程生命周期一致。

Java 栈

栈用于存储栈帧(Stack Frame),栈帧中所存储的是局部变量表,操作数栈,以及方法出口等信息。

Java 堆中存储对象实例,Java 栈中局部变量表用于存储各类原始数据类型,引用(reference)以及 returnAddress 类型。

Java 栈允许被实现为固定或者动态扩展内存大小,如果 Java 栈被设定为固定大小,一旦线程请求分配的栈容量超过 JVM 允许最大值,JVM 会抛出一个 StackOverflowError 异常,如果配置动态,则抛出 OutOfMemoryError。

本地方法栈 Native Method Stack

本地方法栈(Native Method Stack)用于支持本地方法(native 方法,比如调用 C/C++ 方法),和 Java 栈作用类似。

一般来说,Java 对象引用涉及到内存三个区域:堆,栈,方法区

Object o = new Object()
  • o 是一个引用,存储在栈中
  • new Object() 实例对象存在堆中
  • 堆中还记录能够查询到此 Object 对象的类型数据(接口,方法,field,对象类型),实际的数据则放在方法区。

垃圾回收算法

垃圾标记

常见的垃圾回收算法是引用计数法和根搜索法,引用计数虽然实现简单粗暴,但是无法解决相互引用,无法释放内存的问题,所以引入了根搜索算法。根搜索算法是以根对象集合作为起始点,按照从上到下的方式搜索被根对象集合所连接的目标对象是否可达,如果不可达,则对象死亡,标记为垃圾对象。

在 HotSpot 中,根对象集合包含:

  • Java 栈中对象引用
  • 本地方法栈中对象引用
  • 运行时常量池中对象引用
  • 方法区中类静态属性的对象引用
  • 与一个类对应的唯一数据类型 Class 对象

垃圾回收

标记压缩算法

垃圾回收分两个阶段,垃圾标记和内存释放,和下面要说的两种回收算法相比,标记清除效率低下,更重要的是,可能造成回收之后内存空间不连续。

复制算法

为了解决标记压缩算法造成的内存碎片问题,JVM 设计者引入了复制算法。Java 堆区如果进一步细分,可以分为新生代,老年代,而新生代又可以分为 Eden 空间,From Survivor 和 To Survivor 空间。在 HotSpot 中,Eden 空间和另外两个空间默认比 8:1,可以通过 -XX:survivorRatio 来调整。执行 Minor GC(新生代垃圾回收)时,Eden 空间中的存活对象会被复制到 To 空间,之前经历过 Minor GC 并且在 From 空间中存活的对象,也会被复制到 To 空间。当下面两种特殊情况下,Eden 和 From 空间中的存活对象不会被复制到 To 空间:

  • 存活对象分代年龄超过 -XX:MaxTenuringThreshold 所指定的阈值,直接晋升到老年代
  • 当 To 空间容量达到阈值,存活对象直接晋升到老年代

当所有存活对象复制到 To 空间或者变为老年代时,剩下都为垃圾对象,意味着 Minor GC,释放掉 Eden 和 From 空间。然后 From 和 To 空间互换。

复制算法适合高效率的 Minor GC,但是不适合老年代的内存回收。

标记压缩算法

因为以上两种算法都有或多或少的问题,所以 JVM 又引入了 标记压缩算法,在成功标记出内存的垃圾对象后,该算法会将所有的存活对象都移动到一个规整的连续的内存空间,然后执行 Full GC 回收无用对象内存空间。当算法成功之后,已用和未用空间各自存放一边。

reference

  • 《Java 虚拟机精讲》

2018-10-22 java , jvm

Go 语言学习笔记 3:基础类型

和大多数强类型语言一样,Go 也有自己基本的类型系统。Go 语言的类型大致可以分为四大类:

  • basic types
  • aggregate types
  • reference types
  • interface types

基础类型(basic types)包括 numbers,strings,booleans。 聚合类型(aggregate types)包括 arrays , structs 。引用类型(reference types) 包括不同组,包括 pointers , slices, maps, functions, channels ,他们都是程序变量或者状态的引用。接口类型 (interface types) 是特殊的一个类型,会在后面单独介绍。

Integers

Go 数值类型包括整型,浮点数和复数。对于整型

  • 有符号 int8、int16、int32 和 int64
  • 无符号 uint8、uint16、uint32 和 uint64
  • 还有对应特定 CPU 的 int 和 uint,在不同平台上可能为 32bit 或者 64 bit。
  • rune 等同于 int32 用来表示 Unicode
  • byte 等同于 uint8 通常用来表示原始数据
  • uintptr 无符号整数类型,用来存储指针,uintptr 通常用在更加底层编程

注意 intint32 不是同一类型。

书中这边还介绍了运算符优先级和类型转换,进制转换的具体问题,详情可以参考。

Floating-Point 浮点类型

Go 提供了两种精度的浮点数 float32float64 ,常量 math.MaxFloat32 表示 float32 能表示的最大数,math.MaxFloat64 同理。

float32 类型的浮点数可以提供大约 6 个十进制数的精度,而 float64 则可以提供约 15 个十进制数的精度;通常应该优先使用 float64 类型。

Complex Numbers 复数

Go 语言提供了两种精度的复数类型:complex64 和 complex128,分别对应 float32 和 float64 两种浮点数精度。内置的 complex 函数用于构建复数,realimag 函数用来返回实部和虚部:

var x complex128 = complex(1, 2) // 1+2i
var y complex128 = complex(3, 4) // 3+4i
fmt.Println(x*y)                 // "(-5+10i)"
fmt.Println(real(x*y))           // "-5"
fmt.Println(imag(x*y))           // "10"

Booleans

布尔值只有两个值 true or false

Strings

字符串是不可变的字节序列,可以包含任何数据,通常文本会解释为 UTF8 编码 Unicode 。len 函数返回的是字节数目。

标准库中有四个包对字符串处理尤为重要:bytes、strings、strconv 和 unicode 包。strings 包提供了许多如字符串的查询、替换、比较、截断、拆分和合并等功能。

bytes 包也提供了很多类似功能的函数,但是针对和字符串有着相同结构的 []byte 类型。因为字符串是只读的,因此逐步构建字符串会导致很多分配和复制。在这种情况下,使用 bytes.Buffer 类型将会更有效,稍后我们将展示。

strconv 包提供了布尔型、整型数、浮点数和对应字符串的相互转换,还提供了双引号转义相关的转换。

unicode 包提供了 IsDigit、IsLetter、IsUpper 和 IsLower 等类似功能,它们用于给字符分类。每个函数有一个单一的 rune 类型的参数,然后返回一个布尔值。而像 ToUpper 和 ToLower 之类的转换函数将用于 rune 字符的大小写转换。所有的这些函数都是遵循 Unicode 标准定义的字母、数字等分类规范。strings 包也有类似的函数,它们是 ToUpper 和 ToLower,将原始字符串的每个字符都做相应的转换,然后返回新的字符串。

Constants

常量表达式在编译器计算,每种常量的潜在类型都是基础类型。

常量声明语句定义了常量的名字,常量值不能修改。

const pi = 3.1415

或者

const (
    e = 2.71
    pi = 3.14
)

iota 常量生成器

常量声明可以使用 iota 常量生成器初始化,用于生成一组相似规则初始化的常量,const 语句中,第一个声明常量所在行,iota 会被置为 0,然后每行加一。

type Weekday int

const (
    Sunday Weekday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)

2018-10-19 go-lang , programming

服务器监控整理

之前也有分享过两个很不错的服务器监控程序 nodequerynetdata。之后又陆陆续续发现了其他一些不错的监控程序,所以就顺手整理一下。

nodequery

这一个产品只需要在服务器上安装一个脚本,该脚本会定时将 Linux 系统状态发送到 nodequery 的网站,在他的网站后台显示,界面非常简介,提供邮件报警服务,简单的使用完全没有任何问题。

唯一的问题就是该网站已经很多年没有更新,很担心后续是否能够继续使用。

netdata

netdata 是一款开源的监控程序,安装简单,安装之后会开启一个服务端口用来展示服务器状态,这个监控页面上各个参数都有非常好看的图表来展示。

主页:https://github.com/netdata/netdata

ServerStatus

上面两种监控方案需要针对每一台服务器进行安装,如果有多台服务器需要在统一的后台进行监控,那么可以选择 ServerStatus ,ServerStatus 是一个开源的监控系统,可以在同一个页面同时检测多台服务器流量,硬盘,内存等多个参数。

主页:https://github.com/BotoX/ServerStatus 中文版:https://github.com/cppla/ServerStatus

eZ Server Monitor

一款非常轻便的服务器监控程序,PHP 脚本。同时提供了 Bash 和 Web 版本。

主页:https://www.ezservermonitor.com/ 源码:https://github.com/shevabam/ezservermonitor-web

reference


2018-10-18 server , monitor , collection

电子书

最近文章

  • MySQL 中的日志配置和管理 MySQL 中默认是没有开启日志记录的,所以需要手动修改配置文件开启日志。而在 MySQL 中我们需要关心的有三类日志:
  • Java 查漏补缺之:ThreadLocal 使用 ThreadLocal 线程本地变量,每个线程保存变量的副本,对副本的改动,对其他的线程而言是透明的。
  • 为知笔记导出和备份 WizNote 已经用了好几年,虽然也一直在续费,但总感觉将死不死,基于整理这几年近 4000 条的笔记的目的,也一方面为迁移出 WizNote 的目的,研究一下 WizNote 笔记导出和备份的方法。
  • Nginx location 匹配规则 之前的关于 Nginx Config 的文章是当时看 Nginx 书记录下来的笔记,很大略,没有实际操作,等终究用到 location 的时候发现还是有很多需要注意的问题,比如匹配的优先顺序,比如 root 和 alias 的区别等等,所以单独拿一篇文章来记录一下目前遇到的问题,并来解决一下。
  • koajs 简单使用 Koa 是一个背靠 Express 的团队设计的全新的 Web 框架,旨在使之成为一个更轻量,更丰富,更加 robust 的基础框架。通过促进异步方法的使用,Koa 允许使用者抛弃 callback 调用,并且大大简化了错误处理。Koa 并没有将中间件绑定到核心代码,而是提供了一组优雅的方法让编写服务更加快速,通过很多第三方的扩展给 Koa 提供服务,从而实现更加丰富完整的 HTTP server。