Leon Ding Blog - 系统架构
type: channel
link: https://blog.leonding.me/category/%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84.html
description: 收录关于系统架构相关的
items: 密码学的应用之 TLS 协议
items: Java 原生化的未来 Graal AOT 编译器
items: 编码从来不是一件简单事情
items: 独立开发者如何起步
items: 一个合格的站长应该具备的能力
items: 开发基于网络程序该考虑什么？
items: 浅谈 B/S 架构通信技术
items: 未来 PWA 应用是否成为主流应用开发模式
items: 分布式系统设计的挑战
items: 万维网的基础设施 DNS 服务

密码学的应用之 TLS 协议
type: item
date: 2024-07-26T19:14:00+08:00
description: 在现代社会网络通信的隐私和安全性已成为人们日益关注的重要话题，无论是浏览购物网站、在线银行交易，还是日常的社交媒体互动，我们的个人信息都面临着潜在的风险和威胁。在这篇文章中，将探讨在浏览购物网站时，如何保护我们的隐私和确保信息安全，是谁在保护我们的数据隐私和通信的安全性。在谈论网络通信的安全性之前，让我们回顾一下人类在通信保密性的发展史。通信的保密性自古以来就是一个重要的问题，人类不断发展新的方法来保护信息的隐私和安全，最早的加密技术可以追溯到古代文明，古希腊和罗马使用了各种简单的加密方法，如凯撒密码（Caesar Cipher），其中每个字母被替换为其后固定位置的字母。凯撒密码也称为移位密码（Shift Cipher），是以古罗马将军 Julius Caesar 的名字命名的，据说凯撒在军事通信中使用这种密码来加密敏感信息。在战争期间使用这种加密方法来保护军事命令和机密信息，确保即使信息被截获，敌人也无法轻易破译，凯撒通常使用 N <= 25 位移位（即每个字母向后移 N <= 25 个位置）来加密消息。当消息被敌军获取了，敌军在不知道消息加密的方式情况下，只能得到一堆胡乱排列的字母。凯撒密码的数学公式为如下：凯撒密码的核心原理是通信的双方有一个固定字母顺序表，以这个固定顺序的字母表作为消息加密的基础，通过将原本的消息进行移动位置的方式进行加密。凯撒密码易于破译，尤其是使用频率分析法，通过统计文本中各个字母出现的频率，可以很容易地推测出移位量并解密消息。比如，在加密文本中可以通过寻找加密文本中最常出现的字母来推断移位量，当找到这个字母偏移量就很容易破解出来密文信息了。[scode type="blue"]下面是通过 Java 自定义码表实现凯撒密码加密和解密逻辑：[/scode]public class CaesarCipher { private static final char[] ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); public static void main(String[] args) { String text = "HELLO"; int shift = 3; String encrypted = encrypt(text, shift); String decrypted = decrypt(encrypted, shift); System.out.println("Original: " + text); System.out.println("Encrypted: " + encrypted); System.out.println("Decrypted: " + decrypted); } // 加密函数 public static String encrypt(String text, int shift) { StringBuilder result = new StringBuilder(); for (int i = 0; i < text.length(); i++) { char ch = text.charAt(i); if (Character.isUpperCase(ch)) { // 拿到对应字母的下标 int originalPosition = findPosition(ch); // 找到偏移量之后字母下标，A 至 Z 之间循环 int newPosition = (originalPosition + shift) % ALPHABET.length; result.append(ALPHABET[newPosition]); } else { result.append(ch); // 非字母字符不变 } } return result.toString(); } // 解密函数 public static String decrypt(String text, int shift) { StringBuilder result = new StringBuilder(); for (int i = 0; i < text.length(); i++) { char ch = text.charAt(i); if (Character.isUpperCase(ch)) { int originalPosition = findPosition(ch); int newPosition = (originalPosition - shift + ALPHABET.length) % ALPHABET.length; result.append(ALPHABET[newPosition]); } e…

Java 原生化的未来 Graal AOT 编译器
type: item
date: 2024-07-17T22:35:00+08:00
description: Java 这门语言与生俱来的显著特性就是“一次编译，到处运行”，这种功能得益于 JVM 平台的支持，Java 程序通常通过将其打包为 JAR 或 WAR 包，并依赖 JVM 和 Servlet 容器来运行。其底层运行时 JVM 采用 JIT（即时编译）模式来执行程序代码，JVM 会在运行时进行编译优化和动态执行代码，这通常会导致较高的内存占用。这样的好处是采用 JIT 可以热更新和热部署程序，并且 JVM 可以在运行期间对程序进行动态分析，来实时优化程序以达到最好的性能状态。Cloud Native 面临的问题随着云计算的发展，服务端程序的运行和部署方式发生了巨大变化，特别是在云原生（Cloud Native）场景下，服务器程序被打包成一个个容器运行。在这种环境下，传统方式容器化的 Java 程序面临着打包后的镜像文件过大的问题。镜像文件中需要包含 JRE 和各种第三方库，导致镜像体积庞大，占用更多的磁盘空间。目前云计算的计费方式也发生了改变，采用按需分配的实时计费方式，根据程序的内存占用和运行时间计费。这与传统 IDC 自托管式机房中长时间运行服务器程序形成鲜明对比，按需分配的计费方式显然更具性价比，因此优化 Java 程序在容器环境中的运行方式变得尤为重要。Serverless 面临的问题传统的 JVM 工作原理要求程序员先将编写的 Java 程序打包为 JAR 或 WAR 包，这是普通 Java 开发者所能接触到的程序编译和构建阶段。然而当程序运行起来之后，底层的 JVM 会进行许多复杂的操作，包括内存管理、垃圾回收、即时编译等。需要先启动 JRE 中的 JVM 来加载 Tomcat 服务器所需的字节码，再加载 WAR 包中的字节码文件到 Servlet 容器中运行服务，这个过程非常耗时。下图为整个传统 Java 程序从源代码到程序运行各个阶段的工作流程：下图为传统的 Java 运行时执行程序的生命周期，首先应用的开发者将 *.java 源代码编译为 bytecode 和 jar 依赖包通过 maven 构建打包为一个单独的 jar 包。当执行 java -jar xxx.jar 命令时，首先要启动 JRE 中的 JVM 程序，因为 JVM 本身也是一款单独程序对应着 $JAVA_HOME\bin 目录中的 java ，当 JVM 启动之后会在系统中创建一个进程，将要运行的 jar 程序包和 JRE 标准库中 class 文件加载入 JVM 内存中，并且找到 main 方法开始解释执行。当程序运行一段时间之后 JVM 充分获取了 Java 程序的一些执行数据信息，开始对程序进行 JIT 及时编译优化程序，当程序被 JIT 优化之后的程序才能达到和 C/C++ 这种直接编译为 Native Code 程序的性能。在云计算推崇的 Serverless 架构场景下，Java 的 JIT 模式并不适合，Serverless 应用程序需要能够快速启动以响应事件驱动的请求，并且在空闲时自动关机销毁，释放在服务请求期间所使用的内存。然而，这种需求与传统的 Java 程序运行模式背道而驰，使得 Java 在 Serverless 场景中显得不适用，这就是被业界称为的 冷启动 问题。为了解决这一痛点，Oracle Labs 推出一款新的 JDK 产品 GraalVM JDK ，相比传统的 JDK 该产品内置 Java 静态编译工具和一款新的虚拟机，和传统的 JVM 不一样 GraalVM 是支持多语言进行混合编程的虚拟机，这也是它一大亮点。GraalVM 中的静态编译工具不仅是支持将 Java 编译为二进制可执行文件，还支持 JavaScript 和 Python 等其他语言的编译优化功能，本文只关注于 GraalVM 对 Java 程序进行 AOT（Ahead-Of-Time）编译的解决方案。使用 GraalVM 对 Java 程序进行 AOT 编译的解决方案，可以显著缩短启动时间并降低内存占用，使 Java 程序更适合在 Serverless 环境中运行，通过 AOT 编译，编译器生成一个 Native Code 二进制文件，即对应平台架构的机器代码程序。这种方式无需像 JIT 那样依赖于 JVM 运行，在内存和磁盘占用上有显著改善。下图为 AOT 编译工作流程，在编译阶段会程序进行静态分析，通过它内置工具分析 Java 源代码中依赖关系，将其所有依赖和代码执行逻辑都进行提前编译为机器代码，缺点也很明显可能会失去在 JIT 模式下的动态反射功能。如上图，在 AOT 模式下将在运行时的过程放到在程序构建阶段，构建阶段会对 Java 程序执行静态代码分析和依赖可达性分析，将其所有的依赖软件包都编译生产所属平台对应的可执行二进制文件。AOT…

编码从来不是一件简单事情
type: item
date: 2024-03-22T18:32:00+08:00
description: 对于写代码和编写程序来说，放到今天稍微学习一下 Python 和 JavaScript 这类编程语言就可以实现满足自己需求的应用程序，因为已经有很多很好封装好的框架共我们开发者使用，乃至一个中学生都不需要懂一些数学知识，调用相关的 Math 包就可以进行高等数学相关计算。现在有许多高级的编程语言和优秀的框架，使得开发变得更加容易和高效，Python 和 JavaScript 等语言拥有丰富的生态系统和强大的社区支持，为开发者提供了各种各样的工具和框架，使得构建复杂的应用程序变得更加容易。上文提到从需求变成编程再到程序，需求是代码编写者对于软件系统所需功能和特性的描述，例如实现一个四则运算计算器程序，可以是在程序直接编写 console.log("20 + 4 = ",20 + 4) 这种形式来完成需求，也可以是实现一个漂亮的界面完成对应的需求。从 IDEA 到 Coding 到 Product 这个过程中 coding 环节是属于技术实现环节，实现某个功能可以有很多种技术方案，但是方案这么多作为程序员如何选？主流开发的软件都属于应用软件，目前跑在电脑上和跑在手机上的应用软件并没有一些本质区别，都是程序员将自己的大脑逻辑抽象通过代码的形式告诉计算机，当执行这段代码运行之后该程序就会按照你编写的逻辑去交给 CPU 执行，只不过 CPU 要比我们脑子里面计算快得多。这个过程就是 Coding 阶段不能确保程序员大脑里的逻辑一定是正确的，当不合规的逻辑被编写到程序代码中，就可能会产生很严重的后果，例如程序崩溃导致的事件。在电脑和手机只要你开发的不是一些基础的软件，应用软件遇到问题最多就是卡死重启一下，现在的服务器端软件更是搞出来主从或者高可用架构来解决程序员在编码阶段留下的 bug 的问题。在工业时代崇尚机器自动化，现在计算机程序已经渗透到各行各业中了，天上飞的地上跑的水下游的，飞机汽车潜艇这些设备上都靠着计算机程序在辅助人类完成一些操作。机械工程师在设计一些机械结构时犯下的错可能会导致飞机会坠毁，即使是设计合理的机械结构现在控制这些机械装置的已经不是人类了，是跑在计算机中的程序在控制这些机械结构。所以一架飞机或者自动化驾驶的汽车出了事故，责任不完全在机械工程师身上，更有可能的是编写软件的工程师导致的。即现在从一名普通人转行为一名程序员太容易了，普通人通过特定的 IT 培训经过几个月的训练就可以写代码，此种工程师可能不具备专业的 CS 知识就可以动手写程序了，带了问题就上面所说的不可靠软件，如果需要开发专业型的软件还是需要专业型的知识作为铺垫的，例如让一个学土木工程或者做建筑设计的去做软件工程师，那就对应上了中文的俗语 “隔行如隔山” 。一些知名计算机科学界的先驱们就提出了点观点 Dijkstra 曾说过：[scode type="yellow"]我们所使用的工具对我们的思维习惯有着深远的影响，因此也对我们的思维能力有着深远的影响。......使用 COBOL 会削弱人的思维；因此，教授 COBOL 应被视为刑事犯罪。[/scode]简单的工具容易被使用，但是会让使用者不去思考背后的原理，导致缺乏逻辑思维和不去思考事物的本质。这在日常生活中使用的智能手机可以拍照可以打电话可以玩游戏，但是发明和制造一台手机所需要的知识远远不是一个人就能做到的，是一个跨学科和跨领域合作的产品。现在用电脑写一篇文章的时候打错字可以回退修改，但是如果在纸上你写错了一个字可能就报废一张纸，解决办法则你可以使用铅笔进行写字配合着橡皮擦使用，古代人使用毛笔更需要思维和手法练习才发展出来了书法行业。现在已经很少有人去使用了 COBOL 语言进行程序设计了，虽然我本人也是没有写过，但是在研究一些 PL 过程中了解到 COBOL 这门 PL 语言属于结构化加命令式结合产物。但是新的流行语言例如脚本语言和弱语言 JavaScript 和 Python 这些只有在运行的时候才能确定某个变量的类型，还有 C/C++ 这类有空指针设计的 PL ，也会带来底层软件的 Bug ：使用这里语言开发的程序如果程序在开发期没有有效找出 bug 或者程序员的逻辑错误导致 bug 产生，当程序发布之后这些带有 bug 程序会被发布到线上环境，可能在未来的某一时刻会因为这些 bug 导致程序出现故障从而影响到整个系统。一些隐秘的 bug 不一定尽快被修复，例如一个计算闰年和数据类型存储精度丢失的 bug ，要导致触发这个 bug 条件是当前是时间是处于闰年或者闰秒才会发生错误。这类隐秘的 bug 是开发阶段程序员逻辑错误导致的没有处理好，在修复的这些 bug 过程中需要花大量的时间解决，如果在开发这些涉及相关领域知识软件系统时应该提前去查看一些相关领域的资料文件，使用一些业绩主流的解决方案，例…

独立开发者如何起步
type: item
date: 2024-02-18T22:12:00+08:00
description: 随着就业形势不佳，互联网科技公司的寒冬来临，越来越多的程序员被裁员。在这样的背景下，越来越多的程序员开始尝试成为独立开发者（indie Hacker），希望通过开发一款产品来实现财务和工作时间上的自由。他们致力于通过开发和推广自己的产品或服务来实现个人或团队的利润和成功。相比之下，独立开发者在产品开发和运营方面可能相对弱势。独立开发者往往只有一个人，大多数是程序员出身，没有在公司里面一个团队的支持，自己需要去发觉需求，对需求进行分析和产品 UI 设计直到通过代码实现该产品，最后部署上线运营，在这个过程中需要很多资源的支持，这篇博文我将介绍一些关于从小白如何转型为独立开发者经验。编码技能当从一名产品经理和 UI 设计师转型到独立开发者，可能已经熟悉产品开发的流程和方法论，但是缺乏专业的编码技能。产品经理通常需要与程序员合作才能完成产品，但当你想作为一名独立开发者时必须要掌握编程这门技能了，或者找程序员外包的项目，但是我不太推荐这样去做，都不是自己用代码实现出来的产品怎么能称之为独立开发者呢。我建议你最好先学习一门编程语言，因为编程可以帮助你将想法变为现实中的产品，在这方面我建议学习 JavaScript 语言，因为它可以用于开发 Web、Desktop App 和 Mobile App 应用。首先学习最新的 ES6 标准 JavaScript 基础，再学习前端 MVVM 类型的 UI 框架，这里推荐 Vue.js 和 React 框架，此外如果需要将产品开发为 Mobile Native App 产品，要考虑多个手机端的同步开发工作，在过去可能需要两个团队才能完成工作，当现如今已经有很多的第三方框架可以帮助实现此需求，此时推荐你学习 ionic Framework ，ionic 可以允许你使用 Vue 和 React 技术来实现 App 程序，并且已经提供了各种手机设备上的 UI 组件来使用，可以省去一些设计 UI 的时间。对于如果产品考虑多个桌面端推荐考虑使用 Electron ，可以使用 JS + HTML + CSS 来完成传统 QT 的桌面端开发工作，因此推荐要学习编码技能转行的朋友，去学习 JavaScript 技术栈是不二之选。从 IDEA 到产品做独立开发者从来都不是一件轻松的事情，除了要应对前期持续的时间投入和打造产品的费用都投入亏损，想法太多，过于追求完美，很多想要的功能根本无法在短期内实现，最后在慢慢实现的煎熬中放弃了整个产品；第二辛苦做的产品真正上线之后却不被市场认可，数据没有起色，从而失去了继续更新的力量。作为一名程序员出生的开发者，把自己的需求变为一款产品，要经历多个阶段，从 UI 和 UX 原型图再到开发阶段实现，最终发布产品上线这个周期很长，且需要的资源支持非常多，面临着很多问题。首先从产品选型出发是做 App 形式的产品还是基于 Web 的 Saas 形式的产？取决于多种因素，包括您的产品定位、目标用户群、技术栈、团队能力、市场竞争情况等等。如果第一款产品，只是作为一次独立开发者尝试，建议还是采用 App 的方式只需要将应用提交到应用商店（如 App Store 或 Google Play，当然需要支付一些对应平台的软件数字开发者证书费用），由平台管理应用的分发和更新；采用 App 的方式发布产品不需要购买服务器资源和考虑扩容问题，使用用户客户端本地计算和硬件资源，如果在产品发布并且运营一段时间之后没有盈利，乃至盈利金额都不能抵消掉开发阶段的资金投入，那么问题可能就出现在 IDEA 和产品选型上了，可以考虑暂时停止开发这款产品了，因此工具类的 App 产品就不会需要维护服务器后期更多费用。获得用户和收入有的独立开发者在做开源项目，经营开源社区，并构建自己的影响力；有的人通过分享、出书、写专栏获得收益；有的人则会开发工具类软件，笔记、闹钟、创意工具等等；有的独立开发者自己开发了很多款游戏。

一个合格的站长应该具备的能力
type: item
date: 2024-01-01T05:10:00+08:00
description: 现在注册一个域名和购买一台服务器部署一个 Web 网站非常容易，现在流行的 Serverless 架构更是连服务器都不需要购买，直接在各个云厂商的后台点点鼠标就可以部署一个在线的 Web 程序，虽然相比传统建站的方式简单很多，但是也有考虑一些很基础因素和配置，这篇博文我讲以博客系统为例子讲解如何维护一个 Web 服务程序。现在有很多流行的博客系统，例如静态的 Jekyll、Hugo、Hexo，动态以 PHP 为首的 WordPress 系统，WordPress 有着完善的生态系统是世界上目前二次开发使用最多的 Web 系统，静态的博客可以部署在 Github Pages 和 Vercel 等，直接将编写好的网站源代码上传到免费的空间即可。而对于动态博客系统还说，需要 LNMP 环境的支持才能正常运行，现在也流行直接购买已经部署好的数据库环境和运行环境来跑博客，对于动态博客系统还是数据才是最重要的，这篇博客将介绍一些方法一名合格的站长该如何运维自己博客。基础防御[scode type="yellow"]博客系统存放在主机之上通过互联网提供世界各地访问，这些访客里面有普通人也有一些黑客，黑客可以利用博客程序的漏洞例如 XSS 和 SQL 注入漏洞来破坏程序，这对于博客系统安全有了新的要求。普通的静态博客完全可以不用管这些，但是对于 PHP 这类的动态脚本语言来说更需要关注，因为 PHP 是一门动态语言如果被黑客注入了恶意代码会导致程序数据泄露，严重的会破坏程序的运行，目前场景的作为就是在博客核心代码里面对用户输入的数据进行过滤或者做合法性校验，或者在 PHP 使用 disable_functions 配置项来禁用一些敏感函数，来保证程序安全性。[/scode]如果考虑不是在应用程序嵌入代码来做防御，可以使用一些带有代码注入攻击检查防御的网关产品，博客作为一种 Web 程序提供的服务是要经过 HTTP 网络请求的，可以考虑采用带有防御型的网关产品。例如 Cloudflare 它可以提供基于 SASE 和 SSE 平台，将网站的域名的 DNS 接入到 Cloudflare 并且使用它来解析服务网站，可以改善安全和韧性减少攻击参数传入到主程序中，国内也有长亭科技的 SafeLine 的 WAF 产品。Cloudflare 并且提供 CDN 服务，可以隐藏真实的主机 IP 地址，也能确保域名和 DNS 设置的安全，防止域名劫持和 DNS 污染攻击。对于 HTTP 协议来说才有基于 TLS 安全层的 HTTP 协议，启用 SSL/TLS 证书，确保所有数据在传输过程中都是加密的，防止 Cookie 和 Session 信息被截取或篡改。对于基于使用 LNMP 作为基础设施运行的系统，要考虑是不是使用的长期支持版本的软件，如果使用的不是长期支持的版本的软件，如果出现了漏洞，对于这些已经不维护的基础软件会很麻烦，不能得到相应的补丁更新。操作系统环境还是建议使用基于 RHEL 系列的 Linux 发行版本，现在 CentOS 已经是最优的，我推荐使用 Rocky Linux 这个系统是 CentOS 创始人创建的一个克隆 RHEL 的开源操作系统，是在 RHEL 的基础上进行重新编译的，该系统在很大程度上兼容 RHEL 版本的，并且可以通过相同的方式安装 RHEL 软件包，还能得到长期的更新支持。当整个基础环境搭建完成之后，强烈不建议使用 root 账号去运行网站的基础设施，更应该创建一个专用的用户组和用户来运行对于的软件，这样即使黑客绕过前面的基层防护，也没有更高的权限去访问其他的数据和程序。服务器防火墙软件还是推荐使用目前主流的 firewalld ，这是 REHL 企业级的服务器管理软件，相比一些老的 iptables 来管理出入站流量复杂的操作更容易操作。不要开放不必要的端口，一个 Web 服务器最多开发基础的端口 80 、 443 、22 端口，其他端口可以一律禁止外部访问使用，最好的方式使用白名单 IP 的方式进行访问控制，通过跳板机进行管理。对于 root 用户来说应该不使用 root 密码进登录，而使用基于 SSH 的非对称密钥进行登录，可以将登录电脑和被管理的服务器进行绑定，将公钥上传到远程服务器来进行登录管理。此外此种方式还要考虑私钥的安全性，不要将私钥公开，避免在页面或日志中输出敏感信息，如数据库凭据、API 密钥等。数据备份在线系统的核心是数据，这些数据一般会被存储在 MySQL 这些关系型数据库中，目前主流的动态博客系统数据都是存储在关系数据库中，前面提到黑客通过一些特殊的手段获取到了服务器执行权限，从而可能会导致数据库被拖库导致数据泄漏。对于数据库系统来说管理也是不推荐开放 root 权限给远程用户登陆，对于 Web …

开发基于网络程序该考虑什么？
type: item
date: 2023-11-29T02:06:00+08:00
description: 开发基于网络的程序时，有许多因素需要考虑，以确保你的应用程序是高效、安全、可伸缩和用户友好的。但是今天这篇文章更加关注的是 OSI 模型中的应用层协议和运输层的协议，基于这些层的程序开发更加关注什么？如果作为一名应用开发者我们最多接触到的网络层面上的应用协议和运输协议；应用的概念更加面向用户，强调的是提供某种服务或功能，可以是桌面应用、移动应用、Web 应用等，这里围绕的是应用开发如何对网络协议选型，因为应用开发是在操作系统用户态上的开发，对于内核的可编程操作是不允许的，所以本篇文章更加关注的是基于 TCP/UDP 之上的协议选型时要考虑的问题。TCP 和 UDPTCP 和 UDP 都是运输层提供两套协议，提供系统内核的 API 可以使用它进行运输数据包达到另外一台主机上，市面上很多的远程调用框架例如 gRPC 和 Thrift 在底层运输数据包都是采用的内核提供的 API 进行发包的。这些 RPC 框架在应用层之上提供了一种结构化的、高效的远程调用机制，使开发者能够更轻松地构建分布式系统。虽然它们通常在应用层协议的基础上构建，但它们自身更类似于框架或库，提供了更高级别的抽象，用于处理远程服务调用的复杂性。gRPC 使用 Protocol Buffers（ProtoBuf）作为接口定义语言（IDL），这允许开发者定义服务接口、消息格式等，而不用关心底层通信细节。这里围绕的 gRPC 来讨论这个问题，了解 gRPC 协议都知道该协议是基于 HTTP/2 协议，利用了 HTTP/2 的多路复用、头部压缩、二进制帧提供了更高效的网络传输，相比 HTTP/1.1 使用多个 TCP 连接，每个连接只能传输一个请求和响应，这可能导致 TCP 连接的浪费和性能瓶颈，而 HTTP/2 引入了多路复用的特性，允许多个请求和响应在同一 TCP 连接上并行传输，这样也减少了 TCP 连接的数量提高了性能。[scode type="yellow"]通过这么一解释就很容易明白 gRPC 为什么要使用 HTTP/2 协议作为基础的应用数据传输协议了，HTTP/2 协议属于是应用层的协议，它依赖运输层协议是 TCP 协议，确实符合 gRPC 协议的应用场景，针对在分布式系统上对数据的可靠性和高效的数据传输要求。也从侧面角度反映出了为什么 Spring Cloud 没有直接使用 gRPC 这种协议进行数据通信，而是直接使用 HTTP 协议，随着 HTTP 协议版本改进采用更高效的二进制数据传输，性能和 gRPC 也不相上下。Spring Cloud 并没有直接使用 HTTP/2.0 进行通信，而是建立在 HTTP 协议之上。默认情况下 Spring Cloud 使用 HTTP/1.1 作为通信协议，但是可以通过一些配置和定制来启用 HTTP/2.0 ，相比 gRPC 特殊的数据传输格式，HTTP/2.0 反而没有那么多数据格式要求可以自定义是 JSON 还是 XML 这种，最终都是会被编码为二进制数据帧。[/scode]对于需要可靠型网络数据传输的场景，使用 TCP 是一个最佳的选择，但是也并不意味着盲目的去使用它传输数据。 TCP 传输数据之前每次都是需要进行 3 次握手，这 3 个握手数据包是主要的耗时来源，延迟就此产生了，如果在一次性很短暂数据运输需求下，这就显得不是那么适用了，快速打开一个连接使用完成之后快速释放掉，很多应用层的网络程序都是跑在 TCP 协议之上，必须经过这个慢启动的过程。HTTP/1.0 版本主要就是针对此种突发性连接的场景，获取到某个 HTML 内容立马断开 TCP 连接，完全没有利用好 TCP 协议，每次发送一个 HTTP 请求都需要经过一次 TCP 慢启动过程。

浅谈 B/S 架构通信技术
type: item
date: 2023-10-03T00:05:00+08:00
description: 众所周知计算机软件的架构目前最为常见的是 B/S 和 C/S 架构，B/S 和 C/S 架构的软件都采用了分层结构，将系统划分为不同的模块层次，每个模块层负责相应功能，这种架构便于管理和维护，同时提高了系统的可扩展性和灵活性。笔者感觉最主要的特点是在使用软件时都会产生数据，而这些数据如果存储在本地时很不安全的，并且用户产生的数据才是开发对应软件公司想用的东西，数据才是一个软件科技公司的财产。两种架构都将用户界面和数据处理分离，有助于系统的灵活性和维护性，用户界面只需要负责展示信息，而数据处理由服务器端或客户端中的逻辑处理模块完成；无论是 B/S 还是 C/S 架构，都涉及客户端和服务器之间的通信，客户端向服务器发送请求，并接收服务器的响应，这种通信机制实现了应用程序的交互和数据交换，这篇博文将探讨一些在 B/S 架构下的网络通信协议技术。Web 浏览器和客户端[scode type="yellow"]笔者将其 B/S 架构归纳为 C/S 架构中，其 C/S 架构的中的 C （client）一般是指的安装在用户电脑上的软件，那么其浏览器也是归于客户端软件的，浏览器本体也是一款软件，只不过它可能比其他客户端的软件实现了多种的应用层的网络通信协议。B/S 架构中，客户端通常是 Web 浏览器，用户通过浏览器访问应用程序，这种架构具有跨平台、易部署和易维护的优势。B/S 和 C/S 架构都可以支持多个用户同时访问系统，多用户的需求可以通过在服务器端处理实现，架构图：[/scode]当浏览器发生数据请求就会采用 HTTP 协议和应用服务器交换数据，而应用服务器和数据库服务器交互则采用传输层的 TCP 协议。HTTP ProtocolHTTP 协议是 HyperText Transfer Protocol 的缩写 (即超文本传输协议），是由 w3c（万维网联盟）制定的一种应用层协议，用来定义浏览器与 Web 服务器之间如何通信以及通信的数据格式。因为 B/S 架构中的通信模块就是以 HTTP 这个协议作为标准协议的，所以对该协议有所了解可以更好的编写程序。 HTTP 协议的发展历史悠久，已经不断的迭代多个版本，不同时期的 HTTP 版本特性，整理表格：时间段HTTP版本主要特点1989-1995HTTP/0.9- 最初版本，仅支持 GET 请求。- 无请求头或状态码，只有响应主体。- 用于传输 HTML 文件。1996-1999HTTP/1.0- 引入多种 HTTP 方法（GET、POST、HEAD 等）。- 引入状态码、请求头、响应头等概念。- 服务器在每次请求后关闭连接。1999-2015HTTP/1.1- 引入持久连接，减少连接建立的开销。- 引入管道化（Pipelining），实现多请求并发。- 引入 Host 头字段，允许多个域名共享 IP 地址。- 引入分块传输编码和压缩，提高效率。2015-至今HTTP/2.0- 完全重新设计，旨在提高性能和效率。- 改进多路复用，支持并行请求和响应。- 引入头部压缩，减少数据传输大小。- 允许服务器推送资源，提高效率。- 强制使用加密（TLS），提高安全性。2020-至今HTTP/3.0- 基于 QUIC 协议，进一步提高性能和安全性。- 改进连接建立速度和稳定性，减少握手延迟。- 实现数据包级别的错误纠正和拥塞控制。- 采用基于 UDP 的传输协议。[scode type="yellow"]HTTP 协议一次请求对应一次连接，当浏览器再次发请求给服务器时，Web 服务器并不知道这就是上次发请求的客户端，这也是 HTTP 协议的一个特点：无状态协议。这种需要时建立连接，使用结束后立即断开连接的方式使得 Web 服务器可以利用有限的连接为尽可能多的客户提供服务，并且能够处理分散的网络请求，处理来自不同地区浏览器请求，并且服务器也可以多台分散在不同地区的，正因为是一种应用层协议还可以使用多层代理架构，例如使用 Nginx 做代理网关，正是具备了这样的特点，才使得 B/S 架构能够承载企业级应用的大量访问。[/scode]如果要测试 HTTP 协议的一些特性这里推荐使用 telnet 命令行工具，可以在命令行进行 HTTP 数据包封装，Telnet 使用了 TCP/IP 协议进行通讯，Telnet 协议默认是 23 端口。它允许用户通过网络连接到远程主机，并在远程系统上执行命令，就像在本地终端上输入命令一样。完全可以使用 telent 命令行工具，来模拟整个 HTTP 协议连接建立的过程，HTTP 请求和响应的过程如下：[scode type="green"]浏览器根据 IP 地址和端口号与服务器建立连接向 Web 服务器发送请求数据包Web 服务器接收请求数据包后，发送相应的响应数据包浏览器接…

未来 PWA 应用是否成为主流应用开发模式
type: item
date: 2023-07-20T15:40:00+08:00
description: PWA 这个词已经出现很久了，如果读者做嵌入式开发应该不会很了解，如果是做服务端和前端或多或少了解，最近在聊这方面的话题发现还是有一些人不知道这个 PWA 是什么技术？PWA 是 Progressive Web Apps 的缩写，中文翻译为渐进式网络应用，早在 2014 年，W3C 就公布过 Service Worker 的相关草案，Service Worker 可以不干扰视图渲染层线程，运行独立于 Web 站点只请求原始数据，而不涉及任何样式或布局信息，但是其在生产环境被 Chrome 支持是在 2015 年，由 Google 工程师 Alex Russell 和 Frances Berriman 共同提出，在他们的一篇文章Progressive Web Apps: Escaping Tabs Without Losing Our Soul 的博文中详细阐述了 PWA 的概念和优势，PWA 旨在将 Web 应用程序与本地应用程序的最佳特性结合起来，提供一种更好的用户体验，包括快速加载、离线访问和与设备功能的集成。[scode type="yellow"]PWA 优点不需要从应用商店进行安装，而是通过网址 URL 来进行访问，简单的来说就是不用下载，用一个 URL 就能直接打开，将一个传统的网页可以渐进式地变成和本地原生 App 一样来使用，比如被添加到主屏幕、全屏方式运行、离线工作、推送通知消息等，这个和目前腾讯的微信小程序类似的。微信小程序确定是被腾讯锁死在微信里面，只能通过微信的小程序商店和微信内搜索得到，但 PWA 你应用可以被搜索引擎直接搜索得到，搜索引擎就为应用到入口，这和各手机厂商的应用商店是有着明显的差异的。小程序和 PWA 这种应用开发模式出现比较晚，因为首次打开这种应用需要从服务器端下载各种资源文件，在 2015 年时候如果读者有使用过手机都清楚那个时候大部分还是 3G 和 4G 的时代，部分地区 WIFI 也不是全面的覆盖，这就会带来一个新问题，如果全部采用 Web App 的方式进行开发应用，每次都需要加载。而采用传统的原生方式开发者只需要把应用上传到应用商店，用户去应用商店下载得到一个固定资源的 App 安装到手机上即可，最后动态数据从服务器端通过 HTTP 协议进行交互，减少一部分常用资源的加载过程，但是放到今天 5G 已经慢慢普及，网速越来越快，用户手机几乎不会切断流量或者 WIFI 连接的，所以个人感觉 PWA 更适合现在的应用开发模式。[/scode]Native App在介绍原生应用之前，我想我有必要介绍一下什么 Web2.0 结构互联网，Web2.0 也不仅仅限制于在浏览器交互数据上，有了 HTTP 协议和移动互联网，更多是指基于 Web2.0 异步数据交换方式。Web1.0 是互联网的早期阶段人们上网的方式是查看一些固定死的静态网页内容，网站维护者每更新一次网站就需要重新发布一个新的网页版本，而且缺乏和用户互动性。Web2.0 是互联网的现代阶段，大约从 2004 年开始至今，在 Web2.0 时代，互联网变得更加互动和社交，用户可以更积极地参与和贡献内容，用户可以把自己的数据通过 HTTP 协议发送到远端服务器上存储，其他用户可以查看，用户之间可以互动，鼓励用户生成内容用户可以创建、编辑和共享各种形式的内容，如博客、社交媒体帖子、视频、评论等。此后移动互联网来了，更多开发者从事开发移动端 App ，而这些 App 和服务器端数据交换大部分也是采用的 HTTP 协议，不管你是使用 Java 还是 Kotlin 还是现在推荐的 Dart 和 Flutter 开发 App，乃至是 Swift 和 SwiftUI 这种，都是属于视图层的开发，最终数据还是存储在远端服务器之上。上图为一个原生的 App 应用架构，开发者如果是 Apple 开发者需要在 App Store 申请和购买开发者证书，才能把自己开发应用上传到应用商店中，而且还要自己维护复杂 App 服务器端和 App Store 中的应用版本和介绍信息，这就使得开发者开发一个版本 App 需要做很多工作，独立开发者更是要自己设计原型图，在通过代码被 UI 图变成用户软件界面，最后如果开发应用不是单机的应用还需要自己接入 API 服务器，这件使得开发周期很长。Progressive Web Apps但是对于现在的 PWA 应用，开发者更关注的实现 PWA 定义的规范和协议即可，就可以将一个网站变成 PWA 应用，当然这里有人会问了 PWA 应用有原生 App 应用性能高吗？或者能和原生 App 一样调用系统 API 实现类似原生应用功能吗？放到过去可能浏览器没有完全支持和提供这方面的 API ，例如你可能要实现一个读取门禁卡的 NFC 功…

分布式系统设计的挑战
type: item
date: 2023-04-10T17:19:00+08:00
description: 分布式系统对于刚刚入门作研发的程序员还是接触不多，刚刚入行的大部分程序员做的最多工作还是开发单体应用和简单业务操作，遇到最多问题估计也是本地线程并发数据安全问题，和数据状态一致性问题。而往后发展最多把单体应用部署为 HA 主从节点的方式备份架构，采用 nginx 做网络应用层的负载均衡。单体应用 bug 修复往往是万能法重启应用，让应用的状态消失，再恢复到正常状态来解决问题，但是实际上问题还是没有得到解决，除非是一些硬件故障可以跟换硬件，软件本身的问题需要开发者自己修复发布新的版本才能行。分布式系统[scode type="blue"]而分布式系统是由多个独立计算机组成的集合，而大型的软件系统被拆分成了多个部分，部署在网络中计算机中，这些计算机通过网络互相通信协同工作，在设计分布式系统时，需要考虑以下几个问题：[/scode]通信协议：如何让分布式系统中的各个节点进行通信？需要确定适合该系统的通信协议，并确保节点之间的通信稳定可靠。数据一致性：如何确保分布式系统中的所有节点都拥有相同的数据副本？需要选择适合该系统的数据一致性协议，如Paxos或Raft等，并在设计时确保数据的一致性。负载均衡：如何在分布式系统中均衡地分配负载？需要设计适合该系统的负载均衡策略，并确保在节点故障或网络问题时系统依然可用。安全性：如何保护分布式系统中的数据和节点不受攻击？需要在设计时考虑安全问题，并实现适当的安全措施，如加密和身份验证等。可扩展性：如何在需要时扩展分布式系统的规模？需要设计可扩展的系统架构，以便可以添加更多的节点和处理更多的请求。故障恢复：如何在节点故障或网络问题时恢复分布式系统的功能？需要设计故障恢复机制，并确保系统在出现问题时可以快速自动恢复。性能优化：如何优化分布式系统的性能？需要考虑各个节点的性能特点，并尝试优化通信协议、数据一致性协议、负载均衡策略等，以获得最佳的性能表现。[scode type="yellow"]网络和时间是设计分布式系统时必须考虑的关键因素，网络延迟、带宽限制和数据包丢失等问题可能导致节点之间的通信出现问题，从而影响系统的性能和可靠性。时间同步也是分布式系统设计中非常重要的问题，因为节点之间的时钟可能会出现偏差，导致数据一致性和协议的正确性受到影响，因此，在设计分布式系统时，必须考虑网络和时间问题，并采取适当的措施来解决这些问题。目前时间存在于物理世界中，一个系统可能被部署在全球的网络服务器节点中，这些地区的用户时区也不一样，而且是还是多应用进行异步操作的，此时分布式系统设计就不单单是单体系统中加个锁就能解决的问题了。[/scode]扩展性对于单体系统数据的一致性也是一个问题，例如数据库和缓存系统的数据一致性问题，当然缓存系统的数据一致性存在时间窗口的偏差是可以容忍的，但是对于分布式系统的数据一致性要考虑到问题加很多，关系型的数据库的 ACID 已经帮业务层解决了很多问题，而分布式系统特别是针对的分布式数据库，数据被分散在不同网络节点上，如何处理 ACID ？目前很多微服务架构是使用一些数据库中间件代理解决，或者说分库分表，而颠覆性的是 NewSQL 数据库例如 PingCAP 和 Amazon Aurora 分布式关系数据库来解决分布式数据一致性系统。[scode type="yellow"]分布式关系数据库用起来来和传统的 Oracle 数据库是没有区别的，对于业务层是无感知的，但是分布式数据库本身的设计问题是要对 CAP 定律进行权衡的，大部分采用的分区同步复制写入的方式，而这些数据如何保证一致性？总的来说，NewSQL 是一种结合了传统关系型数据库 ACID 的优点，并加入了一些新技术，以提高数据库的性能和可伸缩性。[/scode]为提高 CAP 定律中的 P 容错机制，通常采用的方法是将数据分片复制到多个节点上，以提高数据的可靠性和容错性，同时还需要考虑 CAP 中的 A 可用机制，对节点故障进行监测和自动故障转移处理，保证系统的高可用性，NoSQL 系列例如 Redis 采用的是一致性哈希算法，Facebook Cassandra 也是一致性哈希算法，这种采用 Gossip 协议进行数据同步的，但是数据还是分区存储在当个节点上，遇到 ACID 需求无法将整个集群视为一个整体系统做强一致性数据处理，只能在单个数据节点上做 ACID 处理，并且这种分区有热点数据问题。一致性为了保证分区所有节点数据一致性目前大多数数据库采用的是同步复制，换句话来说往集群中一个节点写入数据其他节点马上会同步到最新的数据，但是由于分布式系统是建立于在不可靠网络之上的，如果出现分布式分区脑裂情况，导致多个分区不能通常通信，此时数据版本该从何处写入，从何处复制到集群中剩下的可以节点中？目前大多数 NewSQL 采用两种分…

万维网的基础设施 DNS 服务
type: item
date: 2023-03-25T19:29:00+08:00
description: 有互联网才有很多网络技术应用，让不同设备接入到网络从而达到互联网目的，就和现实世界中物流系统一样，互联网的前身美苏冷战时期的 ARPANET 最初在 1980 年代作为区域学术和军事网路连接的骨干网络，最后苏联解体了结束了冷战，最后这些已经曾在的网络基础设施，逐渐开始转向为民用网络系统了。[scode type="blue"]这篇文章要讲解的是 World Wide Web 这是存在于因特网之上的互联网应用服务，我日常见过很多人和同行认为万维网就是互联网，这是一个错误的认知，他们是有着本质上的区别的，万维网更多是指我们能通过浏览器或者支持通过 HTTP 协议能访问到的网络资源，例如文档、图片、视频、存在其他计算机所提供的资源集合并且通过网络进行开放访问。日常生活中使用的最多是浏览器访问 www 网站服务，而目前程序员做的事情通过不同编程语言和编程技术实现能动态服务的网站。[/scode]DNS 服务器每当我们访问一个已知的网址都是在浏览器的地址栏中输入一串字符串，这个字符串就时域名，而域名是 IP 地址的一串别名，浏览器底层发送一个网络请求时数据包时提供 TCP 协议栈发送出去的，我们也可以通过 IP 地址进行访问万维网服务，但是很少有人这么做，因为 IP 地址时一串由 4 个字节组成的 32 Bit 位数字串，往往一般人很难记住。这和日常生活中的电话号码一样，我们很少记住别人的电话号码的数字，而是通过手机的通讯录保存一个联系人名字，拨打电话时提供联系人姓名来打电话呼叫别人。[scode type="yellow"]同理在计算机中也是这样设计的只不过名称叫 DNS 服务，通过把目标 IP 地址绑定到一串特定便于记忆的字符串域名上，我们通过域名来服务网站，这比通过记住一串数字要容易的多，DNS 服务就是便于使用者查询对应域名所绑定的 IP 信息的，从而把组装数据包通过 TCP 协议栈发送出去。[/scode]DNS 服务是由全球 13 组根服务器所提供的，10 台在美国，另外 3 台分别在欧洲的挪威、伦敦和东亚的日本，就这 13 组根服务器为我们提供了 DNS 服务，不过目前世界上有很多 DNS 从服务器和缓存服务器方便我们快速查找和及时响应查询请求所设置，缓存服务器大部分是缓存已经查询过的域名信息，目录前世界上流行的公共 DNS 服务器 IP 地址有下列表：IP 地址供应商8.8.8.8Google Public DNS1.1.1.1Cloudflare Public DNS[scode type="blue"]DNS 服务器会根据客户的请求的内容，返回对于的信息，这些信息被注册到 DNS 服务器至上，分为不同类型的 DNS 信息，DNS 在设计的时候就考虑到了不是单单为网站域名服务，还有一些其他的类型服务，例如邮箱服务器地址查询，也是由 DNS 服务器负责的，在 DNS 服务器信息有一个 Class 字段，这个字段就是用来区分不同网络服务类型的，目前大部分类型为 IN，一个简单例子如下：[/scode]NameClassTypeDatawww.ibyte.meINA66.888.88.8ibyte.meINMXmxbiz1.qq.com.imgINAAAA2001:4860:4860::8888blogINCNAMEwww.ibyte.meptrINPTR192.168.0.1sevINSEV127.0.0.1整个 DNS 服务可以看作为一个交通指挥中心，存储着互联网上的交通路由表。Linux 的 DNS 服务在 Linux 中默认的 DNS 相关的配置有很多文件来制定，/etc/resolv.conf 为 DNS 解析服务器的配置文件，这样每当我们查询域名信息时，会从配置文件中找到 DNS 服务器的 IP 发送 DNS 查询请求，/etc/hosts.conf 为本机自定义 DNS 域名绑定映射文件，而另外一个至关重要的是 /etc/nsswitch.conf 文件，可以设置每次查询 DNS 服务的步骤。[scode type="yellow"]在 Linux 中如果默认使用 wget 命令和 curl 命令完全根据本地 DNS 配置方案进行查询步骤的，而 Linux 内置的 host 和 dig 命令是不会按照 hosts.conf 文件和 nsswitch.conf 的规则的，而是按照 resolv.conf 文件配置的远程 DNS 根服务器进行的查询。[/scode][root@cs9 ~]# dig www.ibyte.me ; <<>> DiG 9.16.23-RH <<>> www.ibyte.me ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- o…