普通视图

发现新文章,点击刷新页面。
昨天以前news view

TypeScript 使用 Go 进行了重构

2025年3月13日 08:00

我在 GitHub 上看到了 @ahejlsberg 为 https://github.com/microsoft/typescript-go project 点了 star.

在 11 日, TypeScript 官方博客发布了一篇名为 A 10x Faster TypeScript 的文章, 介绍了微软正在使用 Go 语言重写 TypeScript 编译器, 并取得了显著的性能提升.

Anders Hejlsberg 是 TypeScript 的主要设计者, 也是 C# 和 Delphi 的最初设计者. 为了解决性能问题, 他带领的团队没有使用 C# 或者 Rust 等语言, 而是使用了 Go, 在社区中引起了一些质疑的声音.

我认为最重要的原因就是新项目是 “port”, 而不是 “rewrite”. 在一众备选项中, Go 的语法是最容易从 TypeScript 迁移过去的.

我理解微软开发团队这样的做法, 比如 VS Code 在宣传的时候也会使用 create-react-app 这样的 Facebook 项目来宣传. 可见, 微软想要与技术社区进行融合的意愿很强.

为什么选择 Go?

根据 TypeScript 官方博客 A 10x Faster TypeScript 的介绍, 微软选择 Go 语言进行编译器移植的原因主要包括:

  1. 可维护性: Go 语言拥有高效且易于理解的垃圾收集器, 简单易学的语法以及良好的 开发者生态系统, 使得维护大型代码库更加容易
  2. 性能优势: Go 编译器的多方面优化使其非常适合构建编译器这类程序, 并且与 JavaScript 版本相比, 能够显著提高编译速度
  3. 跨平台能力: Go 的原生编译特性让 TypeScript 编译器能够轻松构建为适用于 各种操作系统的单一二进制文件
  4. 开发效率: Go 语言的错误处理模式, 简单的语法和快速的编译时间, 让开发团队能够快速迭代和实现功能

C/C++ 的跨平台能力较差, 比如不支持简单编译为各个平台的单一二进制文件. 而 Rust 的内存安全模型过于复杂, 不适合作为 port 项目. C# 应该更多是语言特性上与 TypeScript 有区别, 严格的 OOP 风格不适合作为 port.

当前进展与兼容性

repo 的 init commit 是 2024-10-01 https://github.com/microsoft/typescript-go/commit/cdcc0cb808d8ad0c91d9520edac3d815bff92514, 仅用大概半年的时间, 就取得了显著的进展, 据说完成了 80% 的代码迁移.

根据 GitHub 仓库的 README 文档, TypeScript 的 Go 原生移植版(称为”TypeScript 7”) 目前仍处于积极开发阶段. 当前已完成的功能包括:

  • 程序创建(读取 lib, target, reference 等配置)
  • 解析/扫描(读取源文本并确定语法结构)
  • 命令行和 tsconfig.json 解析
  • 类型解析和类型检查

为了实现迁移, 微软会在 TypeScript 6 (仍然是 TypeScript) 的时候, 做一些功能调整, 以保证将来发布 TypeScript 7 (Go 版本) 时大家可以无缝迁移.

TypeScript 6 和 TypeScript 7 的 API 是完全兼容的, 所以开发者可以随时升级或降级. 在 TypeScript 7 被完全接受前, TypeScript 6 会继续开发, 以作为备选.

社区反应

这一项目在公布后引起了开发社区的广泛关注, 截至目前 GitHub 仓库已获得超过 11.9k 的星标. 许多开发者对于提升 TypeScript 性能的前景表示期待, 特别是那些处理大型 代码库的团队.

社区讨论主要集中在性能提升, 工具链兼容性以及 为什么选择 Go 而非其他语言 (如 Rust) 上.

相关链接

JS 中的链式调用

2025年1月14日 08:00

数组中的链式调用

const numbers = [1, 2, 3, 4, 5];
const result = numbers
  .filter(n => n % 2 === 0) // 过滤偶数
  .map(n => n * 2) // 每个数乘以2
  .reduce((sum, n) => sum + n, 0); // 求和

但是仅限于数组, 如果想要在其他对象上实现链式调用, 就需要自定义方法.

多变量链式调用

为了可以对任何数据使用自定义方法, 我们可以这样做:

function add(x, y) {
  return x + y;
}

function multiply(x, y) {
  return x * y;
}

let num = 0;
let num2 = add(num, 5);
let num3 = multiply(num2, 2);
let num4 = add(num3, 3);
console.log(num4); // 13

这种做法的缺点是额外增加了许多变量, 为了这些变量要进行许多不必要的命名.

多函数嵌套链式调用

为了避免不必要的命名, 我们可以直接调用:

const result = add(multiply(add(0, 5), 2), 3);
console.log(result); // 13

可是这样做后, 代码的可读性很差.

对象方法链式调用

class Calculator {
  constructor() {
    this.value = 0;
  }
  add(n) {
    this.value += n;
    return this;
  }

  multiply(n) {
    this.value *= n;
    return this;
  }

  getValue() {
    return this.value;
  }
}

const calc = new Calculator();
const result = calc
  .add(5)
  .multiply(2)
  .add(3)
  .getValue(); // 13

但是需要额外新增对象, 并且许多时候函数作为方法并不合适.

Promise 中的链式调用

为了解决上述问题, 我们可以使用 Promise 的链式调用:

const result = await Promise.resolve(0)
  .then(x => add(x, 5))
  .then(x => multiply(x, 2))
  .then(x => add(x, 3));
console.log(result); // 13

并且这种做法可以仅仅使用匿名函数, 不使用函数, 在一些小变动的时候很方便:

const result = await Promise.resolve(0)
  .then(x => x + 5)
  .then(x => x * 2)
  .then(x => x + 3);
console.log(result); // 13

目前这是我最常用的方法. 但是缺点是强制将代码转为了异步.

JS Pipe Operator (管道操作符) 提案

https://github.com/tc39/proposal-pipeline-operator

参考了几种语言的语法:

  1. Hack
  2. F#

另外, 许多其他语言也有类似语法, 比如 Elixir.

Headless Brower

2020年5月11日 08:00

Headless Brower

Headless Brower 直译为中文是无头浏览器。其中的无头是指没有图形界面的意思。也就是通过代码来控制与浏览器的交互。比如捕获页面内容加载完成事件,执行按钮的点击事件,执行键盘输入,表单提交等。

Headless Chrome

Chrome 59 版本开始,Chrome 浏览器加入了 Headless 运行模式。除了没有图形界面以外,在 Headless 模式下可以实现 Chrome 浏览器的所有功能。

此外,Chrome 团队开发了 Chrome 开发工具协议,简称 CDP(Chrome DevTools Protocol)。该协议提供了很多有用的功能 API,比如在 DOM,调试器和网络方面等。Chrome 浏览器中的开发者工具正是使用了该协议实现其功能。

Chrome 开发工具协议使得 Headless Chrome 的功能更加强大。

Headless Chrome 库

一些第三方代码库提供了更加方便的使用 Headless Chrome 功能的方法。

Chromedp 是使用 Golang 语言实现的一种更加简单快速的方式驱动支持 Chrome DevTools 协议浏览器的方式,无需外部依赖。

Selenium 是用于测试 Web 应用程序用户界面的常用框架。同时也支持所有基于 Web 的管理任务自动化。支持的语言有 Java,Python,C#,Ruby,Javascript 和 Kotlin。

puppeteer 是使用 Node 语言实现的 Web 应用自动化测试工具。是 Google Chrome 团队官方的 Headless Chrome 工具。

Headless Chrome 库应用场景

  • 页面自动化测试
    可以通过 Headless Chrome 直接测试页面中的内容和交互。而不仅限于代码测试。
  • 爬虫
    对于某些不会直接显示在 HTML 节点中的内容,例如页面成功加载后再通过 AJAX 与后端服务器请求的内容或单页应用,可以使用 Headless Chrome 来获取。
  • 保存页面副本
    可以通过代码自动将需要的页面保存为图片或 PDF 文件

总之,Chrome 浏览器能够实现的功能在 Headless 模式中都可以实现。加入程序使得我们可以实现更多更强大的功能。

参考

Awesome

2020年5月6日 08:00

awesome系列github上超有名的Topic

大部分有一定知名度的领域,都有着自己的awesome-xxx项目

awesome Topic简要的事实描述

1、目前awesome系列包含了3325个各类仓库
2、Star数最多的awesome的主仓库,stars>132k
3、Topic中stars超过10k的有超过60个

要了解这个系列,可以直接从awesome项目开始(indresorhus/awesome)项目包含平台类、编程语言、前端开发、后端开发、计算机科学、大数据、理论、书籍、编辑器、游戏开发、开发环境、娱乐、数据库、媒体、学习资料与方法、安全、内容管理系统、硬件、创业与商务、工作方法、网络、分散系统、高等教育、事件、测试等相关集合。

这些集合包括了相关领域的书籍、课程、论文、软件、数据集、教程、博客等。以python为例,awesome-python(star:82.1k),仓库收纳了Web框架、网络爬虫、内容管理系统、计算机视觉、密码学、数据分析、数据可视化、深度学习、游戏开发、机器学习、自然语言处理、内容推荐系统、机器人技术、代码日志分析等内容。

目录对收录的资源做了简要的介绍,点击链接就可以直达资料页。

以下是挑选的一些目前10k+的获取对我们的工作学习有用的热门awesome仓库:

  • sindresorhus/awesome: Github stars 132k (各种有趣话题的精彩列表)
  • vinta/awesome-python: Github stars 82.1k (一份精选的优秀Python框架列表)
  • avelino/awesome-go: Github stars 54.1k (一份精选的优秀Go框架列表)
  • sindresorhus/awesome-nodejs: Github stars 35.6k (有趣的Node.js包和资源)
  • dypsilon/frontend-dev-bookmarks: Github stars 27.1k (前端web开发人员的资源集合。)
  • ziadoz/awesome-php: Github stars 23.5k (一份精选的PHP库列表)
  • brillout/awesome-react-components: Github stars 22.4k (React组件和库的管理列表。)
  • veggiemonk/awesome-docker: Github stars 17.7k (精选的docker资源和项目清单)
  • viatsko/awesome-vscode: Github stars 17.1k (一个令人愉快的VS代码包和资源的列表)
  • AllThingsSmitty/css-protips: Github stars 14.4k (一个帮助你掌握CSS技巧的技巧集)
  • tmrts/go-patterns: Github stars 12.6k (Go语言惯用设计和应用程序模式的集合)
  • gztchan/awesome-design: Github stars 12k (设计资源)
  • xgrommx/awesome-redux: Github stars 11.7k (很赞的Redux示例和中间产品列表)

Pm Em Rem

2020年4月29日 08:00

在css中单位长度用的最多的是px、em、rem,这三个的区别是:

  px是固定的像素,一旦设置了就无法因为适应页面大小而改变。

  em和rem相对于px更具有灵活性,他们是相对长度单位,意思是长度不是定死了的,更适用于响应式布局。 对于em和rem的区别一句话概括:em相对于父元素,rem相对于根元素 rem中的r意思是root(根),这也就不难理解了

em

  • 子元素字体大小的em是相对于父元素字体大小

  • 元素的width/height/padding/margin用em的话是相对于该元素的font-size ```

我是父元素div

我是子元素p 我是孙元素span

div { font-size: 40px; width: 10em; /* 400px / height: 10em; / 400px / border: solid 1px black; } p { font-size: 0.5em; / 20px / width: 10em; / 200px / height: 10em; / 200px / border: solid 1px red; } span { font-size: 0.5em; / chrome有最小字体12px的限制所以这里为 12px / width: 10em; / 120px / height: 10em; / 120px */ }


### rem
rem是全部的长度都相对于根元素,根元素是谁?<html>元素。通常做法是给html元素设置一个字体大小,然后其他元素的长度单位就为rem。

html { font-size: 10px; } div { font-size: 4rem; /* 40px / width: 30rem; / 300px / height: 30rem; / 300px / border: solid 1px black; } p { font-size: 2rem; / 20px / width: 15rem; / 150px / height: 15rem; / 150px / } span { font-size: 1.5rem; / 15px / width: 10rem; / 100px / height: 10rem; / 100px */ } ```

Site By Station Gadgets

2020年4月22日 08:00

前一段时间同事找我扒网站 然后K同学推荐了这个仿站小工具8.1 现在分享给大家

介绍

仿站小工具8.1是一款用来下载网页模板的软件,可以将指定目标网站的资源内容快速索引读取并复制到本地使用的软件工具!可以通过该软件检测网编码,下载网页,它会从html的代码中将JS、Css、Image、Picture、Flash等静态文件网址提取过来,再从下载完好的Css代码中提取出Image静态文件网址,通过网址下载静态文件,根据软件设置好的保存规则,自动修正html和css代码链接路径,最终这些静态文件被按分类保存到电脑文件夹。并且会自动分类归档。

软件特色

1. 从输入的网址下载html代码。
2. 支持提取JS、Css、Image、Picture、Flash等静态文件网址。
3. 可根据软件设置好的保存规则,自动修正html和css代码链接路径。

使用教程

  1. 输入需要下载的网页的网址,点击添加。 image

  2. 选择保存地址,点击开始下载。 image

  3. 等待下载完成,打开保存地址里面的文件就可以了。 image

编程题 聒噪的青蛙

2020年4月22日 08:00

写业务代码, 可能会碰到一些架构上的问题, 但性能不是重点, 很少接触真正的算法.

最近听播客, 道长介绍在硅谷面试时, 一般初级软件工程师就是算法题, 资深之后 才会有架构题, 越资深算法就越不重要.

但代码与性能是一方面, 另一方面, 能否理解问题, 解决问题, 这是任何人都需要的.

我们的前端笔试题已经很久没更新了, 后端则没有正式的笔试题. 春季招聘正需要, 就打算去 LeetCode 上抄那么几道题应对招聘.

最近的一道中等难度的题是: Minimum Number of Frogs Croaking

根据 Hints, 用 JavaScript 写了答案, 思路应该是清晰的, 效率也可以接受. 参考 Hints 写的答案都是基础方法, 但软件工程应该就是使用最简单的思路去解决问题.

以下是具体答案:

Runtime: 76 ms, faster than 85.98% of JavaScript online submissions. Memory Usage: 37.1 MB, less than 100.00% of JavaScript online submissions. (2020-04-22 17:22:18 +8)

/**
 * @param {string} croakOfFrogs
 * @return {number}
 */
var minNumberOfFrogs = function (croakOfFrogs) {
  let max = 0;
  const m = { c: 0, r: 0, o: 0, a: 0, k: 0 };
  for (const v of croakOfFrogs) {
    if (!"croak".includes(v)) {
      return -1;
    }
    if (v === "k") {
      m.c -= 1;
      m.r -= 1;
      m.o -= 1;
      m.a -= 1;
      if (m.c >= max) {
        max = m.c + 1;
      }
    } else {
      m[v] += 1;
      if (m.c < m.r || m.r < m.o || m.o < m.a || m.a < m.k) {
        return -1;
      }
    }
  }
  if (m.c !== 0) {
    return -1;
  }
  return max;
};

这里给出一些我用过的测试:

const test = [
  ["cccroacrkroakroakcroakoak", 4],
  ["cccroacrkroakroakcroakaok", -1],
  ["croakcrook", -1],
  ["croakcroak", 1],
  ["croakcroa", -1],
  ["croakcroac", -1],
];

for (const [v, r] of test) {
  const it = minNumberOfFrogs(v);
  if (it !== r) {
    throw new Error(`${v} 的结果应该为 ${r}, 却是 ${it}`);
  }
}

我最初的答案是这样的:

/**
 * @param {string} croakOfFrogs
 * @return {number}
 */
var minNumberOfFrogs0 = function (croakOfFrogs) {
  return croakOfFrogs
    .split("k")
    .map((v) => cDisplayTime(v))
    .reduce((acc, v) => {
      if (v > acc) {
        return v;
      }
      return acc;
    }, 0);
};

function cDisplayTime(str) {
  let count = 0;
  for (const v of str) {
    if (v === "c") {
      count += 1;
    }
  }
  return count;
}

这个答案性能不是重点, 看起来也更简单. 但不能找出错误参数. 然后我才意识到, 这道题麻烦的是校验错误.

然后老老实实依照 Hints 写了最终的答案.

Hints:

  1. keep the frequency of all characters from “croak” using a hashmap.
  2. For each character in the given string, greedily match it to a possible “croak”.

更新

第二天早上又花了一点时间写了两个版本:

通用版: https://leetcode.com/submissions/detail/328828305/

性能版: https://leetcode.com/submissions/detail/328832285/

再翻看大家的讨论, 看到其他语言基本与我的性能版思路一致, 但通用版大家都没怎么写.

在我目前的实际工作中, 性能要求并不高, 我们更期望可读性强的代码.

这道题前前后后, 写代码, 整理思路, 发帖, 花了两三个小时, 我想 改动一下是可以作为笔试题了.

通用版代码就不写了, 以下是性能版源代码:

/**
 * @param {string} croakOfFrogs
 * @return {number}
 */
var minNumberOfFrogs = function (croakOfFrogs) {
  // 多出的数量
  let max = 0;

  // 储存鸣叫数量
  let c = 0;
  let r = 0;
  let o = 0;
  let a = 0;
  let k = 0;

  for (let i = 0; i < croakOfFrogs.length; i++) {
    const v = croakOfFrogs[i];
    switch (v) {
      case "c":
        c += 1;
        break;
      case "r":
        r += 1;
        if (r > c) {
          return -1;
        }
        break;
      case "o":
        o += 1;
        if (o > r) {
          return -1;
        }
        break;
      case "a":
        a += 1;
        if (a > o) {
          return -1;
        }
        break;
      case "k":
        k += 1;
        if (k > a) {
          return -1;
        }
        if (c - k > max) {
          max = c - k;
        }
        break;
      default:
        return -1;
    }
  }
  if (c !== k) {
    return -1;
  }
  return max + 1;
};

URL and cross-origin

2020年4月13日 08:00

:bulb: 下面的两个章节可能无关.

URL 和 URI 有什么区别

根据 URL 的 wiki, URL 是一种特殊的 URI, 用来表示互联网资源.

URL 的标准是什么

标准由 WHATWG 制定: https://url.spec.whatwg.org/

标准是抽象的, 并且引用套引用, 就像一个包含无数小函数的程序, 需要梳理.

我们来看看 MDN 的文档吧, What is a URL?

在 UTF-8 的现在, 多语言不是问题, 而是大家的守则. 理论上任何语言都可以成为构成 URL 的 valid domainpercent-encoded byte.

但是我们倾向于使用这样的规则: 小写字母, 数字, 连字符.

谈谈跨域 - HTTP 203

这里 HTTP 203 指的是 Chrome 团队出品的一档谈话节目.

HTTP 203 挺好的, 就是说话太快了, 来不及听.

https://www.youtube.com/watch?v=vfAHa5GBLio 提到:

  • 跨域请求问题来源于历史上 Cookies 会被一同发送.
  • Access-Control-Allow-Origin 比 Origin 还有历史.
  • 之所有会有 Origin 是因为历史上避免包含 - 的头被过滤.
  • 但现在 Origin 会有更广泛的作用.

如果网页不使用 Cookies, 似乎就不是什么问题了.

在现在前端当中, Cookies 的功能已经可以完全被替代了.

但可以被取代, 不是会被取代.

Questions Philosophers Ponder

2020年4月9日 08:00

前言

通过了解哲学家们思考的问题能够帮助我们更加简单和通俗的认识到哲学是一门什么样的学科以及哲学家都在做些什么事情。从而对哲学产生一个相对准确和客观印象。

前苏格拉底时代(公元前700~公元前370)

前苏格拉底时代对应中国的春秋战国时期,老子,孔子的时代

  1. 泰勒斯通过观察水的形态变化提出水是构成世界的基本材料
  2. 毕达哥拉斯通过思考数学公式的确定性提出数字是宇宙的统治者
  3. 普罗塔哥拉提出人是万物的尺度,因为一个人看起来是正确的事情,另一个人可能会认为是错误的。
  4. 德谟克利特提出原子论世间是由原子和虚空组成的

前苏格拉底时代的哲学问题大部门集中在对自然哲学的思考,比如客观世界的原貌,万物的构成。

经典希腊时代(公元前470~公元前250)

经典希腊时代对应中国的战国时期

  1. 苏格拉底思考的问题是什么才是好的生活,生命的意义是什么。提出善是事物的本性,是万物追求的目标
  2. 柏拉图思考的问题是人类如何分辨出万物的正确的,完美的最本质的基本形态。比如我们看到不同品种不同形态不同颜色的猫都能分辨出来,那么必然所有的猫都有某种共同特征。这种由共同特征所组成的完美形态的猫才是我们应该去认识的。而现实世界只不过是这种完美形态的复制模型。
    为此柏拉图提出一个“洞穴比喻”来说明。他让人们想象出一个洞穴,囚犯面向墙壁被绑在洞穴中,身后是一个火堆。囚犯和火堆之间不时有人走动或拿起不同物体,这些物体的影子反映在洞穴墙壁上。那么这些囚犯实际上并不能真正了解事物本身。柏拉图觉得人类感官所感知到的物质世界正如洞穴墙壁上的影子一样,是现实的缩影
    这种对人类认识的质疑和问题通常被归为形而上学形而上学是哲学的一个门类,研究世界的本质。比如超自然世界的本原,灵魂是否存在,自由意志是否存在等。通常对于形而上学的理解误区在于这一概念本身的形式,它不常与其他学科放在一起,我们会认为形而上学是一个概念而非学科。实际上它和数学,化学,物理学这些文字形式类似,当我们说某一问题是物理问题时,意味着这一问题属于物理学范畴。而当我们说某一问题是形而上的,也就意味着这一问题属于形而上学这门学科。
  3. 亚里士多德与柏拉图相反,柏拉图质疑自己的感官,而亚里士多德认为我们是通过感知来认识世界的。他认为现实世界中的物体并非物体完美形态的复制模型。我们应当通过研究个体来认识某一种类共有的本质。比如研究不同的猫得出猫共有的本质属性。对于公正,美德这些概念,我们只能通过观察他们在现实世界的各种不同呈现方式来逐渐修正对他们的了解和认识。
    亚里士多德按照生物的特征对生物进行了归类。在这过程中制定了一套成系统的逻辑形式。期望运用逻辑推理出事实。例如,我们通过两个命题“所有人都会死”和“苏格拉底是人”可以推理出“苏格拉底会死”的结论。
  4. 伊壁鸠鲁认为人应当在短暂的人生中享乐。这种快乐并不是纯粹的身体感受到的世俗快乐,而是身体的没有痛苦和灵魂的不受干扰,是一种平静状态。
  5. 芝诺认为宇宙有自己的规律,自然法则是宇宙的主宰。人应该遵循自然法则,追求各自与自然达成的和谐状态

从苏格拉底开始,哲学家们开始关注对人本身的思考和探究。

受限于当时的知识水平,其中某些理论可能被现代人认为是简单的,不值一提的,甚至是错误的,但我们也不能否认他们在自己所处的年代开创出这些理论的革命性,和对人类文明后续发展的重要性。现代的很多领域中我们仍然能够清晰的看到这些理论所留下的印记。

参考

那些年在网吧被盗的号

2020年4月8日 08:00

这里讲一个局域网网络攻击的方法,可能已经有点过时

知识点

ARP(地址解析协议)

定义:根据IP地址获取物理地址的一个TCP/IP协议。 实现过程:主机发送信息时将包含目标IP地址的ARP请求广播到局域网络上的所有主机,并接收返回消息,以此确定目标的物理地址;收到返回消息后将该IP地址和物理地址存入本机ARP缓存中并保留一定时间,下次请求时直接查询ARP缓存以节约资源。

ARP欺骗

通过欺骗局域网内访问者PC的网关MAC地址,使访问者PC错以为攻击者更改后的MAC地址是网关的MAC,导致网络不通。此种攻击可让攻击者获取局域网上的数据包甚至可篡改数据包,且可让网上特定计算机或所有计算机无法正常连线。地址解析协议是建立在网络中各个主机互相信任的基础上的,局域网络上的主机可以自主发送ARP应答消息,其他主机收到应答报文时不会检测该报文的真实性就会将其记入本机ARP缓存;由此攻击者就可以向某一主机发送伪ARP应答报文,使其发送的信息无法到达预期的主机或到达错误的主机,这就构成了一个ARP欺骗。

场景模拟

网吧通常采用的是星型拓扑,所有主机连接到一台或者多台交换机,再由交换机连接到路由器。这样就有数量可观的主机都处在同一个局域网中,也是使用ARP欺骗攻击的理想场所。当这些主机中的某一台主机发起了ARP攻击,将自己伪装成网关,这时这台主机成了这个局域网内所有主机的“代理”。所有主机的网络请求都会通过这台主机转发,而这台伪装成网关的主机可以通过抓包工具轻松的获取他们请求,这其中就包括了一些账号的登录信息。

DynamoDB 心得分享

2020年4月1日 08:00

:warning: 本文可能已过时

简介

DynamoDB 是一种完全托管的 NoSQL 数据库服务。

DynamoDB 的核心

  • 在 DynamoDB 中核心组件是表、项目和属性。
  • 表是项目的集合。
  • 项目是属性的集合。
  • DynamoDB 使用主键来标识表中的每个项目,还提供了二级索引来提供更大的查询灵活性, 还可以使用 DynamoDB 流来捕获 DynamoDB 表中的数据修改事件。

DynamoDB的优点

  1. 性能稳定。为了保证高性能,DynamoDB 采用固态硬盘(SSD)进行存储,对于一般的请求, DynamoDB 可以在 10 毫秒内完成,而且请求速度不会因为数据量增加而减慢。
  2. 读/写流量限制预设。用户可以随时通过控制台或者API更改数据库的读/写流量的限制。
  3. 自动扩容。DynamoDB 不会对用户的数据规模大小做任何限制,后台会默默地把用户的 数据分不到各个机器上去。
  4. 强一致性。用户可以通过参数指定要读的数据是否需要一致性。
  5. 完全分布式,无中心化结构。一个表上的数据可以分布在几百台机器上。

DynamoDB 和 SimpleDB 的区别

DynamoDB 与 SimpleDB 都是 AWS 上的 NoSQL 数据库,DynamoDB 优于 SimpleDB。

  • SimpleDB 有单表限制。SimpleDB也有类似于Table的东西叫做Domain,每个 Domain 最多只能保存10GB的数据,而 DynamoDB 并没有单表存储的任何限制。
  • SimpleDB 默认为表内所有属性创建索引,这就导致 SimpleDB 性能不稳定。
  • SimpleDB 为最终一致性模型,DynamoDB 可以选择弱一致性或者强一致性。

Node.js 生态中的流行包 request 正式被弃用

2020年3月29日 08:00

在 2019 年 3 月份, 作者就创建了相关 issue 讨论逐步让 request 进入历史.

最近, 作者正式宣布弃用该包.

关于弃用的原因, 主要是历史比较久远, 核心代码已经跟不上 Node.js 的发展.

我倾向标准库强大的语言. Go 从设计之初就拥有强大的标准库, Node.js 则是随着 ECMAScript 的进步, 逐渐通过语法来完善与丰富自己.

替代

关于 request 的替代品可以参考这个 issue.

经验

其实我们并没有使用过 request, 在 Node.js 中只是用 node-fetch 和 axios.

在最初进入 Node.js 的时候, 我们关注到了 request, 但语法并没有打动我们, 所以我们转向了 node-fetch.

因为我们同时会写前后端, 在前端标准化的 fetch 让我们能尽快入手. 但正是由于我们前后端同时在写, 我们发现 node-fetch 的语法并不完全等同 fetch. 这让我们更容易因为忽略了差异项而出错.

之后我们再次寻找替代品, 发现了 r2. r2 代码的体积很小, 我们就看看源代码, 发现只是简单调用了 node-fetch.

在最近的项目中, 我们才开始用当时还流行的 axios.

我不太喜欢 axios 的语法, 有些过度了. 但基于流行原则我们还是在继续使用.

现在来看, request 作者的新包 bent 才是我们在 Node.js 中更应该做的选择.

bent

bent 是一个同时支持浏览器与 Node.js 的 HTTP client.

在 Node.js 环境中, 进行了 Promise 化和流的支持.

在浏览器端, 对 fetch 进行了简单封装以符合 bent 的 API.

源代码很小, 依赖也不多. API 简洁实用. 是我们理想的选择.

但在浏览器端, bent 的封装对我们帮助不大, 我们还是会选择 fetch 然后自己封装.

PHP, Go, Node.js 框架性能综合对比

2020年3月28日 08:00

数据来源

https://www.techempower.com/benchmarks/#section=data-r18&hw=cl&test=query

分析的指标

  • JSON serialization 序列化响应
  • Plaintext 简单响应
  • Single query 单行查询
  • Multiple queries 多行查询
  • Data updates 数据更新

分析的对象

考虑的几种方案:

  • Node.js (函数计算)
  • Go (单机部署)

这里的单机部署并非只有一台机器, 只是区别于 网关 + 函数计算 的方式.

具体应用:

  • echo (Go)
  • go-pgx-easyjson
  • nodejs-postgres (ORM)
  • php (nginx)
  • php-pgsql-raw (nginx)
  • lumen (nginx, ORM, MySQL)

没有看到 PHP + PostgreSQL 的组合.

与 Golang 组合的基本都是 PostgreSQL.

分析概览

  • Rust 非常快, 甚至超过 C 与 C++.
  • Java 也很快, 但我不知道这些框架流行程度.
  • 原生 PHP 整体并不慢, 但结合框架就慢了.
  • Go 数据请求的处理性能比 Node.js 更快.
  • 虽然显示 Go 的性能是 Node.js 的 3 倍以上, 但慢一部分出在 ORM 上.
  • 原生写 Go 与使用 echo 性能基本一致.

分析细节

JSON serialization 序列化响应

不涉及具体数据库.

Framework Performance
echo 32.8%
go 31.9%
go-pgx-easyjson 31.4%
nodejs 23.6%
php 12.5%
lumen 1.1%

Plaintext 简单响应

不涉及具体数据库.

Framework Performance
fasthttp 65.3%
nodejs 7.9%
go 6.4%
echo 5.2%
lumen 0.1%

Go 在这个环节比 Node.js 慢有点出乎意料, 但换用 fasthttp 第三方库还是一骑绝尘的.

一般来说, 这种简单响应的应用场景主要是 HTTP OPTIONS method. 但在函数计算中, 可以由网关响应该请求以节省运行开支.

Single query 单行查询

Framework Performance
echo 46.2%
go-pgx-easyjson 43.1%
nodejs-postgres 13.8%
php-pgsql-raw 13.8%
lumen 1.5%

Multiple queries 多行查询

Framework Performance
go-pgx-easyjson 47.8%
echo 44.3%
php-pgsql-raw 30.0%
nodejs-postgres 14.4%
lumen 8.1%

Data updates 数据更新

Framework Performance
go-pgx-easyjson 35.4%
echo 31.8%
php-pgsql-raw 20.4%
nodejs-postgres 8.2%
lumen 6.9%

总结

如果单机部署, 使用 echo 作为基础是一个好的方案, 良好的社区支持, 简单易用.

如果使用了 echo, 就不考虑 fasthttp 了. 虽然在 v2 中支持过 fasthttp, 但在 https://github.com/labstack/echo/issues/665 中, echo 作者解释了为什么放弃, 保持简单及社区兼容, 也就是尽量使用标准库.

如果函数计算, 那么 Node.js 也是不错的选择, 可以接受的性能, 与前端通用的语法.

从以上的性能测试来看, PHP 本身的性能问题不大, 但标准库太过老旧, 必须使用框架才能提高开发效率. 但普通框架导致性能极具下降. 并且因为历史原因, PHP 配合 Nginx 才能高效完成网络任务, 单机部署步骤更多.

国内部分地区用户遭到中间人攻击

2020年3月27日 08:00

根据 V2EX 社区帖子, GitHub 在国内遭受大规模中间人攻击.

什么是中间人攻击? 如何防范?

什么是中间人

在用户与服务器之间的任何层, 都可以称为中间人. 包括但不仅限于:

  • 本机设备的恶意软件
  • 本地路由器
  • 各级电信运营商
  • 国际出口

攻击目的

  1. 阻断沟通.
  2. 伪造身份.

大多数中间人攻击都是为了伪造身份, 比如攻击者不是银行, 伪装成银行与客户进行沟通 以套取密码.

如何进行

  1. 直接劫持并替换数据包.
  2. 通过 DNS spoofing.
  3. 阻断数据包.

什么是 DNS spoofing?

https://en.wikipedia.org/wiki/DNS_spoofing https://www.cloudflare.com/learning/dns/dns-cache-poisoning/

我们经常遇到这种 DNS 攻击, 如何识别与防范以后单独写一下.

如何防范

  1. 本地客户端对等加密.
  2. 传输层加密.

本地客户端对等加密 比较简单, 应用范围有限, 目的是为了防止中间人直接获取有效信息, 提高难度.

什么是本地客户端对等加密? 比如本地向服务器传输 123 这个字符串, 但不直接传输, 而是与服务器端沟通好, 以 ! 代表 1, @ 代表 2, # 代表 3, 实际传输 !@#. 这样, 如果中间人不知道二者的加解密方法, 则无法获取有效信息.

但这种方法有很大局限性, 比如如何保证中间人不知情? 加解密方法不能通过中间人网络. 前期沟通成本高.

为了解决上述问题, 更常用的是传输层加密, 比如 HTTP over TLS, 也就是我们说的 HTTPS.

有人会问, TLS 与 SSL 是什么关系? 调侃地说: 就像 ECMAScript 与 JavaScript 的关系. 中文维基百科上有一句话: “IETF将SSL进行标准化,1999年公布第一版TLS标准文件”.

TLS 是如何防范伪造身份为目的的中间人攻击呢?

  1. 客户端发给服务器端本地可用的加密方法.
  2. 服务器端看一下自己能使用哪一种, 然后告诉客户端用什么方法加密和公钥证书.
  3. 客户端此时判断服务器的公钥证书是否可信. 证书是由第三方发放的, 如果客户端认为 该第三方可信, 即证书颁发者在自己的可信根内, 则认为服务器可信.
  4. 客户端与服务器端建立有效连接.

如果想进一步知道, 客户端与服务器端如何建立有效连接, 服务器如何信任客户端, 请看:

  1. 客户端根据公钥加密一个随机数据, 发给服务器端.
  2. 服务器端解密后得到这个随机数据.
  3. 此时双方都获得了这个随机数据, 然后双方利用这个随机数据再次对等加密收发数据.

详细的 TLS 细节请看: https://en.wikipedia.org/wiki/Transport_Layer_Security#Protocol_details

相关新闻

https://v2ex.com/t/656394 https://www.cnbeta.com/articles/tech/960295.htm

❌
❌