
[{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/centos/","section":"Tags","summary":"","title":"CentOS","type":"tags"},{"content":" centOS7 # 安装yum 查看端口 netstat -anp |grep 80\n安装wget yum install wget\nsystemctl的所有service配置文件目录为/usr/lib/systemd/system\nwatch 命令监听 watch -d -n 2 监听执行某个命令，每2秒刷新一次\ntop 命令实时查看CPU内存\n// 查看内存，每2秒刷新一次 free -m -s 2\n","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tech/centos7/","section":"博客","summary":"centOS7 # 安装yum 查看端口 netstat -anp |grep 80\n安装wget yum install wget\nsystemctl的所有service配置文件目录为/usr/lib/systemd/system\nwatch 命令监听 watch -d -n 2 监听执行某个命令，每2秒刷新一次\n","title":"CentOS7  相关命令","type":"posts"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/clickhouse/","section":"Tags","summary":"","title":"Clickhouse","type":"tags"},{"content":" clickhouse # 安装搭建 # server配置 # 路径 # /etc/clickhouse-server\n用户配置\nuser.xml user.d/ server配置\nconfig.xml config.d/ 设置用户名密码 # vim users.xml，找到 users \u0026ndash;\u0026gt; default \u0026ndash;\u0026gt; 标签下的password修改成password_sha256_hex，并把密文填进去\n\u0026lt;password_sha256_hex\u0026gt;密码密文\u0026lt;/password_sha256_hex\u0026gt; 设置远程可访问 # vim config.xml 找到 listen_host 标签，修改为以下:\n\u0026lt;listen_host\u0026gt;0.0.0.0\u0026lt;/listen_host\u0026gt; 启动 # 访问 # clickhouse-client -h ip地址 -d default -m -u default --password 密码明文 注意参数--password不能简写为-p 基本操作CURD # ","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tech/clickhouse/","section":"博客","summary":"clickhouse # 安装搭建 # server配置 # 路径 # /etc/clickhouse-server\n","title":"clickhouse 数据库使用","type":"posts"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/categories/database/","section":"Categories","summary":"","title":"Database","type":"categories"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/database/","section":"Tags","summary":"","title":"Database","type":"tags"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/categories/development/","section":"Categories","summary":"","title":"Development","type":"categories"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/development/","section":"Tags","summary":"","title":"Development","type":"tags"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/gateway/","section":"Tags","summary":"","title":"Gateway","type":"tags"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/gin/","section":"Tags","summary":"","title":"Gin","type":"tags"},{"content":"基于 Gin 的后端初始化模板，整合路由、认证、响应封装、数据库、缓存、消息队列、定时任务、gRPC、SSE、WebSocket、多环境配置和依赖注入。\n查看 GitHub ","date":"2025-01-08","externalUrl":null,"permalink":"/zh/projects/gin-init/","section":"项目","summary":"基于 Gin 的后端初始化模板，整合路由、认证、响应封装、数据库、缓存、消息队列、定时任务、gRPC、SSE、WebSocket、多环境配置和依赖注入。\n查看 GitHub ","title":"Gin 框架二次开发模板","type":"projects"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/gis/","section":"Tags","summary":"","title":"Gis","type":"tags"},{"content":" Gis # 空间数据 # 空间数据基本特征 # 空间特征\n空间实体的位置或现在所处的地理位置及拓扑关系和几何特征\n属性特征\n主要指的是专题属性，也是非定位数据，指实体所具有的各种性质，如房屋的结构、高度、层数、使用的主要建筑材料、功能等。专题属性通常以数字、符号、文本和图像等方式表示。专题属性的表示方式主要有表格和图形（或图像）两种。\n表格\n通过囿定的表格格式详细列出空间实体的参数和描述数据。一般情况下，表格数据精确、明了、易于理解\n图形或图像\n无论是通过矢量还是栅格表示地理空间中的实体，如果属性特征是通过属性值的级别来表示的，就可以在同一级别的空间范围内充填一定的颜色或图例符号。例如，在绘制或显示某一城市的污染专题图时，任意级别的污染区域可以通过颜色来加以表示。以图形图像表示的属性数据具有隐含的性质，必须通过图例或有关技术规范才能加以理解。\n时间特征\n指现象或物体随时间的变化，其变化的周期有超短期的、短期的、中期的、长期的和超长期的。\n空间特征数据和属性特征数据常常呈相互独立的变化，即在不同的时间，空间位置不变，但属性数据可能发也变化，或者相反。\n对于现有的大量GIS系统，由于它们并非是时态GIS系统，所以把专题属性和时间特征数据统称为属性数据。\n","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tech/gis/","section":"博客","summary":"Gis # 空间数据 # 空间数据基本特征 # 空间特征\n","title":"gis 空间数据特征","type":"posts"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/git/","section":"Tags","summary":"","title":"Git","type":"tags"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/github/","section":"Tags","summary":"","title":"Github","type":"tags"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/categories/go/","section":"Categories","summary":"","title":"Go","type":"categories"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/go/","section":"Tags","summary":"","title":"Go","type":"tags"},{"content":" Go 依赖注入之 wire 库 # PS: 若不了解控制反转和依赖注入，可参考博文：https://blog.csdn.net/qq_38269333/article/details/139300610?spm=1001.2014.3001.5501\n简介 # wire 库是管理和解析依赖关系的。\n随着项目规模的增长，手动管理依赖关系变得越来越困难，容易导致代码的复杂性和耦合度增加。为了解决这一问题，Google 开发了一个名为 wire 的依赖注入工具，它可以自动生成依赖注入代码，帮助开发者管理依赖关系，提高代码的清晰度和可维护性。\nGitHub 地址：https://github.com/google/wire\n安装命令 # go get github.com/google/wire/cmd/wire 使用示例 # 以常见的 controller、service、model 模式为例，示例如下:\n代码片段\nuser.go\npackage main import ( \u0026#34;database/sql\u0026#34; \u0026#34;net/http\u0026#34; ) // User 是用户模型 type User struct { ID int Name string Email string } // UserRepository 是用户存储库 type UserRepository struct { DB *Database } // UserService 是用户服务 type UserService struct { Repo *UserRepository } // UserController 是用户控制器 type UserController struct { Service *UserService } // GetUserByID 处理获取用户的HTTP请求 func (controller *UserController) GetUserByID(w http.ResponseWriter, r *http.Request) { id := 1 // 假设我们从请求中获取了用户ID user, err := controller.Service.GetUserByID(id) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // 返回用户信息 } // NewUserRepository 创建一个新的用户存储库 func NewUserRepository(db *Database) *UserRepository { return \u0026amp;UserRepository{DB: db} } // NewUserService 创建一个新的用户服务 func NewUserService(repo *UserRepository) *UserService { return \u0026amp;UserService{Repo: repo} } // NewUserController 创建一个新的用户控制器 func NewUserController(service *UserService) *UserController { return \u0026amp;UserController{Service: service} } wire.go\n//+build wireinject package main import ( \u0026#34;github.com/google/wire\u0026#34; ) // InitializeUserController 初始化用户控制器 func InitializeUserController() (*UserController, error) { wire.Build(NewUserController, NewUserService, NewUserRepository, NewDatabase) return nil, nil } main.go\npackage main import ( \u0026#34;log\u0026#34; \u0026#34;net/http\u0026#34; ) func main() { controller, err := InitializeUserController() if err != nil { log.Fatalf(\u0026#34;failed to initialize user controller: %v\u0026#34;, err) } http.HandleFunc(\u0026#34;/user\u0026#34;, controller.GetUserByID) log.Println(\u0026#34;Starting server on :8080\u0026#34;) if err := http.ListenAndServe(\u0026#34;:8080\u0026#34;, nil); err != nil { log.Fatalf(\u0026#34;failed to start server: %v\u0026#34;, err) } } 执行 wire 命令即可在当前文件夹下生成 wire_gen.go 文件，内部实现了 InitializeUserController 方法， wire_gen.go内容如下：\n// Code generated by Wire. DO NOT EDIT. //go:generate go run -mod=mod github.com/google/wire/cmd/wire //go:build !wireinject // +build !wireinject package main import ( _ \u0026#34;github.com/go-sql-driver/mysql\u0026#34; ) // Injectors from wire.go: // InitializeUserController 初始化用户控制器 func InitializeUserController() (*UserController, error) { database, err := NewDatabase() if err != nil { return nil, err } userRepository := NewUserRepository(database) userService := NewUserService(userRepository) userController := NewUserController(userService) return userController, nil } ","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/go/go-%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E4%B9%8B-wire-%E5%BA%93/","section":"博客","summary":"Go 依赖注入之 wire 库 # PS: 若不了解控制反转和依赖注入，可参考博文：https://blog.csdn.net/qq_38269333/article/details/139300610?spm=1001.2014.3001.5501\n简介 # wire 库是管理和解析依赖关系的。\n","title":"Go 依赖注入之 wire 库","type":"posts"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/gorm/","section":"Tags","summary":"","title":"Gorm","type":"tags"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/grpc/","section":"Tags","summary":"","title":"GRPC","type":"tags"},{"content":" gRPC 四种模式及应用场景 # 在 gRPC 中，RPC 调用模式有四种，每种模式适用于不同的业务场景。下面将详细分析这四种模式及其对应的应用场景。\n参考官方文档概念：https://grpc.io/docs/what-is-grpc/core-concepts\n1. 简单 RPC (Unary RPC) # 模式描述 # 客户端发送一个请求，服务端返回一个响应。双方的通信都是一次性的。\n类似于传统的 HTTP 请求-响应模型。\n应用场景 # 查询服务：客户端请求一些数据，服务端根据请求参数返回一个结果。比如获取用户信息、查询订单状态等。 简单的计算服务：客户端传入参数，服务端执行计算后返回结果，比如加法、减法、获取某个 ID 的计算结果等。 无需复杂交互的任务：单次请求即能获得结果的场景，如调用支付接口、获取天气数据等。 示例 # rpc GetUserInfo(UserRequest) returns (UserResponse); 2. 服务端流式 RPC (Server Streaming RPC) # 模式描述 # 客户端发送一个请求，服务端返回一个响应流。客户端接收服务端的多个响应数据，直到服务端发送完所有数据。\n应用场景 # 分页或批量获取数据：客户端需要从服务端获取大量数据时，通过流的方式接收数据，不需要一次性加载完所有数据，比如数据导出或查询日志。\n实时数据推送：适合服务端需要不断推送数据给客户端的场景，比如股票行情、实时天气更新、监控数据等。\n文件下载：客户端请求文件，服务端以数据块形式流式返回文件内容。\n示例 # rpc GetLogStream(LogRequest) returns (stream LogEntry); 3. 客户端流式 RPC (Client Streaming RPC) # 模式描述 # 客户端发送一个流请求，服务端接收所有请求后返回一个响应。客户端可以分批次将数据流发送到服务端。\n应用场景 # 批量数据上传: 客户端分批次发送数据到服务端，如文件分块上传、日志批量上传等。\n传感器数据采集: 客户端持续收集传感器的数据，并将其流式发送至服务端进行处理。\n数据聚合: 客户端发送多个值，服务端处理后返回汇总结果，如计算平均值、订单总金额等。\n示例 # rpc UploadLogStream(stream LogEntry) returns (UploadStatus); 4. 双向流式 RPC (Bidirectional Streaming RPC) # 模式描述 # 客户端和服务端都可以通过流的方式不断发送和接收消息。双方可以并发地发送和接收消息，且不需要等待对方完成发送。\n应用场景 # 实时聊天系统: 客户端和服务端不断发送和接收消息，适合聊天或实时通信的场景，比如多人游戏中的玩家交流。\n实时数据交换: 适用于实时监控、视频流等双向数据通信场景。\n协同编辑: 客户端和服务端之间协同编辑，双方可以不断传输状态或修改数据。\n示例 # rpc Chat(stream ChatMessage) returns (stream ChatMessage); 应用场景总结 # 简单 RPC (Unary RPC): 适合简单的请求和响应，通常用于查询、计算等任务。\n服务端流式 RPC (Server Streaming RPC): 适合分页获取、大量数据传输等场景。\n客户端流式 RPC (Client Streaming RPC): 适合批量上传、传感器数据采集等场景。\n双向流式 RPC (Bidirectional Streaming RPC): 适合实时双向通信、聊天系统、实时数据交换等场景。\n","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tech/grpc-%E5%9B%9B%E7%A7%8D%E6%A8%A1%E5%BC%8F%E5%8F%8A%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF/","section":"博客","summary":"gRPC 四种模式及应用场景 # 在 gRPC 中，RPC 调用模式有四种，每种模式适用于不同的业务场景。下面将详细分析这四种模式及其对应的应用场景。\n参考官方文档概念：https://grpc.io/docs/what-is-grpc/core-concepts\n1. 简单 RPC (Unary RPC) # 模式描述 # 客户端发送一个请求，服务端返回一个响应。双方的通信都是一次性的。\n","title":"gRPC 介绍","type":"posts"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/http/","section":"Tags","summary":"","title":"Http","type":"tags"},{"content":" Http # jsonp # 概念：浏览器通过script标签中src属性向服务端发送执行函数的名字，服务端根据函数名、服务端数据，拼接成一个函数调用的字符串给客户端script标签执行，这称之为JSONP\n特点：\n不属于axios请求，因为没有使用XMLHttpRequest对象 仅支持get请求，不支持post、put、delete ","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tech/http/","section":"博客","summary":"Http # jsonp # 概念：浏览器通过script标签中src属性向服务端发送执行函数的名字，服务端根据函数名、服务端数据，拼接成一个函数调用的字符串给客户端script标签执行，这称之为JSONP\n特点：\n","title":"Http 之 jsonp","type":"posts"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/linux/","section":"Tags","summary":"","title":"Linux","type":"tags"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/nodejs/","section":"Tags","summary":"","title":"Nodejs","type":"tags"},{"content":" notebook # win10计算文件Hash # certutil -hashfile 文件名 md5 ","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tech/notebook/","section":"博客","summary":"notebook # win10计算文件Hash # certutil -hashfile 文件名 md5","title":"notebook","type":"posts"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/categories/operation/","section":"Categories","summary":"","title":"Operation","type":"categories"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/categories/others/","section":"Categories","summary":"","title":"Others","type":"categories"},{"content":" postgis # 安装扩展 # 先查找最新postgis插件版本号\nsudo apt-cache search postgis 找到当前已安装postgreSQL版本对应的postgis版本\nps:查看postgreSQL版本 select version()\nsudo apt-get install postgresql-14-postgis-3 赋予某个库空间数据库的能力\nCREATE EXTENSION postgis; postgis_topology支持拓扑\nCREATE EXTENSION postgis_topology; pgrouting 提供了对路网的分析支持，包括双向Dijkstra最短路径等10多种功能，是postgis的插件，需要额外安装\n","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tech/postgis/","section":"博客","summary":"postgis # 安装扩展 # 先查找最新postgis插件版本号\nsudo apt-cache search postgis 找到当前已安装postgreSQL版本对应的postgis版本\n","title":"pg 之空间数据 postgis 插件","type":"posts"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/postgresql/","section":"Tags","summary":"","title":"Postgresql","type":"tags"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/categories/project/","section":"Categories","summary":"","title":"Project","type":"categories"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/redis/","section":"Tags","summary":"","title":"Redis","type":"tags"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/sdk/","section":"Tags","summary":"","title":"SDK","type":"tags"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/ssh/","section":"Tags","summary":"","title":"Ssh","type":"tags"},{"content":" 用户密码方式验证登入过程分析 # 客户端ssh向服务器发送连接请求，传递用户名及ip端口 服务端接收到请求，检查用户名是否存在，不存在则拒绝。反之，会将公钥及指纹（公钥对应的sha256值）发送给客户端 若客户端是第一次请求连接，则会提醒用户确认该指纹是否来自目的服务器，若用户输入yes确认则会将指纹存放到known_host中，下次再连接时，当发现服务器传过来的指纹与已存入到known_host中的指纹一致，则服务器身份直接验证通过，无需提醒确认。反之会发出警告：提醒说该公钥和指纹可能是由于中间人拦截而被篡改的或者可能是服务器更新了新的公私钥对。 客户端完成公钥接收及指纹对比后，需要输入密码，将密码使用服务器公钥进行加密发送到服务器，服务器私钥解密验证成功则允许登入。 公钥身份验证登入过程分析 # 客户端在本地生成一对密钥，包括一个公钥和一个私钥。且将自己的公钥放到到服务器上的 ~/.ssh/authorized_keys 文件中 客户端ssh向服务器发送连接请求 服务端检查用户名存在后，生成一个随机串返给客户端 客户端自己的私钥对随机串进行签名并发送给服务器 服务器使用已存入authorized_keys 中的公钥进行验签，若验签通过则客户端身份认证成功 ","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/legacy/ssh-%E7%99%BB%E5%85%A5%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%BF%87%E7%A8%8B%E5%88%86%E6%9E%90/","section":"博客","summary":"用户密码方式验证登入过程分析 # 客户端ssh向服务器发送连接请求，传递用户名及ip端口 服务端接收到请求，检查用户名是否存在，不存在则拒绝。反之，会将公钥及指纹（公钥对应的sha256值）发送给客户端 若客户端是第一次请求连接，则会提醒用户确认该指纹是否来自目的服务器，若用户输入yes确认则会将指纹存放到known_host中，下次再连接时，当发现服务器传过来的指纹与已存入到known_host中的指纹一致，则服务器身份直接验证通过，无需提醒确认。反之会发出警告：提醒说该公钥和指纹可能是由于中间人拦截而被篡改的或者可能是服务器更新了新的公私钥对。 客户端完成公钥接收及指纹对比后，需要输入密码，将密码使用服务器公钥进行加密发送到服务器，服务器私钥解密验证成功则允许登入。 公钥身份验证登入过程分析 # 客户端在本地生成一对密钥，包括一个公钥和一个私钥。且将自己的公钥放到到服务器上的 ~/.ssh/authorized_keys 文件中 客户端ssh向服务器发送连接请求 服务端检查用户名存在后，生成一个随机串返给客户端 客户端自己的私钥对随机串进行签名并发送给服务器 服务器使用已存入authorized_keys 中的公钥进行验签，若验签通过则客户端身份认证成功 ","title":"ssh 登入服务器过程分析","type":"posts"},{"content":" 用户密码方式验证登入过程分析 # 客户端ssh向服务器发送连接请求，传递用户名及ip端口 服务端接收到请求，检查用户名是否存在，不存在则拒绝。反之，会将公钥及指纹（公钥对应的sha256值）发送给客户端 若客户端是第一次请求连接，则会提醒用户确认该指纹是否来自目的服务器，若用户输入yes确认则会将指纹存放到known_host中，下次再连接时，当发现服务器传过来的指纹与已存入到known_host中的指纹一致，则服务器身份直接验证通过，无需提醒确认。反之会发出警告：提醒说该公钥和指纹可能是由于中间人拦截而被篡改的或者可能是服务器更新了新的公私钥对。 客户端完成公钥接收及指纹对比后，需要输入密码，将密码使用服务器公钥进行加密发送到服务器，服务器私钥解密验证成功则允许登入。 公钥身份验证登入过程分析 # 客户端在本地生成一对密钥，包括一个公钥和一个私钥。且将自己的公钥放到到服务器上的 ~/.ssh/authorized_keys 文件中 客户端ssh向服务器发送连接请求 服务端检查用户名存在后，生成一个随机串返给客户端 客户端自己的私钥对随机串进行签名并发送给服务器 服务器使用已存入authorized_keys 中的公钥进行验签，若验签通过则客户端身份认证成功 ","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tech/ssh-%E7%99%BB%E5%85%A5%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%BF%87%E7%A8%8B%E5%88%86%E6%9E%90/","section":"博客","summary":"用户密码方式验证登入过程分析 # 客户端ssh向服务器发送连接请求，传递用户名及ip端口 服务端接收到请求，检查用户名是否存在，不存在则拒绝。反之，会将公钥及指纹（公钥对应的sha256值）发送给客户端 若客户端是第一次请求连接，则会提醒用户确认该指纹是否来自目的服务器，若用户输入yes确认则会将指纹存放到known_host中，下次再连接时，当发现服务器传过来的指纹与已存入到known_host中的指纹一致，则服务器身份直接验证通过，无需提醒确认。反之会发出警告：提醒说该公钥和指纹可能是由于中间人拦截而被篡改的或者可能是服务器更新了新的公私钥对。 客户端完成公钥接收及指纹对比后，需要输入密码，将密码使用服务器公钥进行加密发送到服务器，服务器私钥解密验证成功则允许登入。 公钥身份验证登入过程分析 # 客户端在本地生成一对密钥，包括一个公钥和一个私钥。且将自己的公钥放到到服务器上的 ~/.ssh/authorized_keys 文件中 客户端ssh向服务器发送连接请求 服务端检查用户名存在后，生成一个随机串返给客户端 客户端自己的私钥对随机串进行签名并发送给服务器 服务器使用已存入authorized_keys 中的公钥进行验签，若验签通过则客户端身份认证成功 ","title":"ssh 连接过程","type":"posts"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/categories/tech/","section":"Categories","summary":"","title":"Tech","type":"categories"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/categories/tool/","section":"Categories","summary":"","title":"Tool","type":"categories"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/tool/","section":"Tags","summary":"","title":"Tool","type":"tags"},{"content":" ubuntu20.04安装部分软件 # 安装docker # 1、卸载可能存在的或者为安装成功的Docker版本 # sudo apt-``get` `remove docker docker-engine docker-ce docker.io 2、添加阿里云的GPG密钥 # curl -fsSL http:``//mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add - 3、使用以下命令设置存储库 # sudo add-apt-repository ``\u0026#34;deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable\u0026#34; 4、安装最新版本的Docker # sudo apt-``get` `update sudo apt-``get` `install docker-ce docker-ce-cli containerd.io docker-compose-plugin 5、验证Docker是否安装成功 # -- 查看docker 版本 docker version 二、安装postgresql # 1、Add Postgre SQL 13 repository # sudo apt update sudo apt install curl gpg gnupg2 software-properties-common apt-transport-https lsb-release ca-certificates add the APT repository，importing GPG key # curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc|sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg add repository contents # echo \u0026#34;deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main\u0026#34; |sudo tee /etc/apt/sources.list.d/pgdg.list 2、Install PostgreSQL 13 # sudo apt update sudo apt install postgresql-13 postgresql-client-13 systemctl status postgresql@13-main.service 3、Configure remote Connection # sudo vi /etc/postgresql/13/main/postgresql.conf # Listen on all interfaces listen_addresses = \u0026#39;*\u0026#39; # Listen on specified private IP address listen_addresses = \u0026#39;192.168.10.11\u0026#39; sudo vi /etc/postgresql/13/main/pg_hba.conf # Accept from anywhere host all all 0.0.0.0/0 md5 # postgres用户通过密码登录 host all postgres trust # Accept from trusted subnet host all all 10.10.10.0/24 md5 sudo systemctl restart postgresql 4、修改默认postgres用户的密码 # # 进入psql sudo -u postgres psql 修改密码 \\password postgres 并输入密码dizai123456 docker安装rabbitMQ:3.8.9 # 1、创建需要映射的目录 # mkdir -p /home/sjcl/docker_file/rabbitmq3.8.9/{data,conf,log} # 记得文件授权！ chmod -R 777 /home/sjcl/docker_file/rabbitmq3.8.9/{data,conf,log} 2、下载并启动 # docker run -d -it --privileged=true --name rabbitmq3.8.9 -p 5672:5672 -p 15672:15672 -v /home/sjcl/docker_file/rabbitmq3.8.9/data:/var/lib/rabbiitmq -v /home/sjcl/docker_file/rabbitmq3.8.9/conf:/etc/rabbitmq -v /home/sjcl/docker_file/rabbitmq3.8.9/log:/var/log/rabbitmq -e RABBITMQ_DEFAULT_USER=dizai -e RABBITMQ_DEFAULT_PASS=123456 rabbitmq:3.8.9-management 3、开启rabbitmq_management插件 # # 进入容器中 docker exec -it \u0026lt;容器id\u0026gt; /bin/bash # 开启插件，则UI可访问 rabbitmq-plugins enable rabbitmq_management docker安装geoserver:2.18.0 # 1、创建需要映射的目录 # mkdir /home/sjcl/docker_file/geoserver2.18.0/data chmod 777 /home/sjcl/docker_file/geoserver2.18.0/data 2、下载并启动 # docker run -d --name geoserver2.18.0 -p 48080:8080 -v /home/sjcl/docker_file/geoserver2.18.0/data:/opt/geoserver/data_dir -e GEOSERVER_DATA_DIR=/opt/geoserver/data_dir -e GEOSERVER_ADMIN_PASSWORD=dizai123456 kartoza/geoserver:2.18.0 apt # 配置目录：/etc/apt\n软件源设置文件：/etc/apt/sources.list（设置阿里源、清华源等）\n安装mysql # 链接：https://developer.aliyun.com/article/758177\nsudo apt update sudo apt install mysql-server sudo systemctl status mysql # 运行初始化安全脚本 sudo mysql_secure_installation 安装nginx # sudo apt update apt install nginx 安装redis # sudo apt update sudo apt install redis-server 配置远程访问，修改/etc/redis/redis.conf\n注释bind 127.0.0.1\n设置 protect-mode=no（不推荐）或者添加密码\n安装samba # sudo apt-get update sudo apt-get upgrade sudo apt-get install samba samba-common 配置使用\n# 创建要共享的目录并赋予权限 mkdir ~/shared sudo chmod 777 ~/shared # 添加samba用户(必须是系统已有用户)，并输入密码 sudo smbpasswd -a \u0026lt;用户名\u0026gt; # 修改samba的配置文件，添加共享目录的设置 sudo vi /etc/samba/smb.conf # 增添以下内容： [shared] comment = share folder browseable = yes path = ~/shared valid users = \u0026lt;your registerd name\u0026gt; public = yes available = yes writable = yes # 重启smbd服务 sudo systemctl restart smbd.service windows访问：win+R输入：\\\\ip 然后回车输入用户名密码\n","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tech/ubuntu20.04%E8%BD%AF%E4%BB%B6/","section":"博客","summary":"ubuntu20.04安装部分软件 # 安装docker # 1、卸载可能存在的或者为安装成功的Docker版本 # sudo apt-``get` `remove docker docker-engine docker-ce docker.io 2、添加阿里云的GPG密钥 # curl -fsSL http:``//mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add - 3、使用以下命令设置存储库 # sudo add-apt-repository ``\"deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable\" 4、安装最新版本的Docker # sudo apt-``get` `update sudo apt-``get` `install docker-ce docker-ce-cli containerd.io docker-compose-plugin 5、验证Docker是否安装成功 # -- 查看docker 版本 docker version 二、安装postgresql # 1、Add Postgre SQL 13 repository # sudo apt update sudo apt install curl gpg gnupg2 software-properties-common apt-transport-https lsb-release ca-certificates add the APT repository，importing GPG key # curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc|sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg add repository contents # echo \"deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main\" |sudo tee /etc/apt/sources.list.d/pgdg.list 2、Install PostgreSQL 13 # sudo apt update sudo apt install postgresql-13 postgresql-client-13 systemctl status postgresql@13-main.service 3、Configure remote Connection # sudo vi /etc/postgresql/13/main/postgresql.conf # Listen on all interfaces listen_addresses = '*' # Listen on specified private IP address listen_addresses = '192.168.10.11' sudo vi /etc/postgresql/13/main/pg_hba.conf # Accept from anywhere host all all 0.0.0.0/0 md5 # postgres用户通过密码登录 host all postgres trust # Accept from trusted subnet host all all 10.10.10.0/24 md5 sudo systemctl restart postgresql 4、修改默认postgres用户的密码 # # 进入psql sudo -u postgres psql 修改密码 \\password postgres 并输入密码dizai123456 docker安装rabbitMQ:3.8.9 # 1、创建需要映射的目录 # mkdir -p /home/sjcl/docker_file/rabbitmq3.8.9/{data,conf,log} # 记得文件授权！ chmod -R 777 /home/sjcl/docker_file/rabbitmq3.8.9/{data,conf,log} 2、下载并启动 # docker run -d -it --privileged=true --name rabbitmq3.8.9 -p 5672:5672 -p 15672:15672 -v /home/sjcl/docker_file/rabbitmq3.8.9/data:/var/lib/rabbiitmq -v /home/sjcl/docker_file/rabbitmq3.8.9/conf:/etc/rabbitmq -v /home/sjcl/docker_file/rabbitmq3.8.9/log:/var/log/rabbitmq -e RABBITMQ_DEFAULT_USER=dizai -e RABBITMQ_DEFAULT_PASS=123456 rabbitmq:3.8.9-management 3、开启rabbitmq_management插件 # # 进入容器中 docker exec -it \u003c容器id\u003e /bin/bash # 开启插件，则UI可访问 rabbitmq-plugins enable rabbitmq_management docker安装geoserver:2.18.0 # 1、创建需要映射的目录 # mkdir /home/sjcl/docker_file/geoserver2.18.0/data chmod 777 /home/sjcl/docker_file/geoserver2.18.0/data 2、下载并启动 # docker run -d --name geoserver2.18.0 -p 48080:8080 -v /home/sjcl/docker_file/geoserver2.18.0/data:/opt/geoserver/data_dir -e GEOSERVER_DATA_DIR=/opt/geoserver/data_dir -e GEOSERVER_ADMIN_PASSWORD=dizai123456 kartoza/geoserver:2.18.0 apt # 配置目录：/etc/apt\n","title":"ubuntu 上安装部分开发软件","type":"posts"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/windows/","section":"Tags","summary":"","title":"Windows","type":"tags"},{"content":"","date":"2025-01-08","externalUrl":null,"permalink":"/zh/tags/wire/","section":"Tags","summary":"","title":"Wire","type":"tags"},{"content":"博客专栏收纳已有 Markdown 文档和后续日常技术沉淀。原有文章的分类和标签会继续保留，用于检索和聚合。\n","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/","section":"博客","summary":"博客专栏收纳已有 Markdown 文档和后续日常技术沉淀。原有文章的分类和标签会继续保留，用于检索和聚合。\n","title":"博客","type":"posts"},{"content":" Hexo # framework based on node.js\n官网： https://hexo.io\ndoc：https://hexo.io/docs/\n简单教程： 【bilibii】 https://b23.tv/xyiGVIV\n安装 # npm install -g hexo-cli 创建自己的blog项目 # hexo init blog 博客文件存放路径 # source/_posts，将自己平时的md文件放入其中即可\n设置主题 # 默认的主题比较单调，可官网查找合适主题安装\ndoc： https://hexo.io/themes/\n主题存放路径 # themes/\n使主题生效 # 修改_config.yml文件，设置theme为你的主题名称（下载在themes目录下的目录名，具体看主题使用说明）\n例如ayer主题的使用说明\nhttps://github.com/Shen-Yu/hexo-theme-ayer\n","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tool/hexo/","section":"博客","summary":"Hexo # framework based on node.js\n官网： https://hexo.io\ndoc：https://hexo.io/docs/\n简单教程： 【bilibii】 https://b23.tv/xyiGVIV\n安装 # npm install -g hexo-cli 创建自己的blog项目 # hexo init blog 博客文件存放路径 # source/_posts，将自己平时的md文件放入其中即可\n","title":"博客建设网站之 hexo","type":"posts"},{"content":" Conventional Commits：让您的Git历史更加清晰和有意义 # 引言 # 在软件开发中，良好的版本控制实践是维持项目健康和可持续发展的关键。一个清晰、一致的提交历史不仅可以加强团队合作，还能提高代码的可维护性。本文将介绍 Conventional Commits 规范，一种帮助您实现这一目标的提交信息格式规范。\n什么是 Conventional Commits？ # Conventional Commits 是一个轻量级的、社区驱动的提交信息格式规范。它的核心目的是使提交信息更加可读和易于理解。遵循这一规范，可以让您的 Git 历史成为一个清晰的故事，而不仅仅是代码的变更记录。\n核心要点 # 清晰的类型定义：规定了一系列预定义的提交类型，如 feat, fix, docs, 等，每种类型对应不同的代码更改目的。 可选的范围：允许在提交类型后指定影响范围，增加了额外的上下文信息。 描述性的消息：鼓励编写简短且具有描述性的信息，概括提交的主要内容。 如何使用 Conventional Commits？ # 使用 Conventional Commits 的基本格式如下：\nmarkdownCopy code \u0026lt;type\u0026gt;[optional scope]: \u0026lt;description\u0026gt; [optional body] [optional footer] 提交类型（Types） # feat：新功能 fix：修复 Bug docs：文档更新 style：代码样式调整（不影响代码运行） refactor：代码重构 perf：性能提升 test：测试相关 chore：日常琐事（如依赖管理） 示例 # gitCopy code feat(auth): 添加 JWT 认证支持 - 实现 JWT 生成和验证 - 更新认证中间件以支持 JWT 关闭问题 #123 在这个示例中，feat 表明这是一个添加新功能的提交，auth 是这次更改的范围，后面紧跟着的是对提交内容的简短描述。接着是一个更详细的解释，最后是相关问题链接。\nConventional Commits 的好处 # 提高可读性：清晰的提交历史使新团队成员更容易理解项目进展。 自动化工具友好：可以被用于自动化生成变更日志和版本控制。 改善协作流程：明确的提交类型和格式有助于代码审查和团队协作。 结语 # Conventional Commits 规范为软件开发提供了一种简单而高效的提交历史管理方法。它的简洁性和自解释性使得项目维护变得更加容易。采纳这一规范，将为您的项目带来长期的好处。\n","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tool/git-commits-specification/","section":"博客","summary":"Conventional Commits：让您的Git历史更加清晰和有意义 # 引言 # 在软件开发中，良好的版本控制实践是维持项目健康和可持续发展的关键。一个清晰、一致的提交历史不仅可以加强团队合作，还能提高代码的可维护性。本文将介绍 Conventional Commits 规范，一种帮助您实现这一目标的提交信息格式规范。\n","title":"代码提交规范之 Conventional Commits","type":"posts"},{"content":" 引言 # 在软件开发中，良好的版本控制实践是维持项目健康和可持续发展的关键。一个清晰、一致的提交历史不仅可以加强团队合作，还能提高代码的可维护性。本文将介绍 Conventional Commits 规范，一种帮助您实现这一目标的提交信息格式规范。\n什么是 Conventional Commits？ # Conventional Commits 是一个轻量级的、社区驱动的提交信息格式规范。它的核心目的是使提交信息更加可读和易于理解。遵循这一规范，可以让您的 Git 历史成为一个清晰的故事，而不仅仅是代码的变更记录。\n可参考链接：https://www.conventionalcommits.org\n核心要点 # 清晰的类型定义：规定了一系列预定义的提交类型，如 feat, fix, docs, 等，每种类型对应不同的代码更改目的。 可选的范围：允许在提交类型后指定影响范围，增加了额外的上下文信息。 描述性的消息：鼓励编写简短且具有描述性的信息，概括提交的主要内容。 如何使用 Conventional Commits？ # 使用 Conventional Commits 的提交注释基本格式如下：\n\u0026lt;type\u0026gt;(\u0026lt;scope\u0026gt;): \u0026lt;description\u0026gt; \u0026lt;body\u0026gt; \u0026lt;footer\u0026gt; 格式描述如下：\n\u0026lt;type\u0026gt;：提交类型，用于说明本次提交的类别，常见类型包括： feat：新功能 fix：修复 Bug docs：文档更新 style：代码风格样式调整（不影响代码运行） refactor：代码重构 perf：性能提升（performance的缩写） test：测试相关 chore：其他无关紧要的改动（单词是琐事的意思） (\u0026lt;scope\u0026gt;)：可选，用于进一步细化提交类型，例如 feat(login) 表示登录功能的新功能。 \u0026lt;subject\u0026gt;：简短描述本次提交的主要内容，建议不超过 50 个字符。 \u0026lt;body\u0026gt;：可选，提供更详细的提交描述，可以包含以下内容： 修改原因 修改内容 影响范围 相关测试 \u0026lt;footer\u0026gt;：可选，包含额外的元信息，例如： 关联的 Jira 问题编号 提交者 评审者 示例 # feat(auth): 添加 JWT 认证支持 - 实现 JWT 生成和验证 - 更新认证中间件以支持 JWT 关闭问题 #123 在这个示例中， feat 表明这是一个添加新功能的提交，auth 是这次更改的范围，后面紧跟着的description是对提交内容的简短描述。 接着body是一个更详细的解释。 最后footer是相关问题的结果。\nConventional Commits 的好处 # 提高可读性：清晰的提交历史使新团队成员更容易理解项目进展。 自动化工具友好：可以被用于自动化生成变更日志和版本控制。 改善协作流程：明确的提交类型和格式有助于代码审查和团队协作。 结语 # Conventional Commits 规范为软件开发提供了一种简单而高效的提交历史管理方法。它的简洁性和自解释性使得项目维护变得更加容易。采纳这一规范，将为您的项目带来长期的好处。\n","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tool/git%E4%BB%A3%E7%A0%81%E6%8F%90%E4%BA%A4%E6%B3%A8%E9%87%8A%E8%A7%84%E8%8C%83/","section":"博客","summary":"引言 # 在软件开发中，良好的版本控制实践是维持项目健康和可持续发展的关键。一个清晰、一致的提交历史不仅可以加强团队合作，还能提高代码的可维护性。本文将介绍 Conventional Commits 规范，一种帮助您实现这一目标的提交信息格式规范。\n什么是 Conventional Commits？ # Conventional Commits 是一个轻量级的、社区驱动的提交信息格式规范。它的核心目的是使提交信息更加可读和易于理解。遵循这一规范，可以让您的 Git 历史成为一个清晰的故事，而不仅仅是代码的变更记录。\n","title":"代码提交规范之 Conventional Commits","type":"posts"},{"content":" Elastic Code # 一枚乐享求知的全栈攻城狮。主攻服务端技术栈、系统架构设计、DevOps，喜欢折腾有趣的项目和工具，目前处于技能成长、知识体系沉淀中。\n关注方向 # 服务端工程：关注后端服务设计、接口抽象、并发处理、数据一致性和工程可维护性。 系统架构设计：关注模块边界、服务拆分、网关、注册发现、链路追踪和监控体系。 DevOps：关注容器化部署、CI/CD、服务运维、日志观测和自动化工具链。 多语言后端开发：围绕 Go、Node.js、Python、Java 构建不同场景下的后端服务。 AI 辅助研发：探索 AI 编程、知识整理、自动化调研和个人研发工作流。 技术栈 # 基础设施与中间件 # MySQL、PostgreSQL、MongoDB、Redis、RabbitMQ、Docker、Nginx、MinIO、ELK、gRPC、Spring Cloud Gateway、Etcd、FastDFS、OpenTelemetry、Prometheus + Grafana。\nGo # Gin、Go-zero、Gorm、Samberlo、Wire、Viper、Zap、Vault、Kratos。\nNode.js # Midway、Express、Koa、Egg、Nest、TypeORM、Mongoose、Lodash、PM2。\nPython # Django、Django REST framework、Flask、SQLAlchemy、Celery、uWSGI、Gunicorn、Supervisor。\nJava # Spring Boot、MyBatis Plus、Starter、Spring Cloud Gateway。\nDevOps 与工程工具 # Jenkins、wrk、Swagger、ApiFox、JetBrains Family、VS Code、Sublime、Notion、Typora、飞书、语雀、VMware Fusion、Wireshark。\nAI 与效率工具 # ChatGPT Desktop、Codex、AI 编程辅助、提示词工作流、技术资料检索、自动化信息整理、AI 工具集成探索。更关注如何把 AI 融入日常研发、文档沉淀和问题定位流程，而不是停留在单次问答。\n项目与经验摘要 # 统一接口代理服务平台 # 一个面向接口服务管理和调用的统一代理平台，围绕接口注册、鉴权、调用、统计、计费、网关路由和多语言 SDK 展开。关注点包括签名认证、防重放、服务拆分、网关治理和跨语言调用体验。\nGin 框架二次开发模板 # 基于 Gin 的后端初始化模板，整合路由、认证、响应封装、数据库、缓存、消息队列、定时任务、gRPC、SSE、WebSocket、多环境配置和依赖注入，目标是快速启动结构清晰的后端项目。\n行业平台项目经验 # 参与过数字孪生、设备监测、工单调度、空间数据分析、BI 可视化、轨迹分析等类型的平台建设。主要关注数据模型抽象、权限设计、消息推送、异步处理、空间查询优化、系统部署和跨端协作。\n技术空间 # 邮箱：hzreal0823@outlook.com 个人技术空间：www.elastic-code.com（维护中） CSDN 博客：https://blog.csdn.net/qq_38269333?type=blog GitHub Organization：https://github.com/code-elastic GitHub Pages：https://hzreal.github.io 工作方式 # 习惯优先阅读官方文档和源码资料，结合搜索与 AI 工具快速定位问题。重视业务场景、模块边界、代码质量、提交规范和可维护性；也关注自动化、工程化和知识体系建设，希望持续把实践经验沉淀成可复用的工具、文档和项目。\n","date":"2025-01-08","externalUrl":null,"permalink":"/zh/profile/","section":"个人中心","summary":"Elastic Code # 一枚乐享求知的全栈攻城狮。主攻服务端技术栈、系统架构设计、DevOps，喜欢折腾有趣的项目和工具，目前处于技能成长、知识体系沉淀中。\n关注方向 # 服务端工程：关注后端服务设计、接口抽象、并发处理、数据一致性和工程可维护性。 系统架构设计：关注模块边界、服务拆分、网关、注册发现、链路追踪和监控体系。 DevOps：关注容器化部署、CI/CD、服务运维、日志观测和自动化工具链。 多语言后端开发：围绕 Go、Node.js、Python、Java 构建不同场景下的后端服务。 AI 辅助研发：探索 AI 编程、知识整理、自动化调研和个人研发工作流。 技术栈 # 基础设施与中间件 # MySQL、PostgreSQL、MongoDB、Redis、RabbitMQ、Docker、Nginx、MinIO、ELK、gRPC、Spring Cloud Gateway、Etcd、FastDFS、OpenTelemetry、Prometheus + Grafana。\n","title":"个人中心","type":"profile"},{"content":" 缓存 # # 简介 # 简而言之，即是数据存储的缓冲区。使用缓存之后，可以减轻访问数据库的压力，显著的提升系统的性能。\n使用缓存的一般流程如下：\n缓存通常有两种：\n服务器主集本身的内存缓存，也就是我们说的二级缓存。 相比于缓存中间件性能更好。但只能应用于本机，若系统分布式部署，会存在数据不一致的问题。 缓存中间件，比如：Redis、Memcached等 适用于分布式缓存，解决分布式数据一致性问题 有些业务场景，分布式缓存和二级缓存可以一起使用\n缓存带来的问题 # 1. 缓存雪崩 # 缓存雪崩指的是当某一个时间段出现大规模的缓存失效的情况，此时大量的并发请求直接命中在数据库上面，导致数据库压力巨大，甚至宕机。\n分析\n造成缓存雪崩的关键在于在同一时间段大量的key失效。出现这个问题的可能性：1. 缓存宕机。2. 采用了相同的过期时间。\n解决方式\n过期时间采用随机值 若真的发生了缓存雪崩，使用熔断机制。当流量到达一定阈值，直接返回“系统拥挤”之类的提示，防止过多的请求打在数据库上。至少能保证一部分用户是可以正常使用，其他用户多刷新几次也能得到结果。 提高数据库的容灾能力，可以使用分库分表，读写分离的策略。 防止Redis缓存宕机导致缓存雪崩的问题，可以搭建Redis集群，提高Redis的容灾性。 2. 缓存击穿 # 缓存击穿指的是大量并发集中访问某一个 key，突然这个 Key 缓存失效了，导致大并发一时间全部命中在数据库上。\n与缓存雪崩对比\n缓存雪崩是大量的 key 失效导致的 而缓存击穿是大规模访问一个失效的 key 导致的 分析\n关键在于某个热点 key 失效了，导致大并发集中打在数据库上。\n所以要从两个方面解决，第一是否可以考虑热点key不设置过期时间，第二是否可以考虑降低打在数据库上的请求数量。\n解决方式\n若业务允许，对热点 key 可以设置永不过期 使用互斥锁。当缓存失效时，只有拿到锁才可以查询数据库，降低了在同一时刻命中在数据库上的请求。 可以对 Key 进行加锁 相应的，加锁会导致系统性能降低。 3. 缓存穿透 # 假如请求的 key 是在缓存中是不存在的，那缓存查不到就会去数据库查询。如果有大量这样的请求，这些请求像“穿透”了缓存一样直接命中在数据库上，这种现象就叫做缓存穿透。\n分析\n关键点是缓存中查不到 Key，这些 key 是不存在的\nPS：说明了边界安全的重要性。应该做好参数检验，外界不可信\n解决方式\n把无效的Key存入缓存。如果缓存查不到，数据库也查不到，可以把这个 Key 值存入缓存，并设置value=\u0026ldquo;null\u0026rdquo;，当下次再通过这个Key 查询时就不需要再查询数据库。\n弊端是，假如传进来的这个不存在的Key值每次都是随机的，那这种做法无意义。 使用布隆过滤器。布隆过滤器的作用是某个 key 不存在，那么就一定不存在，它说某个 key 存在，那么很大可能是存在(存在一定的误判率)。因此，可以在缓存之前再加一层布隆过滤器，在查询的时候先去布隆过滤器查询 key 是否存在，如果不存在就直接返回。\n参考：https://cloud.tencent.com/developer/article/1687510 ","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tech/cache/","section":"博客","summary":"缓存 # # 简介 # 简而言之，即是数据存储的缓冲区。使用缓存之后，可以减轻访问数据库的压力，显著的提升系统的性能。\n","title":"缓存简介","type":"posts"},{"content":"这里是 Elastic Code 的个人技术空间。\n我会在这里持续整理服务端工程、系统架构、DevOps、AI 辅助研发和工具链实践。首页保留为欢迎和导览区域，后续可以承载链接、图片、短视频和精选内容。\n个人中心 技术文档 浏览博客 项目专栏 ","date":"2025-01-08","externalUrl":null,"permalink":"/zh/","section":"欢迎来到 Elastic Code","summary":"这里是 Elastic Code 的个人技术空间。\n我会在这里持续整理服务端工程、系统架构、DevOps、AI 辅助研发和工具链实践。首页保留为欢迎和导览区域，后续可以承载链接、图片、短视频和精选内容。\n个人中心 技术文档 浏览博客 项目专栏 ","title":"欢迎来到 Elastic Code","type":"page"},{"content":" PM2 # 参考：https://mp.weixin.qq.com/s/S8Gw2XzuflnN2QMSVXhLhg\n场景 # node 应用跑的时候突然抛了个错，崩溃了，是不是需要重新跑起来？这时候是不是就需要另一个进程来自动做重启这件事情？ node 应用的日志默认输出在控制台，如果想输出到不同的日志文件，是不是可以让另一个进程获取 node 应用的输出，然后写文件来实现？ node 是单线程的，而机器是多个 cpu 的，为了充分利用 cpu 的能力，我们会用多个进程来跑 node 应用，来提高性能。这种通用逻辑是不是也可以放到一个单独进程里来实现？ node 运行时的 cpu、内存等资源的占用，是不是需要监控？这时候是不是可以让另一个进程来做？ 线上的 node 应用不只是跑起来就行了，还要做自动重启、日志、多进程、监控这些事情。\n简介 # pm2 是 process manager，进程管理，它是第二个大版本，和前一个版本差异很大，所以叫 pm2.\npm2 的主要功能就是进程管理、日志管理、负载均衡、性能监控这些。\n安装 # npm install -g pm2 基本命令 # pm2 start main.js pm2 start \u0026lt;pid\u0026gt; pm2 stop main.js pm2 logs pm2 log \u0026lt;pid\u0026gt; PM2 start 参数 # -i num 就是启动 num 个进程做负载均衡\npm2 start app.js -i max 根据CPU核数启动进程的个数\n动态调整进程数 # pm2 scale main 3 把集群调整为 3 个进程\npm2 scale main +3 把集群增加3 个进程\n性能监控 # 可以看到不同进程的 cpu 和内存占用情况\npm2 monit\nPM2配置文件 # PM2管理前端 # 配置文件：\n{ \u0026#34;apps\u0026#34;: [ { \u0026#34;script\u0026#34;: \u0026#34;serve\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;chilinnew\u0026#34;, \u0026#34;namespace\u0026#34;: \u0026#34;szkj\u0026#34;, \u0026#34;env\u0026#34;: { \u0026#34;PM2_SERVE_PATH\u0026#34;: \u0026#34;./dist\u0026#34;, \u0026#34;PM2_SERVE_PORT\u0026#34;: 8080 } } ] } PM2管理后端\n配置文件：\n# midway 框架 { \u0026#34;apps\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;sjcl-master\u0026#34;, \u0026#34;namespace\u0026#34;: \u0026#34;szkj\u0026#34;, \u0026#34;script\u0026#34;: \u0026#34;./bootstrap.js\u0026#34;, \u0026#34;error_file\u0026#34;: \u0026#34;~/logs/sjcl-master/err.log\u0026#34;, \u0026#34;out_file\u0026#34;: \u0026#34;~/logs/sjcl-master/out.log\u0026#34;, \u0026#34;merge_logs\u0026#34;: true, \u0026#34;env\u0026#34;: { \u0026#34;NODE_ENV\u0026#34;: \u0026#34;production\u0026#34;, \u0026#34;MIDWAY_SERVER_ENV\u0026#34;: \u0026#34;production\u0026#34; } } ] } # nestJS ","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tech/pm2/","section":"博客","summary":"PM2 # 参考：https://mp.weixin.qq.com/s/S8Gw2XzuflnN2QMSVXhLhg\n场景 # node 应用跑的时候突然抛了个错，崩溃了，是不是需要重新跑起来？这时候是不是就需要另一个进程来自动做重启这件事情？ node 应用的日志默认输出在控制台，如果想输出到不同的日志文件，是不是可以让另一个进程获取 node 应用的输出，然后写文件来实现？ node 是单线程的，而机器是多个 cpu 的，为了充分利用 cpu 的能力，我们会用多个进程来跑 node 应用，来提高性能。这种通用逻辑是不是也可以放到一个单独进程里来实现？ node 运行时的 cpu、内存等资源的占用，是不是需要监控？这时候是不是可以让另一个进程来做？ 线上的 node 应用不只是跑起来就行了，还要做自动重启、日志、多进程、监控这些事情。\n","title":"进程管理工具之 pm2","type":"posts"},{"content":" 系统环境 # 环境 # python go node git、svn 编辑器 # pycharm pro、webstome、goland、IntelliJ IDEA、 vscode typora sublime text 终端 # Xshell git bash WSL、window terminal 工具 # 谷歌浏览器 VMware chatGPT postman、apifox、easydoc GripData、navicate、RedisInsight、mongosh、compass、pgAdmin、neo4j Desktop 百度网盘、阿里云盘 bandzip ide-eval-resetter、navicat cracker fileZilla、Xftp7 wireshark UML、亿图图示 utools microsoft To Do 配置 # jetbrains全家桶编辑器配置 vscode编辑器配置 镜像 # ubuntu-20.04-live-server 其他 # WPS 有道 geek.exe、软媒全家桶exe snipaste wechat、Tim ToDesk、向日葵 KMPlayer 支付宝开放平台开发助手 vnc Viewer Linux环境 # python node redis、mysql、postgre、mongodb、elasticsearch、clickhouse kafka、rabbitMQ git、 nginx、gunicorn、uWSGI ssh-server docker、portainer.io supervisor、pm2 jenkins vmware vsphere ","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tool/environment/","section":"博客","summary":"系统环境 # 环境 # python go node git、svn 编辑器 # pycharm pro、webstome、goland、IntelliJ IDEA、 vscode typora sublime text 终端 # Xshell git bash WSL、window terminal 工具 # 谷歌浏览器 VMware chatGPT postman、apifox、easydoc GripData、navicate、RedisInsight、mongosh、compass、pgAdmin、neo4j Desktop 百度网盘、阿里云盘 bandzip ide-eval-resetter、navicat cracker fileZilla、Xftp7 wireshark UML、亿图图示 utools microsoft To Do 配置 # jetbrains全家桶编辑器配置 vscode编辑器配置 镜像 # ubuntu-20.04-live-server 其他 # WPS 有道 geek.exe、软媒全家桶exe snipaste wechat、Tim ToDesk、向日葵 KMPlayer 支付宝开放平台开发助手 vnc Viewer Linux环境 # python node redis、mysql、postgre、mongodb、elasticsearch、clickhouse kafka、rabbitMQ git、 nginx、gunicorn、uWSGI ssh-server docker、portainer.io supervisor、pm2 jenkins vmware vsphere ","title":"开发环境","type":"posts"},{"content":" discuz!Q # 简介 # 快速搭建个人社区\ndoc： https://discuz.com/\napi： https://developer.discuz.chat/#/api/get:_api_v3_check.user.get.redpacket\n安装 # linux下，推荐docker安装 https://discuz.com/docs/Linux%20%E4%B8%BB%E6%9C%BA.html\n安装启动容器命令 # docker run -d --restart=always -p 20080:80 -p 20443:443 -v ~/discuzQ/data/discuz:/var/lib/discuz -v ~/discuzQ/data/mysql-data:/var/lib/mysqldb -v ~/discuzQ/data/certs:/etc/nginx/certs ccr.ccs.tencentyun.com/discuzq/dzq:latest 初始化安装 Discuz! Q # 访问 http://ip:port/install 并配置网站相关信息。\n配置mysql ip 用户名密码 配置后台用户名密码admin admin123456 使用 # 安装后，访问即可，一个社区就出现啦！可以注册咯\nhttp://ip:20080/\nhttps://ip:20443/\n后台站点访问，用户名admin密码admin123456\nhttp://ip:20080/admin\nhttps://ip:20443/admin/\n","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tool/discuzq/","section":"博客","summary":"discuz!Q # 简介 # 快速搭建个人社区\ndoc： https://discuz.com/\n","title":"社区网站之 discuzQ","type":"posts"},{"content":" postgreSQL # 安装搭建 # https://www.postgresqltutorial.com/postgresql-getting-started/install-postgresql-linux/\n指定版本安装\nsudo apt-get install postgresql-13 配置 # 路径 # ubuntu下：\n/etc/postgresql//main\ncentOS下：\n/var/lib/pgsql/13/data/postgresql.conf postgres.conf # pg数据库配置文件\n包含设置data/hba/indet等目录，IP/port，最大连接数，认证，ssl，内存，磁盘等等\npg_hba.conf # 客户端认证配置文件即host-based authentication：基于主机的认证\n多种连接pg数据库的方式\npg_ident.conf # 用户名称映射，可添加映射用户\n用户名密码 # 设置可远程访问 # 启动关闭 # systemctl start systemctl stop 访问 # 切换用户\nsudo -i -u postgres 登入\npsql -h 192.168.1.7 -p 5432 -U postgres -d testdb -W 密码 基本操作 # 数据库操作 # 1、列举数据库：\\l 2、选择数据库：\\c 数据库名 3、查看该某个库中的所有表：\\dt 4、切换数据库：\\c db_name 5、查看某个库中的某个表结构：\\d 表名 6、查看某个库中某个表的记录：select * from apps limit 1; 7、显示字符集：\\encoding 8、退出psgl：\\q\n表、库操作 # 查询 # 基本CURD # insert update set delete from 条件查询 # and or not in not in between and like group having order by 聚合 # 关联查询 # inner join / join left join right join full outer join 全外连接 cross join 交叉连接 （笛卡尔积） Union查询 # union\n用于组合两个或多个SELECT语句的结果，而不返回任何重复的行 要使用UNION，每个SELECT必须具有相同的列数，相同数量的列表达式，相同的数据类型，并且具有相同的顺序，但不一定要相同。 SELECT column1 [, column2 ] FROM table1 [, table2 ] [WHERE condition] UNION SELECT column1 [, column2 ] FROM table1 [, table2 ] [WHERE condition] UNION ALL\n运算符用于组合两个SELECT语句(包括重复行)的结果。 适用于UNION的相同规则也适用于UNION ALL运算符 SELECT column1 [, column2 ] FROM table1 [, table2 ] [WHERE condition] UNION ALL SELECT column1 [, column2 ] FROM table1 [, table2 ] [WHERE condition] 窗口函数 # https://zhuanlan.zhihu.com/p/409561328\n表达式：func(field) over(partition by field, order by field, rows between 3 preceding and current row)\nfunc有多种：\n排序类函数 ROW_NUMBER(field) 每一行记录生成一个序号，依次排序且不会重复。 123456\u0026hellip; RANK(field) 跳跃排序，生成的序号有可能不连续。1114557.. DENSE_RANK(field) 在生成序号时是连续的。1112234444\u0026hellip; ntile(n) 用于将分组数据按照顺序切分成n片，返回当前切片值. 聚合类函数 min()、max()、avg()、sum()、count() 如一年中每月的累计和，每四个月的移动平均，每四个月中的最大/小值 偏移分析函数 lead(field, offset, defval) \u0026mdash;\u0026ndash; lead领先，表示当前记录之前（记录更靠后） field：字段名， offset：偏移量，不指定默认是1。 defval：null时的默认值。 当向上偏移了offset行已经超出了表的范围时，lag()函数将defval这个参数值作为函数的返回值，若没有指定默认值，则返回NULL。 lag(field, offset, defval) \u0026mdash;\u0026ndash; lag落后，表示当前记录之后（记录更靠前） percent_rank()、cume_dist() first_val(expr) 、 last_val(expr)、nth_val(expr, n) 有用特殊函数 # coalesce(expression_1, expression_2, \u0026hellip;,expression_n)\n遇到非null值即停止并返回该值。如果所有的表达式都是空值，最终将返回一个空值。\nselect coalesce(real_name ,nick_name,“xiaoai”) from table_name 当real_name不为null，那么无论nick_name是否为null，都将返回real_name的具体值；当real_name为null，而nick_name,不为null的时候，返回nick_name具体值。只有当real_name和nick_name都为null的时候，才返回xiaoai。 \u0026ndash;\n\u0026ndash;\npg_dump工具 # # 数据库备份 pg_dump -h localhost -U postgres \u0026lt;db_name\u0026gt; \u0026gt; ./dump_out.sql psql工具 # 导入sql数据源 psql \u0026#34;host=localhost port=5432 user=postgres password=\u0026lt;password\u0026gt; dbname=\u0026lt;db_name\u0026gt;\u0026#34; -f ./dump_out.sql 日期和时间函数 # doc: https://www.postgresql.org/docs/current/functions-datetime.html\n所有重要的日期和时间相关函数如下列表所示：\n函数 描述 AGE() 减去参数 CURRENT DATE/TIME() 它指定当前日期和时间。 DATE_PART() 获取子字段(相当于提取) EXTRACT() 获得子字段 ISFINITE() 测试有限的日期，时间和间隔(非+/-无穷大) JUSTIFY 调整间隔 重要函数 # date_part(type, timestamp)\ntype的类型如下 century decade year month day hour minute second microseconds milliseconds dow doy epoch isodow isoyear timezone timezone_hour timezone_minute 返回该类型下提取的值 extract(type from timestamp)\ntype类型与上述date_part的类型一样 date_trunc\n用于设置时间基点\nselect date_trunc(\u0026#39;month\u0026#39;,current_date); -- 计算当月第一天 -- 计算上月最后一天 select date_trunc(\u0026#39;month\u0026#39;,current_date) - interval\u0026#39;1 day\u0026#39; --基于当月第一天,倒退1天 -- 计算上月第一天 select date_trunc(\u0026#39;month\u0026#39;,current_date) + interval\u0026#39;1 month - 1 day\u0026#39; -- 基于当月第一天,前进一个月,再倒退1天 to_char(timestamp, format_string)\n时间格式化字符串如下 模式 描述 HH 一天的小时数(01-12) HH12 一天的小时数(01-12) HH24 一天的小时数(00-23) MI 分钟(00-59) SS 秒(00-59) MS 毫秒(000-999) US 微秒(000000-999999) AM 正午标识(大写) Y,YYY 带逗号的年(4和更多位) YYYY 年(4和更多位) YYY 年的后三位 YY 年的后两位 Y 年的最后一位 MONTH 全长大写月份名(空白填充为9字符) Month 全长混合大小写月份名(空白填充为9字符) month 全长小写月份名(空白填充为9字符) MON 大写缩写月份名(3字符) Mon 缩写混合大小写月份名(3字符) mon 小写缩写月份名(3字符) MM 月份号(01-12) DAY 全长大写日期名(空白填充为9字符) Day 全长混合大小写日期名(空白填充为9字符) day 全长小写日期名(空白填充为9字符) DY 缩写大写日期名(3字符) Dy 缩写混合大小写日期名(3字符) dy 缩写小写日期名(3字符) DDD 一年里的日子(001-366) DD 一个月里的日子(01-31) D 一周里的日子(1-7；周日是1) W 一个月里的周数(1-5)(第一周从该月第一天开始) WW 一年里的周数(1-53)(第一周从该年的第一天开始) age(timestamp，timestamp)＆AGE(timestamp)：\n函数 描述 age(timestamp, timestamp) 当使用第二个参数的时间戳形式调用时，age()减去参数，产生使用年数和月份的类型为“interval”的“符号”结果。 age(timestamp) 当仅使用时间戳作为参数调用时，age()从current_date(午夜)减去。 当前DATE/TIME()\n以下是返回与当前日期和时间相关的值的函数的列表。\n函数 描述 CURRENT_DATE 提供当前日期 CURRENT_TIME 提供带时区的值 CURRENT_TIMESTAMP 提供带时区的值 CURRENT_TIME(precision) 可以选择使用precision参数，这将使结果在四分之一秒的范围内四舍五入到数位数。 CURRENT_TIMESTAMP(precision) 可以选择使用精度参数，这将使结果在四分之一秒的范围内四舍五入到数位数。 LOCALTIME 提供没有时区的值。 LOCALTIMESTAMP 提供没有时区的值。 LOCALTIME(precision) 可以选择使用精度参数，这将使结果在四分之一秒的范围内四舍五入到数位数。 LOCALTIMESTAMP(precision) 可以选择使用精度参数，这将使结果在四分之一秒的范围内四舍五入到数位数。 SQL查询计划分析 # 参考：https://blog.csdn.net/weixin_41287260/article/details/124394206\n背景 # SQL慢了的原因？\n查看下对应的查询计划，从而可以快速定位慢在哪里\nexplain语法 # EXPLAIN [ ANALYZE ] [ VERBOSE ] [BUFFERS]\n节点类型 # 控制节点（Control Node) 扫描节点（ScanNode) 物化节点（Materialization Node) 连接节点（Join Node) 扫描节点 \u0026ndash; 简单来说就是为了扫描表的元组，每次获取一条元组（Bitmap Index Scan除外）作为上层节点的输入。 # Seq Scan，顺序扫描 全表顺序扫描，一般查询没有索引的表需要全表顺序扫描 Index Scan，基于索引扫描，但不只是返回索引列的值 索引扫描，主要用来在WHERE 条件中存在索引列时的扫描 IndexOnly Scan，基于索引扫描，并且只返回索引列的值，简称为覆盖索引 覆盖索引扫描，所需的返回结果能被所扫描的索引全部覆盖 BitmapIndex Scan，利用Bitmap 结构扫描 BitmapIndex Scan 与Index Scan 很相似，都是基于索引的扫描，但是BitmapIndex Scan 节点每次执行返回的是一个位图而不是一个元组，其中位图中每位代表了一个扫描到的数据块。 BitmapHeap Scan，把BitmapIndex Scan 返回的Bitmap 结构转换为元组结构 而BitmapHeap Scan一般会作为BitmapIndex Scan 的父节点，将BitmapIndex Scan 返回的位图转换为对应的元组。这样做最大的好处就是把Index Scan 的随机读转换成了按照数据块的物理顺序读取，在数据量比较大的时候，这会大大提升扫描的性能。 Subquery Scan，扫描一个子查询 \u0026hellip; 扫描节点的小结：\n大多数情况下，Index Scan 要比 Seq Scan 快。但是如果获取的结果集占所有数据的比重很大时，这时Index Scan 因为要先扫描索引再读表数据反而不如直接全表扫描来的快。 如果获取的结果集的占比比较小，但是元组数很多时，可能Bitmap Index Scan 的性能要比Index Scan 好。 如果获取的结果集能够被索引覆盖，则Index Only Scan 因为不用去读数据，只扫描索引，性能一般最好。但是如果VM 文件未生成，可能性能就会比Index Scan 要差。 explain输出结果描述 # PS：\n按照查询计划树从底往上执行 基于火山模型（参考文档火山模型介绍）执行，即可以简单理解为每个节点执行返回一行记录给父节点（Bitmap Index Scan 除外） 代价估计信息 # cost 就是该执行节点的代价估计。它的格式是xxx…xxx，在… 之前的是预估的启动代价，即找到符合该节点条件的第一个结果预估所需要的代价，在…之后的是预估的总代价。而父节点的启动代价包含子节点的总代价。(是PostgreSQL 根据周期性收集到的统计信息（参考PostgreSQL · 特性分析 · 统计信息计算方法），按照一个代价估计模型计算而来的) rows 代表预估的行数（根据表的统计信息预估而来的） width 代表预估的结果宽度，单位为字节 (根据表的统计信息预估而来的) 真实执行信息 # actual time 执行时间，格式为xxx…xxx，在… 之前的是该节点实际的启动时间，即找到符合该节点条件的第一个结果实际需要的时间，在…之后的是该节点实际的执行时间 rows 指的是该节点实际的返回行数 loops 指的是该节点实际的重启次数。如果一个计划节点在运行过程中，它的相关参数值（如绑定变量）发生了变化，就需要重新运行这个计划节点。 高级 # 视图 # 视图(VIEW)是一个伪表。 它不是物理表，而是普通表的查询结果，\n可以是单个表的部分行，部分列，也可以是多个表的关联结果\n作用：\n以自然和直观的方式构建数据，并使其易于查找 限制对数据的访问，使得用户只能看到有限的数据而不是完整的数据 归总来自各种表中的数据以生成报表 存储过程 # 存储在数据库上并可以使用SQL界面调用的一组SQL和过程语句(声明，分配，循环，控制流程等)。\n它有助于在数据库中的单个函数中进行多次查询和往返操作。\nCREATE [OR REPLACE] FUNCTION function_name (arguments) RETURNS return_datatype AS $variable_name$ DECLARE declaration; [...] BEGIN \u0026lt; function_body \u0026gt; [...] RETURN { variable_name | value } END; LANGUAGE plpgsql; 触发器 # 一组动作或数据库回调函数，当表上执行某些数据库事件(如INSERT，UPDATE，DELETE或TRUNCATE语句)时，触发器会被触发而自动运行。\n触发器用于验证输入数据，执行业务规则，保持审计跟踪等。\nCREATE TRIGGER trigger_name [BEFORE|AFTER|INSTEAD OF] event_name ON table_name [ -- Trigger logic goes here.... ]; 索引 # 索引类型 # B-tree Hash GiST SP-GiST GIN\n默认情况下，CREATE INDEX命令使用B树索引\nCREATE INDEX index_name ON table_name; 事务 # 性质：\n原子性(Atomicity) 一致性(Consistency) 隔离性(Isolation) 持久性(Durability) 事务控制\nBEGIN TRANSACTION：开始事务。\nCOMMIT：保存更改，或者您可以使用END TRANSACTION命令。\nROLLBACK：回滚更改。\n锁 # 锁或独占锁或写锁阻止用户修改行或整个表。 在UPDATE和DELETE修改的行在事务的持续时间内被自动独占锁定。 这将阻止其他用户更改行，直到事务被提交或回退\n用户必须等待其他用户当他们都尝试修改同一行时。 如果他们修改不同的行，不需要等待。 SELECT查询不必等待。\n数据库自动执行锁定。 然而，在某些情况下，必须手动控制锁定。 手动锁定可以通过使用LOCK命令完成。 它允许指定事务的锁类型和范围。\nLOCK [ TABLE ] name IN lock_mode 死锁 # 当两个事务正在等待彼此完成操作时，可能会发生死锁。 虽然PostgreSQL可以检测到它们并使用ROLLBACK结束，但死锁仍然可能不方便。 为了防止您的应用程序遇到此问题，请确保以这样的方式进行设计，以使其以相同的顺序锁定对象。\n咨询锁 # PostgreSQL提供了创建具有应用程序定义含义的锁的方法。这些称为咨询锁(劝告锁,英文为：advisory locks)。 由于系统不强制使用它，因此应用程序正确使用它们。 咨询锁可用于锁定针对MVCC模型策略。\n例如，咨询锁的常见用途是模拟所谓的“平面文件”数据管理系统的典型的悲观锁定策略。 虽然存储在表中的标志可以用于相同的目的，但是建议锁更快，避免了表的膨胀，并且在会话结束时被服务器自动清除。\n","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tech/postgresql/","section":"博客","summary":"postgreSQL # 安装搭建 # https://www.postgresqltutorial.com/postgresql-getting-started/install-postgresql-linux/\n指定版本安装\n","title":"数据库之 postgresql","type":"posts"},{"content":"一个面向接口服务管理和调用的统一代理平台，关注接口注册、鉴权、防重放、服务拆分、网关治理和多语言 SDK 调用体验。\n查看 GitHub ","date":"2025-01-08","externalUrl":null,"permalink":"/zh/projects/open-api-platform/","section":"项目","summary":"一个面向接口服务管理和调用的统一代理平台，关注接口注册、鉴权、防重放、服务拆分、网关治理和多语言 SDK 调用体验。\n查看 GitHub ","title":"统一接口代理服务平台","type":"projects"},{"content":" Github # github官方文档\nhttps://docs.github.com/cn/search-github/searching-on-github/searching-for-repositories\n操作技巧 # 按s 快速定位到搜索栏 按t 将项目目录树以列全部呈现 按. 将项目在在线vscode中查看 搜索技巧 # 搜索仓库https://docs.github.com/cn/search-github/searching-on-github/searching-for-repositories\n搜索指令如下 # 文档https://docs.github.com/cn/search-github\nin操作进行位置搜索 in:name 在标题中搜 如 golang in:name，表示标题中含有golang的 如python in:name in:description in:readme 组合使用： 逗号分开每个位置 如 vue in:name,description stars、fork stars:\u0026gt;=2000 fork:\u0026lt;5000 组合使用： 空格分开多个条件 高亮代码块进行共享 awesome 获取有关优秀项目、学习资料 awesome 内容 如awesome python 可获取大量关于python各个方面的资源 location、language location指定地区 language指定语言（常用）如language:c 高级搜索界面 # UI界面搜索https://github.com/search/advanced Github API URL # explore\n官方根据你的习惯推荐 链接https://github.com/explore topics\n不同的话题分类 https://github.com/topics 如数据库sql的，https://github.com/topics/sql 如go的，https://github.com/topics/go 如python的, https://github.com/topics/python 还有个很特殊的 awesome https://github.com/topics/awesome 提供优质资源 trending\n搜索相关优质项目，还可以指定今天，本周，本月\nhttps://github.com/trending\n如搜索go https://github.com/trending/go 如搜索python https://github.com/trending/python 如搜索本周sql https://github.com/trending/sql?since=weekly 实用网站 # https://www.libhunt.com/\n追踪github仓库，根据语言、标签筛选项目仓库，仓库热度，开源软件分类并排名的实用网站\nhttps://gitstar-ranking.com/\n用户、组织、仓库排名\nhttps://githuber.cn/\n编程语言统计，星榜，排名等\nhttps://unbug.github.io/codelf/\n变量命名工具，也可找到项目仓库\nhttps://searchcode.com/\n代码搜索（全网搜索，源包括github，google，bitbucket，gitlab等，还可以语言分类）\nff\nff\nff\n","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tech/github/","section":"博客","summary":"Github # github官方文档\nhttps://docs.github.com/cn/search-github/searching-on-github/searching-for-repositories\n操作技巧 # 按s 快速定位到搜索栏 按t 将项目目录树以列全部呈现 按. 将项目在在线vscode中查看 搜索技巧 # 搜索仓库https://docs.github.com/cn/search-github/searching-on-github/searching-for-repositories\n","title":"玩转 Github 搜索技巧","type":"posts"},{"content":" docsify # https://docsify.js.org/#/\n安装\nnpm i docsify-cli -g 搭建项目\ndocsify init ./docs 启动\ndocsify serve .\\docs\\ ","date":"2025-01-08","externalUrl":null,"permalink":"/zh/posts/tool/docsify/","section":"博客","summary":"docsify # https://docsify.js.org/#/\n安装\nnpm i docsify-cli -g 搭建项目\ndocsify init ./docs 启动\ndocsify serve .\\docs\\","title":"文档建设网站之 docify","type":"posts"},{"content":"这里用于展示个人项目、工具模板和实验性实践。每个项目可以用卡片展示简介、标签和外部链接。\n","date":"2025-01-08","externalUrl":null,"permalink":"/zh/projects/","section":"项目","summary":"这里用于展示个人项目、工具模板和实验性实践。每个项目可以用卡片展示简介、标签和外部链接。\n","title":"项目","type":"projects"},{"content":"","date":"2024-11-08","externalUrl":null,"permalink":"/zh/tags/ioc/","section":"Tags","summary":"","title":"IoC","type":"tags"},{"content":" 控制反转 IoC 与依赖注入 DI # 引入 # 讨论之前，先来简述一下软件对象中的耦合\n在软件开发过程中，对象之间的耦合关系是一个常见的问题。传统的面向对象设计中，对象之间的依赖通常是硬编码在代码中的，这导致了代码的高耦合度和低灵活性。当一个对象依赖于另一个对象时，它通常会直接实例化或调用该对象，这使得两个对象之间的关系非常紧密，难以进行单元测试和代码重用。\n例如，考虑一个电商网站的购物车功能。在购物车中直接实例化商品对象可能会导致购物车模块与商品模块之间的紧密耦合，使得如果需要修改商品类的实现或者替换为其他商品类时，就必须要修改购物车模块的代码。这种紧耦合的设计不仅使代码难以维护和扩展，还降低了代码的可测试性和可重用性。\n将这个例子缩小到类对象之间的调用：对象 A 依赖于对象 B，那么对象 A 在初始化或需要调用 B 的时候，自己必须主动去创建对象 B 或者使用已经创建的对象 B。无论是创建还是使用对象 B，控制权都在自己手上。\n为了解决这一问题，控制反转（IoC）和依赖注入（DI）应运而生。它们提供了一种解耦的方法，通过将对象之间的依赖关系转移到外部容器（loC 容器）或框架中管理，实现了对象之间的松耦合。\n简言之，通过 IoC 与 DI，调用者将依赖对象的创建初始化逻辑从自身抽离解耦出来，调用者只负责声明调用，依赖对象只负责实现自己，而创建依赖对象的控制权交给了“第三方”（IoC容器）\n控制反转 loC # 案例：假设你现在需要与外部客户对接某业务，这个业务随着外部客户的不同，对接流程也不同，于是每个客户，都需要制定相应的业务对接流程，每当有了新的客户，不得不继续制定该客户的业务对接流程。显然，这严重耦合并依赖各个不同客户。最终你受不了了，自己制定一个对接方案，开发一个对接平台，各个客户必须遵守你制定对接方案才能进行业务对接，于是各个客户反过来依赖你的对接标准，反转了控制，倒置了依赖。\n这个过程中，你就是调用者，客户就是依赖对象，你制定的标准、对接平台就是 IoC 容器。从调用者不得不自身一个个实现对接业务到将对接业务解耦到统一标准中的这一过程，可以理解为控制反转\n控制反转是一种思想，其基本思想是：借助于“第三方”实现具有依赖关系的对象之间的解耦。\n依赖注入 DI # 以 python 代码为例\n未使用依赖注入之前：\nclass Dependency: def __init__(self): pass def operation(self): return \u0026#34;Dependency operation\u0026#34; class Client: def __init__(self): pass def do_something(self): # 手动创建 Dependency 对象 dependency = Dependency() return dependency.operation() 此时，若 Dependency 的初始化函数发生变化，Client 中手动创建 Dependency 对象的代码也需要改动！\n使用依赖注入之后：\nclass Dependency: def __init__(self): pass def operation(self): return \u0026#34;Dependency operation\u0026#34; class Client: def __init__(self, dependency): # 通过参数的形式将 Dependency 实例传入 self.dependency = dependency def do_something(self): return self.dependency.operation() dependency = Dependency() client = Client(dependency) # 在 Client 对象中注入 Dependency 对象依赖 result = client.do_something() print(result) 此时，Dependency 的初始化函数改变，并不会影响调用者，因为 Dependency 是以对象的形式注入到调用者的\n应用 # 后端框架 Spring（java），midway（node.js）都应用了 IoC 思想及 DI 依赖注入。\n总结 # 控制反转是一种借助于“第三方”实现具有依赖关系的对象之间的解耦的思想。\n依赖注入是实现控制反转的一种方式，一种设计模式。还有其他的方式如：Service Locator\n","date":"2024-11-08","externalUrl":null,"permalink":"/zh/posts/tech/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC-ioc-%E4%B8%8E%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5-di/","section":"博客","summary":"控制反转 IoC 与依赖注入 DI # 引入 # 讨论之前，先来简述一下软件对象中的耦合\n","title":"控制反转 IoC 与依赖注入 DI","type":"posts"},{"content":"Go 专题用于沉淀 Go 语言学习、工程实践、框架选型和工具链经验。\n目录 # 基础语法 高阶 框架 工具 ","externalUrl":null,"permalink":"/zh/docs/go/","section":"技术文档","summary":"Go 专题用于沉淀 Go 语言学习、工程实践、框架选型和工具链经验。\n目录 # 基础语法 高阶 框架 工具 ","title":"Go 技术文档","type":"docs"},{"content":"","externalUrl":null,"permalink":"/zh/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"这里后续沉淀 Go 并发模型、GMP、内存管理、性能分析、工程设计和复杂场景实践。\n","externalUrl":null,"permalink":"/zh/docs/go/advanced/","section":"技术文档","summary":"这里后续沉淀 Go 并发模型、GMP、内存管理、性能分析、工程设计和复杂场景实践。\n","title":"高阶","type":"docs"},{"content":"这里后续沉淀 Wire、Viper、Zap、Vault、测试工具、构建工具和研发效率工具。\n","externalUrl":null,"permalink":"/zh/docs/go/tools/","section":"技术文档","summary":"这里后续沉淀 Wire、Viper、Zap、Vault、测试工具、构建工具和研发效率工具。\n","title":"工具","type":"docs"},{"content":"这里后续沉淀 Go 基础语法、类型系统、函数、接口、错误处理和常用标准库。\n","externalUrl":null,"permalink":"/zh/docs/go/basic/","section":"技术文档","summary":"这里后续沉淀 Go 基础语法、类型系统、函数、接口、错误处理和常用标准库。\n","title":"基础语法","type":"docs"},{"content":"技术文档用于沉淀相对系统化、可持续维护的知识内容。这里优先按目录树组织，分类和标签用于横向检索。\n当前规划 # Go DevOps Database AI 工具链 第一阶段先建立 Go 文档树，后续逐步补充正文。\n","externalUrl":null,"permalink":"/zh/docs/","section":"技术文档","summary":"技术文档用于沉淀相对系统化、可持续维护的知识内容。这里优先按目录树组织，分类和标签用于横向检索。\n当前规划 # Go DevOps Database AI 工具链 第一阶段先建立 Go 文档树，后续逐步补充正文。\n","title":"技术文档","type":"docs"},{"content":"这里后续沉淀 Gin、Go-zero、Kratos、Gorm 等框架和工程模板实践。\n","externalUrl":null,"permalink":"/zh/docs/go/framework/","section":"技术文档","summary":"这里后续沉淀 Gin、Go-zero、Kratos、Gorm 等框架和工程模板实践。\n","title":"框架","type":"docs"}]