读完本期周刊,大概需要花费您15分钟的宝贵时间,不过超值哦。
- 1. 心理:马蝇效应(Horse Flies effect)之赶紧找到让自己跑起来的马蝇吧
- 2. 言论:敏捷开发的实践心得
- 3. 认知:知识的半衰期
- 4. KeepAlive:HTTP的会话保持信息咋看不到呢
- 5. 路线图:2020年Web开发人员前后端路线图
- 6. 上古面试题:当你在浏览器中输入 google.com 并且按下回车之后发生了什么
- 7. 以后:你的代码中有曾经说好的"以后"吗
- 8. 蹭热点:Go和 Rust的优缺点
- 9. 理念:云原生的设计理念
- 10. 历史周刊回顾-CPU飙了内存爆了请用Arthas阿尔萨斯
再懒惰的马,只要身上有马蝇叮咬,它也会精神抖擞,飞快奔跑。
图片来自于500px
林肯少年时在肯塔基老家的农场里耕地。耕地的牛很懒,经常磨洋工,林肯对此无可奈何。然鹅,有一段时间牛却走得飞快。林肯开始大惑不解,后来才发现原来是有几只硕大的牛蝇叮在牛背上,它的叮咬使牛身上发痒,于是它走得比任何时候都快。林肯从中悟出一个道理,要想让牛走得快,必须有刺激物给予它足够的刺激。这在管理学中被称之为“牛蝇效应”,即领导者通过刺激物给予下属以刺激,以此激发其工作的积极性。
如果没有马蝇,懒马不会努力干活;如果没有鲶鱼,渔船上的沙丁鱼在到港之前都会死亡。这是在没有马蝇或鲶鱼时,懒马和沙丁鱼的生活太安逸了,就像温水里的青蛙,看着很舒适,但却逐渐走向死亡。
启示:
不要总干自己顺手的事,把一个已经会了的技能在人生中无限重复,人是需要折腾自己的。人是需要一根鞭子的,只有被不停地抽打,才不会松懈,才会努力拼搏,不断进步。这根鞭子是压力,是挫折和困难,是危机意识。这一解释不仅适用于个人,同样也适用于企业管理。
来自公众号逸言
敏捷是一把双刃剑,用不好自毁武功,用好了,提高不止1倍效率。
敏捷本质是它的多重收益:
- 工程实践技术 – 测试驱动开发,持续集成,重构,自动化测试等
- 高效编程习惯 – 小步提交,快捷键,Clean Code 等
- 全栈开发能力 – React/VueJS + Spring Boot
为什么敏捷开发能提高写代码效率:
- 习惯测试先行,拿到需求先写测试,再写代码
- 养成持续集成的习惯,确定需求,小步前进,避免反复修改
- 做好重构打算,改进代码坏味道,提高可读性和扩展性,不为重构发愁
在核物理学中,放射性物质的半衰期是指它减少(衰变)到一半所需的时间。 同样,知识、信息或事实的半衰期指的是知识的半衰期变得无关紧要或过时所需的时间。
大约在1930年,一个工程学位的半衰期是35年,到了1960年,这个半衰期变成了10年。
心理学的半衰期从3.3年到19年不等,平均7年多一点。
这篇文章将知识分为三种不同的形式:
- Unconscious Knowledge 无意识知识 - 员工积极使用但可能没有意识到的知识(例如使用工具或特定工作流程的特定方式)
- Conscious Knowledge 有意识的知识 - 雇员知道的方法、程序、标准作业程序等
- Recorded Knowledge 记录的知识 - 所有被捕捉到的东西都是以一种可接受的和有形的形式
你可能已经猜到,记录的知识半衰期最长,而无意识的知识半衰期最短。
以下是一些你可以马上实现的方法:
-
默认为异步(邮件、留言、GITHUB上的Issue活评论等)
即时信息或聊天的半衰期通常较短,大约5-10分钟,而长信息的半衰期至少为几个小时。 此外,当您默认使用异步长形式对话时,您反过来会鼓励更周到的响应并减少来回
-
在团队之间分享知识
这是扩展你的公司记录知识的第一步。 单个团队通常拥有大量的数据和洞察力,如果在整个公司共享这些数据和洞察力,可以帮助建立一个坚实的知识基础。
-
开始建立一个学习型组织
更进一步,致力于在你的组织中建立一种学习文化。哈佛商业评论文章 优美地解释了什么是学习型组织,以及它如何促进员工的持续学习
-
使用协作工作管理工具
像 Slack 这样的即时通讯工具非常棒,也是你堆栈中不可或缺的一部分。 然而,您还应该投资于协作工作管理工具,如 Notion 或 Slite,以便存储、维护、优先排序和共享知识。
使用curl -v 查看keep alive,服务端没有返回任何keep-alive的信息,比较疑惑,于是补充了一下知识。
$ curl -v http://127.0.0.1:8812 http://127.0.0.1:8812
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8812 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8812
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Tue, 31 Dec 2019 03:52:27 GMT
< Content-Length: 6
< Content-Type: text/plain; charset=utf-8
<
Hello
* Connection #0 to host 127.0.0.1 left intact
* Found bundle for host 127.0.0.1: 0x7fda13614800 [can pipeline]
* Could pipeline, but not asked to!
* Re-using existing connection! (#0) with host 127.0.0.1
* Connected to 127.0.0.1 (127.0.0.1) port 8812 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8812
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Tue, 31 Dec 2019 03:52:27 GMT
< Content-Length: 6
< Content-Type: text/plain; charset=utf-8
<
Hello
* Connection #0 to host 127.0.0.1 left intact
* Closing connection 0
➜ http-upload-benchmark git:(master)
“left intact”意思是“保留不变的”,例句:when the nest is upset no egg is left intact [idiom.]—覆巢之下无完卵
HTTP协议采用“请求-应答”模式,当使用普通模式,即非KeepAlive模式时,每个请求/应答客户和服务器都要新建一个连接,完成之后立即断开连接(HTTP协议为无连接的协议);当使用Keep-Alive模式(又称持久连接、连接重用)时,Keep-Alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。来源
在 HTTP 1.0 中, 没有官方的 keepalive 的操作。通常是在现有协议上添加一个指数。如果浏览器支持 keep-alive,它会在请求的包头中添加:
> Connection: Keep-Alive
然后当服务器收到请求,作出回应的时候,它也添加一个头在响应中:
> Connection: Keep-Alive
这样做,连接就不会中断,而是保持连接。当客户端发送另一个请求时,它会使用同一个连接。这一直继续到客户端或服务器端认为会话已经结束,其中一方中断连接。
在 HTTP 1.1 中 所有的连接默认都是持续连接,除非特殊声明不支持。
HTTP 持久连接不使用独立的 keepalive 信息,而是仅仅允许多个请求使用单个连接
。
按照这个思路,我做了一下验证:
$ telnet 127.0.0.1 8812
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.0
Host: 127.0.0.1:8812
HTTP/1.0 200 OK
Date: Tue, 31 Dec 2019 03:59:34 GMT
Content-Length: 6
Content-Type: text/plain; charset=utf-8
Hello
Connection closed by foreign host.
$
http-upload-benchmark git:(master) telnet 127.0.0.1 8812
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.0
Host: 127.0.0.1:8812
Connection: Keep-Alive
HTTP/1.0 200 OK
Date: Tue, 31 Dec 2019 04:00:38 GMT
Content-Length: 6
Content-Type: text/plain; charset=utf-8
Connection: keep-alive
Hello
GET / HTTP/1.0
Host: 127.0.0.1:8812
HTTP/1.0 200 OK
Date: Tue, 31 Dec 2019 04:00:48 GMT
Content-Length: 6
Content-Type: text/plain; charset=utf-8
Hello
Connection closed by foreign host.
$ telnet 127.0.0.1 8812
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.1
Host: 127.0.0.1:8812
HTTP/1.1 200 OK
Date: Tue, 31 Dec 2019 04:02:11 GMT
Content-Length: 6
Content-Type: text/plain; charset=utf-8
Hello
GET / HTTP/1.1
Host: 127.0.0.1:8812
HTTP/1.1 200 OK
Date: Tue, 31 Dec 2019 04:02:12 GMT
Content-Length: 6
Content-Type: text/plain; charset=utf-8
Hello
^[
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Connection: close
400 Bad RequestConnection closed by foreign host.
通过netstat查看端口监听情况
$ netstat -an | grep 8812
tcp4 0 0 127.0.0.1.8812 127.0.0.1.52493 ESTABLISHED
tcp4 0 0 127.0.0.1.52493 127.0.0.1.8812 ESTABLISHED
tcp46 0 0 *.8812 *.* LISTEN
服务端可以这么告知客户端:Keep-Alive: timeout=15, max=100
15秒内还可以再发100个请求,然后就会关闭连接了。
- The Keep-Alive header is a hop-by-hop header that provides information about a persistent connection. Both client and server are able to provide information independently. Hypertext Transfer Protocol (HTTP) Keep-Alive Header
- 客户端不能设置keep-alive的超时时间,这个由服务器端配置。The client cannot specify the timeout, it is the server configuration that determines the maximum timeout value. The extra Keep-Alive header can inform the client how long the server is willing to keep the connection open (timeout=N value) and how many requests you can do over the same connection (max=M) before the server will force a close of the connection.
参考 TCP connection reuse when HTTP in golang
$ sudo tcpdump -i lo0 -s 0 -n tcp -c 1000 -w /tmp/p1.pcap
tcpdump: listening on lo0, link-type NULL (BSD loopback), capture size 262144 bytes
1000 packets captured
1032 packets received by filter
0 packets dropped by kernel
➜ ~
再用wireshark打开p1.pcap,指定过滤条件tcp.port == 8087,过滤src或dst port是8087的,就是我们感兴趣的tcp包
go-http-client关闭keep-alive再抓包试试,
$ go-http-client -sleep 1s -keepalive=false
2019/12/31 13:18:46 server http://127.0.1:8812
2019/12/31 13:18:46 sleep 1s
2019/12/31 13:18:46 keepalive false
2019/12/31 13:18:46 retry mode false
2019/12/31 13:18:46 dumpResponse HTTP/1.1 200 OK
Connection: close
Content-Length: 6
Content-Type: text/plain; charset=utf-8
Date: Tue, 31 Dec 2019 05:18:46 GMT
Hello
2019/12/31 13:18:46 start sleep 1s
2019/12/31 13:18:47 dumpResponse HTTP/1.1 200 OK
Connection: close
Content-Length: 6
Content-Type: text/plain; charset=utf-8
Date: Tue, 31 Dec 2019 05:18:47 GMT
Hello
从图中,就可以看出每次都是从头三次握手🤝:姑娘贵庚?小女18,大哥您贵庚?我81了。如果反复这么聊,姑娘还没怒,也是有失忆症了。
查看更详细版本。
大概是这么一个流程,可以对比一下,我们猜对了哪几步,细看请移步
- 按下"g"键
- 回车键按下
- 产生中断[非USB键盘]
- (Windows)一个 WM_KEYDOWN 消息被发往应用程序
- (Mac OS X)一个 KeyDown NSEvent被发往应用程序
- (GNU/Linux)Xorg 服务器监听键码值
- 解析URL
- 输入的是 URL 还是搜索的关键字?
- 转换非 ASCII 的 Unicode 字符
- 检查 HSTS 列表
- DNS 查询
- ARP 过程
- 使用套接字
- TLS 握手
- HTTP 协议
- HTTP 服务器请求处理
- 浏览器背后的故事
- 浏览器
- HTML 解析
- CSS 解析
- 页面渲染
- GPU 渲染
- Window Server
- 后期渲染与用户引发的处理
- 按下"g"键
这是我最近看otter源代码,突然看到一处注释,好像我就猜到了"结果"似的,害得我赶紧在我的所有代码中顺手搜索一下"以后"。
最近,Go和Rust语言都很热门,有人就来聊聊聊他们之间的优缺点。
他从 2014 年开始接触 Rust 和 Go 语言,当时他从事的某个项目要求能够静态编译为二进制文件。由于 Go 和 Rust 语言均具备这样的能力,因此他深入研究了这两门语言的优缺点。
使用 Go 编程:过去,人们认为 Go 是“用户友好的C”。但是就我的使用经验和对社区的观察来说,Go 可能是 Java 的更好继承者,因为它对构建分布式系统很有用。Go 对于构建服务器和分布式系统体系结构很有用。这就是 Kubernetes,Docker 和所有其他系统首先使用 Go 编写的原因。
使用 Rust 编程:该语言在时间/空间受限的情况下(例如微控制器)特别有用。Rust 的另一个重要用途是 Web Assembly。在 Rust 社区中,围绕着构建可编译为 Web 程序并在浏览器中运行的工具的做法很多。我认为 Rust 自然感觉上看更类似于 C++。
云原生本身甚至不能称为是一种架构,它首先是一种基础设施,运行在其上的应用称作云原生应用,只有符合云原生设计哲学的应用架构才叫云原生应用架构。
- 面向分布式设计(Distribution):容器、微服务、API 驱动的开发;
- 面向配置设计(Configuration):一个镜像,多个环境配置;
- 面向韧性设计(Resistancy):故障容忍和自愈;
- 面向弹性设计(Elasticity):弹性扩展和对环境变化(负载)做出响应;
- 面向交付设计(Delivery):自动拉起,缩短交付时间;
- 面向性能设计(Performance):响应式,并发和资源高效利用;
- 面向自动化设计(Automation):自动化的 DevOps;
- 面向诊断性设计(Diagnosability):集群级别的日志、metric 和追踪;
- 面向安全性设计(Security):安全端点、API Gateway、端到端加密;
http://rdc.hundsun.com/portal/article/975.html
相比于传统应用,云原生应用意味着统一:
- 统一的技术标准:通常以微服务架构进行服务开发,服务之间使用标准的API契约进行通信。松耦合的架构方式会减轻因需求变更导致的系统迭代成本,并加快交付速度。
- 微服务倡导基于API接口通信,避免在服务间共享数据库,文件等有状态的实体。单个服务能独立的更新,扩展,重启而不影响其他服务。
- 微服务使得单个服务的开发团队更小,也更加独立。亚马逊内部采取2个披萨原则——即团队规模维持在刚好两个披萨能够吃饱,来限定团队规模。团队沟通成本可以维持在较低水平, 同时每个人都承担富有挑战性的工作。
- 松耦合+独立小型的团队使得持续更新和敏捷管理协作成为可能,因此微服务成为了云原生应 用的最佳开发实践之一。
- 统一的交付方式:标准容器化的打包方式实现了真正的应用可移植性,不依赖于特定的基础架构(虚拟机,混合云等)。
- 容器技术起源于linux的进程隔离虚拟化技术,将一组进程单独限定在同一个资源目录下,并可以限定其资源使用的配额。
- Docker的出现将应用的整个运行时环境都打包成一个镜像文件,并基于容器本身的隔离特性,实现了应用在特定容器中可以轻松的在不同环境间移植,同时确保宿主机的安全。
- 在资源有限的场景,容器基于进程粒度的资源使用方式,也会降低系统的资源开销。
- 基于容器的应用将有助于构建统一的开发,交付和集成环境,是云原生应用的最佳交付载体。
- 统一的运维部署:基于容器的编排平台,可以充分利用不可变基础设施的特性。这使得在传统运维模式中的复杂部署策略(如灰度,蓝绿)变得轻而易举。
- 容器编排平台提供的自动化运维特性和声明式资源管理方式简化了持续构建中工具链之间的协作和调度。
- 容器编排带来的弹性伸缩能力和容错调度能力也让智能化运维成为可能。
- 技术驱动下的敏捷协作,和编排平台带来的运维能力共同构建了云原生应用的最佳运维实践——DevOps
云原生应用又意味着分离:
- 存储与计算分离:存储与计算混合模型往往导致应用节点复杂且难以扩展和维护,云原生中利用存储抽象层实现了更加灵活的计算实例与存储实例绑定调度。
- 业务组件与通用组件分离:平台化运行将使应用本身只需保留业务相关的组件,其余的通用组件、监控运维功能则都由平台来提供。
- 关注度分离:平台提供的开箱即用能力,使应用开发者可以专注于编写为客户创造价值的业务代码。
云原生应用VS传统应用:
面对未来趋势,传统软件必须从架构、流程和文化层面进行重构,来逐步实现云原生应用的转型。我们需要:
- 更快:通过敏捷协作与自动构建,将修改的代码快速交付生产。 打造效能平台,在统一的技术栈和流水线支持下,实现全流程自动构建,自动发布。缩短从需求到生产的代码交付时间。
- 更轻:基于平台开发一个应用,而非独立开发一个系统 利用统一的开发运维平台,并将通用组件不断下沉至平台,业务应用开发则专注于特定领域,最终实现平台化开发与交付。
- 更开放:采用开源技术,拥抱开源社区 开源社区不仅仅提供技术组件,还制定了云原生相关的技术交互标准。诸如OpenTracing, CloudEvents等云原生标准接口定义了未来在云原生语义下,各个异构框架如何进行统一的交互。我们应当在技术选型时,主动接入这些标准,并将相关实现积极回馈于开源社区。
Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。 当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:
- 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
- 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
- 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
- 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
- 是否有一个全局视角来查看系统的运行状况?
- 有什么办法可以监控到JVM的实时运行状态?
- 怎么快速定位应用的热点,生成火焰图?
面板:
更多简介,请看上期“系统架构部”周刊2020年07期