1 . 七十年代末,Donald E. Knuth(高德纳) 在看到其多卷巨著 “The Art of Computer Programming” 第二卷的校样时,对由计算机排版的校样的低质量感到无法忍受。因此决定自己来开发一个高质量的计算机排版系统,这样就有了 TeX 。
2 . TeX 的第一版于 1978 年面世,TeX 的源程序是用 Pascal
写成的,原因是 Knuth 希望 TeX 尽可能方便地移植到其它的操作系统中去。当时 Pascal
是最适合于这一要求的编程语言。这也使得 TeX 现在已经在几乎所有的计算机系统中得到实现。
3 . TeX 的另一个重要的特征就是它的输出是与 设备无关 的。TeX 的输出文件称为 DVI 文件,即是 “Device Independent” 。一旦 TeX 处理了你的文件,你所得到的 DVI 文件就可以被送到任何输出设备如打印机,屏幕等并且总会得到相同的结果,而这与这些输出设备的限制没有任何关系。这说明 DVI 文件中所有的元素,从页面设置到文本中字符的位置都被固定,不能更改。
4 . TeX 现在已经被它的开发者 Knuth 所 “冰封”(frozen),基本不再开发了。但新的变种版本不断出现,
5 . LaTex 发型版本:
发行版本就是 LaTeX 多种标准实现类型。主要是分为 TeXLive 和 MiKTeX,其他版本都是基于这两个主流版本衍生而来的。这两大发行版本都是全平台支持的。MacTeX 实际上就是 TeXLive 的 MacOS 系统上的实现,因此这里归类其为 TeXLive。相关说明如下:
如果需要安装的话,大家看官网实际上都能找到地址的,为方便这里列下不同发行版本的不同平台下载地址:
TeXLive
MiKTeX, 这个资源简单,同一个页面选择不同系统即可
LaTeX 实际上和 Java 语言一样,都是需要先配置环境,然后选择一款自己喜欢的编辑器或 IDE 进行编写“代码”。当然所有文本编辑器都可以编写 LaTeX 或者 Java 等其他语言“代码”。这里的 IDE 指集成了一些语言本地化的功能,比如编译、特殊符号等等。
LaTeX 的发行版中会自带一款编辑器,用 TexLive 的话,MacOS 上会有个叫 TexShop 的编辑器,而 Windows 上则会是一个叫 TexWorker 的编辑器,这些是都可以胜任编写工作的。另外,也有第三的 LaTeX 编辑器,下面我整理出所有编辑器说明,我个人用的也推荐程度也放上了,这是个人喜好,大家根据自己的偏好选择。
我个人推荐前两个,因为第三个收费且不跨平台,之所以写上第三个,主要是因为网络上很多博客或用户都推荐用第三个,这个我使用时也感觉不错,但每次我使用都得到 Windows 上使用,比较麻烦。当然,如果你使用 Windows 且有钱,WinEdt 确实使用体验和功能都是比较好的。
MacOS: 安装后会自动将命令加入到环境变量, 并自带 TeXShop 编辑器。
1 | $ which latex |
MacOS: TeXShop 是 MacTeX 自带的编辑器,我个人倾向于再安装一个编辑器 TeXMaker,其内置较多可视化符号,可点击插入,简单便捷。
编译推荐: 用 pdflatex
( 英文文档 ) 或 xelatex
( 中文文档 ) 编译, 生成相应的 pdf 文件。
1 . 基本结构
1 | \documentclass[a4paper]{article} % 指定文档类型 |
\documentclass{...}
(论文、书籍、幻灯片、海报)\beigin{...}
开头,\end{...}
结尾。\documentclass[]{}
就是引用模板,默认提供 article 等模板。2 . 排版命令
方括号中的是可选的 (称为选项), 花括号中的参数是必需的
1 | \command |
定义新命令
1 | \newcommand{新命令}{命令内容} |
文档类型:\documentclass[选项]{文档类}
3 . 中文排版
用 xelatex
编译! 用 xelatex 编译! 用 xelatex 编译!
1 | \documentclass[12pt,a4paper]{ctexart} |
1 . 代码和注释:
1 | % 页面布局:页面宽度, 页面高度, 页眉高度, 页脚高度,各种边距等等 |
2 . 特殊说明
带星号的章节命令: 不参与自动编号
1 | \章节命令[短标题]{标题} |
章节举例
文本对齐方式 (缺省为左对齐)
\raggedright
或使用 flushleft
环境 \raggedleft
或使用 flushright
环境 \centering
或使用 center
环境字体大小
英文/中文都适用
中文字体: ctex
提供的命令和字号命令如下
1 | \heiti, \songti, \fangsong, \kaishu, \lishu, \youyuan, \yahei |
1 . \usepackage[选项列表]{hyperref}
\hypersetup{选项列表}
来设置) bookmarks
→ 创建书签, 缺省为 trueCJKbookmarks
→ 支持中日韩文字的书签colorlinks
→ 使用彩色显示链接, 缺省为红色方框linkcolor
→ 内部普通链接 (如页码) 的颜色, 缺省为 red citecolor
→ 文献引用链接的颜色, 缺省为 greenurlcolor
→ URL 链接的颜色, 缺省为 magentabreaklinks
→ 允许在链接中断行, 缺省不允许2 . 例子:
1 | \usepackage{hyperref} |
3 . 创建网页链接: \url
和 \href
1 | \url{网址} |
\url
→ 生成 网址 的同时在页面上输出其内容\href
→ 生成 网址 的同时在页面上输出 文本 的内容1 | \documentclass{article} |
虽然目前还没有经济和时间出去旅游,但自己还是很喜欢美好的景色的。
随手记录下自己所见,留存在手机和博客中,想来老去后才会有值得回忆之事。
图1
图2
图3
图4:
图5:
图6:
图7:
]]>从大学、到研究生,再到工作,一路走来,都很庆幸自己没有走太多的弯路。
本科时,同学关系很铁,这也是本科给我的最有价值的东西,到现在最常联系的也是本科的那一波人。而本科的魄力和能力则十分欠缺。虽然最终保研了,但对于我而言,保研靠的是我那书呆子的”读书“能力。我不认为这是值得表扬的事情,因为这种能力对我而言是比较”畏弱“的表现,没有特色、没有想法、没有表达,只会做题。。。
读研期,与导师关系很铁。看到网上很多导师压榨学生的新闻,我感觉遇到导师则是用尽了毕生的运气了。从大二开始就加入了导师实验室。谈项目,见客户,去企业做技术支持、企业招标导师都带上我,让我见识到了更多的人与事,真的让这个从农村来的我眼界大开。此外,师弟师妹的加入,让研究生的实验室更加活泼、朝气、美好。让原本孤独的自己有了陪伴,很喜欢那种一起”挥霍时间“的感觉。
到现在,虽然没有成为什么成功人士,但心性和认知我自认为自己了解了他们是什么。
公司最近裁员了,实习期认识的同事被迫离职了。。。,不知道自己能干多久。我知道命运应该要掌握在自己手里,如何掌握还需进一步探索。希望刚入行或即将入行的同学提早规划下吧!
]]>互联网的世界绝大部分的数据传输都是基于 HTTP 协议。自从 1990 年伯纳斯·李发明这 超文本传输协议(HTTP, HyperText Transfer Protocol) 后,于 1999 年 IETF 才正式发布 HTTP1.1(RFC 2616) 协议版本,而这个版本一用就是 15 年,至 2015 年 IETF 才正式发布 HTTP2(RFC 7540) 协议版本。其中 HTTP2 相比 HTTP1.1 的优化,我将会专门写个文章详细说明下。而今天我们会谈未来的 HTTP 协议 – QUIC/HTTP3。
视频示例:
协议截图:
相信大家很早听说的下一代协议是 QUIC, 即 Quick UDP Internet Connections,意为快速UDP网络连接。该协议最早是由谷歌于 2013 推出的,项目首页: https://www.chromium.org/quic,并且在谷歌自家的 Youtube 和搜索引擎上试验,有兴趣的可以看下 The QUIC Transport Protocol - Design and Internet-Scale Deployment 这篇谷歌发表的论文。下图是谷歌应用 QUIC 于搜索引擎的的搜索延迟降低的百分比。(标1的地方是当时谷歌发现了 Bug,到 标2处修复后重新上线。标3处则是谷歌进行了一次优化,所以性能有了逐步提高) 整体上能降低搜索 6% 的延迟。
正因为 QUIC 的优势(为什么会有这优势,下面会进行说明),谷歌已经逐步将 QUIC 推广到自家的各个服务中了。
而当时 IETF 组织正忙于 HTTP2 协议的标准化,最终于2015年5月正式发布 HTTP2 规范(RFC 7540)。在 HTTP2 标准化完后,IETF 终于有时间进行下一代 HTTP 标准化,也就是 HTTP3。 而谷歌也有意将 QUIC 作为普适性协议进行推广。因而,于同年6月,谷歌提交 QUIC 草案给 IETF,以期作为下一代 HTTP 协议标准进行普及。
至 2018年,IETF组织中专门制定 HTTP 协议的组织(HTTP WG)正式确定将基于 QUIC 的 HTTP (HTTP Over QUIC) 作为下一代 HTTP 协议,并重命名为 HTTP3。并且 HTTPWG 认为标准化的 QUIC 协议应该支持 HTTP 以外的应用层协议。因此,IETF 将 QUIC 作为单独的传输层进行标准化,并成立了专门的 QUIC 协议标准化小组。
这里 QUIC 出现的频次较多,由于历史原因,导致大家会很难理解 QUIC 的具体含义,这里着重讲解下。
为便于更好地区分 HTTP3 标准化前后两种 QUIC,现在,一般我们现在将谷歌的 QUIC 改称为 gQUIC,而 QUIC 则仅仅指代 IETF 的传输层 QUIC。文字不好理解,我特意画了张图。
虽然现在我们知道这 QUIC 应该指 IETF 的基于 UDP 的上层 “传输层协议”,但是由于 HTTP3 还没有出正式标准,且了解 IETF 中规范的 QUIC 标准很少,因此互联网上所说的 QUIC 一般都是 gQUIC。下文除非特别说明,QUIC 一律指 gQUIC。
对于 QUIC 的特性这里简单说下,以后会对每个点进行详细解释。
我们直接看谷歌 QUIC 官方首页:https://www.chromium.org/quic
一共总结四点:
通过整理了网上的特性说明,我将转换了下说法:
对应的图示如下:
RTT 是指互联网上两个点建立连接的一次往返时间(round-trip time)。QUIC 建立握手的时间能达到 1 或 0 RTT。这是因为原有的 HTTP 是基于 TCP 的方式中:
而 QUIC 则是基于 UDP 的,UDP 无需建立握手就能传输数据,那 1 RTT 只指 SSL 的建立时间。因此建立连接效率比原有 HTTP 快。这个我会专门写篇文章介绍。
而裸 HTTP 是不安全的,谷歌表示未来的协议都将默认加入安全传输 SSL 协议,因此 QUIC 本身就是要基于加密的,目前的大多数 QUIC 协议的实现也都默认含有加密的,因此,对于 QUIC 的对比,一般都是以 HTTPS 进行对比的。
我们知道 TCP 的拥塞控制是内嵌于操作系统的,Linux 内核从 2.6.19 开始就是用 Cubic 拥塞控制算法。而谷歌于 2016 年发明了全新的 TCP 拥塞控制算法 – BBR (Bottleneck Bandwidth and RTT),这个已经验证是比 Cubic 性能更好的拥塞控制算法。因此,Linux 内核从 4.19 开始加入了 BBR 算法。
每一次的拥塞控制算法的更新都要更新内核,这使得新算法的迭代和普及极其缓慢。而 QUIC 协议则是基于 UDP 实现的类似 TCP 的协议,诸如有序控制、拥塞避免、拥塞控制都进行了重新实现。从宏观上来看,QUIC 则就是应用层协议,这对于拥塞控制算法的更新极其容易,不用更新操作系统。
此外 QUIC 中的拥塞控制是可拔插式的,这可更进一步优化和迭代各种新的拥塞控制算法。这也是 QUIC 极具吸引人的地方。
这里的消除队头阻塞在 QUIC 上是彻底地消除了。我们都知道 HTTP2 协议的规范上也说了消除队头阻塞。但那仅仅是消除了 HTTP 连接的队头阻塞,而根本上的 TCP 队头阻塞是没有消除的。因为只要基于 TCP 就铁定有 TCP 队头阻塞的。而 QUIC 是基于 UDP 的,非面向连接的,从而自动消除 TCP 队头阻塞。
连接迁移这个就是指手机在 4G/5G 流量网络与 WIFI 网络之间可以无缝切换,而无需重建连接。
我们知道原有的基于 TCP 的 HTTP 协议,在切换网络时,都需要我们的手机与服务器进行重新建立 TCP 连接,然后才能重新发送 HTTP 数据包。
而 QUIC 协议是基于 UDP 的,天生无面向连接之说,但是我们还是需要维持客户端与服务的逻辑连接的。QUIC 中在数据包的头部加了 ConnectionID,这样每个 UDP 包里都有同一个连接的 ID,即使手机从 4G 切为 WIFI 了,手机在发送包时,仍然正常发送,而服务器可以根据 ConnectionID 进行组装即可,这也就是无缝连接迁移。
废话不多说,看谷歌给的连接迁移 Demo: QUIC Connection Migration demo
由于 HTTP3 协议还在制定中,目前还没有正式的标准出台,只有每年定期举行的会议会给出 Draft 手稿。 IETF 官方 QUIC 工作组有统计在协议设计期间的各种实现库:
大家可以看下,我自己对比了比较常用和著名的库,表格如下,希望对大家选择有帮助。
今天我们先用 openlitespeed 库进行部署支持 QUIC 协议的 WEB 网站。因为该库官网放出性能是 Nginx 的几倍,且支持 gQUIC 和 HTTP3 协议。
openlitespeed 安装很简单,可以看官方教程: Install OpenLiteSpeed, 为防止部分人看不到,我记录了下:
先根据系统安装 repo 源:
1 | # CentOS 5 |
再执行安装命令(根据自己的系统选择安装命令)
1 | # CentOS |
安装完了就进行配置,配置说明请参考官方文档: Configuration
说实话,这个库是有后台的,是可视化配置,但是比较难用,需要自己熟悉才行。为避免大家浪费时间,我将整个服务安装好,并配置好了,构建了一个基础镜像:https://hub.docker.com/r/jiyiren/http-base-quic,如何配置自己的服务呢,下面简单讲下。
部署和配置代码已放 Github 上:https://github.com/jiyiren/quic-web
效果展示,我部署在我自己的服务器上: https://http3.godjiyi.cn:9445/ 。
并且我也写了一个 HTTP1.1 、HTTP2、HTTP3 速度加载对比示例: https://demo.godjiyi.cn
分别用 Chrome 浏览器和 Firefox 浏览器进行测试。这里说明下:
目前 Chrome 浏览器支持谷歌自家的 QUIC 协议,而谷歌家的 QUIC 原来单叫 QUIC,比如 gQUIC/46。在提交草案给 IETF 后,IETF 也开始了标准化,因此逐渐地谷歌开始将自家的 QUIC 像 IETF 标准靠,因此为 gQUIC-h3-50,相信以后 Chrome 会逐渐替换到 HTTP3 来。
而 Firefox 浏览器本身就和 Chrome 是竞争关系,所以虽然 QUIC 好,但没有国际组织的支持 Firefox 也不会支持的。而在谷歌提交草案后,得到了 IETF 的认可,并且 IETF 在进行标准化了,因此 Firefox 直接实现了 IETF 正规的 Draft 版本协议。所以 Firefox 浏览器标识时正规的 HTTP3 协议。
但不论怎样,这些协议思想一致,都是 UDP 进行传输,握手 RTT 很低,大家通过上面的速度加载对比就能看出来 HTTP3 的快速,当然在弱网情况下效果会更好点。
项目已放入 Github:https://github.com/jiyiren/quic-web 基于 Docker 构建,可以非常快速的构建自己的站点或 API。
主要关键词: IEC、ISA、ISO、ITU、EMCA 、IETF、W3C 等说明;
时间线图示:
下面会分别对各个组织进行详细介绍。
IEC 成立于 1906 年,是世界上最早的国际性电工标准化机构,总部设在日内瓦。但由于 IEC 只关注电工领域标准化,而其他方面缺少标准化组织进行全球化规范。而直到 1947 年 国际化标准组织 ISO(下面会介绍) 成立,其关注全方面的标准化内容。
而 IEC 既然已经在电工领域进行标准化运作了,因此,IEC 作为电工部门并入 ISO,但在技术上、财务上仍保持其独立性。根据 1976 年 ISO 与 IEC 的新协议,两组织都是法律上独立的组织,IEC 负责有关电工、电子领域的国际标准化工作,其他领域则由 ISO 负责。 IEC 的工作领域包括了电力、电子、电信和原子能方面的电工技术。现已制订国际电工标准 3000 多个。
IEC 是世界上最早的国际性标准化组织了,也是因为全球化的发展起源于电力的运用,因此电力领域是最早进行规范化的部分,而其他部分随着历史的发展也不得不成立新的组织。
前面 1906 年 成立的国际电工委员会 ( IEC ) 是世界上最早的国际标准化机构, 但它主要专注于电子领域。其他技术领域的工作还没有标准化组织进行规定约束,因此在 1926 年成立了 国家标准化协会的国际联盟 ( ISA ) 来承担该角色,ISA 的重点在于机械工程方面。直到 1939.9.1-1945.9.2 爆发的第二次世界大战,ISA 的工作最终在 1942 年终止运行。
上面也说了 ISA 由于二战最终终止运行了,那非电子领域的国际标准化工作总的有人来做吧。于是,在二战后一年的 1946 年,来自 25 个国家的代表在伦敦召开会议,决定成立一个新的国际组织,其目的是促进国际间的合作和工业标准的统一。于是,ISO 这一新组织于1947年2月23日正式成立,总部设在瑞士的日内瓦。
ISO 标准的内容涉及广泛,从基础的紧固件、轴承各种原材料到半成品和成品,其技术领域涉及信息技术、交通运输、农业、保健和环境等。
多人注意到国际标准化组织( International Organization for Standardization )的全名与缩写之间存在差异,为什么不是“IOS”呢? 其实,“ISO”并不是首字母缩写,而是一个词 ,它来源于希腊语,意为“相等”,现在有一系列用它作前缀的词,诸如“isometric”(意为“尺寸相等”)、“isonomy”(意为“法律平等”)。从“相等”到“标准”,内涵上的联系使“ISO”成为组织的名称。
说到 ISO 作为计算机专业的大家是不是一下子就能想到一个互联网基础协议– OSI 七层网络互联协议,这里的 OSI 是指开放系统互联( Open System Interconnection ) 的意思,也是英文缩写,并且该标准就是 ISO 这一标准组织设定的。虽然这七层互联协议在工业实践中被 TCP/IP (注: TCP/IP 协议不是由组织定义的而由罗伯特·卡恩、温顿·瑟夫在1978年发明的)协议超越,但它提供的分层设计思想为后来很多系统和协议提供了很大的帮助。
目前,ISO已经发布了至少 17000多个国际标准,如 ISO公制螺纹、ISO的A4纸张尺寸、ISO的集装箱系列(世界上95%的海运集装箱都符合ISO标准)、ISO的胶片速度代码,以及最出名的ISO9000 品质保证标准也是由 ISO 发布的。
由于 ISO 是国际标准,有的时候并不适合国内,因此我国一般会对国际标准进行修改以适应国内情况,为避免国内标准与国际标准不一致,因此国内命名的标准一般以 GB/T (GB 国标的首字母,T 表示推荐标准) 开头,对于国际标准的,一般后面接 1+ 国际标准代号,如 ISO9000 在国内则叫 GB/T19000。
ITU 的历史可追溯到 1865 年。为了顺利实现国际电报通信(注: 1864年,麦克斯韦建立电磁理论),1865年5月17日。当时有20个国家的代表在巴黎签订了一个《国际电报公约》。
之后,
这使得电通信得到长足发展,而无线通信也逐渐步入人类生活。于是,
1932年, 70多个国家代表在西班牙马德里开会,决定把上述两个公约合并为一个《国际电信公约》,并将电报、电话、无线电咨询委员会改为“国际电信联盟”。并决定自1934年1月1日起正式改称为“国际电信联盟 (ITU)”。
1947年10月15日,经联合国统一,国际电信联盟( ITU )成为联合国的一个专门机构,其总部由瑞士伯尔尼迁至到日内瓦。ITU 是联合国的15个专门机构之一,但在法律上不是联合国附属机构,它的决议和活动不需联合国批准,但每年要向联合国提出工作报告。
对于 ITU 的标准规范,我们最熟悉的莫过于无线网络的传输方面的规划了,比如频谱管理、无线电波传播、卫星业务等。未来的 5G 相关传输标准和频率划分也离不开 ITU 的规范。
这个机构我们一般都比较熟悉其下的一个标准 ECMA-262 ECMAScript 标准,因为 JavaScript 语法就是由其标准化的。
ECMA 是 1961 年 成立的,与前面的 IEC、ISO、ITU 等机构不同,它本身并不是官方机构,而是由主流厂商组成的一个非盈利组织。旨在建立统一的电脑操作格式标准(包括程序语言和输入输出)的组织。该组织的目标是评估、开发和认可电信和计算机标准。
简单说吧,就是 IEC、ISO、ITU 等官方机构定义基础设施标准,比如安全级别、通用尺寸、频谱划分等,各种类别都有定义,而 ECMA 则只关注计算机方面的基础设施标准,并进一步对应用层方面进行更细致的标准定义,一般由谷歌、微软、苹果等大厂通过实践经验进行提交参议稿,大家可以看百度百科 ECMA, 已发布的标准名单里基本都是编码、磁带、软盘、磁盘、文件系统等与计算机相关的标准。
由于 ECMA 也是跟随国际组织的标准的,为便于跟进国际标准,大家决定把 ECMA 的总部设在日内瓦,因为这样能够让它与其它与之协同工作的标准制定组织更接近一些,比方说国际标准化组织(ISO)和国际电工委员会(IEC)。
对于前面讲的 ECMAScript 标准与 JavaScript 的关系这里也说下。在 1995 年时,网景公司发布了世界上第一个商用浏览器 Netscape, 并同时发明了 JavaScript 以便于用户网页动态交互。之后微软发现浏览器的重要性,于是就爆发了世界著名的浏览器大战。微软在 IE 中也发布自家的 JScript 脚本,之后还有其他的脚本语言出现,如: CEnvi 的 ScriptEase。
这么多脚本语言的出现,导致不同浏览器的脚本写法各不相同,因此,急需一个标准来规范各种网页脚本语言。
try / catch
异常处理,更严格的错误定义,数字输出的格式化以及预期未来语言增长的微小变化。第三版在当时应用非常广泛,几乎所有的浏览器都支持 ES3 的标准。ECMA-262 第四版本( ES4 )夭折,部分功能被迁移到ES6中。
ES6 主要新增了如下功能:主要增强包括模块,类声明,词法块范围,迭代器和生成器,异步编程的承诺,解构模式和适当的尾部调用。内置的 ECMAScript 库被扩展为支持额外的数据抽象,包括映射,集合和二进制数值数组,以及对字符串和正则表达式的使用。
从 ES1 到 ES6 ( ES4 除外),不管大家有没有亲身经历过,至少到现在为止我们可以使用由 ECMA 制定好的标准规范,而无需担心各种兼容性问题了。
IETF 也是非官方机构,它也只是一个公开性质的大型民间国际团体,汇集了与互联网架构和互联网顺利运作相关的网络设计者、运营者、投资人和研究人员,并欢迎所有对此行业感兴趣的人士参与。
IETF 内部引领者也基本都是苹果、谷歌、微软等公司员工。因为 IETF 的目标是互联网中的技术规范,而互联网中最具影响力的就是操作系统与浏览器(包括搜索引擎),而这些工具都被苹果、微软、谷歌牢牢掌握住了。也正因为此,这些巨头也希望规范标准向着有利于自身的利益方向发展,从而很多新的规范标准得建议稿以及代码实现、测评也都是由这些巨头公司完成的,毕竟他们的用户都是覆盖全球的、网络流量也都是数一数二的,从这些公司中测试的结果中才能更好地体现标准的性能或优劣。
就比如我们最常用的 HTTP 协议吧,从 1999 年 HTTP 1.1 标准发布后,经过了 16 年,直到 2015 年才发布 HTTP2。而其中对其贡献最大的莫过于 Google 公司了。因为 HTTP2 是基于 Google 在 2009 年提出的 Spdy 协议的。而未来的 HTTP3 也是基于 Google 提出的 QUIC 协议进行设计的,HTTP3 的草案也是由 Google 在 2015 年提交给 IETF 的。
为什么谷歌能引领 HTTP 协议呢?最主要的就是谷歌浏览器占有时长 80% 以上的市场份额,因为 HTTP 新协议的测试必须有两个要素,第一个就是浏览器、第二个就是服务端。服务端很好控制,新协议部署就行,但是浏览器要修改新协议则必须是自家的才行。而 Google 正是因为拥有了大量的浏览器市场份额,才能很早就设计并实验超高性能的新协议。
正因为有这些巨头乐意参与标准的制定,才会推出性能越来越好、适用范围越来越广的标准规范,推动着时代进步。
IETF 制定的是整个互联网的标准,比下面讲的 W3C 机构的范围更广。因为 21 世纪是“万物基于互联网”的时代,而 W3C 负责的仅仅是网页内容的规范制定,虽然网页是互联网中最大的应用场景,但仍然有很多其他互联网应用,比如:FTP,邮件,DNS 等等。简单来说,W3C 制定的仅仅是基于 HTTP 协议的网页方面的规范标准,而 IETF 则制定互联网互联互通规范标准,包括各种协议,比如 HTTP 就是 IETF 制定的。另外 IP(包括 IPv4, IPv6)、DNS、SMTP 也都是由 IETF 制定。目前 IETF 的最新工作内容则是 HTTP3,还没有发布正式版本。
万维网联盟,又称 W3C 理事会。1994 年10月在麻省理工学院计算机科学实验室成立。建立者是万维网的发明者蒂姆·伯纳斯·李。到目前,W3C 已成为 Web 技术领域最具权威和影响力的国际中立性技术标准机构。到目前为止,W3C 已发布了200多项影响深远的 Web 技术标准及实施指南,包括 HTML、XML、WCAG,而最新的 HTML5、CSS3 分别在 2014年10月28日和 2015年5月20日由 W3C 组织正式发布。
W3C 主要关注前端 Web 方面的规范制定,基本上都是静态渲染的语言规范。JavaScript 这个则不是由 W3C 制定的,这个是由前面的欧洲计算机制造商协会( ECMA ) 制定的,目前已到 ECMAScript6,也是 2015 年提出的。
项目源码地址:https://github.com/jiyiren/ProjectDoc
有色Demo预览地址:https://jiyiren.github.io/project-doc/
无色Demo预览地址:https://jiyiren.github.io/project-doc-plain/
无色样式:
下载安装后测试下 Node 是否安装成功:
1 | $ node -v |
如果提示命令没找到,那么是由于 Node 没有加入环境变量,大家将安装的 Node 环境地址放在环境变量里就可以了。
直接输入命令进行安装:
1 | $ npm install gitbook-cli -g |
npm
也是和 node
一起安装的,node
存在 npm
就存在。-g
参数表示全局安装,也就是模块包会安装到全局环境里,这个是推荐做法,因为像这种工具命令全局安装是最好的。而项目依赖模块则项目内安装即可。
测试 gitbook 命令是否安装成功:
1 | $ gitbook -V |
任意找一个空目录,执行:
1 | $ gitbook init |
会在当前目录下创建出两个文件,分别是:
1 | README.md |
暂且先不管其他的,我们现在可以直接运行试试,先把流程走通:
1 | $ gitbook build |
上面的 gitbook build
是编译整个 markdown
文件,然后在当前目录生成 _book
目录,里面是 html 页面。这个主要在部署的时候用到。
而 gitbook serve
是本地调试开启服务命令,项目最终是要成网站的,因此, 该命令可以开启本地 http://127.0.0.1:4000
地址作为网站浏览地址。
假如大家执行 gitbook serve
出错,建议大家先 gitbook build
在 gitbook serve
.
我们上面通过 gitbook init
生成的只有下面两个文件:
1 | README.md |
但实际上我们要定制我们的 gitbook 项目,项目还有一个配置文件的: book.json,只不过 gitbook init
没有自动创建出来。我们一看这文件名就应该知道这个配置文件就是 json
格式的。最基本的 book.json 长什么样子呢?如下所示:
1 | { |
基本 book.json 内容:
上面最基本的 book.json,对默认界面基本无变动,其界面显示为:
我们来一个简单 gitbook 定制,book.json 如下:
1 | { |
其界面为如下,多出左侧栏 本文托管,和文章右侧的 目录以及回到开头 按钮。
插件使用
插件添加:插件的使用就放在 book.json 的 plugins 和 pluginsConfig 键中,形式如:pluginName@versionName
也就是插件名@版本,当然没有版本时,采用最新默认版本。
插件删除:要删除自带的插件则使用 -pluginName
即 -插件名
下面介绍本文档使用到的插件。
使侧边栏的宽度可以自由调节
1 | "plugins": [ |
文章页面右上角显示目录,这个目前已经被废弃,建议每个页面自己生成 md 目录。
1 | { |
支持中文搜索, 需要将默认的 search 和 lunr 插件去掉
1 | { |
为页面添加页脚
1 | "plugins": [ |
添加Toc到侧边悬浮导航以及回到顶部按钮,这个自动生成的悬浮目录必须以下面形式书写,也就是一定要有一个是 h1 开头的,否则不能识别。
1 | # h1 |
配置代码:
1 | { |
使用 Prism.js
为语法添加高亮显示,需要将 highlight
插件去掉。该插件自带的主题样式较少,可以再安装 prism-themes
插件,里面多提供了几种样式,具体的样式可以参考 这里,在设置样式时要注意设置 css 文件名,而不是样式名。
1 | { |
其他插件大家可以参考这个博主的:http://gitbook.zhangjikai.com/plugins.html
最终的页面示例
有配色见:https://jiyiren.github.io/project-doc/
无配色见:https://jiyiren.github.io/project-doc-plain/
今年都说工作形式不好,多家互联网企业被爆裁员。互联网,曾今是多么充满活力的名词,基本上是”万众创业”的根本,而现在给人的则是”赢者通吃”的感觉,再无敢与巨头相争,百花齐放的景象了。
虽说现在巨头当道,创业热情没有以前的高,但互联网仍然是最大的创业领域。目前,移动互联的创业确实少了,但是 5G、IoT、人工智能的创业项目却有很多。
很多时候我们都在以我们自己能看到的信息进行评判和决策的,由于我们都处于历史长河中的一部分,在那一部分中我们的绝大部分技能、信息都聚焦于某一种东西上,如果时代改变了这种东西的价值,那我们自身的价值也会相应变化,最坏的情况就是贬值,而这正是互联网的特性。
心有不安,不安在时代正在极速地改变着最吃香的技能,而自己的技能却在逐步被淘汰!很多人怕的不是被裁,而是怕自己已经适应不了社会的需求了!
]]>项目源码地址:https://github.com/jiyiren/ReportPaper
文档预览地址:https://img.godjiyi.cn/report_paper.pdf
需要先安装 LaTeX 环境,而支持中文的 LaTeX 为 CTeX,CTeX 也只是一种标准定义,其通常分为两个发行版:
两个发行版都是全平台支持的,很多人会将 MacTex 也作为一种发行版,但我喜欢将其归类于 TeXLive 发行版中,这看自己的意愿。
因此,环境的话大家可以自行选择,这里为使用 TeXLive,下面为安装包地址,大概有 3 GB 左右:
安装后将命令加入环境变量,并使之生效,通过 which latex
可查看是否设置成功:
1 | $ which latex |
LaTeX 实际上如果 Java 语言一样,都需要先配置环境,然后选择一款自己喜欢的 IDE 进行编写“代码”。
当然所有文本编辑器都可以编写 LaTeX 或者 Java 等其他语言“代码”。这里的 IDE 指集成了一些语言本地化的功能,比如编译、特殊符号等等。
LaTeX 的发行版中会自带一款编辑器,用 TexLive 的话,MacOS 上会有个叫 TexShop 的编辑器,而 Windows 上则会是一个叫 TexWorker 的编辑器,这些是都可以胜任编写工作的。
另外,对于第三的 LaTeX 编辑器,笔者也用的不多,这里我推荐两个:
我个人推荐前两个,因为第三个收费且不跨平台,之所以写上第三个,主要是因为网络上很多博客或用户都推荐用第三个,这个我使用时也感觉不错,但每次我使用都得到 Windows 上使用,比较麻烦。当然,如果你使用 Windows 且有钱,WinEdt 确实使用体验和功能都是比较好的。
上面两点都讲了没实际作用的环境配置,对于使用本项目实际上很简单:
使用 XeLaTeX 编译:
1 | xelatex report_paper.tex |
用 BiBTeX 再次编译生成的 report_paper.aux
文件:
1 | bibtex report_paper.aux |
之后再次用 XeLaTeX 编译:
1 | xelatex report_paper.tex |
这时候生成的 pdf
文件是最全和完整的文档。
上面是命令行编译的,大家若使用 IDE 则是比较简单的操作:
这时产生的 pdf
和上面一致;通过编辑器形式不用自己指定文件名,因此更简单方便。
最终生成的 pdf 文档示例:
Kafka 是一个基于 发布-订阅 的分布式消息系统,主要面向于大数据应用场景。它最初由 LinkedIn 公司开发,之后成为 Apache 项目的一部分。Kafka 是一种快速、可扩展、本身就专注于分布式的、实时消息流系统。Kafka 在2010 年正式向 Apache 社区开源,目前社区活跃。目前在互联网公司使用非常广泛,已经成为大数据分析的基础服务。
Kafka 是众多消息系统中的一种实现方式,那我们为什么需要用到 消息系统 ?这里我列出以下几种在业务中常常碰到的场景,分别从 系统架构视角、消息传播视角、消息处理视角、自身系统结构视角 共四个角度的应用场景来说明:
从系统架构视角看
这个我想是最明显的一点了,在业务系统上通常会存在一些系统产生数据,一些系统消费数据,这实际上就是 生产者–消费者 模式。这里的解耦是什么意思呢?我把消息系统独立出来了,那我的消息系统则会依赖生产者系统,消费系统则会依赖消息系统,不是多出了两个依赖,何为 解耦 呢?
这里的解耦实际上是解耦 不等速率依赖 (这个名词是我造的,纯属个人观点)。也就是说生产者系统和消费者系统之间会有 生产和消费速度不一致 而导致消息丢失的情况。而消息系统与生产者系统、消息系统与消费者系统之间则没有这种情况,即使有也不会造成消息丢失(只会暂存下来)。因此,破除了生产者系统和消费者系统的依赖关系就叫 解耦,而消息系统正是为此而生。
从消息传播视角看
很多时候,或并发达到一定量级的时候,系统是不能完全提供 实时消息 处理的能力的。这时不能立即处理的消息我们必须把这些请求放入 缓冲队列 中以等待处理。这种场景在一个公司中可能会有多种业务都会涉及到,因此,领先者们自然想到可以将缓冲队列设计成一个独立的平台,以满足各种业务的接入,从而,消息系统作为缓冲异步队列轰然降临。
这里我使用的是 缓冲 队列而非 缓存 队列,主要是因为通常我们所说的缓存都是基于内存的,而 缓冲 则更普遍一点,你可以让它基于内存,也可以让它基于 硬盘 的。通常消息系统基本上都是基于硬盘存储的,包括 Kafka 其也是持久化到硬盘的。
那 缓冲队列 与我们的 Redis/Memcached 缓存 或者 DB 数据库 有什么区别呢,为什么不用 Redis/Memcached 或者 DB 实现 缓冲 功能呢?
这里我也大概谈下自己的看法:
从消息处理视角看
有些情况下,我们将数据提交给某个系统处理,有可能那个系统突然崩溃了,那传给它的数据就都覆灭了,这可是企业不能容忍的!因此,我们可以利用消息系统,作为临时备份处,将消息同时发送给消息系统以及那个处理系统,当处理系统处理成功后,发送确认操作让消息系统删除那条消息,也就是采用 “插入-获取-删除“ 范式。这样,假如处理系统崩溃,那数据仍然在消息队里中,重启处理系统就可以了。
这里,我们都是假设消息系统很可靠,比处理系统更可靠!为什么有这个依据呢?这实际上也是消息系统的另一大特性,就是可扩展性强,部分组件失效可容忍。
从自身结构视角看
这部分的内容我们在后面几节进行说明。
消息模式就是消息系统实现时需要考虑的业务场景中的不同情况。由于生产者(系统)和消费者(系统)都可能是多个,那么就会产生一些微妙的不同。这里我们只考虑它们都处理相同的消息。对于多个生产者而言,消息系统就是不断接受消息的一个存储域,因此没有什么不同。而对于多个消费者而言就会有两种情况了:
以上两种模式就是消息模式最常见的两种,所有的消息系统的实现都会考虑这两种模式的,因此大家在学习一个新的消息系统的时候就可以考虑这个消息系统 如何实现者两种情况的。对于 Kafka 我们会在下面讲到其对应的两种模式。
消息系统目前最有名气的大概有四个:ActiveMQ、RabbitMQ、Kafka、RocketMQ. 它们的对比网上也应该有很多了,我就不一一列举了。
这里我将我之前做过的 PPT 拿过来放这里作下对比,我简单说明下:
下图是 Kafka 消息系统的 分布式宏观架构图,这里分别讲下各个组件的作用及其关系:
Kafka 内部消息传递流程 如下图所示:
一个 消息主题,也就是一个分布式业务消息队列。不同的生产者将不同的业务消息分发到不同的 topic 上,这样,消费者就可以根据 topic 进行对应的业务消息消费了。
这个就是 topic 分布式的体现,由于一个 topic 就是一个业务消息,这些消息可能会源源不断来,并且有可能会同时并发很大地进入队列,将这些消息合理地分布在分布式机器中则可以保证机器的负载均衡性,同时也可以使得不同的消费者可以同时拉取不同 partition 中的消息,可提升消费者并发性能,这里总结下 partition 特性:
消费者组应该是 Kafka 最大的特色了,消费者组就是消费者组成的一个组,消费者在向 Kafka 拉取数据的时候需要提供一个组名,这个名称就是消费者组名,上面的两种消息模式都可以在消费者组中得到实现:
这里还有如下注意点:
OpenTSDB 是为存储时序数据而设计的,它基于 HBase 存储数据,充分发挥了 HBase 的分布式列存储特性,支持数百万每秒的读写,支持千万数目的 Metric,它的特点就是容易扩展,具有灵活的 Tag 机制。其主要用途,就是做监控系统,譬如收集大规模集群( 包括网络设备、操作系统、应用程序 )的 监控数据 并进行存储和聚合查询,在目前的 IoT 方面具有很大的应用价值。
OpenTSDB 是基于 Hbase 存储系统的,主要利用了 Hbase 数据自动排序 以及 可靠的分布式特性。 OpenTSDB 在安装启动时,默认在 Hbase 里面创建 四张 表。分别为:
下面主要说明下 tsdb 和 tsdb-uid 的表结构。
这里我们主要分析下 OpenTSDB 存储 UID 的表 – tsdb-uid。
首先,看下 tsdb-uid 的表结构。其存储的是字符串到 UID 的映射关系。
我们可以通过 hbase shell
通过 Hbase 访问数据库入口查看小 tsdb-uid 的表结构:
1 | list 'tsdb-uid' |
上面结果我只列出了少量信息,主要看 NAME 就可以了,表示该表的 Column Family, 分别为 name 列族和 id 列族。那具体怎么将字符串映射为 UID ? 这里就需要通过实际的例子来说明。
我们先上传一个数据,格式内容如下,可以通过 Postman 进行上传测试,OpenTSDB 默认上传地址为 http://ip:4242/api/put?details
,后缀 details 是为了查看上传反馈。
1 | [ |
上传成功后返回:
1 | { |
我们先看下我们上传的数据格式,需要进行映射的字符串是对应 metric
, tagkey
, tagvalue
的,这里 tag
有两组,所以要映射的有 5 个字符串,分别为: sys.test.metric, hostname, jiyiren, area, shanghai.
那我们就再用 Hbase Shell 查看表内容:
1 | scan 'tsdb-uid' |
从中我们可以看到,数据总是成对出现的,包括 UID 映射字符串 和 字符串映射 UID。上面 5 组是 UID 映射成字符串,下面 5 组是字符串映射为 UID. 前面已经看过 tsdb-uid 表有两个列族,而其中的 name
列族对应的就是 UID 映射成字符串,而 id
列族对应字符串映射为 UID, 这正是这两个列族的作用,这样对于正反查找速度都是极快的。
此外,对于 UID 映射字符串的,每行数据,也就是 rowkey
相同的,至少包含三个列,分别是 metrics
, tagk
, tagv
. 我们可以通过前三行结果看出。
到这里我们知道了 UID 与字符串间是怎么映射以便于查询的,但是 UID 到底是怎么生成的呢?
实际上大家在前面操作 scan 'tsdb-uid'
的时候,结果会列出额外三行以 \x00
开头的数据:
1 | ROW COLUMN+CELL |
实际上 UID 是用 3 bytes 表示的非负整型数,并且是自增的,而自增的就要依赖于上一次插入的最新 ID
值,这三行就是分别保存 metrics, tagk, tagv 插入的最新数据的 UID,这样下次插入新的数据只要在对应的值上加 1 就能得到其对应的 UID 了。
我们再看看 OpenTSDB 的实际存储时序数据的表 – tsdb。
既然 UID 与字符串的映射关系搞定了,那么真实的时序数据存储就好理解了。tsdb 保存了所有的时序数据,其 rowkey
就是由各个字段对应的 UID 组成的。
先查看下 tsdb 数据库结果:
1 | scan 'tsdb' |
结果值太长了,可以分开看,先看列族里的数据【18.7.10 更正为】:
1 | column=t:I\x00, timestamp=1528521000278, value=\x0A |
其中 value=\x0A
而 0X0A
化为十进制就是 10, 正好是我们前面上传的 metric
的值。
再看看 rowkey:
1 | \x00\x00\x01[\x1Fa`\x00\x00\x01\x00\x00\x01\x00\x00\x02\x00\x00\x02 |
rowkey
是 OpenTSDB
设计的独特之处,其构成规则为:
1 | [salt]<metric_uid><timestamp><tagk1><tagv1>[...<tagkN><tagvN>] |
salt
是为了更好的分布式,
我们的上面添加的 metric
为,其中 tagk
会自动按字母排序,所以 area 排在前面:
1 | # 字符串对应 |
除了 timestamp 和上面结果完全对应,而 timestamp 则是按小时存储的,也就是取 3600 的整数倍的 timestamp 作为当前时间戳。计算方法 timestamp - timestamp % 3600.
这样,我们应该对 OpenTSDB 的 UID 以及 Rowkey 的生成和存储结构都基本了解了。
img.godjiyi.cn
]]>时序数据 就是基于时间序列的数据,其常常表现为同一指标按时间序列记录的数据列,在需求实时性的场景中比较常见。而对于此种数据的运用通常使用基本的 聚合 方式就能达到需求了。当然,目前 AI
盛行的时代,机器学习领域也不断出现很多基于 时序预测 的算法。但本文主要介绍时序数据的基础认识,这部分的认知主要是从自己目前所做的数据监控项目的经验所得,若有不正确,请大家批评指正。
时序数据 和一般的数据没什么区别的,基本上也都用 json 格式表示,唯一不同点就是数据中一定包含关于 时间 的信息,比如: 时间戳。
一般一条时序数据只表示一个键值信息,而在时序数据中,这个键常常称为 指标 或 指标名 (英: metric ),而值则就是指标对应的值了。因而,一个时序数据的基本格式如下:
1 | { |
主要包含了 时间戳、指标名、指标值。其中,对于指标的值,也就是上面的 value 字段值,这个值一般都是 数值型 ( Integer、Float、Double ) 的, 为什么大多是 数值型 的呢?这个下面会进行说明。
到这里,我们已经知道了一个时序数据的基本格式。但是,难道时序数据就是一个格式吗?即使加入了时间信息,那也和普通的 json 数据也没什么本质区别呀?
的确,时序数据的存在可不是因为一个数据格式,而是由于 数据聚合 应用的需求而出现的。比如,我们现在有 1 台服务器,我想快速地知道今天上午 10 点到上午 12 点之间这台服务器的 内存 使用的 平均值、最高值、最小值,那我们怎么办?其中,这个指标可能也会是 CPU、磁盘 IO 等其他指标。
这里我们可以看出像 平均值、最高值、最小值 等等功能对于 metric 是通用的,因此,我们只需要将各种需求功能设计成通用的 聚合函数,那么我们需要看哪种指标的 平均、最高、最小 等聚合值时,只要选择对应的函数即可了。
这实际上也就是为什么 value 字段大多是 数值型 的原因,因为聚合函数绝大部分只是一些常规的 数学计算,数值型是最好处理的类型。当然这不是绝对的,只要你的后台明白如何处理对应的值类型即可了。
然而,上面举例是 1 台服务器,那我们如果有 2 或多台服务器,比如叫 hostA,hostB,host… 。 那么,我们直接对内存指标进行聚合那会计算到两台机器的聚合值,这个不是我们想要的,我们需要能对特定的主机进行聚合的能力,那该如何做?
这时就用到了 标签 功能,一个标签就是一个 键值对,通常标签是作为后台的过滤条件的,而由于过滤条件的多样化也需要标签的多样化,因此一个时序数据中可以包含多个标签的。从而我们需要在基本的时序数据格式中再添加一种 键名,即 标签组 – tags,如下:
1 | { |
这样表示的时序数据更具有通用化、个性化、定制化的能力,从而我们可以先进行指标、标签的过滤后再进行相应的 聚合操作,这样就能更满足多样化的业务需求。而上面多台服务器的情况,则需要在上传数据时加入 host.name
值, 这样后台可根据该字段检索 host.name = hostA
也就是 A 主机的指标,然后对特定指标进行聚合即可。
时序数据的需求通常是出现在 随着时间的推移某个指标值变化关系到业务运转 的情况下,因此我们就需要 间隔性地上传那个指标的数据 以实时地知道其状态值以应对突发情况,这个实际上就是一种 数据监控 场景。这里的 监控 可不是我们平常的视频监控,而是 指标的检测与上报,比如我们用 脚本 实时检测网站服务器的内存指标状态、CPU 状态、磁盘 IO 状态并上传到统一的后台,这个传输过程的数据格式就是使用时序数据,这样后台只需通过简单的 聚合功能 就能够对服务器的运行状态 了如指掌 了。
目前,时序数据应用最为广泛的也就是上面提到的 实时检测服务器主机指标 状态信息了,比如:阿里云、腾讯云等这些公有云服务提供商,当你购买一台服务器后,你在后台是可以看到一些服务器的性能指标的,这些指标信息就是实时监控主机并以 时序数据 格式传输出来的。
明白了上面的时序格式和应用方式,我们可以反过来想下,实际上时序数据的出现主要是由于 我们很想知道一段时间内一些指标信息的聚合结果 而产生的。为实现这种目的,我们不希望重复实现聚合功能,因此只需要实现一次可复用的聚合函数即可,这就产生了通用的 聚合函数。这相应地要求一条时序数据只包含一条 指标信息 以实现简单统一、包含一组 标签信息 以便实现筛选过滤。
因此,时序数据和其他一切业务名词一样,也都是由大量的实际需求逐渐演变成的统一化、规格化的结果!都是历史的选择!
]]>很多情况下,我们的程序需要在操作系统 后台 一直运行,这在程序代码里的实现就是用死循环 ( while (true)
) 来实现的。但是,这样会出现一个问题,就是我们想要关闭程序怎么办?如果用暴力结束进程方式,那程序的内存中若还有未输出的数据,这部分数据将会遗失。因此,我们要对程序实现 退出收尾 操作,这就需要我们完善我们的程序,实现 “优雅” 地退出。
首先,我们需要知道什么是后台进程。众所周知,我们与服务器进行交互都需要通过终端进行实现,而在终端上执行的程序都会默认将输出打印在终端界面里,而这中方式就 交互式进程,并且当前终端只能运行一个交互进程的,所以如果我们想在一个终端里运行多个任务,我们就需要将某些进程丢到 后台 ,而这些进程不影响当前终端的交互执行,就被称为 “后台进程”。
所有的 交互式进程 都是可以转为 后台进程 的,因为进程的操作任务是一定的,只不过是它们的显示方式不同罢了,通常我们在一个终端里在任务后面加上 & 操作符就可以让交互式进程变为后台执行进程了。如:
前台进程:
1 | git clone https://gitee.com/jiyiren/linuxfile |
如果按 ctrl + c
将会结束 clone 操作。
转为 后台进程:
1 | git clone https://gitee.com/jiyiren/linuxfile & |
我们可以看到此时该命令输出一个编号 70235,这个就是后台 job 的 ID,此时你按 ctrl + c 并不会结束改任务。如果要 查看 job 列表,可以使用 jobs -l
, 如下:
1 | jobs -l |
可以看到该任务在运行中,此时若想将该任务再 调到前台,可以使用 fg % jobid
( 注意百分号前后都有空格 ), 如下:
1 | fg % 70235 |
此时,显示的就是正在进程的任务,如果此时按 ctrl + c
则将取消 clone 操作。
上面是基本的 Linux 前后台任务转换命令,我们可以看到我们结束进程都是将任务调到前台,然后用 ctrl + c
, 来结束进程的。然而,将任务从后台调到前台的方式只能在同一个终端里操作的,如果用户在将任务掉入后台后关闭了终端窗口,那么该任务是永远无法通过 fg % jobid
调到前台了。这时如果要结束该进程怎么办?
还好我们有终极杀器 – kill
命令,但 kill
命令操作的是 进程 ID 而非 job ID。也就是说 job ID 只能是同一个终端下的操作,相当于终端局域性的,而脱离了该终端后,该局域的 job ID 就不再有效。而 进程 ID 则是全局性的,任意终端都可以操作的,并且局域的 job ID 都会有与之对应的全局 进程 ID 的,因此如果关闭了那个 job ID 所在的终端,我们可以通过 kill
job ID 对应的进程 ID 来结束此任务进程。
在我们平常的开发中,我们不可能一直维持着一个服务器的终端的,因此通过 ctrl + c
的方式结束 job ID 的方式对正式部署应用很不适合的,它只能适合个人的简单测试,因此 kill
命令方式才是 统一而确实有效 结束进程的方式。
假如,我们上面执行下面命令之后,就关闭掉了终端 ( 也不用管 job ID 了 ):
1 | git clone https://gitee.com/jiyiren/linuxfile & |
我们可以先通过 ps
命令来拿到我们的 进程 ID:
1 | ps -aux | grep linuxfile | grep -v grep |
上面第一个 grep
后面就是自己要搜索的进程中包含的 关键词,这个自己根据自己的命令选择命令中的关键词,这样便于更好地过滤。第二个 grep
则是去除本身这个查找命令的意思。
我们从上面命令结果可以看到有三个进程与此任务对应,其中第二列是 进程的 ID, 我们可以用下面命令杀死该任务的所有进程:
1 | kill -9 70376 70377 70379 |
这样在终端里通过 jobs -l
可以看到已经没有任务在运行了。
通过上面的叙述,我们知道 kill
命令的作用。那么,上面的结束进程的命令 kill -9
的 9 是什么意思呢?实际上 kill -9
是 kill -s 9
的缩写,-s
后面接信号名称或者信号序号。而 9 代表的信号名为 SIGKILL, 也就是说 kill -9
也可以写成 kill -s SIGKILL
. 此外,如果用信号名,字符的大小写是不敏感的,因此大家也可以写成 kill -s sigkill
. 最后,由于所有的信号名都是以 SIG 打头的,因此,通常在我们自己写的程序中都是去掉 SIG 作为信号名的,因此,此命令还可以写成 kill -s kill
. 这里我整理出 信号 9 所有相同功能的命令操作:
1 | kill -9 [PID] |
大家可以把 SIGKILL 这个信号换成其他的也适用,但由于信号名称有点长,不太好记,因此,通常我们在操作命令的时候使用序号来执行 kill
命令。
那我们怎么知道有哪些信号?以及这些信号对应的序号呢?实际上 kill
命令还有一个参数 -l
, 可以列出所有支持的 信号序号 以及 信号名:
1 | kill -l |
大家也看到了,信号太多了,这里我挑选出最长用的信号进行说明:
1 | 信号名 信号序号含义 |
这里我们只取其中的 结束进程的信号 来讲:
1 | SIGINT 2 中断(同 Ctrl + C) |
其中大家经常使用的 ctrl + c
快捷键就是发送了 SIGINT(2) 信号给进程的。另外,整个信号中,最特殊的命令就是 SIGKILL(9), 它代表 无条件结束进程,也就是通常说的强制结束进程,这种方式结束进程有可能会导致进程内存中 数据丢失。而另外两个信号对于进程来说是可以选择性忽略的,但目前的绝大部分的进程都是可以通过这三个信号进行结束的。
那这三个结束命令到底有啥区别?对比如下表:
信号 | 快捷键 | 正常结束 | 无条件结束 | 应用场景 |
---|---|---|---|---|
SIGINT(2) | ctrl + c | 是 | 否 | 前台进程快捷终止 |
SIGTERM(15) | 无 | 是 | 否 | 后台进程正常终止 |
SIGKILL(9) | 无 | 否 | 否 | 后台进程强制终止 |
大家主要关注下各个信号的 应用场景 即可。
然而,我们的上线程序绝大部分都是后台进程在跑的,本篇内容也是讨论后台进程,因此我们主要看 后台进程的正常结束( SIGINT(2)、SIGTERM(15) ) 与 后台进程的强制结束 ( SIGKILL(9) ) 的区别。
本篇讨论 Java 程序的后台程序 正常 与 强制结束 方式对比。在 Java 中,强制结束代表 直接立即结束 进程中的 Main 线程和其他所有线程,这里强调 直接和立即,也就是说通过强制方式,进程不会做任何收尾工作。而 正常结束 则非立即结束进程,而是先调用程序的 收尾线程,等收尾线程结束后再结束所有线程。
这里出现了 收尾线程,实际上这个就是 Java 程序中通过 Runtime.getRuntime().addShutdownHook()
方式注册的线程就是收尾线程。为了更详细地说明正常结束与强制结束的区别我们先定义一个工作线程 JobThread
:
1 | // 工作线程,每秒钟输出一个递增的数字 |
另外我们再定义一个收尾线程 ShudownHookThread
:
1 | // 收尾线程,没 0.5 秒输出一个递减的数字 |
现在在 Main 函数中先注册收尾线程,然后再启动工作线程:
1 | public class Main { |
然后打包成 Jar 包 ( 假设名字为 jvmexit-example.jar ),我们通过下面命令启动程序:
1 | java -jar jvmexit-example.jar |
我们可以看到工作线程每隔 1 秒输出一个数字,此时如果我们来通过正常和强制执行看看他们相应的输出。
正常结束 kill -2 [PID]
或者 kill -15 [PID]
:
强制结束 kill -9 [PID]
:
从中我们可以看出 正常结束 方式,会 先调用收尾线程,然后再结束,而 强制结束 则直接 杀死所有线程。因此,这里给出优雅结束进程说明:
kill
的 SIGIN(2) 和 SIGTERM(15) 两个信号进行进程结束,则 收尾线程 会被调用;我们前面也讲过,除了信号 SIGKILL(9) 外,其他信号对于进程来说都是可忽略的。而这个忽略就是自己在自己的任务进程里实现这些信号的监听。
Java 中有提供一个接口 SignalHandler
,完整名 sun.misc.SignalHandler
,我们只要实现该接口,就可以在接收到信号后进行一些相应处理了。
我们定义类 SignalHandlerImp
其实现接口 SignalHandler
:
1 | public class SignalHandlerImp implements SignalHandler { |
类内部只有一个要实现的方法 public void handle(Signal signal)
, 而我们在方法里仅仅是打印了信号的名称和序号。然后在 Main 函数里注册一下
1 | public class Main { |
主函数里我们监听了三个信号:SIGINT(2), SIGTERM(15), SIGUSR2(12), 同时我们也用到了上一节使用的工作线程 JobThread
( 注意这里没有用到上节的扫尾进程 ), 让我们来重新打包并启动任务 。
1 | java -jar jvmexit-example.jar |
执行结果是一样的,每秒输出一个数字,那我们来分别执行:
1 | // pid 换成自己的进程 ID |
得到的结果如下:
从中我们可以看出自定义的信号处理方式,正常结束的信号 ( SIGINT(2) 和 SIGTERM(15) ) 都不会结束进程,而只是执行自己自定义的方法,然而 强制结束信号 ( SIGKILL(9) ) 则不会被自定义监控,大家自己可以尝试下在 Main 函数中注册 KILL 信号,如下:
1 | Signal.handle(new Signal("KILL"), signalHandlerImp); // 9 : 强制终止 |
这个在运行的时候就会报错,因此 SIGKILL(9) 信号是唯一不能够被自定义的信号。
那既然我们自己可以自定义信号,那我们通过自定义的信号来处理我们的收尾操作也是可行的。因此我们只要在 SignalHandler
接口的实现类中 handle
方法中处理自己的收尾操作就可以了。这里也整理下自定义信号处理进行收尾的说明:
SignalHandler
接口,在 handle
方法中实现自己的收尾操作;kill
的 对应 自定义信号名 进行任务进程的结束,就可以正常收尾了。另外,在实际操作中使用自定义信号的方式通常是直接让 工作线程 实现 SignalHandler
接口的,我们上面是为了举例,以不至于发送对应信号后进程就停止了,而实际情况下是需要我们发送信号工作线程就应该停止,因此可以将上面的工作线程修改如下:
1 | // 工作线程,每秒钟输出一个递增的数字 |
如上所示,加一个运行 标识,并在收到信号后进行 标识 的反赋值,这样工作线程就会自动停止,当然还可以进行其他相关操作。
本文接收两种优雅 ( 而非暴力 kill -9
) 结束进程方式:
Runtime.getRuntime().addShutdownHook(new ShudownHookThread());
实现收尾进程的注册,这样在收到默认正常结束信号 ( SIGINT(2) 和 SIGTERM(15) ) 就可优雅退出;Signal.handle(new Signal("USR2"), new SignalHandlerImp());
注册 自定义信号 以及 信号处理实现类,这样使用 kill -自定义信号 ( 如: SIGUSR2(12) ) [PID] 就可以达到收尾操作在 信号处理实现类 里实现,从而也可实现优雅退出。那这两种方式哪个更好点?或者说适应性更广泛一点?
这里我参考了 JVM 安全退出 这篇文章,它给出了 JVM 关闭的不止有 正常关闭、强制关闭 还有一种 异常关闭 如下图:
这种方式还是会调用以 Runtime.getRuntime().addShutdownHook(new ShudownHookThread());
此方法注册的 收尾线程 的,而不会触发自定义的信号通信的。因此,还是第一种默认信号处理机制,通过 Hook 线程方式适应性更广泛。
目前,随着大数据方向的推进,越来越多的数据被应用于数据分析和挖掘,而其中一大部分就是项目中的 日志数据。而 Java 项目中有很多的日志输出包,不同项目使用不同的日志工具会造成数据结构的不一致,这样就为 数据分析 增添了一定的麻烦,今天记录下对各中日志工具的说明。
这个是 java.util.logging 的缩写,也就是 Java 本身 JDK 自带的日志工具,但是通常它的功能有限,因此,项目中的日志输出都是采用特有的日志工具进行记录。而日志工具中得到广泛使用的就是 log4j.
Java 界里有许多实现日志功能的工具,最早得到广泛使用的是 log4j, 许多应用程序的日志部分都交给了 log4j, 不过作为组件开发者,他们希望自己的组件不要紧紧依赖某一个工具,毕竟在同一个时候还有很多其他很多日志工具,假如一个应用程序用到了两个组件,恰好两个组件使用不同的日志工具,那么应用程序就会有两份日志输出了。
为了解决这个问题,Apache Commons Logging ( 之前叫 Jakarta Commons Logging, 所以缩写为 JCL )粉墨登场,JCL 只提供 log 接口,具体的实现则在运行时 动态寻找。这样一来组件开发者只需要针对 JCL 接口开发,而调用组件的应用程序则可以在运行时搭配自己喜好的日志实践工具。
所以即使到现在你仍会看到很多程序应用 JCL + log4j 这种搭配,不过当程序规模越来越庞大时,JCL 的 动态绑定 并不是总能成功,具体原因大家可以 Google 一下,这里就不再赘述了。解决方法之一就是在程序部署时 静态绑定 指定的日志工具,这就是 SLF4J 产生的原因。
跟 JCL 一样,SLF4J 也是只提供 log 接口,具体的实现是在打包应用程序时所放入的绑定器( 名字为 slf4j-XXX-version.jar
)来决定,XXX
可以是 log4j12, jdk14, jcl, nop 等,他们实现了跟具体日志工具( 比如 log4j )的绑定及代理工作。举个例子:如果一个程序希望用 log4j 日志工具,那么程序只需针对 slf4j-api 接口编程,然后在打包时再放入 slf4j-log4j12-version.jar 和 log4j.jar 就可以了。
现在还有一个问题,假如你正在开发应用程序所调用的组件当中已经使用了 JCL 的,还有一些组建可能直接调用了 java.util.logging,这时你需要一个桥接器( 名字为 XXX-over-slf4j.jar )把他们的日志输出重定向到 SLF4J, 所谓的桥接器就是一个假的日志实现工具,比如当你把 jcl-over-slf4j.jar 放到 CLASS_PATH 时,即使某个组件原本是通过 JCL 输出日志的,现在却会被 jcl-over-slf4j “骗到” SLF4J 里,然后 SLF4J 又会根据绑定器把日志交给具体的日志实现工具。过程如下。
这时,你可能会发现一个有趣的问题,假如在 CLASS_PATH 里同时放置 log4j-over-slf4j.jar 和 slf4j-log4j12-version.jar 会发生什么情况呢?没错,日志会被踢来踢去,最终进入死循环。
日志工具那么多,有门面也有具体实现,那到底如何进行搭配呢?这里主要给出目前最流行的两种搭配:
这种方式是采用 JCL 作为日志门面抽象接口,具体日志输出使用 Log4j. 具体用到的 Jar 包和资源文件如下:
1 | 1. commons-logging-1.1.jar// JCL 日志门面 |
对于 Log4j.properties 如何配置,下面会讲到,这里给出常用日志定义代码:
1 | // 注意导入的包 |
这种方式采用 SLF4j 作为日志门面抽象接口,具体日志输出仍然使用 Log4j. 具体用到的 Jar 包和资源文件如下:
1 | 1. slf4j-api-1.5.11.jar |
我们可以看到除了各自的 API jar 包 还有一个 slf4j-log4j12-1.5.11.jar, 这个就是输出流重定向的意思,将 slf4j 接口输出转到具体的 log4j 实现。而假如你目前项目中已经用 JUL 实现日志输出了,你想用此种配置方式怎么办?那就再加一个 jar 包:
1 | jul-to-slf4j-1.7.25.jar |
或者你已经使用了 JCL 日志门面接口,那如何转,只要加下面的 jar 包:
1 | jcl-over-slf4j-1.7.25.jar |
从中我们可以看出,slf4j 接口使用还是很广泛的,不管是入口还是出口都有各种对应的 jar 包可供使用的,那它可定制化以及适应性是非常广泛的,因此我推荐大家以后尽量使用 SLF4j 这个日志门面作为通用日志输出接口。
使用 SLF4j 的日志代码:
1 | // 注意导入的包和上面的 JCL 不一样的,不要混淆了 |
这里我们发现一个不同点,就是 SLF4j 可以用 {}
作为占位符,进行日志字符串的拼接操作,那这个有什么好处呢?这里也说明下:
首先看不用占位符是怎么使用多字符串拼接的:
1 | logger.debug("This is debug: " + "debug stack string"); |
如上所示,完成了一个 debug 日志输出,很多人都是这样实现的,但是大家知道,我们上线的应用不能将 debug 日志输出的,因为 debug 只能在开发调试阶段使用。因此,我们需要配置我们的日志工具,使其只能输出 info, warn, error 的日志信息。那么,logger.debug
这句话内部就会自动判断是否要进行输出,当在内部判断后确实不需要输出!
但是,我们发现一个问题,就是参数字符串拼接都是要先执行的,也就是不管你内部要不要输出,字符串都是要先拼接好才能进入 logger 内部判断的。如果日志记录很少有加的字符串还没多少性能问题,但如果有很多字符串拼接操作,并且拼接很多个字符串,那么会白白地浪费这些字符串拼接过程的性能。因此,正确的做法是:
1 | if (logger.isDebugEnabled()){ |
但是没次输出都要先进行判断是不是太过于重复了,因此,带占位符的字符串拼接操作就诞生了:
1 | logger.debug("This is debug: {}" , "debug stack string"); |
这种方式多个字符串当做参数传入,不会先进行拼接再传入,而是在内部判断后再进行拼接操作,因此这也是 SLF4j 日志工具的一大优势。那下面就主要讲下 SLF4j 的配置参数。
SLF4j 由于其适配广泛,通用性强,因此很多开源项目中都是使用它作为自己的日志记录接口,就如 Hadoop 系列生态。我们在开发 Hadoop 生态应用的时候,常常会在调试的时候打印出:
1 | log4j:WARN No appenders could be found for logger (org.apache.hadoop.util.Shell). |
这个就是因为没有配置 log4j.properties 所导致的。那通常的解决方法就是在项目路径里新建一个 log4j.properties, 然后填下面信息就可以了:
1 | log4j.rootLogger=INFO, stdout, logfile |
那这些配置信息到底是什么意思?下面详细讲下。
首先看第 1 行,也就是 log4j.rootLogger
的配置,其语法为:
1 | log4j.rootLogger = [ level ] , appenderName1, appenderName2, ... |
(1). level : 是日志记录的优先级,分为 OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL 或者您定义的级别。Log4j 建议只使用四个级别,优先级从高到低分别是 ERROR、WARN、INFO、DEBUG. 如果设置为 INFO 则,ERROR, WARN, INFO 都会输出,而 DEBUG 不会输出。
(2). appenderName : 是日志输出的目的地,名字是 自定义,也可以写多个;上面的配置 stdout 就是一个 appenderName 名字,当然你也可以叫其他名字的。当然你这里定义了这个名字,那么下面就要配置这个名字对应的输出地的相关信息,那下面配置的信息就得和这里设置的一致。下面会讲到。
上面代码的第 4-7 和 12-16 行都是配置日志输出目的地的,目的地有多种类型:
(1). org.apache.log4j.ConsoleAppender(控制台)
1 | Threshold=WARN// 指定日志消息的输出最低层次。 |
(2). org.apache.log4j.FileAppender(文件)
1 | Threshold=WARN// 指定日志消息的输出最低层次。 |
(3). org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
1 | Threshold=WARN // 指定日志消息的输出最低层次。 |
(4). org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
1 | Threshold=WARN// 指定日志消息的输出最低层次。 |
(5). org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
这个用得相对较少,这里就不介绍了。
上面代码的第 8-9 和 17-18 行都是配置日志输出格式的,也有多种类型:
(1). org.apache.log4j.HTMLLayout(以HTML表格形式布局)
1 | LocationInfo=true// 默认值是 false, 输出 java 文件名称和行号 |
(2). org.apache.log4j.PatternLayout(可以灵活地指定布局模式)
1 | ConversionPattern=%m%n// 指定怎样格式化指定的消息。 |
(3). org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
1 | LocationInfo=true:默认值是false,输出java文件和行号 |
(4). org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
对于 PatternLayout 模式下:
ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n
这里需要说明的就是日志信息格式中几个符号所代表的含义:
1 | -x号: x信息输出时左对齐; |
下面给出一个完整的配置说明:
1 | # 这里里配置了DEBUG等级,则可显示DEBUG以上的所有信息; |
Docker
是一个开源的 应用容器引擎,基于 Go
语言 并遵 从Apache2.0
协议开源。Docker
可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux
机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口( 类似 iPhone
的 App
),更重要的是容器性能开销极低。
Web
应用的自动化打包和发布;OpenShift
等平台来搭建自己的PaaS环境;仓库:
docker
官网仓库: http://hub.docker.comdocker
中国区官网仓库: https://registry.docker-cn.com安装:
MacOS
安装: 官网教程地址Ubuntu
安装: 官网教程地址Ubuntu
简单旧版安装:
1 | uname -r # 内核版本需要 > 3.10 |
docker pull [OPTIONS] NAME[:TAG]
NAME 必须指定,TAG 表示版本,默认不写为 latest
最新版本
默认 pull
的地址为 docker
官方镜像地址: https://hub.docker.com/explore/
如:
1 | docker pull hello-world# 拉取 hello-world 镜像 |
但是国内该地址常常被墙,所以国内大公司出了自己的镜像仓库,在上一节已经说明。
这里以用 163 蜂巢的镜像为例,地址: https://c.163.com/hub#/m/home/
地址里含有 libary 为 163 从 docker
官网复制过来的镜像。
如:
1 | docker pull hub.c.163.com/library/tomcat:latest# 拉取 tomcat 镜像 |
docker images [OPTIONS] [REPOSITORY[:TAG]]
1 | docker images |
docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG]
一个镜像运行起来就变为容器了,容器是动态的运行时的称呼。同一个镜像可以运行出多个容器,因为只要运行镜像,其内部内存占用等等一定不相同,所以镜像与容器是 1对多 的关系。
1 | # 直接运行 |
相信用过 Linux 的同学应该知道 ps
这个工具,代表 process
即进程。程序与进程的区别 和 镜像与容器的关系是一致的,进程和容器都是运行时状态,因此,查看本机容器的关键名也是 ps
.
1 | # 查看当前 docker 所运行的容器 |
docker exec [OPTIONS] CONTAINER [COMMAND] [ARG]
前面 运行镜像 时已经说明直接运行镜像时可以带上命令以一起运行,那如果容器已经被启动了,怎么再在里面运行命令呢?那就是这个命令。
1 | # 在容器中运行命令,查看帮助文档 |
docker stop CONTAINER_ID
1 | # 停止某个容器 ( 可以为容器的 Id,也可以是容器的名称 ) |
docker commit CONTAINER_ID IMAGE_NAME
容器既然是镜像运行时状态,那我们如果在容器里装很多软件,而自己又不想每次用这个镜像都想重新装一遍软件,那么就可以将自己 DIY 的容器存为镜像,以后我们再 DIY 时就从我们已经装完软件的那个时刻开始了。将容器打包为镜像命令如上。
1 | # 将容器打包成一个新的镜像 |
1 | # 1. 删除容器 |
下面给出做 web 方向常用到的一些命令:
1 | # 从 163镜像中心 下载 nginx 镜像 |
1 | # 将本机 8080 端口映射到容器的 80 端口,-p 代表开放端口 |
这是我在一个项目里面用这种方式尝试过了,使用 tomcat
镜像运行出的容器,把本地的 war
包映射到容器 tomcat
里的 ROOT
目录,同时端口也要映射出来,最后可以通过外网访问。
1 | docker run -d -it -p 8089:8080 -v $(pwd)/ROOT.war:/usr/local/tomcat/webapps/ROOT.war -v $(pwd)/ROOT:/usr/local/tomcat/webapps/ROOT -v $(pwd)/uploads:/usr/local/webapps/uploads tomcat |
这个是从构建文件制作自己的镜像的,主要是写 Dockerfile
, 这个目前还涉及的较少,只写下基本的构建过程。主要分为 Dockerfile
编写 和 build
命令:
Dockerfile
1 | from hub.c.163.com/library/tomcat# 镜像从 tomcat 继承 |
docker build [OPTIONS] [PATH]
1 | # 直接在包含 Dockerfile 的当前目录 build |
很多公司里需要构建自己的私有仓库,因此这里也简单介绍下。
假设有两台机器:
A: 192.168.31.115 (作为仓库)
B: 192.168.31.215 (提交镜像者)
(1). A: 下载仓库镜像
1 | docker pull registry |
(2). A: 运行镜像
应该将本地某个目录映射到 /tmp/registry
中以保存镜像,官方文档: 点我,这里还是说下不同版本的 registry
镜像中仓库地址是不一样的:
示例命令如下:
1 | docker run -d -p 5000:5000 -v /opt/data/registry:/var/lib/registry registry |
(3). B: 先下载一个小镜像
1 | docker pull busybox |
(4). B: 修改该镜像的 tag
1 | docker tag busybox 192.168.31.115:5000/busybox |
(5). B: 上传镜像
1 | docker push 192.168.31.115:5000/busybox |
出现问题不能 https,官方说明配置:https://docs.docker.com/registry/insecure/,这里说下我自己尝试的方法:
如果是在 Mac 上操作的,直接在docker
图标的设置里添加 Insecure-registry:192.168.31.115:5000
, 保存重启软件就可以了。
如果是 Linux 系统,则修改 Docker 的配置文件 /etc/docker/daemon.json
, 如下:
1 | { |
(6). B: 检查是否上传成功
1 | curl -XGET http://registry:5000/v2/_catalog |
返回结果示例:
1 | { |
最近发论文的需求逐渐被提高上来,身边的同学也在忙碌自己的研究,准备发篇论文以应对论文开题。但我们学校对发的论文有一定要求,需要被至少 SCI、EI、北大核刊 录入才可以,但是全球期刊和会议多之又多,怎么确定哪个会议被哪个平台是否录入,哪个学术会议或期刊价值更高点,以及怎么对各个平台进行索引搜索,这就需要自己详细地了解各种平台以及各个名词,下面记录下自己的了解。
SCI 的英文全称为 Science Citation Index ,即 《科学引文索引》,简称 《SCI》,是美国科技情报研究所( Institute for Scientific Information 简称“ ISI ” )于 1961 年创办的国际权威检索系统,其创始人为 尤金·加菲尔德( Eugene Garfield, September 16, 1925~2017 )。
SCIE 即 SCI Expanded 为 《科学引文索引扩展版》(即网络版),上面的 SCI 则主要 指来源刊为3700多种的SCI印刷版和SCI光盘版;而 SCIE 则收录了5600多种来源期刊,提供WEB检索服务,随着网络的普及,现在我们 高校所讲的SCI 通常是包 原SCI+SCIE 的,而网络上能检索的通常是 SCIE,所以一般网上讲的 SCI 通常都是指 SCIE。
SSCI 即 Social Sciences Citation Index 为 《社会科学引文索引》,也由 ISI 创建,是 SCI(原SCI+SCIE) 的姊妹篇。主要收录 1809 种世界最重要的 社会科学 期刊,内容覆盖包括人类学、法律、经济、历史、地理、心理学等 55 个领域。收录文献类型包括:研究论文,书评,专题讨论,社论,人物自传,书信等。选择收录 ( Selectively Covered ) 期刊为 1300 多种。
JCR 即 Journal Citation Reports 为 《期刊引用报告》,也由 ISI 出版,每年出版一次,其主要是对 SCIE 和 SSCI 收录的期刊的引用和被引用数据进行统计、运算,并针对每种期刊定义了 影响因子(Impact Factor,缩写IF) 等指数加以报道。该指数主要作为 期刊(并非论文)的评价工具。一种刊物的影响因子越高,也即其刊载的文献被引用率越高,一方面说明这些文献报道的研究成果影响力大,另一方面也反映该刊物的学术水平高。
ESI 即 Essential Science Indicators 为 《基本科学指标》,也由 ISI 出版,于 2001 年推出的衡量科学研究绩效、跟踪科学发展趋势的基本分析评价工具,也是基于 SCI 和 SSCI 所收录的 12000 多种学术期刊的1000多万条文献记录而建立的计量分析数据库,这个可用于 评价论文、作者、高校、学术机构、国家/地区国际学术水平及影响力的重要评价指标,因此很多学校很看重这个。
ESI 指标会每 两个月更新一次,其每次都会将评判的所有期刊和会议列表成文档,放在官网提供下载,因此也会有很多高校会要求学生发表的论文期刊或会议在此列表中。ESI 每次更新的文档下载地址:ESI评价期刊列表下载
Web of Science 简称 WOS 是由 Thomson Scientific(汤姆森科技信息集团) 推出的综合性学术信息资源平台,其具有丰富而强大的检索功能–普通检索、被引文献检索、化学结构检索,您可以方便快速地找到有价值的科研信息,即可以越查越旧,也可以越查越新,全面了解有关某一学科、某一课题的研究信息。它的资源库 除了SCIE,还有SSCI,还有A&HCI ( 人文和艺术引文索引 ),也包括其他扩充的数据资源库 ( 如:KCI,韩国期刊数据库 )。
Web of Knowledge 是由 Thomson Scientific(汤姆森科技信息集团) 开发的信息检索平台,该平台包含了 Web of Science(主要是学术论文、会议录和新增的学术图书数据库),还有 专利数据库,还有 JCR(Journal Citation Reports),以及 ESI(Essential Science Indicators) 等数据检索服务。这个是所有检索服务的入口平台,官网为:http://www.webofknowledge.com。
对于 Web of Science 和 Web of Knowledge区别?,可以打个比方:Web of Knowledge 就相当于Microsoft Windows操作系统,而 Web of Science 则相当于Microsoft Office一套办公软件,正如Microsoft Windows平台除了可以跑Microsoft Office之外,还可以跑各种应用程序一样,Web of Knowledge平台除了 Web of Science 还包括 专利数据库服务、JCR服务、ESI服务 等。详细说明可以查看 这里。
注意:这个网址 不是免费的,各大高校访问会根据学校 ip 自动登录就可以检索了,因为高校通常购买了使用权,但如果使用的普通外网则不能够访问。
官网首页–默认为 Web of Science 板块,查询 SCI 文章:
其次– JCR 板块,查询期刊影响因子:
另外– ESI 板块,查询论文、作者、机构等排名:
EI 即 The Engineering Index 为 《工程索引》,由 美国工程师学会联合会 于 1884 年创办的历史上最悠久的一部大型综合性检索工具,EI 每月出版1期,文摘 1.3 万至 1.4 万条;每期附有主题索引与作者索引;每年还另外出版年卷本和年度索引,年度索引还增加了作者单位索引。收录文献几乎涉及 工程技术 各个领域。例如:动力、电工、电子、自动控制、矿冶、金属工艺、机械制造、土建、水利等。它具有综合性强、资料来源广、地理覆盖面广、报道量大、报道质量高、权威性强等特点。
ISTP 即 Index to Scientific & Technical Proceedings,为《科技会议录索引》 简称 ISTP,创刊于 1978 年,由 美国科学情报研究所(ISI) 编辑出版。SCI、EI、ISTP是世界著名的三大科技文献检索系统,是国际公认的进行科学统计与科学评价的主要检索工具,其中以SCI最为重要。ISI 基于 Web of Science 的检索平台,将 ISTP( 科学技术会议录索引 )和 ISSHP( 社会科学及人文科学会议录索引 )两大会议录索引集成为 ISI Proceedings。集成之后 ISTP 分为文科和理科两种检索,分别是 CPCI-SSH 和 CPCI-S。所以它们还统称为 ISTP,也有人叫它们 CPCI。
IEEE 即 Instituteof Electrical and Electronics Engineers 为 美国电气和电子工程师协会 是一个国际性的电子技术与信息科学工程师的协会,是世界上最大的专业技术组织之一( 成员人数 ),拥有来自 175 个国家的 36 万会员( 到2005年 )。该组织在太空、计算机、电信、生物医学、电力及消费性电子产品等领域中都是主要的权威。
CCF : 即 China Computer Federation 为 中国计算机学会,成立于 1962 年,是中国 计算机科学与技术领域 群众性学术团体。注意此处是专注 计算机科学与技术,因此很多计算机专业的学院都会要求发表的论文为 CCF推荐会议或期刊 论文,CCF自动将各个期刊和会议分为 A、B、C类,A 类为顶会期刊或会议。
中科院JCR期刊分区 : 分区是为了区别期刊的影响力等级,这与 CCF 中的 A类、B类、C类是同一个意思,只不过这里通常划分为四个分区。而分区主要还是基于 汤森路透公司(ISI隶属于此公司)的 JCR(《期刊引用报告》), JCR 中定义了的 影响因子 ( Impact Factor 简称 IF )就是决定分区方法的主要因素。分区也主要分为两种分区方式,一个就是汤森路透公司自己的分区方法 – 汤森路透分区法,另外就是我们中国中科院的分区方法– 中国科学院分区法,两者分区区别请看 SCI汤森路透分区法和中国科学院分区法的区别
(1). 汤森路透分区法 : 检索是通过上文提到的 Web of Knowledge 中的 JCR模块查询。
(2). 中科院JCR期刊分区 : 检索地址为:http://www.fenqubiao.com/,这也需要账号的,高校通常会提供。
首页登录
浏览期刊排名
检索服务
上面讲的核心都是国外提供的索引服务,对于国内的(比如:北大核刊),也是有的,下面这些来源于知乎,地址:https://www.zhihu.com/question/31558495
下面是我自己学校研究生论文要求平台而提供的说明:
最近应项目公司需求,我们需要为其构建一个大数据平台。开源的Hadoop生态应用 由于没有技术服务支持,公司不采用此种方案进行构建。因此我们选择采购具有技术支持的 第三方大数据平台,当然这些公司的平台也完全基于 Hadoop, 只不过他们有着一帮技术团队,可以为自己平台出现的问题提供技术服务。
第三方平台目前我们所接触了解的有四家:
而由于 Cloudera 提供了开源免费版本的产品,因此这篇博客主要对 Cloudera 公司产品的部署过程进行记录。
目前,Cloudera 最新版为 CDH5.13,因此本教程基于 CDH5.13 安装
搭建一个包含四个节点的集群,1 个 master, 3 个 slave, Host IP 对应关系如下:
1 | 1. 192.168.1.100 master |
各个主机的物理配置如下,master 至少 16G 内存,slave [ 1-3 ]至少 4G 内存:
1 | master: CPU 16 核,32GB 内存,500G SSD |
系统环境为Ubuntu16.04-Server版本,我们需要提前下好如下数据包:
其下载好的对应文件名如下:
下载时需要确定自己系统的版本名称,Ubuntu 16.04 的对应的版本名称为 xenial,下面为对应文件的下载地址:
正式的环境安装,以下操作均以 root 身份执行
主要修改 Hostname 以及 Hosts 配置。修改 Hostname 是为了我们集群好查看和管理各个结点,而修改 Hosts 则是为了我们不用每次输入命令时都输入对方主机的 IP,我们直接输入 Hosts 中 IP 对应的名称即可。
修改hostname: 所有节点,将各个主机名设置为对应的名称: master, slave1, slave2, slave3,master 主机修改为 master, slave[1-3] 则修改为 slave1,slave2,slave3, 修改命令如下:
1 | # 所有节点 |
Hosts配置:主要修改 /etc/hosts
文件,修改命令如下:
1 | # 所有节点 |
安装过程全部自动化,因此需要让 master 主机到所有其他 slave 主机可以免密登录。
由于是刚装的系统,Ubuntu 默认没有开启 root 远程登录的权限,所以需要先配置所有主机可 root 远程 ssh 登录,主要编辑配置文件 /etc/ssh/sshd_config
,修改命令如下:
1 | # 所有节点 |
其次,要配置 slave 信任 master 主机。首先在 master 节点上生成 ssh 公钥私钥,执行 ssh-keygen -t rsa
一直回车,会生成在用户目录下的 .ssh 文件夹里,里面存放了公钥和私钥。然后再将生成的公钥添加到 master 和 slave[1-3] 主机的信任授权列表里,命令如下:
1 | # master节点上 |
最后,测试 master 免密登录各个节点是否成功,使用 ssh [hostname]
,若回车后自动登录,则成功,操作命令如下:
1 | # master节点 |
首先,解压 JDK 压缩包
1 | # 所有节点 |
然后,配置 JAVA_HOME
和 PATH
环境变量
1 | # 所有节点 |
最后,测试 Java 是否安装成功,输入 java -version
,显示 Java 版本即可。
这个在 master 节点安装即可
1 | # master节点 |
安装之前需要将必要的软件上传到 master 节点上,文件包含:
解压 cloudera-manager-xxx.tar.gz 文件到 /opt
1 | # master节点 |
安装过程中 Cloudera Manager 需要用到 mysql 服务,所以这里需要先提供好 mysql 资源。
首先,安装 mysql-connector-java 包,并链接到 CM 库:
1 | # 所有节点安装库 |
然后,再创建 cm5 的数据库:
1 | # master节点 |
这个 Agent 实际上就是安装代理,分布式的每台机器安装过程中都会产生这个进程,那这些 Agent 由谁来操控,通常是由 master 节点进行管理操作,所以要先将配置文件的管理这指向 master.
首先,修改配置文件的 server, 指向 master
1 | # master节点 |
然后,同步 Agent 到 slave[1-3] 节点
1 | # master节点上 |
最后,为所有节点创建 cloudera-scm 用户
1 | # 所有节点 |
要安装 CDH5, 我们需要配置 Parcels,这个是 Cloudera 给的安装包的镜像仓库地址,但是国内访问速度很慢,因此我们不使用在线安装,而是通过已经下载的 parcel 包,将其放置在下载镜像的目录,表示 parcel 包已经下载好了,这样就不会再联网下载了。
首先,在 master 节点,用下面命令创建文件夹
1 | # master 节点 |
然后,传入三个文件:
在 master 节点启动 Server 进程
1 | # master 节点 |
在所有节点启动 Agent 进程
1 | cd /opt/cm-5.13.0/etc/init.d |
这里假如大家有错误,可以查看对应进程的日志:
Server 进程 log 日志文件:
/opt/cm-5.13.0/log/cloudera-scm-server/cloudera-scm-server.log
Agent 进程 log 日志文件:
/opt/cm-5.13.0/log/cloudera-scm-agent/cloudera-scm-agent.log,
如果没有 .log 文件,可以试试 .out 文件
等待 Cloudera Manager Server 和 Agent 启动完毕后,就可以进行 CDH5 的可视化界面安装了,在浏览器打开 http://master:7180, 注意,如果这是在 master, slave[1-3] 主机上时可以直接在浏览器里输入这个 url, 但如果 不在 master 和 slave[1-3] 节点上访问此 url,则需要修改主机的 hosts 文件,修改方法见 正式部署环境配置 中的 hosts 配置。
默认用户名和密码都是:admin
选择中间的试用版本,肯定要体验最全的功能
这里只显示启动了Agent进程的主机
这里选择 Parcel, 我们之前已经把离线包下载好并放在了 master 的仓库目录下,因此这里直接显示了我们已经下载的 CDH5
这里自动由 master 将 CDH 包分发到 slave[1-3]
这里出现下面警告,通过在所有主机上执行 echo 10 > /proc/sys/vm/swappiness
, 然后再重新检测下环境即可
这里我就选择核心服务,因为后面管理界面可以任意 增加、删除 各个服务。
这里默认即可
这里是最重要也是最容易出错的地方,配置数据库前,需要先创建数据库,我们只需要在 master 主机上创建,这里主要需要创建 四个数据库 ( hive, rman, ozzie, hue ) 和 四个用户名 ( hive, rman, ozzie, hue ).
1 | # master主机 |
此外,这里还常常出现 Hue 测试连接数据库时失败,这种情况是因为 依赖包 没有安装,这里给出我测试成功需要的依赖包,更详细的依赖包查看 官网的 Install Package Dependencies 说明:
1 | # 仅master主机 |
然后,点击 测试连接,成功后进行下一步
点击 集群右侧倒三角,选择添加服务
出现服务选择界面,自己根据需求选择 Hadoop 生态中的应用:
点击菜单栏上的 主机–所有主机
会看到当前集群的所有主机列表,点击右上角的 向集群添加新主机
进入添加主机流程,大家一步一步下一步安装即可
应大数据和机器学习的要求,对处理数据的计算机语言需求越来大,之前虽然弄过 Python 环境,但是没有彻底搞清楚 Python 多版本执行程序以及多版本的模块位置,导致自己电脑上环境紊乱,今天花了点功夫解决下。
我的电脑是 MacOS 系统,并且 MacOS 系统是自带了 Python,而之前在捣鼓 Python 时,可能后装了很多次不同的版本,导致在我电脑上的的 Python 有诸多问题,这里主要说明以下几个问题:
Python 现在分为两个版本 2.X 版本 和 3.X 版本,两代版本并不能够完全兼容,因此两个版本的模块包也会分开安装的,因此完全把握两种版本环境是很有必要的。
Python 2.X版本
此版本对应旧版本,其默认执行命令是 python
, 因此我们在终端输入 python --version
通常会显示 2.X 的版本字样。
通过 which python
命令查看 Python 路径,然后用 ls -l
可查看该路径的 Python 执行文件属性如下:
上图显示 /usr/bin/python 文件非软链接文件,由于我们自己安装的 Python 通常是用软链接的方式链接到安装目录,因此这里的 python 应该属于本机自带的 Python, 其安装位置即为 /usr/bin/python.
Python 3.X版本
此版本对应新版本,其默认执行命令是 python3
, 不是完全兼容旧版本的,但是代表了 Python 的发展方向,新人的话建议直接学习此版本。
通过 which python3
命令查看 Python3 路径,然后用 ls -l
查看路径的的文件属性:
上图显示 /usr/local/bin/python3 文件为软链接文件,链接的源位置为 /Library/Frameworks/Python.framework/Versions/3.5/bin/python3, 因此这就是 Python3 的安装位置,由此也可以知道当前 Python3 的版本是 3.5.
实际上系统自带的命令通常安装在 /usr/bin/ 目录下,而用户自己安装的命令通常在 /usr/local/bin/ 目录下,这通常是因为普通安装命令都是在 /usr/local/bin/ 下有写的权限的。
Python 版本的不同导致 Python 下的包管理工具 pip 命令也不同,因此使得 pip 命令也产生很多的 pipX 命令。通常而言,对于 Python2x 版本其对应的 pip 命令为 pip, 而对于 Python3.X 版本其对应的 pip 命令为pip3. 此外,还有一种可同时执行不同版本 Python 的 pip 方式:python -m pip install xxx 或者 python3 -m pip install xxx, 这就不需要用不同的命令了,但是前提还是需要摸清自己本机的环境。
Python2.X 版本的 pip
通过 which pip
查看 pip 路径,然后用 ll
查看文件属性,该文件为非链接文件,其与上面的 Python 相对应
我们也可以通过命令来查看 pip 目录中有哪些 pip
:
1 | ll /usr/local/bin | grep pip |
结果:
我们看到上图有好多 pip
命令,到底哪些可用或者一致呢?我们可以看这些命令的 创建时间 基本可以判断前三个 pip
是同一时刻安装的,最后一个是另外时间安装的 pip
工具,我们可以通过 pip
命令安装一个已经存在的模块查看其已经安装的模块路径如下图,我们可以知道 pip, pip2, pip2.7 都是 Python2.X 版本的 python
命令所对应的 pip
.
Python3.X 版本的 pip3
通过 which pip3
查看 pip 路径,然后用 ll
查看文件属性如下图,然而,pip3
并没有此命令,但我们从之前的查看命令可知存在 pip3.5
命令
1 | ll /usr/local/bin | grep pip |
再次执行 which pip3.5
,情况如下:
我们天真地以为 pip3.5
命令就是我们 Python3.X 版本的 pip
,我们执行安装命令试试 pip3.5 install numpy
,结果如下:
执行报错,这错误网上有解决方法 这里, 但是我们这里不确定此 Python3.5 是否关联我们本机的 Python3,因为这有可能是自己以前安装了 Python3.5 又卸载后存留的。这里我们再试试另外一种可能,就是本机 Python3 的 pip 在另外地方,只不过它没有在环境变量中,我们可以如下测试 python3 -m pip --version
, 我们也可以尝试安装一个包,如图,可以确定通过 Python3 内部的 pip 可以将模块安装在内部目录里。
至此可以确定,Python3 对应的 pip
在自己安装路径内部,而不通过环境变量直接定位,需要通过 python3 -m pip install xxxx
来安装模块。
到这里我想大家已经知道如何确定 pip 模块的安装位置了,主要要首先确定 Python 各个版本对应的 pip,然后通过 pip 的重复安装即可确定模块安装位置。
注意:下面的安装位置仅仅是我自己系统上的环境位置,并非适用你自己的环境,需要自己确定自己的 Python 版本和 pip 版本
Python2.X版本的模块位置:
1 | pip install numpy |
Python3.X版本的模块位置:
1 | python3 -m pip install numpy |
Python2.X:
执行路径: /usr/bin/python
pip 路径: /usr/local/bin/pip, /usr/local/bin/pip2, /usr/local/bin/pip2.7
模块位置:/usr/local/lib/python2.7/site-packages
Python3.X:
执行路径: /Library/Frameworks/Python.framework/Versions/3.5/bin/python3
pip 路径: /Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/pip-9.0.1-py3.5.egg
模块位置: /Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages
Python 存在不统一路径查找的原因:
(1). 通过 pkg 安装包: Python 官网 https://www.python.org/downloads/, 下载后文件为 python-3.6.3-macosx10.6.pkg 形式的文件,这种文件可直接双击安装,其安装的位置通常在 /Library/Frameworks 下。
(2). 通过 homebrew 安装: Homebrew 官网 https://brew.sh/, 其安装方式为:
1 | brew install python # 安装python2.X |
这种方式安装是先将包下载在 /usr/local/Cellar 目录下,然后自动在 /usr/local/bin/ 下新建一个执行链接到 /usr/local/Cellar.
如:我们安装一个wget 工具:brew install wget
,然后我们可以查看wget的原路径:
对于安装用 brew 安装 Python3,其也会自动下载对应的 pip3 的,并且都会将其放置在 /usr/local/bin/目录下,以软链接的形式链接到 /usr/local/Cellar下的对应文件.
(3). 通过源码包: 这会自动放置在 /usr/local/bin 目录下,且不会用软链接。