分类 默认分类 下的文章

1. 克隆主仓库时直接拉取子模块(首次拉取代码时,默认会拉取子模块,推荐)

# --recursive 递归拉取所有子模块
git clone --recursive git@gitcode.com:xxx/supo-admin.git

2. 已克隆主仓库,后续同步子模块

# 初始化子模块(若未初始化)
git submodule init

# 拉取子模块代码并更新到最新版本
git submodule update --remote

3. 常用子模块维护命令

# 3-1. 更新子模块到远程最新版本
# 只更新 admin-web 子模块
git submodule update --remote admin-web

# 或更新所有子模块
git submodule update --remote

# 3-2. 进入子模块操作(如提交子模块代码)
# 进入子模块目录
cd admin-web
# 拉取子模块的远程更新
git pull origin 子模块分支名

git add .
git commit -m "fix: 子模块修复bug"

# 推送子模块的代码到远程
git push origin master

4. 移除子模块(如需删除)

# 4-1. 解除主仓库对子模块的跟踪
git submodule deinit -f admin-web

# 4-2. 删除子模块的本地目录
rm -rf admin-web

# 4-3. 删除主仓库中 .git 目录下的子模块配置
rm -rf .git/modules/admin-web

# 4-4. 从 .gitmodules 中删除该子模块的配置(或手动编辑 .gitmodules 删除对应段落)
git rm --cached admin-web

# 4-5. 提交删除操作
git commit -m "feat: 移除 admin-web 子模块"
git push

5. 添加子模块

git submodule https://gitcode.com/xxx/merchant-web.git merchant-web

恢复到项目 未执行 git submodule init 的状态,后续可再次拉子模块代码

要让 Git 子模块目录恢复到“未初始化/未拉取代码”的状态(和执行 git submodule init 前一致,仅保留子模块配置,删除本地代码),核心是 保留子模块的 Git 配置(.gitmodules.git/config 中的记录),但删除子模块的本地代码目录和 Git 跟踪文件。以下是具体步骤:

核心逻辑

目标状态:

  • 保留 .gitmodules 文件(子模块配置记录);
  • 保留 .git/config 中可能存在的子模块配置(可选,若之前执行过 init);
  • 删除子模块的本地代码目录(如 submodule-dir/);
  • 删除 Git 对其子模块的跟踪文件(.git/modules/[子模块名])。

具体操作步骤

假设你的子模块目录名为 submodule-dir(替换为实际子模块目录名):

1. 先确保子模块目录无未提交的修改(可选,避免数据丢失)

如果子模块目录中有自己的修改,先备份或提交(否则删除后会丢失):

# 进入子模块目录查看状态
cd submodule-dir
git status
# 若有未提交文件,可备份或提交(根据需求选择)

2. 删除子模块的本地代码目录

直接删除子模块的物理目录(这是“看起来没拉取代码”的核心):

# 回到项目根目录
cd ..
# 删除子模块目录(本地代码全部删除)
rm -rf submodule-dir/

3. 删除 Git 对其子模块的跟踪文件

Git 会将子模块的版本信息、配置等存储在 .git/modules/[子模块名] 目录中,必须删除才能彻底恢复到“未初始化”状态:

# 删除 Git 跟踪的子模块元数据(替换为实际子模块名)
rm -rf .git/modules/submodule-dir/

4. (可选)保留子模块配置(关键!和 git submodule init 前一致)

如果想保留 .gitmodules 中的配置(后续可重新执行 git submodule init && git submodule update 拉取代码),无需删除 .gitmodules 文件

若之前执行过 git submodule init.git/config 中会有子模块的配置记录,若想完全恢复到“从未 init”的状态,可删除该记录:

# 取消 .git/config 中对子模块的配置(可选,不删也不影响“无代码”状态)
git config --unset-all submodule.submodule-dir

最终效果验证

操作完成后,项目状态如下:

  • 子模块目录 submodule-dir/ 不存在(或为空);
  • .gitmodules 文件保留(子模块配置还在);
  • 执行 git submodule status 会显示类似 -xxxxxx submodule-dir (未初始化) 的提示(说明子模块未拉取代码);
  • 后续若需重新拉取子模块,只需执行:

    git submodule init  # 初始化(读取 .gitmodules 配置)
    git submodule update  # 拉取子模块代码

简化命令(一键操作,替换为实际子模块名)

# 替换 submodule-dir 为你的子模块目录名
SUBMODULE_NAME="submodule-dir" && rm -rf $SUBMODULE_NAME && rm -rf .git/modules/$SUBMODULE_NAME && git config --unset-all submodule.$SUBMODULE_NAME

总结

通过“删除子模块目录 + 删除 Git 跟踪元数据”,即可让子模块恢复到“未拉取代码”的初始状态,和未执行 git submodule init/update 时完全一致,且保留了后续重新拉取的配置。

在 Yii2 中编写自定义类时,继承框架基础类的核心原则是:根据自定义类的功能场景,选择最贴合的「抽象基类/接口」或「功能基类」,避免盲目继承(如直接继承 yii\base\Object 已过时,Yii2.0.13+ 推荐 yii\base\BaseObject)。

以下按「功能场景分类」,整理常用的可继承基础类,附使用场景和代码示例,覆盖绝大多数开发需求:

一、核心基础类(所有自定义类的通用父类)

这类类是 Yii2 所有组件的根基,提供「属性访问、事件触发、依赖注入」等核心能力,适合所有需要框架核心特性的自定义类。

框架基础类核心功能适用场景代码示例
yii\base\BaseObject提供 __get()/__set()hasProperty()init() 等属性和初始化能力(Yii2.0.13+ 推荐)所有需要「属性访问、初始化逻辑」的自定义类php namespace app\components; use yii\base\BaseObject; class MyTool extends BaseObject { public $name; public function init() { parent::init(); // 初始化逻辑(如参数校验) if (empty($this->name)) { throw new \Exception('名称不能为空'); } } }
yii\base\Component继承 BaseObject,额外提供「事件机制、行为(Behavior)支持」需事件触发、行为扩展的类(如组件、服务)php namespace app\components; use yii\base\Component; class MyService extends Component { const EVENT_AFTER_DO = 'afterDo'; public function doSomething() { // 触发事件 $this->trigger(self::EVENT_AFTER_DO); } } // 使用时绑定事件 $service = new MyService(); $service->on(MyService::EVENT_AFTER_DO, function() { echo '执行后触发'; });
yii\base\Object旧版核心基类(Yii2.0.13 前),功能与 BaseObject 一致,已被弃用兼容旧版本代码(不推荐新增)-

二、数据模型类(处理数据验证、数据库交互)

若自定义类需「数据验证、数据库 CRUD」,优先继承以下模型基类,直接复用 Yii2 的数据处理能力。

框架基础类核心功能适用场景代码示例
yii\base\Model提供「属性验证、场景支持、错误信息管理」(无数据库交互)表单提交、接口参数验证(非数据库模型)php namespace app\models; use yii\base\Model; class LoginForm extends Model { public $username; public $password; public function rules() { return [ [['username', 'password'], 'required'], ['password', 'string', 'min' => 6], ]; } } // 使用时验证 $form = new LoginForm(['username' => 'test', 'password' => '123456']); if ($form->validate()) { // 验证通过 }
yii\db\ActiveRecord继承 Model,额外提供「数据库 CRUD、关联查询、属性映射」数据库表对应的模型(核心推荐)php namespace app\models; use yii\db\ActiveRecord; class User extends ActiveRecord { public static function tableName() { return 'user'; // 对应数据库表名 } } // 数据库操作 $user = User::findOne(1); $user->username = 'newName'; $user->save();
yii\db\BaseActiveRecordActiveRecord 的抽象基类,可用于自定义 ORM 逻辑(如非关系型数据库)扩展数据库驱动、自定义 ORM 时使用-
yii\base\DynamicModel动态模型,无需预先定义属性,适合临时数据验证(如单次接口参数)临时数据验证、动态表单(无需创建实体类)php $model = DynamicModel::validateData([ 'email' => 'test@example.com', 'age' => 20, ], [ ['email', 'email'], ['age', 'integer', 'min' => 18], ]); if ($model->hasErrors()) { // 处理错误 }

三、控制器/动作类(Web 接口/页面逻辑)

用于编写 Web 接口、页面渲染逻辑,继承后可直接复用「请求处理、响应输出、权限控制」等能力。

框架基础类核心功能适用场景代码示例
yii\web\ControllerWeb 应用核心控制器,提供「视图渲染、请求获取、响应处理、行为支持」普通 Web 页面、HTML 接口控制器php namespace app\controllers; use yii\web\Controller; class SiteController extends Controller { public function actionIndex() { // 渲染视图 return $this->render('index'); } }
yii\rest\Controller专为 RESTful 接口设计,提供「JSON 响应、HTTP 方法映射、接口权限」RESTful API 控制器(如前后端分离接口)php namespace app\controllers; use yii\rest\Controller; class ApiController extends Controller { public function actionUser($id) { return ['id' => $id, 'name' => 'test']; // 自动转为 JSON 响应 } }
yii\rest\ActiveController继承 rest\Controller,自动实现「CRUD 接口」(无需手动写增删改查)快速实现 RESTful CRUD 接口(如资源管理)php namespace app\controllers; use yii\rest\ActiveController; class UserController extends ActiveController { public $modelClass = 'app\models\User'; // 关联数据模型 } // 自动生成接口:GET /user(列表)、GET /user/1(详情)、POST /user(新增)
yii\console\Controller命令行控制器,用于编写定时任务、脚本(如数据迁移、批量处理)控制台命令、定时任务(如 yii xxx/xxxphp namespace app\commands; use yii\console\Controller; class TestController extends Controller { public function actionIndex() { echo '控制台命令执行成功'; } } // 执行:php yii test/index

四、过滤器/行为类(请求拦截、功能扩展)

用于「请求拦截、权限校验、日志记录、缓存控制」等横切逻辑,继承后可嵌入控制器/组件的生命周期。

框架基础类核心功能适用场景代码示例
yii\base\ActionFilter动作过滤器基类,提供 beforeAction()(动作执行前)、afterAction()(执行后)钩子接口权限校验、日志记录、请求频率限制php namespace app\filters; use yii\base\ActionFilter; class AuthFilter extends ActionFilter { public function beforeAction($action) { // 动作执行前校验 if (!Yii::$app->user->isGuest) { return true; } Yii::$app->response->statusCode = 401; return false; } } // 在控制器中使用 public function behaviors() { return [ 'auth' => ['class' => AuthFilter::class], ]; }
yii\filters\AccessControl内置权限过滤器,支持「角色权限、IP 白名单、HTTP 方法限制」快速实现权限控制(如仅管理员访问)php public function behaviors() { return [ 'access' => [ 'class' => \yii\filters\AccessControl::class, 'rules' => [ ['allow' => true, 'roles' => ['@'], // 仅登录用户允许 'actions' => ['index'], ], ], ], ]; }
yii\base\Behavior行为基类,用于给组件动态添加属性/方法(无侵入扩展)组件功能扩展(如给模型添加日志方法)php namespace app\behaviors; use yii\base\Behavior; use yii\db\ActiveRecord; class LogBehavior extends Behavior { public function events() { return [ ActiveRecord::EVENT_AFTER_INSERT => 'afterInsert', ]; } public function afterInsert($event) { // 模型新增后记录日志 Yii::info('新增数据:' . $event->sender->id); } } // 给模型添加行为 public function behaviors() { return ['log' => ['class' => LogBehavior::class]]; }

五、工具/辅助类(特定功能场景)

针对「缓存、日志、验证器、命令行」等特定场景,继承对应基类可复用框架内置能力。

框架基础类核心功能适用场景代码示例
yii\caching\Cache缓存抽象基类,提供 get()/set()/delete() 等缓存操作接口自定义缓存驱动(如 Redis 扩展、本地文件缓存)php namespace app\caching; use yii\caching\Cache; class MyCache extends Cache { protected function getValue($key) { // 自定义获取缓存逻辑 } protected function setValue($key, $value, $duration) { // 自定义设置缓存逻辑 } }
yii\log\Target日志目标基类,用于自定义日志存储方式(如写入数据库、第三方日志服务)扩展日志存储(如 ELK、阿里云日志)php namespace app\log; use yii\log\Target; class DbTarget extends Target { public function export() { $logs = $this->getLogs(); // 将日志写入数据库 foreach ($logs as $log) { Yii::$app->db->createCommand()->insert('sys_log', [ 'message' => $log[0], 'level' => $log[1], ])->execute(); } } }
yii\validators\Validator验证器基类,用于自定义数据验证规则(如手机号、身份证号校验)扩展验证规则(框架未提供的自定义校验)php namespace app\validators; use yii\validators\Validator; class PhoneValidator extends Validator { public function validateAttribute($model, $attribute) { $value = $model->$attribute; if (!preg_match('/^1[3-9]\d{9}$/', $value)) { $this->addError($model, $attribute, '手机号格式错误'); } } } // 在模型中使用 public function rules() { return [ ['phone', PhoneValidator::class], ]; }
yii\console\Action命令行动作基类,用于编写复杂命令行逻辑(拆分命令为多个动作)复杂控制台命令(如批量导入数据的子步骤)php namespace app\commands\actions; use yii\console\Action; class ImportAction extends Action { public function run($file) { // 执行导入逻辑 echo "从 {$file} 导入数据"; } } // 在命令行控制器中绑定 public function actions() { return [ 'import' => ImportAction::class, ]; } // 执行:php yii test/import --file=data.csv

六、继承原则与避坑要点

  1. 最小继承原则:不需要的功能不继承(如仅需属性验证 → 继承 Model,无需继承 ActiveRecord);
  2. 优先接口/抽象类:框架提供的抽象基类(如 CacheValidator)定义了标准接口,继承后需实现必填方法(如 CachegetValue());
  3. 避免多层继承:Yii2 推荐「组合优于继承」,复杂功能优先用「行为(Behavior)」扩展,而非多层继承;
  4. 注意版本兼容性BaseObject 是 Yii2.0.13+ 新增,若项目版本低于该版本,需用 Object
  5. 初始化逻辑写在 init():继承 BaseObject/Component 时,不要重写 __construct(),而是通过 init() 实现初始化(框架会自动调用)。

总结

选择继承的框架类,核心看「自定义类的功能场景」:

  • 数据验证 → yii\base\Model
  • 数据库交互 → yii\db\ActiveRecord
  • Web 接口 → yii\rest\Controller
  • 请求拦截 → yii\base\ActionFilter
  • 通用组件 → yii\base\Component(需事件/行为)或 yii\base\BaseObject(仅需属性/初始化)。

按场景选择合适的基类,可最大化复用框架能力,减少重复代码,同时保证代码符合 Yii2 的设计规范。

订单搜索优化方案

核心目标:解决订单搜索(多条件、大数据量)慢问题,兼顾「查询性能」「业务适配」「开发成本」,核心思路:索引优化+查询逻辑优化+存储/架构优化,按优先级逐步落地。

一、先做低成本高收益:索引优化(优先级最高)

订单搜索慢80%是索引缺失/不合理,优先针对性建索引,无代码侵入,见效最快。

  1. 核心搜索字段索引(必建)

订单搜索高频字段: order_no (订单号)、 user_id (用户ID)、 order_status (订单状态)、 create_time (创建时间),按「单字段索引+联合索引」组合设计:

  • 单字段索引: idx_order_no (订单号,精确匹配为主,如搜索某订单号)、 idx_user_id (用户ID,查用户所有订单)
  • 联合索引(覆盖高频多条件搜索,遵循「左前缀原则」):
  •  idx_status_create ( order_status ,  create_time ):适配「按状态+时间筛选」(如待发货+近7天)
  •  idx_user_status ( user_id ,  order_status ):适配「按用户+状态筛选」(如用户A的待付款订单)
  •  idx_user_create ( user_id ,  create_time ):适配「按用户+时间筛选」(如用户A近30天订单)
  • 索引语法(MySQL):
    -- 单字段索引
    CREATE INDEX idx_order_no ON order(order_no);
    CREATE INDEX idx_user_id ON order(user_id);
    -- 联合索引
    CREATE INDEX idx_status_create ON order(order_status, create_time);
    CREATE INDEX idx_user_status ON order(user_id, order_status);
     
  1. 索引避坑要点
  • 不建冗余索引:如已有 idx_user_status_create (user_id,order_status,create_time),无需再建 idx_user_status 
  • 高基数字段放联合索引左前:如 user_id (基数高)比 order_status (基数低,仅待付/待发/完成等)优先
  • 避免过度索引:单表索引≤6个,过多会拖慢订单创建/更新速度

二、查询逻辑优化(开发成本低,见效快)

  1. 过滤条件优先用索引字段
  • 搜索时先通过「status/create_time/user_id」等索引字段缩小数据范围,再过滤非索引字段(如 consignee 收件人)
  • 反例:先查所有订单,再筛选收件人;正例:先按「待发+近7天」(索引字段)过滤,再匹配收件人
  1. 精准匹配优先,模糊匹配优化
  • 订单号搜索:用 = 精准匹配(走 idx_order_no 索引),避免 like %xxx% (索引失效);若需前缀匹配(如订单号前6位),用 like 'xxx%' (可走索引)
  • 收件人/手机号搜索:
  • 手机号:存明文/脱敏后,用 like '138%' (前缀匹配,建 idx_consignee_mobile 索引)
  • 收件人:避免全模糊,可做「分词+冗余字段」(如 consignee_pinyin 存拼音首字母,搜“张三”匹配“ZS%”)
  1. 分页与字段裁剪
  • 分页防深页:用 create_time+id 游标分页(代替 limit 10000,20 ,深页时索引失效),示例:
    // 游标分页:上一页最后一条的create_time和id
    $query->where(['>', 'create_time', $lastCreateTime])

    ->orWhere(['=', 'create_time', $lastCreateTime])
    ->andWhere(['>', 'id', $lastId])
    ->limit(20);

     

  • 字段裁剪:只查需要的字段(避免 select * ),联合索引可覆盖查询(如 idx_user_status 覆盖 user_id,status,id ,无需回表查主表)
  1. 避免查询无效数据
  • 过滤已删除订单( is_delete=0 ,可加进联合索引,如 idx_status_delete_create )
  • 时间范围不超过90天:默认只查近90天订单,历史订单提供「按年月筛选」入口(缩小范围)
  1. 批量查询防N+1
  • 查订单时关联「用户/商品」,用Yii2 with() 预加载,避免循环查关联数据:
    Order::find()->with(['user', 'orderGoods'])->where(...); // 预加载,仅2次查询
     

三、中成本优化:存储与冗余设计

  1. 历史订单分表(订单量≥100万条必做)
  • 按时间分表:近3个月订单存在 order 主表,历史订单按「年月」分表(如 order_202501 )
  • 分表策略:
  • 写入:新订单写主表,每月底定时将上月订单迁移到历史分表
  • 读取:近3个月查主表,历史订单查对应分表;跨表搜索用「分表聚合」(如UNION ALL,仅历史数据用)
  1. 冗余高频搜索字段
  • 非索引字段(如 consignee 收件人、 mobile 手机号)若高频搜索,可冗余到订单表(避免关联地址表)
  • 示例:订单表冗余 consignee (收件人)、 mobile (脱敏手机号),建 idx_consignee_mobile 索引,适配收件人/手机号前缀搜索
  1. 大字段拆分
  • 订单表中 order_desc (订单备注)、 ext_info (扩展信息)等大字段,拆分到 order_ext 表(一对一关联),减少主表数据量,提升查询速度

四、高成本高收益:架构层优化(订单量≥500万条)

  1. 引入ES(Elasticsearch)做全文检索
  • 适用场景:多字段混合搜索(如“用户A+待付款+近7天+收件人张三”)、全模糊搜索(如收件人“%三%”)
  • 实现方案:
  • 数据同步:订单创建/更新时,同步数据到ES索引(用MQ解耦,避免影响订单主流程)
  • 搜索查询:复杂搜索走ES,简单查询(订单号/用户ID)仍走MySQL,ES返回订单ID,再从MySQL查详情(ES+MySQL混合查询,兼顾速度与数据一致性)
  1. 读写分离
  • 订单创建/更新写主库,搜索查询读从库,分摊主库压力
  • 适配场景:QPS≥1000,主库压力大时,读从库可提升查询响应速度
  1. 热点数据缓存
  • 高频搜索数据(如用户近30天订单、热门状态订单)缓存到Redis,过期时间5-15分钟(根据订单更新频率调整)
  • 示例:缓存用户1001近30天订单,键 order:user:1001:30d ,值存订单ID列表,查询时先查缓存,无则查DB再回写缓存

五、落地优先级&效果预期

优化层级 具体方案 开发成本 性能提升 适用订单量
1级(必做) 核心索引优化+查询逻辑优化 低 50%-200% <100万
2级(推荐) 历史订单分表+字段冗余 中 200%-500% 100万-500万
3级(按需) ES全文检索+读写分离 高 500%+ ≥500万

六、监控与迭代

1. 监控慢查询:开启MySQL慢查询日志(阈值≥500ms),定期分析订单搜索慢SQL,针对性优化
2. 监控索引使用率:用 sys.schema_unused_indexes 查看未使用索引,及时删除冗余
3. 迭代优化:先落地1-2级方案,观察1-2周,若仍不满足需求,再推进ES等3级方案

按此方案落地,可解决大部分订单搜索慢问题,兼顾成本与效果,适配不同订单量规模的业务场景。

要使用 Nginx + ACME 自动文件验证 申请 SSL 证书,推荐使用轻量工具 acme.sh(自动化程度高、支持自动续期、无需依赖复杂环境)。以下是完整步骤,适用于 CentOS/Debian/Ubuntu 系统。

一、核心原理

ACME 协议的 HTTP-01 验证(文件验证)流程:

  1. 向 Let's Encrypt 申请证书时,ACME 客户端会生成一个随机验证文件(如 /.well-known/acme-challenge/xxx)。
  2. Let's Encrypt 服务器会访问你的域名 http://example.com/.well-known/acme-challenge/xxx,验证文件是否存在且内容正确。
  3. 验证通过后,Let's Encrypt 颁发证书。

二、前置条件

  1. 已安装 Nginx(需确保 Nginx 正常运行,且 80 端口对外开放,HTTP-01 验证依赖 80 端口)。
  2. 域名已解析到服务器 IP(如 example.com 指向你的服务器)。
  3. 服务器已安装 curlgit(用于安装 acme.sh)。

三、步骤 1:安装 acme.sh

acme.sh 是纯 Shell 脚本,无需 root 也可安装(推荐普通用户安装,更安全),但申请证书时可能需要读写 Nginx 配置目录,建议用 sudo 执行。

# 1. 安装依赖(CentOS 示例,Debian/Ubuntu 用 apt-get)
sudo yum install -y curl git openssl

# 2. 安装 acme.sh(自动添加环境变量,支持全局调用)
curl https://get.acme.sh | sh -s email=your-email@example.com
# 替换为你的邮箱(用于证书到期提醒)

# 中国大陆安装可参考,或者多试几次以上安装命令
https://github.com/acmesh-official/acme.sh/wiki/Install-in-China

# 3. 生效环境变量(无需重启,临时生效)
source ~/.bashrc

安装完成后,验证是否可用:

acme.sh --version
# 输出类似:acme.sh v3.0.6 (版本号可能不同)

四、步骤 2:配置 Nginx 支持 ACME 验证

需让 Nginx 对 /.well-known/acme-challenge/ 路径的请求进行正确转发,指向 acme.sh 生成验证文件的目录(默认 /home/[用户名]/.acme.sh/)。

步骤 1:创建验证转发规则

编辑 Nginx 全局配置或域名配置文件(以 CentOS 为例,Nginx 配置目录 /etc/nginx/conf.d/):

sudo vim /etc/nginx/conf.d/acme-verify.conf

添加以下内容(核心是暴露验证目录):


    # ACME 验证文件目录转发
    location /.well-known/acme-challenge/ {
        # 指向 acme.sh 生成验证文件的目录(默认路径,无需修改)
        root /www/wwwroot/acmewww/;
        # 允许访问该目录下的所有文件
        try_files $uri =404;
        # 关闭缓存(避免验证文件被缓存导致失败)
        add_header Cache-Control "no-store, no-cache, must-revalidate";
    }

步骤 2:已存在域名配置引入转发规则

如果已有 example.com 的 Nginx 配置(80 端口),直接在 server 块中添加以下 location 即可:

server {
    listen 80;
    server_name example.com www.example.com;
    # 其他原有配置...

    # 引入规则
    include acme-verify.conf;
}

验证 Nginx 配置并重启

# 检查配置是否有误
sudo nginx -t

# 重启 Nginx 生效
sudo systemctl restart nginx

五、步骤 3:用 acme.sh 自动申请证书(文件验证)

执行以下命令,acme.sh 会自动生成验证文件、完成 Let's Encrypt 验证,并颁发证书:

acme.sh --issue \
  -d example.com \          # 主域名(必填)
  -d www.example.com \      # 额外域名(可选,支持多个 -d)
  --webroot /home/$(whoami)/.acme.sh/ \  # 验证文件存放目录(与 Nginx 配置一致)
  --nginx \                 # 自动检测 Nginx 配置(可选,辅助验证)
  --force \                 # 强制重新申请(可选,首次申请可省略)
  --debug                   # 调试模式(可选,失败时启用排查问题)

成功标志

输出类似以下内容,说明证书申请成功:

[Thu Nov 20 10:00:00 UTC 2025] Your cert is in: /home/your-user/.acme.sh/example.com/example.com.crt
[Thu Nov 20 10:00:00 UTC 2025] Your cert key is in: /home/your-user/.acme.sh/example.com/example.com.key
[Thu Nov 20 10:00:00 UTC 2025] The intermediate CA cert is in: /home/your-user/.acme.sh/example.com/ca.crt
[Thu Nov 20 10:00:00 UTC 2025] And the full chain certs is there: /home/your-user/.acme.sh/example.com/fullchain.crt

六、步骤 4:配置 Nginx 使用 SSL 证书

将申请到的证书配置到 Nginx 的 HTTPS 服务(443 端口),编辑域名配置文件:

sudo vim /etc/nginx/conf.d/example.com.conf

添加以下 HTTPS 配置(替换证书路径为实际路径):

# HTTPS 服务(443 端口)
server {
    listen 443 ssl;
    server_name example.com www.example.com;

    # 证书路径(从 acme.sh 成功输出中复制)
    ssl_certificate /home/your-user/.acme.sh/example.com/fullchain.crt;  # 完整链证书
    ssl_certificate_key /home/your-user/.acme.sh/example.com/example.com.key;  # 私钥

    # SSL 优化配置(推荐添加)
    ssl_protocols TLSv1.2 TLSv1.3;  # 仅支持安全的 TLS 版本
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;

    # 静态资源缓存(可选)
    location / {
        root /usr/share/nginx/html;  # 你的网站根目录
        index index.html index.htm;
    }
}

# HTTP 重定向到 HTTPS(强制 HTTPS,可选但推荐)
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

验证配置并重启 Nginx

sudo nginx -t  # 检查配置
sudo systemctl restart nginx  # 重启生效

七、步骤 5:自动续期证书

acme.sh 会自动添加 定时任务(每天检查一次,证书到期前 30 天自动续期),无需手动操作。

验证定时任务

# 查看 acme.sh 的定时任务
crontab -l | grep acme.sh

输出类似(自动生成,无需修改):

0 0 * * * "/home/your-user/.acme.sh"/acme.sh --cron --home "/home/your-user/.acme.sh" > /dev/null

手动测试续期(可选)

acme.sh --renew -d example.com --force

常见问题排查

  1. 验证失败(提示文件不存在)

    • 检查 Nginx 配置的 root 路径是否与 --webroot 一致(均为 /home/your-user/.acme.sh/)。
    • 确保 80 端口未被防火墙拦截(sudo firewall-cmd --permanent --add-port=80/tcp && sudo firewall-cmd --reload)。
    • 域名解析是否生效(本地 ping example.com 确认指向服务器 IP)。
  2. Nginx 启动失败

    • 检查证书路径是否正确(复制 acme.sh 成功输出的路径,避免手动输入错误)。
    • sudo nginx -t 查看具体错误信息(如端口被占用、语法错误)。
  3. acme.sh 命令未找到

    • 执行 source ~/.bashrc 生效环境变量,或重新登录服务器。

总结

通过 acme.sh + Nginx 实现 ACME 自动文件验证,核心是:

  1. 安装 acme.sh 工具。
  2. 配置 Nginx 暴露 ACME 验证目录。
  3. 用 acme.sh 自动申请/续期证书。
  4. 将证书配置到 Nginx HTTPS 服务。

整个流程无需手动上传验证文件,证书到期自动续期,适合生产环境长期使用。

Nacos 部署在本地内网服务器(无公网 IP)
本地服务器无公网 IP 时,需用内网穿透工具将 8848 端口映射到公网,以常用的 cpolar 为例:
安装 cpolar:在本地服务器执行一键安装命令

curl -L https://www.cpolar.com/static/downloads/install-release-cpolar.sh | sudo bash

启动 cpolar 服务

# 设置开机自启
sudo systemctl enable cpolar
# 启动服务
sudo systemctl start cpolar

配置 Nacos 公网隧道:浏览器访问http://本地服务器IP:9200,登录 cpolar 控制台,进入隧道管理→创建隧道:
隧道名称:自定义(如 nacos-public)
协议:选 HTTP
本地地址:填 8848(Nacos 的端口)
域名类型:免费版选随机域名,付费版可自定义固定域名
公网访问:创建隧道后,在 cpolar 的在线隧道列表中获取公网地址,拼接/nacos(如https://xxx.cpolar.top/nacos),即可公网访问 Nacos 控制台