让 AI 节约你的每一分钟

gRPC 介绍

gRPC 四种模式及应用场景 在 gRPC 中,RPC 调用模式有四种,每种模式适用于不同的业务场景。下面将详细分析这四种模式及其对应的应用场景。 参考官方文档概念:https://grpc.io/docs/what-is-grpc/core-concepts 1. 简单 RPC (Unary RPC) 模式描述 客户端发送一个请求,服务端返回一个响应。双方的通信都是一次性的。 类似于传统的 HTTP 请求-响应模型。 应用场景 查询服务:客户端请求一些数据,服务端根据请求参数返回一个结果。比如获取用户信息、查询订单状态等。 简单的计算服务:客户端传入参数,服务端执行计算后返回结果,比如加法、减法、获取某个 ID 的计算结果等。 无需复杂交互的任务:单次请求即能获得结果的场景,如调用支付接口、获取天气数据等。 示例 rpc GetUserInfo(UserRequest) returns (UserResponse); 2. 服务端流式 RPC (Server Streaming RPC) 模式描述 客户端发送一个请求,服务端返回一个响应流。客户端接收服务端的多个响应数据,直到服务端发送完所有数据。 应用场景 分页或批量获取数据:客户端需要从服务端获取大量数据时,通过流的方式接收数据,不需要一次性加载完所有数据,比如数据导出或查询日志。 实时数据推送:适合服务端需要不断推送数据给客户端的场景,比如股票行情、实时天气更新、监控数据等。 文件下载:客户端请求文件,服务端以数据块形式流式返回文件内容。 示例 rpc GetLogStream(LogRequest) returns (stream LogEntry); 3. 客户端流式 RPC (Client Streaming RPC) 模式描述 客户端发送一个流请求,服务端接收所有请求后返回一个响应。客户端可以分批次将数据流发送到服务端。 应用场景 批量数据上传: 客户端分批次发送数据到服务端,如文件分块上传、日志批量上传等。 传感器数据采集: 客户端持续收集传感器的数据,并将其流式发送至服务端进行处理。 数据聚合: 客户端发送多个值,服务端处理后返回汇总结果,如计算平均值、订单总金额等。 示例 rpc UploadLogStream(stream LogEntry) returns (UploadStatus); 4. 双向流式 RPC (Bidirectional Streaming RPC) 模式描述 客户端和服务端都可以通过流的方式不断发送和接收消息。双方可以并发地发送和接收消息,且不需要等待对方完成发送。

Http 之 jsonp

Http jsonp 概念:浏览器通过script标签中src属性向服务端发送执行函数的名字,服务端根据函数名、服务端数据,拼接成一个函数调用的字符串给客户端script标签执行,这称之为JSONP 特点: 不属于axios请求,因为没有使用XMLHttpRequest对象 仅支持get请求,不支持post、put、delete

ssh 连接过程

用户密码方式验证登入过程分析 客户端ssh向服务器发送连接请求,传递用户名及ip端口 服务端接收到请求,检查用户名是否存在,不存在则拒绝。反之,会将公钥及指纹(公钥对应的sha256值)发送给客户端 若客户端是第一次请求连接,则会提醒用户确认该指纹是否来自目的服务器,若用户输入yes确认则会将指纹存放到known_host中,下次再连接时,当发现服务器传过来的指纹与已存入到known_host中的指纹一致,则服务器身份直接验证通过,无需提醒确认。反之会发出警告:提醒说该公钥和指纹可能是由于中间人拦截而被篡改的或者可能是服务器更新了新的公私钥对。 客户端完成公钥接收及指纹对比后,需要输入密码,将密码使用服务器公钥进行加密发送到服务器,服务器私钥解密验证成功则允许登入。 公钥身份验证登入过程分析 客户端在本地生成一对密钥,包括一个公钥和一个私钥。且将自己的公钥放到到服务器上的 ~/.ssh/authorized_keys 文件中 客户端ssh向服务器发送连接请求 服务端检查用户名存在后,生成一个随机串返给客户端 客户端自己的私钥对随机串进行签名并发送给服务器 服务器使用已存入authorized_keys 中的公钥进行验签,若验签通过则客户端身份认证成功

代码提交规范之 Conventional Commits

Conventional Commits:让您的Git历史更加清晰和有意义 引言 在软件开发中,良好的版本控制实践是维持项目健康和可持续发展的关键。一个清晰、一致的提交历史不仅可以加强团队合作,还能提高代码的可维护性。本文将介绍 Conventional Commits 规范,一种帮助您实现这一目标的提交信息格式规范。 什么是 Conventional Commits? Conventional Commits 是一个轻量级的、社区驱动的提交信息格式规范。它的核心目的是使提交信息更加可读和易于理解。遵循这一规范,可以让您的 Git 历史成为一个清晰的故事,而不仅仅是代码的变更记录。 核心要点 清晰的类型定义:规定了一系列预定义的提交类型,如 feat, fix, docs, 等,每种类型对应不同的代码更改目的。 可选的范围:允许在提交类型后指定影响范围,增加了额外的上下文信息。 描述性的消息:鼓励编写简短且具有描述性的信息,概括提交的主要内容。 如何使用 Conventional Commits? 使用 Conventional Commits 的基本格式如下: markdownCopy code <type>[optional scope]: <description> [optional body] [optional footer] 提交类型(Types) feat:新功能 fix:修复 Bug docs:文档更新 style:代码样式调整(不影响代码运行) refactor:代码重构 perf:性能提升 test:测试相关 chore:日常琐事(如依赖管理) 示例 gitCopy code feat(auth): 添加 JWT 认证支持 - 实现 JWT 生成和验证 - 更新认证中间件以支持 JWT 关闭问题 #123 在这个示例中,feat 表明这是一个添加新功能的提交,auth 是这次更改的范围,后面紧跟着的是对提交内容的简短描述。接着是一个更详细的解释,最后是相关问题链接。 Conventional Commits 的好处 提高可读性:清晰的提交历史使新团队成员更容易理解项目进展。 自动化工具友好:可以被用于自动化生成变更日志和版本控制。 改善协作流程:明确的提交类型和格式有助于代码审查和团队协作。 结语 Conventional Commits 规范为软件开发提供了一种简单而高效的提交历史管理方法。它的简洁性和自解释性使得项目维护变得更加容易。采纳这一规范,将为您的项目带来长期的好处。

代码提交规范之 Conventional Commits

引言 在软件开发中,良好的版本控制实践是维持项目健康和可持续发展的关键。一个清晰、一致的提交历史不仅可以加强团队合作,还能提高代码的可维护性。本文将介绍 Conventional Commits 规范,一种帮助您实现这一目标的提交信息格式规范。 什么是 Conventional Commits? Conventional Commits 是一个轻量级的、社区驱动的提交信息格式规范。它的核心目的是使提交信息更加可读和易于理解。遵循这一规范,可以让您的 Git 历史成为一个清晰的故事,而不仅仅是代码的变更记录。 可参考链接:https://www.conventionalcommits.org 核心要点 清晰的类型定义:规定了一系列预定义的提交类型,如 feat, fix, docs, 等,每种类型对应不同的代码更改目的。 可选的范围:允许在提交类型后指定影响范围,增加了额外的上下文信息。 描述性的消息:鼓励编写简短且具有描述性的信息,概括提交的主要内容。 如何使用 Conventional Commits? 使用 Conventional Commits 的提交注释基本格式如下: <type>(<scope>): <description> <body> <footer> 格式描述如下: <type>:提交类型,用于说明本次提交的类别,常见类型包括: feat:新功能 fix:修复 Bug docs:文档更新 style:代码风格样式调整(不影响代码运行) refactor:代码重构 perf:性能提升(performance的缩写) test:测试相关 chore:其他无关紧要的改动(单词是琐事的意思) (<scope>):可选,用于进一步细化提交类型,例如 feat(login) 表示登录功能的新功能。 <subject>:简短描述本次提交的主要内容,建议不超过 50 个字符。 <body>:可选,提供更详细的提交描述,可以包含以下内容: 修改原因 修改内容 影响范围 相关测试 <footer>:可选,包含额外的元信息,例如: 关联的 Jira 问题编号 提交者 评审者 示例 feat(auth): 添加 JWT 认证支持 - 实现 JWT 生成和验证 - 更新认证中间件以支持 JWT 关闭问题 #123 在这个示例中, feat 表明这是一个添加新功能的提交,auth 是这次更改的范围,后面紧跟着的description是对提交内容的简短描述。 接着body是一个更详细的解释。 最后footer是相关问题的结果。
use_cache

缓存简介

缓存 简介 简而言之,即是数据存储的缓冲区。使用缓存之后,可以减轻访问数据库的压力,显著的提升系统的性能。 使用缓存的一般流程如下: 缓存通常有两种: 服务器主集本身的内存缓存,也就是我们说的二级缓存。 相比于缓存中间件性能更好。但只能应用于本机,若系统分布式部署,会存在数据不一致的问题。 缓存中间件,比如:Redis、Memcached等 适用于分布式缓存,解决分布式数据一致性问题 有些业务场景,分布式缓存和二级缓存可以一起使用 缓存带来的问题 1. 缓存雪崩 缓存雪崩指的是当某一个时间段出现大规模的缓存失效的情况,此时大量的并发请求直接命中在数据库上面,导致数据库压力巨大,甚至宕机。 分析 造成缓存雪崩的关键在于在同一时间段大量的key失效。出现这个问题的可能性:1. 缓存宕机。2. 采用了相同的过期时间。 解决方式 过期时间采用随机值 若真的发生了缓存雪崩,使用熔断机制。当流量到达一定阈值,直接返回“系统拥挤”之类的提示,防止过多的请求打在数据库上。至少能保证一部分用户是可以正常使用,其他用户多刷新几次也能得到结果。 提高数据库的容灾能力,可以使用分库分表,读写分离的策略。 防止Redis缓存宕机导致缓存雪崩的问题,可以搭建Redis集群,提高Redis的容灾性。 2. 缓存击穿 缓存击穿指的是大量并发集中访问某一个 key,突然这个 Key 缓存失效了,导致大并发一时间全部命中在数据库上。 与缓存雪崩对比 缓存雪崩是大量的 key 失效导致的 而缓存击穿是大规模访问一个失效的 key 导致的 分析 关键在于某个热点 key 失效了,导致大并发集中打在数据库上。 所以要从两个方面解决,第一是否可以考虑热点key不设置过期时间,第二是否可以考虑降低打在数据库上的请求数量。 解决方式 若业务允许,对热点 key 可以设置永不过期 使用互斥锁。当缓存失效时,只有拿到锁才可以查询数据库,降低了在同一时刻命中在数据库上的请求。 可以对 Key 进行加锁 相应的,加锁会导致系统性能降低。 3. 缓存穿透 假如请求的 key 是在缓存中是不存在的,那缓存查不到就会去数据库查询。如果有大量这样的请求,这些请求像“穿透”了缓存一样直接命中在数据库上,这种现象就叫做缓存穿透。 分析 关键点是缓存中查不到 Key,这些 key 是不存在的 PS:说明了边界安全的重要性。应该做好参数检验,外界不可信 解决方式 把无效的Key存入缓存。如果缓存查不到,数据库也查不到,可以把这个 Key 值存入缓存,并设置value=“null”,当下次再通过这个Key 查询时就不需要再查询数据库。 弊端是,假如传进来的这个不存在的Key值每次都是随机的,那这种做法无意义。 使用布隆过滤器。布隆过滤器的作用是某个 key 不存在,那么就一定不存在,它说某个 key 存在,那么很大可能是存在(存在一定的误判率)。因此,可以在缓存之前再加一层布隆过滤器,在查询的时候先去布隆过滤器查询 key 是否存在,如果不存在就直接返回。

控制反转 IoC 与依赖注入 DI

控制反转 IoC 与依赖注入 DI 引入 讨论之前,先来简述一下软件对象中的耦合 在软件开发过程中,对象之间的耦合关系是一个常见的问题。传统的面向对象设计中,对象之间的依赖通常是硬编码在代码中的,这导致了代码的高耦合度和低灵活性。当一个对象依赖于另一个对象时,它通常会直接实例化或调用该对象,这使得两个对象之间的关系非常紧密,难以进行单元测试和代码重用。 例如,考虑一个电商网站的购物车功能。在购物车中直接实例化商品对象可能会导致购物车模块与商品模块之间的紧密耦合,使得如果需要修改商品类的实现或者替换为其他商品类时,就必须要修改购物车模块的代码。这种紧耦合的设计不仅使代码难以维护和扩展,还降低了代码的可测试性和可重用性。 将这个例子缩小到类对象之间的调用:对象 A 依赖于对象 B,那么对象 A 在初始化或需要调用 B 的时候,自己必须主动去创建对象 B 或者使用已经创建的对象 B。无论是创建还是使用对象 B,控制权都在自己手上。 为了解决这一问题,控制反转(IoC)和依赖注入(DI)应运而生。它们提供了一种解耦的方法,通过将对象之间的依赖关系转移到外部容器(loC 容器)或框架中管理,实现了对象之间的松耦合。 简言之,通过 IoC 与 DI,调用者将依赖对象的创建初始化逻辑从自身抽离解耦出来,调用者只负责声明调用,依赖对象只负责实现自己,而创建依赖对象的控制权交给了“第三方”(IoC容器) 控制反转 loC 案例:假设你现在需要与外部客户对接某业务,这个业务随着外部客户的不同,对接流程也不同,于是每个客户,都需要制定相应的业务对接流程,每当有了新的客户,不得不继续制定该客户的业务对接流程。显然,这严重耦合并依赖各个不同客户。最终你受不了了,自己制定一个对接方案,开发一个对接平台,各个客户必须遵守你制定对接方案才能进行业务对接,于是各个客户反过来依赖你的对接标准,反转了控制,倒置了依赖。 这个过程中,你就是调用者,客户就是依赖对象,你制定的标准、对接平台就是 IoC 容器。从调用者不得不自身一个个实现对接业务到将对接业务解耦到统一标准中的这一过程,可以理解为控制反转 控制反转是一种思想,其基本思想是:借助于“第三方”实现具有依赖关系的对象之间的解耦。 依赖注入 DI 以 python 代码为例 未使用依赖注入之前: class Dependency: def __init__(self): pass def operation(self): return "Dependency operation" class Client: def __init__(self): pass def do_something(self): # 手动创建 Dependency 对象 dependency = Dependency() return dependency.operation() 此时,若 Dependency 的初始化函数发生变化,Client 中手动创建 Dependency 对象的代码也需要改动!