Drools Workbench 中有很多的 Assets (资源)类型,每一种类型的 asset 都意味着一种类型的规则模型,下面就记录下学习的过程。
这个是最好理解的概念了,和 Java 的对象一样。可以通过基础类型定义一些抽象的概念。
枚举,和常见的枚举也没有太大差别,不过在 Drools 中会被下拉菜单用到。
Fact | Field | Context |
---|---|---|
Applicant | age | [20, 25, 30] |
然后会生成这样的代码
'Applicant.age' : [20,25,30]
如果想要缩写可以使用等号,比如
'Person.gender' : ['M=Male','F=Female']
向导型规则,通过 WHEN ,THEN 语句快速建立规则,相对比较简单的一种。在规则设计器中可以轻松的添加条件和结果规则。
Guided rules 规则相对比较简单适合用于单一简单的规则建立。
向导型决策表是一种以表格形式表现规则的工具,非常适合描述条件判断很多,条件又可以相互组合,有很多决策方案的情况。决策表可以将这些复杂的逻辑以一种精确而简单的表格形式整理出来,通过 Workbench 中直观的表格形式非常清晰。
Drools 中的决策表可以非常轻松的引导用户制作一个基于 UI 的规则,可以定义规则 attributes, metadata, conditions 和 actions。一旦通过 UI 形式定义好规则,那么所有的规则都会编译为 Drools Rule Language(DRL) 规则。
Hit policy 决定了决策表中的每一个规则(每一行)按照什么样的顺序执行,从上往下,或者按照优先级等等
Drools 中支持两种类型的决策表:Extended entry and Limited entry
在创建完 Guided decision tables 之后可以向表中添加列。
必备条件:所有在列参数中使用的 Facts 或者 Fields 都需要提前创建,并且在同一个包中。
步骤:
Conditions 代表着 fact patterns 中表示左侧 “WHEN” 部分的规则。使用该列类型,你可以定义一个或者多个条件列,用来检查特定属性值的输入,然后影响 “THEN” 部分的规则。可以定义 bindings,或者选择之前的定义。
when
$i : IncomeSource( type == "Asset" ) // binds the IncomeSource object to $1 variable
then
...
end
Business Rule Language (BRL) 是规则 “WHEN” 部分,action BRL fragment 是 “THEN” 部分规则。
可以定义 metadata 元素作为列,每一列都代表这普通的 metadata。
action BRL fragment 是 “THEN” 部分的规则,定义该列可以定义 THEN 的动作。
通过该列,可以添加一个或者多个属性,代表着 DRL 规则的属性,比如 Saliance,Enabled, Date-Effective. 通过定义 Salience 100 可以定义优先级。
不过需要注意的是,根据不同的 Hit Policy 设置有些属性可能被禁用。
通过该列,可以定义一些操作,比如删除之前添加的 fact 等等。
通过该列,可以执行之前定义的 work item handler. (work item 可以通过 Menu → Design → Projects → [select project] → Add Asset → Work Item definition 来创建 )
很好理解,通过该列,可以设置一个 field。
通过该列可以给 THEN 部分规则设置一个通过 work item hander 得到的结果。 work item 必须和结果参数使用相同的类型以便于赋值。
当创建 Guided Decision Table Graph 之后系统会自动扫描存在 Guided Decision Tables。
在菜单栏中点击 Documents 添加 graph
规则模板,可以使用占位符来生成模板来给其他使用
向导型决策树,当新建一个决策树之后,编辑器是空白的,左边是可用的数据对象,以及他们的 fields 和 Actions。右边是一张可编辑的图,可以将左侧的内容拖拽到图上来构造一棵树。
构造树有一些简单的限制:
由用户上传一张 excel 表
Decision tables 是 XLS 或者 XLSX spreadsheets ,可以用来定义业务规则。可以直接上传到 Business Central 中。
表中的每一行都是一条规则,列都是条件,动作或者其他规则属性。当创建并上传了决策表之后,规则会被编译成 DRL。
Test Scenario 用来验证规则是否符合预期,当规则发生改变,可以使用 Test Scenario 来回归测试。
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。
如果需要设置 zsh 的 autocomplete 可以
# Setup autocomplete for zsh:
mkdir -p ~/.zsh/completions
cp ./hub-linux-amd64-$VERSION/etc/hub.zsh_completion ~/.zsh/completions/_hub
echo "fpath=(~/.zsh/completions $fpath)" >> ~/.zshrc
echo "autoload -U compinit && compinit" >> ~/.zshrc
echo "eval "$(hub alias -s)"" >> ~/.zshrc
如果是开源项目贡献者,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
维护者工作流目前还没有用到先略过。
将本地 branch push 到 remote,和 git 命令类似
hub push REMOTE[,REMOTE2...] [REF]
比如
hub push origin,staging,qa branch_name
在 GitHub 创建 repo 并且添加 remote.
hub create [-poc] [-d DESC] [-h HOMEPAGE] [[ORGANIZATION/]NAME]
编辑:
git config --global --edit
添加:
[alias]
pr="!f() { \
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD); \
git push -u origin $BRANCH_NAME; \
hub pull-request; \
};f "
这样以后使用 git pr
,就可以实现,push
当前分支,并创建 PR 了。
看 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
查看。
microk8s 只是最精简的安装,所以只有 api-server, controller-manager, scheduler, kubelet, cni, kube-proxy 被安装运行。额外的服务比如 kube-dns, dashboard 可以通过 microk8s.enable
启动
microk8s.enable dns dashboard
禁用
microk8s.disable dns dashboard
可用的扩展
snap disable microk8s # 停止
snap enable microk8s # 重启
microk8s.reset
snap remove microk8s
更多配置参考官网
免责声明:这篇文章只是在了解 Kubernetes 时的一些笔记记录,非常不全面,如果需要全面了解 Kubernetes 那么还请看书或者文档。
Kubernetes is an open-source system for automating deployment, scaling, and management of containerized applications.
Kubernetes 是 Google 开源的容器集群管理系统,为容器化应用提供资源调度,部署运行,服务发现,扩容和缩容等一系列服务。
Kubernetes 最重要的功能就是容器编排(container orchestration),也就是确保所有的容器能够按照预先的设定在物理机或者虚拟机上执行不同的工作流程。容器必须按照约定打包, Kubernetes 会监控所有正在运行的容器,如果容器”死亡“,或者”无响应“,都会被 Kubernetes 处理。
图片来自 Architecture 101
Node(除去 Master 的其他机器), single host, a physical or virtual machine. job is to run pods,可以通过 kubectl get nodes
查看 node,或者 kubectl describe node <node_name>
查看详细信息
Master (集群控制节点), is the control plane of Kubernetes, consists of several components, like API server, a scheduler, and a controller manager.
Replication Controller(RC),核心概念之一,定义一个期望的场景,声明某种 Pod 的副本数量在任意时刻都符合某个期望值
The Sidecar pattern 是在 pod 中除去 main application 容器之外额外附加的一个容器模式。主要的应用感知不到 sidecar 容器。最好的例子就是中心化的日志收集器,主要的应用容器可以将日志打到 stdout,然后 sidecar 容器收集,然后将所有的日志发送到中心日志服务。
这种方式和在主应用中加入日志系统带来的优势是巨大的,首先,应用可以不被中心日志服务拖累,其次如果想要升级或者改变日志服务,只需要更新 sidecar 容器即可,而不需要修改主应用。
关于更加详细的分析可以参考这篇文章
The Ambassador pattern 可以理解为一个代理层,对于一个远程服务,可以表现为一个本地服务加一些规则来代替提供相同的服务。最常见的使用场景就是,有一个 Redis 集群,master 用来写,而其他 replicas 用来读。
一个 local Ambassador 容器通过代理提供服务,然后暴露 Redis 给主应用容器。主应用容器通过 localhost:6379 来连接 Redis,但是实际上是连接到了同一个 pod 中的 ambassador ,这个代理层会过滤请求,发送写请求给真正的 Redis master,然后读请求会随机的发送给从服务器(replicas)。和 Sidecar 模式中主应用容器一样,主应用是不感知这样的模式的。
这种模式的优点是当 Redis 集群配置发生改变时,只需要 ambassador 做相应的修改即可,主应用不用任何改动。
The Adapter pattern 可以理解成将输出标准化。考虑一种模式,一个服务是逐渐发布的,他产生的报告格式可能和之前的格式不相同,但是其他接收输出报告的服务或者应用还没有升级。那么一个 Adapter 容器可以被部署到同一个 pod,将主应用输出的内容转换成老的格式,直到所有的报告消费者都升级完成。Adapter 容器和主应用容器共享一个文件系统,他可以监控本地文件系统,一旦新的应用写入内容,立即将其修改。
单节点 patterns 被 Kubernetes 通过 pod 直接支持。Multi-node 模式,比如 leader election, work queues, 和 scatter-gather 并没有直接支持,但是通过标准接口组合 pods 可以实现。
具体的教程参考官网
microk8s 是另外一个用以提供 Kubernetes 快速安装的工具,参考这里
样例文件:
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 实例spec
Pod 中容器定义发布到 Kubernetes
kubectl create -f nginx-rc.yaml
用命令查看
kubectl get rc
kubectl get pods
Drools 是一个 Java 的商业过程实现,这是 Bob McWhirter 所编写的一个开源项目,由 JBoss 和 Red Hat Inc 支持。 Drools 提供一个核心的 Business Rules Engine(BRE) 和一个网页编写规则的管理系统(Drools Workbench)和 一个 Eclipse IDE 的插件,一同构成完整的 Drools 生态。
Drools 是一个 Java 实现的开源[[规则引擎]] (Rule Engine),或者又被称为 Business Rules Management System(BRMS) 。Drools workbench 被叫做 Drools-WB,KIE-WB(或者也叫 KIE Drools workbench) 组合了 Guvnor, Drools 和 jBPM 插件。1
简单地来说,Drools 是一系列的工具集合允许用户将业务逻辑和数据分离。
Kie Server 是一个模块化的,独立的组件,可以用来演示和执行规则和流程。
KIE 全称是 Knowledge Is Everything 2
Drools 大致可以分为两个部分:Authoring 和 Runtime
Authoring 包括:
Drools Runtime 需要告诉如何执行特定 jar,用户可以在不同的 Runtime 中执行程序。
Working Memory 是 Drools Engine 的核心要素:Facts 被插入的时候。Facts 是 plain Java Classes,被插入到 Working Memory 的 Facts 会被修改或者扩展。
在企业复杂项目演进过程中随着外部条件复杂化会造成不断变化的业务逻辑,在系统实现时需要考虑将应用开发实现和商业业务决策逻辑剥离。这些规则可以集中管理,也可以在运行时动态管理修改,规则引擎正是基于上面的背景诞生的解决方案。
Drools 用来解决复杂规则的问题。现实问题往往会有很多逻辑判断,而如果将这些逻辑判断都编码写死在代码逻辑中,不仅实现混乱,而不易于维护。Drools 可以让应用逻辑和数据逻辑分离,通过直观的规则编排将数据逻辑单独处理。
通常来讲,如果一个系统需要接受一系列的参数,根据这些参数做一些决策,那么 Drools 应该都能够处理。
规则引擎(BRMS)的特点:
可以这么理解规则引擎,是一种在应用程序中可嵌入的组件,将业务逻辑从应用代码中分离,使用行业特定的规则模块编写业务逻辑,接受数据输入,解释业务规则,并根据规则做出业务决策。
规则引擎并不万能,在业务中使用规则引擎需要预先分析业务的使用场景,规则引擎适用于下面的场景:
规则引擎描述做什么,而不是如何去做。规则可以对复杂问题进行简化,规则的事先声明也使得困难问题得以分步解决,并且可以通过规则来验证。不像程序代码,规则使用比较简单的语法规则书写,规则比编码更易读。
数据保存在系统对象,逻辑保存在规则,打破了面向对象编程系统中数据和逻辑耦合的问题。
当逻辑跨领域时更为有用,通过将逻辑规则集中在一起维护,取代了分散在代码中的问题。
Rete 算法,Leaps 算法,提供了系统数据对象有效的匹配。RETE 算法来自 Dr. Charles Forgy 在 1979 年的 《专家系统原理和编程》中 CIS587:The RETE Algorithm
通过规则,可以建立一个可执行的规则库,规则库代表着现实业务策略,理想情况下可读性高的规则还可以作为文档。
通过 DSL 领域特定语言,可以让编码者通过接近自然语言的方式来编写规则。这让非技术人员和领域专家可以使用自己的逻辑来理解和编写规则。
类似于 Eclipse 这样的工具提供了方法用来编辑和管理规则,并且可以用来提供反馈,校验。同时也有审计和调试的工具。
Rule 系统提供了方法可以记录决策的结果,以及如何被决策的过程。
规则引擎至少应该包括:
使用规则引擎遵循五个典型步骤:
一个开放的规则引擎可以被嵌在程序任何位置。
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 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 大体可以分为两个部分:Authoring 构建 和 Runtime 运行。
构建过程涉及到 .drl
规则文件创建,通过上面的 workbench 可以使用界面来创建规则。
运行时则是在执行规则的服务,kie 提供了 server 可以用来执行规则。
如果要 clone KIE 中的规则,那么在项目的 General Settings 中获取 SSH 地址
git clone ssh://0.0.0.0:8001/MySpace/example
这个地址需要注意,如果是使用 Docker 安装的,那么在 clone 的地址中需要加入用户
git clone ssh://[email protected]:8001/MySpace/example
然后再使用密码即可。
Netty 是异步、事件驱动的网络框架,可以用于开发高性能的网络服务器程序。
传统的多线程服务端程序是 Blocking (阻塞的),也就是接受客户端连接,读数据,发送数据是阻塞的,线程必须处理完才能继续下一个请求。而 Netty 的 NIO 采用事件机制,将连接,读,写分开,使用很少的线程就能够异步 IO。Netty 是在 Java NIO 的基础上的一层封装。
Netty 的官方文档和入门手册已经非常详细了,几乎是手把手的实现了 DISCARD ,ECHO 和 TIMESERVER 的例子,把官方的例子实现一遍对 Netty 就会有一点的了解了。
首先要了解 TCP 的粘包和拆包,TCP 是一个流协议,是一串没有边界的数据,TCP 并不了解上层业务数据含义,他会根据 TCP 缓冲区实际情况进行包划分,所以业务上,一个完整的包可能被 TCP 拆分为多个包发送,也可能把多个小包封装为一个大数据包发送。
业界对 TCP 粘包和拆包的解决方案:
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 在 RPC 框架中有大量的使用,提到 RPC 就不得不提 Java 的编解码。Java 序列化的主要目的:
但是 Java 序列化也有缺陷:
代码库:https://gitlab.com/einverne/netty-guide-book
在看 Java 的 Netty 的时候,了解到了 NIO,从 NIO 了解到了 C 语言实现的 libevent 。我们为什么需要这样一个库,他的出现是为了解决什么问题。对于熟悉网络编程,或者多线程的人来说,都会知道一个普遍存在的问题,CPU 要远远快过 IO。所以如果我们要同时处理多个任务,而当前的任务阻塞了 IO,那么理想的状态应该是让 CPU 执行其他任务,而让阻塞 IO 的任务放到后台执行。
libevent 库提供了一种事件响应机制,当事件发生在用户关心的文件描述符上时,通知用户,并且隐藏后台真正使用的方法(select,epoll,kqueue) ,这避免了让用户为各个平台书写不同代码的问题。
Linux 内核将所有外部设备看做一个文件来操作,对一个文件的读写操作会调用内核提供的命令,返回 file descriptor。对一个 socket 的读写也有相应的描述符,socketfd,描述符是一个数字,指向内核中一个结构体。
UNIX 网络编程对 IO 模型划分了 5 类:
更多的可以参考《UNIX 网络编程》这本书。
NIO 在 JDK 1.4 引入,弥补了 JAVA 原来的同步阻塞 IO 的不足。
Buffer 是一个对象,包含一些要写入或者要读的数据。在面向流的 IO 中,数据可以直接写入或者读取到 Stream 对象中,在 NIO 库中,所有的数据都是用缓冲区处理。
缓冲区实质上是数组,通常是字节数组 ByteBuffer,缓冲区也不仅是一个数组,缓冲区提供了数据结构化访问以及维护读写位置等信息。
最常用的是 ByteBuffer ,但是每一种 Java 基本类型都对应一个缓冲区。
网络数据通过 Channel 读写,通道和流不同的是通道是双向的,流只是一个方向的移动,通道可以同时用于读、写或者同时进行。
Channel 可以分为两类:
Selector 会不断轮询注册在上面的 Channel,如果某 Channel 发生读写时间,Channel 处于就绪状态,被 Selector 轮询出来,通过 SelectionKey 获取就绪 Channel 集合,进行后续 IO。
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 频繁回收,又称“永久代”。
运行时常量池属于方法区中一部分。有效的字节码文件包含类的版本信息、字段、方法和接口等描述信息之外,还包含常量池表(Constant Pool Table),运行时常量池就是字节码文件中常量池表的运行时表现形式。
和共享内存区不同,私有内存区是不允许被所有线程共享访问的。线程私有内存区是只允许被所属的独立线程进行访问的一类内存区域,包括 PC 寄存器,Java 栈,本地方法栈三个。
JVM 中的 PC 计数器(又被称为 PC 寄存器,不同于物理的寄存器,这里只是代称),JVM 中的 PC 寄存器是对物理 PC 寄存器的抽象,线程私有,生命周期和线程生命周期一致。
Java 虚拟机栈描述的是 Java 方法执行的内存模型:每个方法执行时会创建栈帧(Stack Frame)。
Java 虚拟机栈用于存储栈帧(Stack Frame),栈帧中所存储的是局部变量表,操作数栈,以及方法出口等信息。
Java 堆中存储对象实例,Java 栈中局部变量表用于存储各类原始数据类型,引用(reference)以及 returnAddress 类型。
Java 栈允许被实现为固定或者动态扩展内存大小,如果 Java 栈被设定为固定大小,一旦线程请求分配的栈容量超过 JVM 允许最大值,JVM 会抛出一个 StackOverflowError 异常,如果配置动态,则抛出 OutOfMemoryError。
本地方法栈(Native Method Stack)用于支持本地方法(native 方法,比如调用 C/C++ 方法),和 Java 栈作用类似。
一般来说,Java 对象引用涉及到内存三个区域:堆,栈,方法区
Object o = new Object()
o
是一个引用,存储在栈中new Object()
实例对象存在堆中常见的垃圾回收算法是引用计数法和根搜索法,引用计数虽然实现简单粗暴,但是无法解决相互引用,无法释放内存的问题,所以引入了根搜索算法。根搜索算法是以根对象集合作为起始点,按照从上到下的方式搜索被根对象集合所连接的目标对象是否可达,如果不可达,则对象死亡,标记为垃圾对象。
在 HotSpot 中,根对象集合包含:
垃圾回收分两个阶段,垃圾标记和内存释放,和下面要说的两种回收算法相比,标记清除效率低下,更重要的是,可能造成回收之后内存空间不连续。
为了解决标记压缩算法造成的内存碎片问题,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 空间或者变为老年代时,剩下都为垃圾对象,意味着 Minor GC,释放掉 Eden 和 From 空间。然后 From 和 To 空间互换。
复制算法适合高效率的 Minor GC,但是不适合老年代的内存回收。
因为以上两种算法都有或多或少的问题,所以 JVM 又引入了 标记压缩算法,在成功标记出内存的垃圾对象后,该算法会将所有的存活对象都移动到一个规整的连续的内存空间,然后执行 Full GC 回收无用对象内存空间。当算法成功之后,已用和未用空间各自存放一边。
和大多数强类型语言一样,Go 也有自己基本的类型系统。Go 语言的类型大致可以分为四大类:
numbers
,strings
,booleans
arrays
, structs
pointers
, slices
, maps
, functions
, channels
,他们都是程序变量或者状态的引用Go 数值类型包括整型,浮点数和复数。对于整型
rune
等同于 int32 用来表示 Unicodebyte
等同于 uint8 通常用来表示原始数据uintptr
无符号整数类型,用来存储指针,uintptr 通常用在更加底层编程注意 int
和 int32
不是同一类型。
书中这边还介绍了运算符优先级和类型转换,进制转换的具体问题,详情可以参考。
Go 提供了两种精度的浮点数 float32
和 float64
,常量 math.MaxFloat32
表示 float32 能表示的最大数,math.MaxFloat64
同理。
float32 类型的浮点数可以提供大约 6 个十进制数的精度,而 float64 则可以提供约 15 个十进制数的精度;通常应该优先使用 float64 类型。
Go 语言提供了两种精度的复数类型:complex64(8 字节) 和 complex128(16 个字节),分别对应 float32 和 float64 两种浮点数精度。内置的 complex 函数用于构建复数,real
和 imag
函数用来返回实部和虚部:
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"
布尔值只有两个值 true or false
字符串是不可变的字节序列,可以包含任何数据,通常文本会解释为 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,将原始字符串的每个字符都做相应的转换,然后返回新的字符串。
常量表达式在编译器计算,每种常量的潜在类型都是基础类型。
常量声明语句定义了常量的名字,常量值不能被修改。
const pi = 3.1415
或者
const (
e = 2.71
pi = 3.14
)
常量声明可以使用 iota 常量生成器初始化,用于生成一组相似规则初始化的常量,const 语句中,第一个声明常量所在行,iota 会被置为 0,然后每行加一。
type Weekday int
const (
Sunday Weekday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)
手上的 VPS 多余一台的时候总是想着通过一个统一的界面来监控管理。
之前也有分享过两个很不错的服务器监控程序 nodequery 和 netdata。之后又陆陆续续发现了其他一些不错的监控程序,所以就顺手整理一下。
商业方案,也就是提供服务在线监控,并且如果超过一定使用量向用户收取一定费用的服务:
商业开源方案:
Netdata 也提供一个免费的云端 Netdata Cloud。
Self-hosted 方案,容器化方案,所有监控内容可以直接通过容器部署:
[[2021-08-28-nezha-monitor | 哪吒监控安装及简单使用说明]] |
SyAgent 是我在 LET 上面看到的一个选项,不过该网站由个人运营维护。
EHEH 是一个闭源的监控平台,类似于 Nodequery,安全性待评估。
服务已经不存在。果然闭源的不可信啊。
一个容器化解决方案,集成了 Prometheus, Grafana, cAdvisor 等等。
Zabbix 是一个企业级的开源监控方案。
GPL 开源协议。
[[Cockpit]] 是一款开源的 Linux 服务器管理解决方案。可以执行如启动容器,管理存储,配置网络,检查日志等操作。它能够同时控制和监控多台服务器。它提供的主要功能如:可视化的监控系统性能,管理 Docker 容器,终端窗口中基于 Web,管理用户帐号,收集系统配置和诊断信息,修改网络设置等。它的配置文档也很实用,可以快速安装并开始监控服务器。
这一个产品只需要在服务器上安装一个脚本,该脚本会定时将 Linux 系统状态发送到 nodequery 的网站,在他的网站后台显示,界面非常简介,提供邮件报警服务,简单的使用完全没有任何问题。
唯一的问题就是该网站已经很多年没有更新,很担心后续是否能够继续使用。
担心的事情总是会发生,nodequery 已经停止了服务。
[[Netdata]] 是一款开源的监控程序,安装简单,安装之后会开启一个服务端口用来展示服务器状态,这个监控页面上各个参数都有非常好看的图表来展示。
主页:https://github.com/netdata/netdata
上面两种监控方案需要针对每一台服务器进行安装,如果有多台服务器需要在统一的后台进行监控,那么可以选择 ServerStatus ,ServerStatus 是一个开源的监控系统,可以在同一个页面同时检测多台服务器流量,硬盘,内存等多个参数。
主页:https://github.com/BotoX/ServerStatus 中文版:https://github.com/cppla/ServerStatus
一款非常轻便的服务器监控程序,PHP 脚本。同时提供了 Bash 和 Web 版本。
主页:https://www.ezservermonitor.com/ 源码:https://github.com/shevabam/ezservermonitor-web
[[nezha 哪吒面板]] 是一款使用 Go 编写的服务器内存,CPU,网络使用情况的开源监控面板。
Datadog 是一个企业级的服务监控,日志记录等等平台。