整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

「云原生」Helm 架构和基础语法详解

「云原生」Helm 架构和基础语法详解

、概述

我们可以将Helm看作Kubernetes下的apt-get/yum。Helm是kubernetes的包管理器,helm仓库里面只有配置清单文件,而没有镜像,镜像还是由镜像仓库来提供,比如hub.docker.com、私有仓库。

官方文档:https://v3.helm.sh/zh/docs/

其实之前也写过关于helm的一篇文章【Kubernetes(k8s)包管理器Helm(Helm3)介绍&Helm3安装Harbor】,可能讲的不够细致,这里会更加细致的讲解helm。

二、Helm 架构

三、Helm 安装

下载地址:https://github.com/helm/helm/releases

# 下载包
$  wget https://get.helm.sh/helm-v3.9.4-linux-amd64.tar.gz
# 解压压缩包
$ tar -xf helm-v3.9.4-linux-amd64.tar.gz
# 制作软连接
$ ln -s /opt/helm/linux-amd64/helm /usr/local/bin/helm
# 验证
$ helm version
$ helm help

四、Helm 组件及相关术语

  • Helm——Helm 是一个命令行下的客户端工具。主要用于 Kubernetes 应用程序 Chart 的创建、打包、发布以及创建和管理本地和远程的 Chart 仓库。
  • Chart——Chart 代表着 Helm 包。它包含在 Kubernetes 集群内部运行应用程序,工具或服务所需的所有资源定义。你可以把它看作是 Homebrew formula,Apt dpkg,或 Yum RPM 在Kubernetes 中的等价物。
  • Release——Release 是运行在 Kubernetes 集群中的 chart 的实例。一个 chart 通常可以在同一个集群中安装多次。每一次安装都会创建一个新的 release。
  • Repoistory——Repository(仓库) 是用来存放和共享 charts 的地方。它就像 Perl 的 CPAN 档案库网络 或是 Fedora 的 软件包仓库,只不过它是供 Kubernetes 包所使用的。

五、Helm Chart 详解

1)Chart 目录结构

# 通过helm create命令创建一个新的chart包
helm create nginx
tree nginx

nginx/
├── charts  #依赖其他包的charts文件
├── Chart.yaml # 该chart的描述文件,包括ico地址,版本信息等
├── templates  # #存放k8s模板文件目录
│   ├── deployment.yaml # 创建k8s资源的yaml 模板
│   ├── _helpers.tpl # 下划线开头的文件,可以被其他模板引用
│   ├── hpa.yaml # 弹性扩缩容,配置服务资源CPU 内存
│   ├── ingress.yaml # ingress 配合service域名访问的配置
│   ├── NOTES.txt # 说明文件,helm install之后展示给用户看的内容
│   ├── serviceaccount.yaml # 服务账号配置
│   ├── service.yaml # kubernetes Serivce yaml 模板
│   └── tests # 测试模块
│       └── test-connection.yaml 
└── values.yaml # 给模板文件使用的变量

可能有写包还会有以下几个目录:

wordpress/
...
  LICENSE             # 可选: 包含chart许可证的纯文本文件
  README.md           # 可选: 可读的README文件
  values.schema.json  # 可选: 一个使用JSON结构的values.yaml文件
  charts/             # 包含chart依赖的其他chart
  crds/               # 自定义资源的定义
...

2)Chart.yaml 文件

apiVersion: chart API 版本 (必需)
name: chart名称 (必需)
version: chart 版本,语义化2 版本(必需)
kubeVersion: 兼容Kubernetes版本的语义化版本(可选)
description: 一句话对这个项目的描述(可选)
type: chart类型 (可选)
keywords:
  - 关于项目的一组关键字(可选)
home: 项目home页面的URL (可选)
sources:
  - 项目源码的URL列表(可选)
dependencies: # chart 必要条件列表 (可选)
  - name: chart名称 (nginx)
    version: chart版本 ("1.2.3")
    repository: (可选)仓库URL ("https://example.com/charts") 或别名 ("@repo-name")
    condition: (可选) 解析为布尔值的yaml路径,用于启用/禁用chart (e.g. subchart1.enabled )
    tags: # (可选)
      - 用于一次启用/禁用 一组chart的tag
    import-values: # (可选)
      - ImportValue 保存源值到导入父键的映射。每项可以是字符串或者一对子/父列表项
    alias: (可选) chart中使用的别名。当你要多次添加相同的chart时会很有用
maintainers: # (可选)
  - name: 维护者名字 (每个维护者都需要)
    email: 维护者邮箱 (每个维护者可选)
    url: 维护者URL (每个维护者可选)
icon: 用做icon的SVG或PNG图片URL (可选)
appVersion: 包含的应用版本(可选)。不需要是语义化,建议使用引号
deprecated: 不被推荐的chart (可选,布尔值)
annotations:
  example: 按名称输入的批注列表 (可选).
  • 从 v3.3.2,不再允许额外的字段。推荐的方法是在 annotations 中添加自定义元数据。
  • 每个chart都必须有个版本号(version)。版本必须遵循 语义化版本 2 标准。 不像经典Helm, Helm v2以及后续版本会使用版本号作为发布标记。仓库中的包通过名称加版本号标识。

比如 nginx chart的版本字段version: 1.2.3按照名称被设置为:

nginx-1.2.3.tgz

【温馨提示】appVersion字段与version字段并不相关。这是指定应用版本的一种方式。比如,这个drupal chart可能有一个 appVersion: "8.2.1",表示包含在chart(默认)的Drupal的版本是8.2.1。

3)Chart 依赖管理(dependencies)

当前chart依赖的其他chart会在dependencies字段定义为一个列表。

dependencies:
  - name: apache
    version: 1.2.3
    repository: https://example.com/charts
  - name: mysql
    version: 3.2.1
    repository: https://another.example.com/charts
  • name字段是你需要的chart的名称
  • version字段是你需要的chart的版本
  • repository字段是chart仓库的完整URL。注意你必须使用helm repo add在本地添加仓库
  • 你可以使用仓库的名称代替URL

示例演示:

helm repo add bitnami https://charts.bitnami.com/bitnami
helm pull bitnami/wordpress
tar -xf wordpress
cat wordpress/Chart.yaml


一旦你定义好了依赖,运行
helm dependency update 就会使用你的依赖文件下载所有你指定的chart到你的charts/目录。

helm dependency update ./wordpress

当 helm dependency update 拉取chart时,会在charts/目录中形成一个chart包。因此对于上面的示例,会在chart目录中期望看到以下文件:

wordpress/charts/
├── common
├── common-2.0.1.tgz
├── mariadb
├── mariadb-11.2.2.tgz
├── memcached
└── memcached-6.2.3.tgz

依赖中的tag和条件字段

除了上面的其他字段外,每个需求项可以包含可选字段 tagscondition所有的chart会默认加载。如果存在 tags 或者 condition 字段,它们将被评估并用于控制它们应用的chart的加载。

  • Condition ——条件字段field 包含一个或多个YAML路径(用逗号分隔)。 如果这个路径在上层values中已存在并解析为布尔值,chart会基于布尔值启用或禁用chart。 只会使用列表中找到的第一个有效路径,如果路径为未找到则条件无效。
  • Tags——tag字段是与chart关联的YAML格式的标签列表。在顶层value中,通过指定tag和布尔值,可以启用或禁用所有的带tag的chart。
# parentchart/Chart.yaml

dependencies:
  - name: subchart1
    repository: http://localhost:10191
    version: 0.1.0
    condition: subchart1.enabled, global.subchart1.enabled
    tags:
      - front-end
      - subchart1
  - name: subchart2
    repository: http://localhost:10191
    version: 0.1.0
    condition: subchart2.enabled,global.subchart2.enabled
    tags:
      - back-end
      - subchart2
# parentchart/values.yaml

subchart1:
  enabled: true
tags:
  front-end: false
  back-end: true
  • 在上面的例子中,所有带 front-end tag的chart都会被禁用,但只要上层的value中 subchart1.enabled 路径被设置为'true'该条件会覆盖 front-end标签且 subchart1 会被启用
  • 一旦 subchart2使用了back-end标签并被设置为了 true,subchart2就会被启用。 也要注意尽管subchart2 指定了一个条件字段, 但是上层value没有相应的路径和value,因此这个条件不会生效。

--set 参数可以用来设置标签和条件值。

helm install --set tags.front-end=true --set subchart2.enabled=false

标签和条件的解析:

  • 条件 (当设置在value中时)总是会覆盖标签 第一个chart条件路径存在时会忽略后面的路径。
  • 标签被定义为 '如果任意的chart标签是true,chart就可以启用'。
  • 标签和条件值必须被设置在顶层value中。
  • value中的tags:键必须是顶层键。

4)通过依赖导入子Value

  • 在某些情况下,允许子chart的值作为公共默认传递到父chart中是值得的。使用 exports格式的额外好处是它可是将来的工具可以自检用户可设置的值。
  • 被导入的包含值的key可以在父chart的 dependencies 中的 import-values字段以YAML列表形式指定。 列表中的每一项是从子chart中exports字段导入的key。
  • 导入exports key中未包含的值,使用 子-父格式。两种格式的示例如下所述。

使用导出格式:
如果子chart的values.yaml文件中在根节点包含了exports字段,它的内容可以通过指定的可以被直接导入到父chart的value中, 如下所示:

# parent's Chart.yaml file

dependencies:
  - name: subchart
    repository: http://localhost:10191
    version: 0.1.0
    import-values:
      - data
# child's values.yaml file

exports:
  data:
    myint: 99

只要我们再导入列表中指定了键data,Helm就会在子chart的exports字段查找data键并导入它的内容。

最终的父级value会包含我们的导出字段:

# parent's values

myint: 99

【注意】父级键 data 没有包含在父级最终的value中,如果想指定这个父级键,要使用'子-父' 格式

下面示例中的import-values 指示Helm去拿到能再child:路径中找到的任何值,并拷贝到parent:的指定路径。

# parent's Chart.yaml file

dependencies:
  - name: subchart1
    repository: http://localhost:10191
    version: 0.1.0
    ...
    import-values:
      - child: default.data
        parent: myimports

上面的例子中,在subchart1里面找到的default.data的值会被导入到父chart的myimports键中,细节如下:

# parent's values.yaml file

myimports:
  myint: 0
  mybool: false
  mystring: "helm rocks!"
# subchart1's values.yaml file

default:
  data:
    myint: 999
    mybool: true

父chart的结果值将会是这样:

# parent's final values

myimports:
  myint: 999
  mybool: true
  mystring: "helm rocks!"

六、Templates and Values

1)Templates and Values 简介

  • Helm Chart 模板是按照 Go模板语言书写, 增加了50个左右的附加模板函数 来自 Sprig库 和一些其他 指定的函数。
  • 所有模板文件存储在chart的 templates/ 文件夹。 当Helm渲染chart时,它会通过模板引擎遍历目录中的每个文件。

模板的Value通过两种方式提供:

  • Chart开发者可以在chart中提供一个命名为 values.yaml 的文件。这个文件包含了默认值。
  • Chart用户可以提供一个包含了value的YAML文件。可以在命令行使用 helm install命令时通过-f指定value文件。

模板示例

apiVersion: v1
kind: ReplicationController
metadata:
  name: deis-database
  namespace: deis
  labels:
    app.kubernetes.io/managed-by: deis
spec:
  replicas: 1
  selector:
    app.kubernetes.io/name: deis-database
  template:
    metadata:
      labels:
        app.kubernetes.io/name: deis-database
    spec:
      serviceAccount: deis-database
      containers:
        - name: deis-database
          image: {{ .Values.imageRegistry }}/postgres:{{ .Values.dockerTag }}
          imagePullPolicy: {{ .Values.pullPolicy }}
          ports:
            - containerPort: 5432
          env:
            - name: DATABASE_STORAGE
              value: {{ default "minio" .Values.storage }}

上面的例子,松散地基于 https://github.com/deis/charts, 是一个Kubernetes副本控制器的模板。可以使用下面四种模板值(一般被定义在values.yaml文件):

  • imageRegistry: Docker镜像的源注册表
  • dockerTag: Docker镜像的tag
  • pullPolicy: Kubernetes的拉取策略
  • storage: 后台存储,默认设置为"minio"

2)预定义的 Values

Values通过模板中.Values对象可访问的values.yaml文件(或者通过 --set 参数)提供, 但可以模板中访问其他预定义的数据片段。

以下值是预定义的,对每个模板都有效,并且可以被覆盖。和所有值一样,名称 区分大小写。

  • Release.Name: 版本名称(非chart的)
  • Release.Namespace: 发布的chart版本的命名空间
  • Release.Service: 组织版本的服务
  • Release.IsUpgrade: 如果当前操作是升级或回滚,设置为true
  • Release.IsInstall: 如果当前操作是安装,设置为true
  • Chart: Chart.yaml的内容。因此,chart的版本可以从 Chart.Version 获得, 并且维护者在Chart.Maintainers里。
  • Files: chart中的包含了非特殊文件的类图对象。这将不允许您访问模板, 但是可以访问现有的其他文件(除非被.helmignore排除在外)。 使用{{ index .Files "file.name" }}可以访问文件或者使用{{.Files.Get name }}功能。 您也可以使用{{ .Files.GetBytes }}作为[]byte访问文件内容。
  • Capabilities: 包含了Kubernetes版本信息的类图对象。({{ .Capabilities.KubeVersion }}) 和支持的Kubernetes API 版本({{ .Capabilities.APIVersions.Has "batch/v1" }})

考虑到前面部分的模板,values.yaml文件提供的必要值如下:

imageRegistry: "quay.io/deis"
dockerTag: "latest"
pullPolicy: "Always"
storage: "s3"

values文件被定义为YAML格式。chart会包含一个默认的values.yaml文件。 Helm安装命令允许用户使用附加的YAML values覆盖这个values:

helm install --generate-name --values=myvals.yaml wordpress

3)范围,依赖和值

Values文件可以声明顶级chart的值,以及charts/目录中包含的其他任意chart。 或者换个说法,values文件可以为chart及其任何依赖项提供值。比如,上面示范的WordPress chart同时有 mysql 和 apache 作为依赖。values文件可以为以下所有这些组件提供依赖:

title: "My WordPress Site" # Sent to the WordPress template

mysql:
  max_connections: 100 # Sent to MySQL
  password: "secret"

apache:
  port: 8080 # Passed to Apache

高阶的chart可以访问下面定义的所有变量。因此WordPress chart可以用.Values.mysql.password访问MySQL密码。 但是低阶的chart不能访问父级chart,所以MySQL无法访问title属性。同样也无法访问apache.port。

4)全局Values

从2.0.0-Alpha.2开始,Helm 支持特殊的"global"值。设想一下前面的示例中的修改版本:

title: "My WordPress Site" # Sent to the WordPress template

global:
  app: MyWordPress

mysql:
  max_connections: 100 # Sent to MySQL
  password: "secret"

apache:
  port: 8080 # Passed to Apache

面添加了global部分和一个值app: MyWordPress。这个值以.Values.global.app所有 chart中有效

比如,mysql模板可以以{{.Values.global.app}}访问app,同样apache chart也可以访问。 实际上,上面的values文件会重新生成为这样:

title: "My WordPress Site" # Sent to the WordPress template

global:
  app: MyWordPress

mysql:
  global:
    app: MyWordPress
  max_connections: 100 # Sent to MySQL
  password: "secret"

apache:
  global:
    app: MyWordPress
  port: 8080 # Passed to Apache

七、Helm 资源安装顺序

  • Namespace
  • NetworkPolicy
  • ResourceQuota
  • LimitRange
  • PodSecurityPolicy
  • PodDisruptionBudget
  • ServiceAccount
  • Secret
  • SecretList
  • ConfigMap
  • StorageClass
  • PersistentVolume
  • PersistentVolumeClaim
  • CustomResourceDefinition
  • ClusterRole
  • ClusterRoleList
  • ClusterRoleBinding
  • ClusterRoleBindingList
  • Role
  • RoleList
  • RoleBinding
  • RoleBindingList
  • Service
  • DaemonSet
  • Pod
  • ReplicationController
  • ReplicaSet
  • Deployment
  • HorizontalPodAutoscaler
  • StatefulSet
  • Job
  • CronJob
  • Ingress
  • APIService

八、Helm 安装 Chart 包的三种方式

Helm 自带一个强大的搜索命令,可以用来从两种来源中进行搜索:

  • helm search hub 从 Artifact Hub 中查找并列出 helm charts。 Artifact Hub中存放了大量不同的仓库。
  • helm search repo 从你添加(使用 helm repo add)到本地 helm 客户端中的仓库中进行查找。该命令基于本地数据进行搜索,无需连接互联网。
# 添加bitnami仓库源
helm repo add bitnami https://charts.bitnami.com/bitnami
# 从bitnami源查找所有chart包,不指定具体源的话,会查找本地添加的所有源地址的所有chart包
helm search repo bitnami

1)values 传参

安装过程中有两种方式传递配置数据:

  • --values (或 -f):使用 YAML 文件覆盖配置。可以指定多次,优先使用最右边的文件。
  • --set:通过命令行的方式对指定项进行覆盖。

如果同时使用两种方式,则 --set 中的值会被合并到 --values 中,但是 --set中的值优先级更高。在--set 中覆盖的内容会被被保存在 ConfigMap 中。可以通过 helm get values <release-name>查看指定 release 中 --set 设置的值。也可以通过运行 helm upgrade 并指定 --reset-values 字段来清除 --set 中设置的值。示例如下:

echo '{mariadb.auth.database: user0db, mariadb.auth.username: user0}' > values.yaml
helm install -f values.yaml bitnami/wordpress --generate-name

2)【第一种方式】直接在线 安装不需要先下载包到本地

helm install mysql bitnami/mysql
helm list

3)【第二种方式】离线安装 直接通过安装包安装

# 先删除
helm uninstall mysql
# 拉包到本地
helm pull bitnami/mysql
# 不解压直接安装
helm install mysql ./mysql-9.3.1.tgz
helm list

4)【第三种方式】离线安装 解压包再安装

# 拉包到本地
helm pull bitnami/mysql
# 解压安装
tar -xf mysql-9.3.1.tgz

# 开始安装
helm install mysql ./mysql \
--namespace=mysql \
--create-namespace \
--set image.registry=myharbor.com \
--set image.repository=bigdata/mysql \
--set image.tag=8.0.30 \
--set primary.service.type=NodePort \
--set service.nodePorts.mysql=30306

# 查看在运行的Release
helm list

# 卸载
helm uninstall mysql -n mysql 

九、Helm 基础语法

1)变量

模板(templates/)中的变量都放在{{}}中,比如:{{ .Values.images }} 表示 Values 对象下的images 字段。Values来源于values.yaml文件或者-f指定的yaml文件,或者--set设置的变量。

【温馨提示】使用-删除空格和换行符,要想删除那行其他的空格和换行符可以用{{-或者-}}一个是删除左边的空格换行符一个是删除右边的空格换行符

2)内置对象

  • Release: Release对象描述了版本发布本身。包含了以下对象:Release.Name: release名称;Release.Namespace: 版本中包含的命名空间(如果manifest没有覆盖的话);Release.IsUpgrade: 如果当前操作是升级或回滚的话,该值将被设置为trueRelease.IsInstall: 如果当前操作是安装的话,该值将被设置为trueRelease.Revision: 此次修订的版本号。安装时是1,每次升级或回滚都会自增;Release.Service: 该service用来渲染当前模板。Helm里始终Helm。
  • Values: Values对象是从values.yaml文件和用户提供的文件传进模板的。默认为空
  • ChartChart.yaml文件内容。 Chart.yaml里的所有数据在这里都可以可访问的。比如 {{ .Chart.Name }}-{{ .Chart.Version }} 会打印出 mychart-0.1.0。
  • Template: 包含当前被执行的当前模板信息Template.Name: 当前模板的命名空间文件路径 (e.g. mychart/templates/mytemplate.yaml);Template.BasePath: 当前chart模板目录的路径 (e.g. mychart/templates)。

3)常用的内置函数

1、quote and squote

该函数将值转换成字符串双引号(quote) 或者单引号(squote)括起来。示例如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | quote }}
  food: {{ .Values.favorite.food | upper | quote }}

倒置命令是模板中的常见做法。可以经常看到 .val | quote 而不是 quote .val。实际上两种操作都是可以的。

2、default

这个函数允许你在模板中指定一个默认值,以防这个值被忽略。

# 如果.Values.favorite.drink是非空值,则使用它,否则会返回tea。
drink: {{ .Values.favorite.drink | default "tea" | quote }}

# 还可以这样写,如果.Bar是非空值,则使用它,否则会返回foo。
default "foo" .Bar

"空"定义取决于以下类型:

整型: 0
字符串: ""
列表: []
字典: {}
布尔: false
以及所有的nil (或 null)

3、print

返回各部分组合的字符串,非字符串类型会被转换成字符串。

print "Matt has " .Dogs " dogs"

【温馨提示】当相邻两个参数不是字符串时会在它们之间添加一个空格。

4、println

和 print效果一样,但会在末尾新添加一行。

5、printf

返回参数按顺序传递的格式化字符串。

printf "%s has %d dogs." .Name .NumberDogs
{{- printf "%d" (.Values.externalCache.port | int ) -}}
{{- printf "%s" .Values.existingSecret -}}

{{- printf "%v" .context.Values.redis.enabled -}}

# %s 字符串占位符,未解析的二进制字符串或切片
# %d 数字占位符,十进制
# %v 默认格式的值,当打印字典时,加号参数(%+v)可以添加字段名称

更多占位符的使用,可以参考官方文档:https://helm.sh/zh/docs/chart_template_guide/function_list/

6、trim

trim行数移除字符串两边的空格:

trim "   hello    "

7、trimAll

从字符串中移除给定的字符:

trimAll "$" ".00"

上述结果为:5.00 (作为一个字符串)。

8、lower

将整个字符串转换成小写:

lower "HELLO"

上述结果为: hello

9、upper

将整个字符串转换成大写:

upper "hello"

上述结果为: HELLO

10、title

首字母转换成大写:

title "hello world"

上述结果为: Hello World

11、substr

获取字符串的子串,有三个参数:

  • start (int)
  • end (int)
  • string (string)
substr 0 5 "hello world"

上述结果为: hello

12、abbrev

用省略号截断字符串 (...)

abbrev 5 "hello world"
# 第一个参数:最大长度
# 第二个参数:字符串

上述结果为: he..., 因为将省略号算进了长度中。

13、contains

测试字符串是否包含在另一个字符串中:

contains "cat" "catch"

14、cat

cat 函数将多个字符串合并成一个,用空格分隔:

cat "hello" "beautiful" "world"

上述结果为: hello beautiful world

15、indent

indent 以指定长度缩进给定字符串所在行,在对齐多行字符串时很有用:

indent 4 $lots_of_text

上述结果会将每行缩进4个空格。

16、nindent

nindent 函数和indent函数一样,但可以在字符串开头添加新行。

nindent 4 $lots_of_text

上述结果会在字符串所在行缩进4个字符,并且在开头新添加一行。

17、replace

执行简单的字符串替换。

# 下面两行等价
replace " " "-" "I Am Henry VIII" 
"I Am Henry VIII" | replace " " "-"

# 参数1:待替换字符串
# 参数2:要替换字符串
# 参数3:源字符串

上述结果为: I-Am-Henry-VIII

18、date

date函数格式化日期,日期格式化为YEAR-MONTH-DAY:

now | date "2006-01-02"

想了解更多内置函数,可以参考官方文档:https://helm.sh/zh/docs/chart_template_guide/function_list/

4)类型转换函数

Helm提供了以下类型转换函数:

  • atoi: 字符串转换成整型。
  • float64: 转换成 float64。
  • int: 按系统整型宽度转换成int。
  • int64: 转换成 int64。
  • toDecimal: 将unix八进制转换成int64。
  • toString: 转换成字符串。
  • toStrings: 将列表、切片或数组转换成字符串列表。
  • toJson (mustToJson): 将列表、切片、数组、字典或对象转换成JSON。
  • toPrettyJson (mustToPrettyJson): 将列表、切片、数组、字典或对象转换成格式化JSON。
  • toRawJson (mustToRawJson): 将列表、切片、数组、字典或对象转换成HTML字符未转义的JSON。

5)正则表达式(Regular Expressions)

Helm 包含以下正则表达式函数

  • regexFind(mustRegexFind)
  • regexFindAll(mustRegexFindAll)
  • regexMatch (mustRegexMatch)
  • regexReplaceAll (mustRegexReplaceAll)
  • regexReplaceAllLiteral(mustRegexReplaceAllLiteral)
  • regexSplit (mustRegexSplit)

6)编码和解码函数

Helm有以下编码和解码函数:

  • b64enc/b64dec: 编码或解码 Base64
  • b32enc/b32dec: 编码或解码 Base32

7)Dictionaries and Dict Functions

Helm 提供了一个key/value存储类型称为dict("dictionary"的简称,Python中也有)。dict是无序类型。字典的key 必须是字符串但值可以是任意类型,甚至是另一个dict 或 list

1、创建字典(dict)

下面是创建三个键值对的字典:

$myDict :=dict "name1" "value1" "name2" "value2" "name3" "value 3"

2、获取值(get)

给定一个映射和一个键,从映射中获取值。

get $myDict "name1"

上述结果为: "value1"

注意如果没有找到,会简单返回""。不会生成error。

3、添加键值对(set)

使用set给字典添加一个键值对。

$_ :=set $myDict "name4" "value4"

注意set 返回字典 (Go模板函数的一个要求),因此你可能需要像上面那样使用使用$_赋值来获取值。

4、删除(unset)

给定一个映射和key,从映射中删除这个key。

$_ :=unset $myDict "name4"

和set一样,需要返回字典。

5、判断key(hasKey)

hasKey函数会在给定字典中包含了给定key时返回true。

hasKey $myDict "name1"

如果key没找到,会返回false。

6、pluck

pluck 函数给定一个键和多个映射,并获得所有匹配项的列表:

pluck "name1" $myDict $myOtherDict

上述会返回的list包含了每个找到的值([value1 otherValue1])。

7、合并 dict(merge, mustMerge)

将两个或多个字典合并为一个, 目标字典优先:

$newdict :=merge $dest $source1 $source2

8、获取所有 keys

keys函数会返回一个或多个dict类型中所有的key的list。由于字典是 无序的,key不会有可预料的顺序。 可以使用sortAlpha存储。

keys $myDict | sortAlpha

当提供了多个词典时,key会被串联起来。使用uniq函数和sortAlpha获取一个唯一有序的键列表。

keys $myDict $myOtherDict | uniq | sortAlpha

9、获取所有 values

values函数类似于keys,返回一个新的list包含源字典中所有的value(只支持一个字典)。

$vals :=values $myDict

上述结果为: list["value1", "value2", "value 3"]。

注意 values不能保证结果的顺序;如果你需要顺序, 请使用sortAlpha

8)Lists and List Functions

Helm 提供了一个简单的list类型,包含任意顺序的列表。类似于数组或切片,但列表是被设计用于不可变数据类型。

1、创建列表

$myList :=list 1 2 3 4 5

上述会生成一个列表 [1 2 3 4 5]。

2、获取列表第一项(first, mustFirst)

获取列表中的第一项,使用 first。

first $myList
# 返回 1

first 有问题时会出错,mustFirst 有问题时会向模板引擎返回错误。

3、获取列表的尾部内容(rest, mustRest)

获取列表的尾部内容(除了第一项外的所有内容),使用rest。

rest $myList
# 返回 [2 3 4 5]

rest有问题时会出错,mustRest 有问题时会向模板引擎返回错误。

4、获取列表的最后一项(last, mustLast)

使用last获取列表的最后一项:

last $myList 
# 返回 5。这大致类似于反转列表然后调用first。

5、获取列表所有内容(initial, mustInitial)

通过返回所有元素 但 除了最后一个元素。

 initial $myList
 # 返回 [1 2 3 4]。

initial有问题时会出错,但是 mustInitial 有问题时会向模板引擎返回错误。

6、末尾添加元素(append, mustAppend)

在已有列表中追加一项,创建一个新的列表。

$new=append $myList 6

上述语句会设置 $new 为 [1 2 3 4 5 6]。 $myList会保持不变。

append 有问题时会出错,但 mustAppend 有问题时会向模板引擎返回错误。

7、前面添加元素(prepend, mustPrepend)

将元素添加到列表的前面,生成一个新的列表。

prepend $myList 0

上述语句会生成 [0 1 2 3 4 5]。 $myList会保持不变。

prepend 有问题时会出错,但 mustPrepend 有问题时会向模板引擎返回错误。

8、多列表连接(concat)

将任意数量的列表串联成一个。

concat $myList ( list 6 7 ) ( list 8 )

上述语句会生成 [1 2 3 4 5 6 7 8]。 $myList 会保持不变。

9、反转(reverse, mustReverse)

反转给定的列表生成一个新列表。

reverse $myList

上述语句会生成一个列表: [5 4 3 2 1]。

reverse 有问题时会出错,但 mustReverse 有问题时会向模板引擎返回错误。

10、去重(uniq, mustUniq)

生成一个移除重复项的列表。

list 1 1 1 2 | uniq

上述语句会生成 [1 2]

uniq 有问题时会出错,但 mustUniq 有问题时会向模板引擎返回错误。

11、过滤(without, mustWithout)

without 函数从列表中过滤内容。

without $myList 3
# 上述语句会生成 [1 2 4 5]

一个过滤器可以过滤多个元素:

without $myList 1 3 5
# 这样会得到: [2 4]

without 有问题时会出错,但 mustWithout 有问题时会向模板引擎返回错误。

12、判断元素是否存在(has, mustHas)

验证列表是否有特定元素。

has 4 $myList

上述语句会返回 true, 但 has "hello" $myList 就会返回false。

has 有问题时会出错,但 mustHas 有问题时会向模板引擎返回错误。

13、删除空项(compact, mustCompact)

接收一个列表并删除空值项。

$list :=list 1 "a" "foo" ""
$copy :=compact $list

compact 会返回一个移除了空值(比如, "")的新列表。

compact 有问题时会出错,但 mustCompact 有问题时会向模板引擎返回错误。

14、index

使用index list [n]获取列表的第n个元素。使用index list [n] [m] ...获取多位列表元素。

  • index $myList 0 返回 1,同 myList[0]
  • index $myList 0 1 同 myList[0][1]

15、获取部分元素(slice, mustSlice)

从列表中获取部分元素,使用 slice list [n] [m]。等同于 list[n:m].

  • slice $myList 返回 [1 2 3 4 5]。 等同于 myList[:]。
  • slice $myList 3 返回 [4 5]等同于 myList[3:]。
  • slice $myList 1 3 返回 [2 3]等同于 myList[1:3]。
  • slice $myList 0 3 返回 [1 2 3]等同于 myList[:3]。

slice 有问题时会出错,但 mustSlice 有问题时会向模板引擎返回错误。

16、构建一个整数列表(until)

until 函数构建一个整数范围。

until 5

上述语句会生成一个列表: [0, 1, 2, 3, 4]。

对循环语句很有用: range $i, $e :=until 5。

17、seq

seq 5=> 1 2 3 4 5
seq -3=> 1 0 -1 -2 -3
seq 0 2=> 0 1 2
seq 2 -2=> 2 1 0 -1 -2
seq 0 2 10=> 0 2 4 6 8 10
seq 0 -2 -5=> 0 -2 -4

9)数学函数(Math Functions)

1、求和(add)

使用add求和。接受两个或多个输入。

add 1 2 3

2、自加1(add1)

自增加1,使用 add1。

3、相减(sub)

相减使用 sub。

4、除(div)

整除使用 div。

5、取模(mod)

取模使用mod。

6、相乘(mul)

相乘使用mul。接受两个或多个输入。

mul 1 2 3

7、获取最大值(max)

返回一组整数中最大的整数。

max 1 2 3
# 返回 3

8、获取最小值(min)

返回一组数中最小的数。

min 1 2 3 
# 会返回 1。

9、获取长度(len)

以整数返回参数的长度。

len .Arg

10)Network Functions

Helm提供了几个网络函数:

  • getHostByName接收一个域名返回IP地址。
  • getHostByName "www.google.com"会返回对应的www.google.com的地址。

10)条件语句

运算符:

eq: 等于(equal to)
ne: 不等于(not equal to)
lt: 小于(less than)
le: 小于等于(less than or equal to)
gt: 大于(greater than)
ge: 大于等于(greater than or equal to)

if/else 用法:

{{if 命令}}
…
{{else if 命令}}
…
{{else}}
…
{{end}}

如果是以下值时,管道会被设置为 false:

布尔false
数字0
空字符串
nil (空或null)
空集合(map, slice, tuple, dict, array)

【示例】

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  drink: {{ .Values.favorite.drink | default "tea" | quote }}
  food: {{ .Values.favorite.food | upper | quote }}
  {{ if eq .Values.favorite.drink "coffee" }}mug: "true"{{ end }}

11)变更作用域 with

下一个控制结构是with操作。这个用来控制变量范围。回想一下,.是对 当前作用域 的引用。因此.Values就是告诉模板在当前作用域查找Values对象。

with的语法与if语句类似:

{{ with PIPELINE }}
  # restricted scope
{{ end }}

作用域可以被改变。with允许你为特定对象设定当前作用域(.)。比如,我们已经在使用.Values.favorite。 修改配置映射中的.的作用域指向.Values.favorite:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  {{- end }}

但是这里有个注意事项,在限定的作用域内,无法使用.访问父作用域的对象。错误示例如下:

{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ .Release.Name }}
{{- end }}

这样会报错因为Release.Name不在.限定的作用域内。但是如果对调最后两行就是正常的, 因为在{{ end }}之后作用域被重置了。

{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
release: {{ .Release.Name }}

或者,我们可以使用$从父作用域中访问Release.Name对象。当模板开始执行后$会被映射到根作用域,且执行过程中不会更改。 下面这种方式也可以正常工作:

{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ $.Release.Name }}
{{- end }}

也可以在外边定义变量,遵循$name变量的格式且指定了一个特殊的赋值运算符::=。 我们可以使用针对Release.Name的变量重写上述内容。

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- $relname :=.Release.Name -}}
  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  release: {{ $relname }}
  {{- end }}

注意在with块开始之前,赋值$relname :=.Release.Name。 现在在with块中,$relname变量仍会执行版本名称。

12)rang循环语句

很多编程语言支持使用for循环,foreach循环,或者类似的方法机制。 在Helm的模板语言中,在一个集合中迭代的方式是使用range操作符。

定义values

favorite:
  drink: coffee
  food: pizza
pizzaToppings:
  - mushrooms
  - cheese
  - peppers
  - onions

现在我们有了一个pizzaToppings列表(模板中称为切片)。修改模板把这个列表打印到配置映射中:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  {{- with .Values.favorite }}
  drink: {{ .drink | default "tea" | quote }}
  food: {{ .food | upper | quote }}
  {{- end }}
  toppings: |-
    {{- range .Values.pizzaToppings }}
    - {{ . | title | quote }}
    {{- end }}

有时能在模板中快速创建列表然后迭代很有用,Helm模板的tuple可以很容易实现该功能。在计算机科学中, 元组表示一个有固定大小的类似列表的集合,但可以是任意数据类型。这大致表达了tuple的用法。

  sizes: |-
    {{- range tuple "small" "medium" "large" }}
    - {{ . }}
    {{- end }}

上述模板会生成以下内容:

  sizes: |-
    - small
    - medium
    - large    

13)命名模板

此时需要越过模板,开始创建其他内容了。该部分我们会看到如何在一个文件中定义 命名模板,并在其他地方使用。命名模板 (有时称作一个 部分 或一个 子模板)仅仅是在文件内部定义的模板,并使用了一个名字。有两种创建方式和几种不同的使用方法。

  • 三种声明和管理模板的方法:definetemplate,和block,在这部分,我们将使用这三种操作并介绍一种特殊用途的 include方法,类似于template操作。
  • 命名模板时要记住一个重要细节:模板名称是全局的。如果您想声明两个相同名称的模板,哪个最后加载就使用哪个。 因为在子chart中的模板和顶层模板一起编译,命名时要注意 chart特定名称。
  • 一个常见的命名惯例是用chart名称作为模板前缀:{{ define "mychart.labels" }}。使用特定chart名称作为前缀可以避免可能因为 两个不同chart使用了相同名称的模板而引起的冲突。

在编写模板细节之前,文件的命名惯例需要注意:

  • templates/中的大多数文件被视为包含Kubernetes清单
  • NOTES.txt是个例外
  • 命名以下划线(_)开始的文件则假定 没有 包含清单内容。这些文件不会渲染为Kubernetes对象定义,但在其他chart模板中都可用。

这些文件用来存储局部和辅助对象,实际上当我们第一次创建mychart时,会看到一个名为_helpers.tpl的文件,这个文件是模板局部的默认位置

1、用define和template声明和使用模板

define操作允许我们在模板文件中创建一个命名模板,语法如下:

{{- define "MY.NAME" }}
  # body of template here
{{- end }}

比如我们可以定义一个模板封装Kubernetes的标签:

{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
{{- end }}

现在我们将模板嵌入到了已有的配置映射中,然后使用template包含进来:

{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
{{- end }}
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  {{- template "mychart.labels" }}
data:
  myvalue: "Hello World"
  {{- range $key, $val :=.Values.favorite }}
  {{ $key }}: {{ $val | quote }}
  {{- end }}

当模板引擎读取该文件时,它会存储mychart.labels的引用直到template "mychart.labels"被调用。 然后会按行渲染模板,因此结果类似这样:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: running-panda-configmap
  labels:
    generator: helm
    date: 2022-09-04
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"

注意:define不会有输出,除非像本示例一样用模板调用它。

按照惯例,Helm chart将这些模板放置在局部文件中,一般是_helpers.tpl。把这个方法移到那里:

{{/* Generate basic labels */}}
{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
{{- end }}

2、设置模板范围

在上面定义的模板中,我们没有使用任何对象,仅仅使用了方法。修改定义好的模板让其包含chart名称和版本号:

{{/* Generate basic labels */}}
{{- define "mychart.labels" }}
  labels:
    generator: helm
    date: {{ now | htmlDate }}
    chart: {{ .Chart.Name }}
    version: {{ .Chart.Version }}
{{- end }}

3、include 方法

假设定义了一个简单模板如下:

{{- define "mychart.app" -}}
app_name: {{ .Chart.Name }}
app_version: "{{ .Chart.Version }}"
{{- end -}}

现在假设我想把这个插入到模板的labels:部分和data:部分:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  labels:
    {{ template "mychart.app" . }}
data:
  myvalue: "Hello World"
  {{- range $key, $val :=.Values.favorite }}
  {{ $key }}: {{ $val | quote }}
  {{- end }}
{{ template "mychart.app" . }}

如果渲染这个,会得到以下错误:

$ helm install --dry-run measly-whippet ./mychart
Error: unable to build kubernetes objects from release manifest: error validating "": error validating data: [ValidationError(ConfigMap): unknown field "app_name" in io.k8s.api.core.v1.ConfigMap, ValidationError(ConfigMap): unknown field "app_version" in io.k8s.api.core.v1.ConfigMap]

要查看渲染了什么,可以用--disable-openapi-validation参数重新执行: helm install --dry-run --disable-openapi-validation measly-whippet ./mychart。 输入不是我们想要的:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: measly-whippet-configmap
  labels:
    app_name: mychart
app_version: "0.1.0"
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"
app_name: mychart
app_version: "0.1.0"

注意两处的app_version缩进都不对,为啥?因为被替换的模板中文本是左对齐的。由于template是一个行为,不是方法无法将 template调用的输出传给其他方法,数据只是简单地按行插入

为了处理这个问题,Helm提供了一个include,可以将模板内容导入当前管道,然后传递给管道中的其他方法。下面这个示例,使用indent正确地缩进了mychart.app模板:

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
  labels:
{{ include "mychart.app" . | indent 4 }}
data:
  myvalue: "Hello World"
  {{- range $key, $val :=.Values.favorite }}
  {{ $key }}: {{ $val | quote }}
  {{- end }}
{{ include "mychart.app" . | indent 2 }}

现在生成的YAML每一部分都可以正确缩进了:

# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: edgy-mole-configmap
  labels:
    app_name: mychart
    app_version: "0.1.0"
data:
  myvalue: "Hello World"
  drink: "coffee"
  food: "pizza"
  app_name: mychart
  app_version: "0.1.0"

include 相较于使用template,在helm中使用include被认为是更好的方式 只是为了更好地处理YAML文档的输出格式。

14)NOTES.txt文件

该部分会介绍为chart用户提供说明的Helm工具。在helm install 或 helm upgrade命令的最后,Helm会打印出对用户有用的信息。 使用模板可以高度自定义这部分信息。

要在chart添加安装说明,只需创建templates/NOTES.txt文件即可。该文件是纯文本,但会像模板一样处理, 所有正常的模板函数和对象都是可用的。让我们创建一个简单的NOTES.txt文件:

Thank you for installing {{ .Chart.Name }}.

Your release is named {{ .Release.Name }}.

To learn more about the release, try:

  $ helm status {{ .Release.Name }}
  $ helm get all {{ .Release.Name }}

现在如果我们执行helm install rude-cardinal ./mychart 会在底部看到:

RESOURCES:==> v1/Secret
NAME                   TYPE      DATA      AGE
rude-cardinal-secret   Opaque    1         0s==> v1/ConfigMap
NAME                      DATA      AGE
rude-cardinal-configmap   3         0s


NOTES:
Thank you for installing mychart.

Your release is named rude-cardinal.

To learn more about the release, try:

  $ helm status rude-cardinal
  $ helm get all rude-cardinal

使用NOTES.txt这种方式是给用户提供关于如何使用新安装的chart细节信息的好方法。尽管并不是必需的,强烈建议创建一个NOTES.txt文件

15)模板调试

调试模板可能很棘手,因为渲染后的模板发送给了Kubernetes API server,可能会以格式化以外的原因拒绝YAML文件。以下命令有助于调试:

  • helm lint 是验证chart是否遵循最佳实践的首选工具
  • helm install --dry-run --debughelm template --debug:我们已经看过这个技巧了, 这是让服务器渲染模板的好方法,然后返回生成的清单文件。
  • helm get manifest: 这是查看安装在服务器上的模板的好方法。

当你的YAML文件解析失败,但你想知道生成了什么,检索YAML一个简单的方式是注释掉模板中有问题的部分, 然后重新运行 helm install --dry-run --debug

apiVersion: v2
# some: problem section
# {{ .Values.foo | quote }}

以上内容会被渲染同时返回完整的注释:

apiVersion: v2
# some: problem section
#  "bar"

其实这里主要是正对官方文档进行整理,列出了常见的使用语法,想了解更多,可以参考官方文档,官方文档讲解的很细致,有疑问的小伙伴欢迎给我留言哦,后续会持续分享【云原生和大数据】相关的文章,请小伙伴耐心等待哦~

Java中,工具类定义了一组公共方法,这篇文章将介绍Java中使用最频繁及最通用的Java工具类。以下工具类、方法按使用流行度排名,参考数据来源于Github上随机选取的5万个开源项目源码。

一. org.apache.commons.io.IOUtils

closeQuietly:关闭一个IO流、socket、或者selector且不抛出异常,通常放在finally块
toString:转换IO流、 Uri、 byte[]为String
copy:IO流数据复制,从输入流写到输出流中,最大支持2GB
toByteArray:从输入流、URI获取byte[]
write:把字节. 字符等写入输出流
toInputStream:把字符转换为输入流
readLines:从输入流中读取多行数据,返回List<String>
copyLarge:同copy,支持2GB以上数据的复制
lineIterator:从输入流返回一个迭代器,根据参数要求读取的数据量,全部读取,如果数据不够,则失败

二. org.apache.commons.io.FileUtils

deleteDirectory:删除文件夹
readFileToString:以字符形式读取文件内容
deleteQueitly:删除文件或文件夹且不会抛出异常
copyFile:复制文件
writeStringToFile:把字符写到目标文件,如果文件不存在,则创建
forceMkdir:强制创建文件夹,如果该文件夹父级目录不存在,则创建父级
write:把字符写到指定文件中
listFiles:列举某个目录下的文件(根据过滤器)
copyDirectory:复制文件夹
forceDelete:强制删除文件

三. org.apache.commons.lang.StringUtils

isBlank:字符串是否为空 (trim后判断)
isEmpty:字符串是否为空 (不trim并判断)
equals:字符串是否相等
join:合并数组为单一字符串,可传分隔符
split:分割字符串
EMPTY:返回空字符串
trimToNull:trim后为空字符串则转换为null
replace:替换字符串

四. org.apache.http.util.EntityUtils

toString:把Entity转换为字符串
consume:确保Entity中的内容全部被消费。可以看到源码里又一次消费了Entity的内容,假如用户没有消费,那调用Entity时候将会把它消费掉
toByteArray:把Entity转换为字节流
consumeQuietly:和consume一样,但不抛异常
getContentCharset:获取内容的编码

五. org.apache.commons.lang3.StringUtils

isBlank:字符串是否为空 (trim后判断)
isEmpty:字符串是否为空 (不trim并判断)
equals:字符串是否相等
join:合并数组为单一字符串,可传分隔符
split:分割字符串
EMPTY:返回空字符串
replace:替换字符串
capitalize:首字符大写

六. org.apache.commons.io.FilenameUtils

getExtension:返回文件后缀名
getBaseName:返回文件名,不包含后缀名
getName:返回文件全名
concat:按命令行风格组合文件路径(详见方法注释)
removeExtension:删除后缀名
normalize:使路径正常化
wildcardMatch:匹配通配符
seperatorToUnix:路径分隔符改成unix系统格式的,即/
getFullPath:获取文件路径,不包括文件名
isExtension:检查文件后缀名是不是传入参数(List<String>)中的一个

七. org.springframework.util.StringUtils

hasText:检查字符串中是否包含文本
hasLength:检测字符串是否长度大于0
isEmpty:检测字符串是否为空(若传入为对象,则判断对象是否为null)
commaDelimitedStringToArray:逗号分隔的String转换为数组
collectionToDelimitedString:把集合转为CSV格式字符串
replace 替换字符串
7. delimitedListToStringArray:相当于split
uncapitalize:首字母小写
collectionToDelimitedCommaString:把集合转为CSV格式字符串
tokenizeToStringArray:和split基本一样,但能自动去掉空白的单词

八. org.apache.commons.lang.ArrayUtils

contains:是否包含某字符串
addAll:添加整个数组
clone:克隆一个数组
isEmpty:是否空数组
add:向数组添加元素
subarray:截取数组
indexOf:查找某个元素的下标
isEquals:比较数组是否相等
toObject:基础类型数据数组转换为对应的Object数组

九. org.apache.commons.lang.StringEscapeUtils

参考十五:org.apache.commons.lang3.StringEscapeUtils

十. org.apache.http.client.utils.URLEncodedUtils

format:格式化参数,返回一个HTTP POST或者HTTP PUT可用application/x-www-form-urlencoded字符串
parse:把String或者URI等转换为List<NameValuePair>

十一. org.apache.commons.codec.digest.DigestUtils

md5Hex:MD5加密,返回32位字符串
sha1Hex:SHA-1加密
sha256Hex:SHA-256加密
sha512Hex:SHA-512加密
md5:MD5加密,返回16位字符串

十二. org.apache.commons.collections.CollectionUtils

isEmpty:是否为空
select:根据条件筛选集合元素
transform:根据指定方法处理集合元素,类似List的map()
filter:过滤元素,雷瑟List的filter()
find:基本和select一样
collect:和transform 差不多一样,但是返回新数组
forAllDo:调用每个元素的指定方法
isEqualCollection:判断两个集合是否一致

十三. org.apache.commons.lang3.ArrayUtils

contains:是否包含某个字符串
addAll:添加整个数组
clone:克隆一个数组
isEmpty:是否空数组
add:向数组添加元素
subarray:截取数组
indexOf:查找某个元素的下标
isEquals:比较数组是否相等
toObject:基础类型数据数组转换为对应的Object数组

十四. org.apache.commons.beanutils.PropertyUtils

getProperty:获取对象属性值
setProperty:设置对象属性值
getPropertyDiscriptor:获取属性描述器
isReadable:检查属性是否可访问
copyProperties:复制属性值,从一个对象到另一个对象
getPropertyDiscriptors:获取所有属性描述器
isWriteable:检查属性是否可写
getPropertyType:获取对象属性类型

十五. org.apache.commons.lang3.StringEscapeUtils

unescapeHtml4:转义html
escapeHtml4:反转义html
escapeXml:转义xml
unescapeXml:反转义xml
escapeJava:转义unicode编码
escapeEcmaScript:转义EcmaScript字符
unescapeJava:反转义unicode编码
escapeJson:转义json字符
escapeXml10:转义Xml10

这个现在已经废弃了,建议使用commons-text包里面的方法。

十六. org.apache.commons.beanutils.BeanUtils

copyPeoperties:复制属性值,从一个对象到另一个对象
getProperty:获取对象属性值
setProperty:设置对象属性值
populate:根据Map给属性复制
copyPeoperty:复制单个值,从一个对象到另一个对象
cloneBean:克隆bean实例

现在你只要了解了以上16种最流行的工具类方法,你就不必要再自己写工具类了,不必重复造轮子。大部分工具类方法通过其名字就能明白其用途,如果不清楚的,可以看下别人是怎么用的,或者去网上查询其用法。

另外,工具类,根据阿里开发手册,包名如果要使用util不能带s,工具类命名为 XxxUtils

文本标记语言(英语:HyperText Markup Language,简称:HTML)是一种用于创建网页的标准标记语言。HTML是一种基础技术,常与CSS、JavaScript一起被众多网站用于设计令人赏心悦目的网页、网页应用程序以及移动应用程序的用户界面。网页浏览器可以读取HTML文件,并将其渲染成可视化网页。HTML描述了一个网站的结构语义随着线索的呈现,使之成为一种标记语言而非编程语言。

HTML元素是构建网站的基石。HTML允许嵌入图像与对象,并且可以用于创建交互式表单,它被用来结构化信息——例如标题、段落和列表等等,也可用来在一定程度上描述文档的外观和语义。HTML的语言形式为尖括号包围的HTML元素(如<html>),浏览器使用HTML标签和脚本来诠释网页内容,但不会将它们显示在页面上。

HTML可以嵌入如JavaScript的脚本语言,它们会影响HTML网页的行为。网页浏览器也可以引用层叠样式表(CSS)来定义文本和其它元素的外观与布局。维护HTML和CSS标准的组织万维网联盟(W3C)鼓励人们使用CSS替代一些用于表现的HTML元素。

历史

W3C制作的早期HTML标志

发展

蒂姆·伯纳斯-李

1980年,物理学家蒂姆·伯纳斯-李在欧洲核子研究中心(CERN)在承包工程期间,为使CERN的研究人员使用并共享文档,他提出并创建原型系统ENQUIRE。1989年,伯纳斯-李在一份备忘录中提出一个基于互联网的超文本系统。他规定HTML并在1990年底写出浏览器和服务器软件。同年,伯纳斯-李与CERN的数据系统工程师罗伯特·卡里奥联合为项目申请资助,但未被CERN正式批准。在他的个人笔记中伯纳斯-李列举“一些使用超文本的领域”,并把百科全书列为首位。

HTML的首个公开描述出现于一个名为“HTML标签”的文件中,由蒂姆·伯纳斯-李于1991年底提及。它描述18个元素,包括HTML初始的、相对简单的设计。除了超链接标签外,其他设计都深受CERN内部一个以标准通用标记语言(SGML)为基础的文件格式SGMLguid的影响。这些元素在HTML 4中仍有11个存在。

伯纳斯-李认为HTML是SGML的一个应用程序。1993年中期互联网工程任务组(IETF)发布首个HTML规范的提案:“超文本标记语言(HTML)”互联网草案,由伯纳斯-李与丹·康纳利(英语:Dan Connolly (computer scientist))撰写。其中包括一个SGML文档类型定义来定义语法。草案于6个月后过期,不过值得注意的是其对NCSA Mosaic浏览器自定义标签从而将在线图像嵌入的行为的认可,这反映IETF把标准立足于成功原型的理念。同样,戴夫·拉格特(英语:Dave Raggett)在1993年末提出的与之竞争的互联网草案“HTML+(超文本标记格式)”建议规范已经实现的功能,如表格与填写表单。

在HTML和HTML+的草案于1994年初到期后,IETF创建一个HTML工作组,并在1995年完成"HTML 2.0",这是第一个旨在成为对其后续实现标准的依据的HTML规范。

在IETF的主持下,HTML标准的进一步发展因竞争利益而遭受停滞。自1996年起,HTML规范一直由万维网联盟(W3C)维护,并由商业软件厂商出资。不过在2000年,HTML也成为国际标准(ISO/ IEC15445:2000)。HTML 4.01于1999年末发布,进一步的勘误版本于2001年发布。2004年,网页超文本应用技术工作小组(WHATWG)开始开发HTML5,并在2008年与W3C共同交付,2014年10月28日完成标准化。

版本时间线

1995年11月24日

HTML 2.0作为IETF RFC 1866发布。追加RFC的附加功能:

1995年11月25日:RFC 1867(基于表单的文件上传)

1996年5月:RFC 1942(表格)

1996年8月:RFC 1980(客户端图像映射)

1997年1月:RFC 2070(国际化)

1997年1月14日

HTML 3.2作为W3C推荐标准发布。这是首个完全由W3C开发并标准化的版本,因IETF于1996年9月12日关闭它的HTML工作组。

最初代号为“威尔伯”(Wilbur),HTML 3.2完全去除数学公式,协调各种专有扩展,并采用网景设计的大多数视觉标记标签。由于两家公司达成了协议,网景的闪烁元素(英语:blink element)和微软的滚动元素(英语:marquee element)被移除。HTML对数学公式的支持最后成为另外一种被称为MathML的标准。

1997年12月18日

HTML 4.0作为W3C推荐标准发布。它提供三种变化:

严格,过时的元素被禁止。

过渡,过时的元素被允许。

框架集,大多只与框架相关的元素被允许。

最初代号“美洲狮”(Cougar), HTML 4.0采用许多特定浏览器的元素类型和属性,并试图淘汰网景的视觉标记功能,将其标记为不赞成使用。HTML 4是遵循ISO 8879 - SGML的SGML应用程序。

1998年4月24日

HTML 4.0进行微调,不增加版本号。

1999年12月24日

HTML 4.01作为W3C推荐标准发布。它同样提供三种变化,最终勘误版于2001年5月12日发布。

2000年5月

ISO/IEC 15445:2000("ISO HTML",基于HTML 4.01严格版)作为ISO/IEC国际标准发布。在ISO中这一标准位于ISO/IEC JTC 1/SC 34(英语:ISO/IEC JTC 1/SC 34)域(ISO/IEC联合技术委员会1、小组委员会34 – 文档描述与处理语言)。

2014年10月28日

HTML 5作为W3C推荐标准发布。

草案时间线

HTML5的Logo

1991年10月

HTML标签,一个非正式CERN文件首次公开18个HTML标签。

1992年6月

HTML DTD的首个非正式草案, 后续有七个修订版(7月15日,8月6日,8月18日,11月17日,11月19日,11月20日,11月22日)。

1992年11月

HTML DTD 1.1(首个版本号,基于RCS修订版,版本号从1.1开始而非1.0),非正式草案。

1993年6月

超文本标记语言由IETF IIIR工作小组作为互联网草案(一个粗略的建议标准)。在被第二版代一个月后,IETF又发布6个草案,最终在RFC1866中发布HTML 2.0。

1993年11月

HTML+由IETF作为互联网草案发布,是超文本标记语言草案的一个竞争性提案。它于1994年5月到期。

1995年4月 (1995年3月编写)

HTML 3.0[33]被提议作为IETF的标准,但直到提案在五个月过期后(1995年9月28日)仍没有进一步的行动。它包含许多拉格特HTML+提案的功能,如对表格的支持、围绕数据的文本流和复杂的数学公式的显示。W3C开始开发自己的Arena浏览器作为HTML 3和层叠样式表的试验台(英语:Test bed),但HTML 3.0并没有获得成功。浏览器厂商,包括微软和网景,选择实现HTML3草案功能的不同子集并引入它们自己的插件(见浏览器大战)。

2008年1月

HTML5由W3C作为工作草案(链接)发布。虽然HTML5的语法非常类似于SGML,但它已经放弃任何成为SGML应用程序的尝试,除了一种替代的基于XML的HTML5序列,它已明确定义自己的“HTML”序列。

2011年 HTML5 – 最终征求

2011年5月,工作小组将HTML5推进至“最终征求”(Last Call)阶段,邀请W3C社区内外人士以确认本规范的技术可靠性。W3C开发一套综合性测试套件来实现完整规范的广泛交互操作性,完整规范的目标日期为2014年。2011年1月,WHATWG将其“HTML5”活动标准重命名为“HTML”。W3C仍然继续其发布HTML5的项目。

2012年 HTML5 – 候选推荐

2012年7月,WHATWG和W3C的工作产生一定程度的分离。W3C继续HTML5规范工作,重点放在单一明确的标准上,这被WHATWG称为“快照”。WHATWG组织则将HTML5作为一个“活动标准”(Living Standard)。活动标准的概念是从未完成但永远保持更新与改进,可以添加新特性,但功能点不会被删除。

2012年12月,W3C指定HTML5作为候选推荐阶段。 该阶段的标准为“两个100%完成,完全实现交互操作”。

2014年 HTML5 – 提案推荐与推荐

2014年9月,HTML5进入提案推荐阶段。

2014年10月28日,HTML5作为稳定W3C推荐标准发布,这意味着HTML5的标准化已经完成。

XHTML版本

XHTML是使用XML 1.0改写自HTML 4.01的独立语言。它不再被作为单独标准开发。

XHTML 1.0, 2000年1月26日作为W3C推荐标准发布。修订版于2002年8月1日发布,它提供与HTML 4.0和4.01相同的三个变化,这些变化被重新在XML中制定。

XHTML 1.1,基于XHTML 1.0 严格版,2001年5月31日 作为W3C推荐标准发布。修订版可使用模块化XHTML的模块,2001年4月10日作为W3C推荐标准发布。

XHTML 2.0为工作草案,但为支持HTML5与XHTML5的工作,此草案被放弃。 XHTML 2.0与XHTML 1.x不兼容,因此更确切的说这是一个XHTML风格的新语言而不是XHTML 1.x的更新。

在HTML5草案中规定一个XHTML语法,称为“XHTML5.1”。

标记

HTML标记包含标签(及其属性)、基于字符的数据类型、字符引用和实体引用等几个关键部分。HTML标签是最常见的,通常成对出现,比如<h1>与</h1>。这些成对出现的标签中,第一个标签是开始标签,第二个标签是结束标签。两个标签之间为元素的内容,有些标签没有内容,为空元素,如<img>。

HTML另一个重要组成部分为文档类型声明(英语:document type declaration),这会触发标准模式渲染。

以下是一个经典的Hello World程序的例子:

<!DOCTYPE html>

<html>

<head>

<title>This is a title</title>

</head>

<body>

<p>Hello world!</p>

</body>

</html>

<html>和</html>之间的文本描述网页,<body>和</body>之间的文本为可视页面内容。标记文本<title>This is a title</title>定义了浏览器的页面标题。

文档标记类型<!DOCTYPE html>用于HTML5。 如果未进行声明,浏览器会使用“怪异模式”进行渲染。

元素

HTML文档由嵌套的HTML元素构成。它们用HTML标签表示,包含于尖括号中,如<p>

在一般情况下,一个元素由一对标签表示:“开始标签”<p>与“结束标签”</p>。元素如果含有文本内容,就被放置在这些标签之间。

在开始与结束标签之间也可以封装另外的标签,包括标签与文本的混合。这些嵌套元素是父元素的子元素。

开始标签也可包含标签属性。这些属性有诸如标识文档区段、将样式信息绑定到文档演示和为一些如<img>等的标签嵌入图像、引用图像来源等作用。

一些元素如换行符<br>,不允许嵌入任何内容,无论是文字或其他标签。这些元素只需一个单一的空标签(类似于一个开始标签),无需结束标签。

许多标签是可选的,尤其是那些很常用的段落元素<p>的闭合端标签。HTML浏览器或其他媒介可以从上下文识别出元素的闭合端以及由HTML标准所定义的结构规则。这些规则非常复杂,不是大多数HTML编码人员可以完全理解的。

因此,一个HTML元素的一般形式为:<tag attribute1="value1" attribute2="value2">''content''</tag>。一些HTML元素被定义为空元素,其形式为<tag attribute1="value1" attribute2="value2">。空元素不能封装任何内容。例如<br>标签或内联标签<img>。一个HTML元素的名称即为标签使用的名称。注意,结束标签的名称前面有一个斜杠“/”,空元素不需要也不允许结束标签。如果元素属性未标明,则使用其默认值。

例子

HTML文档的页眉:<head>...</head>。标题被包含在头部,例如:

<head>

<title>The Title</title>

</head>

标题:HTML标题由<h1>到<h6>六个标签构成,字体由大到小递减:

<h1>Heading level 1</h1>

<h2>Heading level 2</h2>

<h3>Heading level 3</h3>

<h4>Heading level 4</h4>

<h5>Heading level 5</h5>

<h6>Heading level 6</h6>

段落:

<p>第一段</p> <p>第二段</p>

换行:<br>。<br>与<p>之间的差异是br换行但不改变页面的语义结构,而p部分的页面成段。

<p>這是<br>一個<br>使用換行<br>段落</p>

链接:使用<a>标签来创建链接。href属性包含链接的URL地址。

<a href="https://zh.wikipedia.org/">中文維基百科的連結!</a>

注释:

<!-- This is a comment -->

注释有助于理解标记,但它不会在网页上显示。

HTML中存在以下几种类型的标记元素:

用于文本的结构式标记

例如,<h2>羽毛球</h2>将“羽毛球”定义为二级标题。结构式标记不指示任何特定的渲染,但大多数网页浏览器都会采用元素格式的默认样式。要在内容上实现进一步的风格可以使用层叠样式表(CSS)。

用于文本外观的表现式标记,不论其目的

例如,<b>粗体</b>表示视觉输出设备应将文本“粗体”加粗,但如果设备无法做到这一点(如朗读文本的听觉设备),就不会发生什么现象。在这种情况下,<b>粗体</b>与''斜体''也可能有相同的视觉效果,但在本质上它们更加语义化。如同<strong>加强文字</strong>与<em>强调文字</em>的区别。为支持CSS的使用,大多数表现式标记在HTML 4.0规范中不再被推荐使用。

超文本标记使文档的一部分链接到其他文档

锚元素在文档中创建超链接,其href属性设置链接的目标URL。例如:HTML标记<a href="https://zh.wikipedia.org/">中文维基百科</a>会将文字"中文维基百科"渲染为超链接。要将图片渲染为超链接,img元素要作为内容插入到a元素中:<a href="https://example.org"><img src="image.gif" alt="说明文字" width="50" height="50" border="0"></a>。

属性

大多数元素的属性以“名称-值”的形式成对出现,由“=”分离并写在开始标签元素名之后。值一般由单引号或双引号包围,有些值的内容包含特定字符,在HTML中可以去掉引号(XHTML不行)。不加引号的属性值被认为是不安全的[58]。有些属性无需成对出现,仅存在于开始标签中即可影响元素[6],如img 元素的ismap 属性。

许多元素存在一些共通的属性:

id属性为元素提供了在全文档内的唯一标识。它用于识别元素,以便样式表可以改变其表现属性,脚本可以改变、显示或删除其内容或格式化。对于添加到页面的URL,它为元素提供了一个全局唯一标识,通常为页面的子章节。例如,ID "属性"对于https://zh.wikipedia.org/wiki/HTML#属性

class属性提供一种将类似元素分类的方式。常被用于语义化或格式化。例如,一个HTML文档可指定类<class="标记">来表明所有具有这一类值的元素都从属于文档的主文本。格式化后,这样的元素可能会聚集在一起,并作为页面脚注而不会出现在HTML代码中。类属性也被用于微格式的语义化。类值也可进行多声明。如<class="标记 重要">将元素同时放入標記与重要两类中。

style属性可以将表现性质赋予一个特定元素。比起使用id或class 属性从样式表中选择元素,“style”被认为是一个更好的做法,尽管有时这对一个简单、专用或特别的样式显得太繁琐。

title属性用于给元素一个附加的说明。 大多数浏览器中这一属性显示为工具提示(英语:Tooltip)。

lang属性用于识别元素内容的语言,它可能与文档的主要语言不同。例如,在中文文档中:

<p>法语<span lang="fr">c'est la vie</span>在法国的应用很普遍,意为“这就是生活” 。<p>

缩写元素abbr可用于说明一些属性:

<abbr id="ID" class="术语" style="color:purple;" title="超文本标记语言">HTML</abbr>

这个例子显示为HTML; 在大多数浏览器中,光标指向缩写时会显示标题文字“超文本标记语言”。

大多数元素采用与语言相关的属性dir 来指定文字方向,如 "rtl"采用从右到左的文本,比如阿拉伯语、波斯语以及希伯来语。

字符与实体引用

参见:XML与HTML字符实体引用列表和Unicode与HTML

在4.0版本中,HTML定义了一系列共252个字符实体引用和1,114,050个字元值参考。二者都支持单个字符通过简单的标记写入。文字字符与其对应的标记渲染的效果相同。

用这种方式“转义”字符的能力允许字符<与&(当分别被写作<和&时)被理解为字符数据而不是标记。例如<通常为标签的开头,&通常为字符实体引用与数字字符引用的开头;&或&或&将&作为元素的内容或属性的值。双引号字符"在不被用于属性值的标示时必须转义为"或"或";相等地,当于单引号字符'不被用于属性值的标示时,也必须转义为'或'(或HTML5与XHTML文档中的')。

如果文档作者忽略了转义这样的字符,一些浏览器会尝试通过上下文猜测他们的意图。如果结果仍为无效标记,这会使其他浏览器或用户代理难以访问到该文档,并使它们尝试使用搜索和索引来解析该文档。

那些难以输入或不在该文档字符编码中的字符也可通过转义来实现。例如通常只在西欧或南美的键盘出现的重音符e(é),可以在HTML文档中用作实体引用é 或数字引用é或é。 诸如UTF-8的Unicode字符编码与所有的现代浏览器兼容并允许直接访问全球书写系统几乎所有的字符 。

数据类型

HTML为元素内容定义了多种数据类型,如脚本数据、样式表数据以及许多属性值的类型,包括ID、名称、URI、数字长度单位、语言、媒体描述符颜色、字符编码、日期和时间等等。所有这些数据类型都是字符数据的特殊化。

文档类型声明

HTML文档需要以文档类型声明(英语:document type declaration)(英语非正式说法“doctype”)开头。在浏览器中,文档类型声明有助于确定渲染模式——特别是是否使用怪异模式。

文档类型声明的初衷是通过基于文档类型定义(DTD)的SGML工具来解析并验证HTML文档。

HTML5未定义DTD,所以在HTML5中文档类型声明更为简短:

<!DOCTYPE html>

HTML 4文档类型声明举例:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

该声明引用HTML 4.01“严格”版的DTD。基于SGML的验证器可读取DTD,正确解析这些文档并执行验证。在现代浏览器中,一个有效的文档类型激活标准模式有别于怪异模式。

另外,HTML 4.01提供过渡型与框架集型的DTD。过渡型涵盖最广,它可整合当前以及老旧或“过时”的标签,而严格型DTD排除了过时的标签。框架集拥有所有构建框架所需的标签以及过渡型的标签。

语义化HTML

语义化HTML是一种编写HTML的方式,它强调编码信息的含义在其格式(样子)之上。HTML从创立之初就包括语义化标记,但也包括标识性标记如<font>、<i>和<center>标签。也存在一些语义上中立的span与div标签。自1990年代末层叠样式表开始应用于大多数浏览器,网页制作者就被鼓励使用CSS以便呈现与内容分离。

在2001年一次对语义网的讨论中,蒂姆·伯纳斯-李等人给出了一种的方法,使智能软件“代理人”可能有一天会自动抓取网页进行查找、过滤并将之前不相关的联系起来。这种代理甚至在现在也不普遍,但一些Web 2.0、混搭和价格比较网站的想法可能会结束。这些网页应用程序的混合与伯纳斯-李的语义代理人的之间主要区别基于以下事实:当前的聚合与信息混合通常由网页开发者设计,他们早已知道网络位置和他们希望混搭、比较与结合的特定数据的API语义。

网页代理的一个重要类型是网络爬虫或搜索引擎蜘蛛。这些软件代理依赖于它们发现的网页的语义清晰度,因为它们一天要使用各种技术与算法来读取和索引数百万个网页并给网页用户提供搜索工具,没有这些万维网的有效性就会大大降低。

为使搜索引擎蜘蛛评估它们在HTML文档中发现的文本片段的重要性,也为那些创建标记等混合的人与更多的自动化代理工具,HTML中的语义结构需要广泛一致地应用从而将文本的含义呈现给浏览者。

表示性标记在当前的HTML和XHTML推荐中不被鼓励使用,HTML5中则被视为非法。

好的语义化HTML也改善了网页文档的可访问性。例如,当屏幕阅读器或音频浏览器可以正确判定一个文档的结构时,视觉障碍用户不会再因阅读重复或无关的信息而浪费时间。

分发

HTML文档分发的方法和其他计算机文件相同。不过,它们最常通过网页服务器的超文本传输协议或电子邮件传输。

HTTP

万维网主要由从服务器通过HTTP协议向浏览器发送的HTML文档组成。但是,HTTP也可以被用于传输HTML之外的数据,例如图像、声音和其他内容。为使浏览器了解如何处理接收到的文档,在传输文档时必须同时传递文件类型。这种元数据包含MIME类型(对于HTML 4.01或更早版本是text/html,而对于XHTML 1.0或之后的版本是application/xhtml+xml),以及字符编码(参见HTML字符编码方式)。

在现在的浏览器中,和HTML文档一起发送的MIME类型影响文档的解读方式。和XHTML MIME类型一起发送的文档被认为是良构的XML,而语法错误会导致浏览器无法呈现文档。完全相同的文档如果和HTML MIME类型一起发送,则可能被正常显示,因为浏览器对HTML的语法检查更加松懈些。

W3C的推荐指出,遵循规定的推荐指引的XHTML 1.0文档可标记二者任一的MIME类型。XHTML 1.1还指出,XHTML 1.1文档应标有两种MIME类型。

HTML邮件

大多数图形电子邮件客户端允许使用HTML的子集(经常界限不清)提供格式化和无法使用纯文本的语义标记。这可能包括印刷信息,如彩色标题、强调和引用文本、内嵌图片和图表等。许多这样的客户包含一个编写HTML电子邮件消息的图形用户界面编辑器和一个用于显示的渲染引擎。在邮件中使用HTML受到了一些兼容性的批评,由于一些盲人或具有视觉障碍的人的访问问题,这种方式有利于伪装的钓鱼攻击。因其消息大小超过明文,所以它可混淆垃圾邮件过滤器。

命名规则

最常用的计算机文件扩展名为.html,通用缩写为.htm。它起源于某些早期操作系统与文件系统,如DOS以及FAT数据结构的局限性,它将文件扩展名限制为3个字母。

HTML应用程序

HTML应用程序(HTA;文件扩展名".hta")是一个Microsoft Windows应用程序,它在浏览器中使用HTML和动态HTML提供应用程序图形界面。正规HTML文件被限制在浏览器的安全模型中,只能分别通过网页服务器和网页对象与站点Cookie进行通信和操作。HTA作为完全受信任的应用程序运行,因此拥有更多的权限,如创建/编辑/删除文件与注册表项。因为它们在浏览器安全模式之外操作,所以HTA不能通过HTTP执行,必须下载(就像EXE文件)并在本地文件系统执行。

所见即所得编辑器

所见即所得编辑器使用图形用户界面(GUI)显示HTML文档,常常类似于文字处理器,所以用户可以设计一切。编者面对的是文档,而不是代码,所以作者并不需要太多的HTML知识。这种所见即所得的编辑模式一直受到诟病,主要因为它生成的代码质量不高;也有人主张将其改变至WYSIWYM模型(所见即所指)。