<?xml version="1.0"?>
<rss version="2.0">
    <channel>
        <title>艾瑞可erik • Posts by &#34;undefined&#34; categories</title>
        <link>https://erik.xyz</link>
        <description>一只PHP开发的程序猿，偶尔做做运维、Goland、Python、Java、摄影、画画、写作、顺便睡觉，反正整站都搞过。</description>
        <language>zh-CN</language>
        <pubDate>Mon, 25 Aug 2025 15:42:00 +0800</pubDate>
        <lastBuildDate>Mon, 25 Aug 2025 15:42:00 +0800</lastBuildDate>
        <category>php扩展</category>
        <category>php技巧</category>
        <category>php类库</category>
        <category>php资源</category>
        <category>日志</category>
        <category>工具</category>
        <category>jquery</category>
        <category>jquery插件</category>
        <category>js</category>
        <category>css</category>
        <category>php</category>
        <category>web</category>
        <category>代码</category>
        <category>压缩</category>
        <category>php服务器</category>
        <category>随笔</category>
        <category>php实例</category>
        <category>说说</category>
        <category>趣闻</category>
        <category>php库</category>
        <category>转载</category>
        <category>nginx</category>
        <category>互联</category>
        <category>项目实战</category>
        <category>javascript</category>
        <category>漏洞</category>
        <category>php应用</category>
        <category>浮点</category>
        <category>http</category>
        <category>http服务器</category>
        <category>负载均衡</category>
        <category>kali</category>
        <category>kali安装</category>
        <category>农业系统</category>
        <category>系统设计</category>
        <category>克莱姆法则</category>
        <category>行列式</category>
        <category>n阶行列式</category>
        <category>支付系统</category>
        <category>支付</category>
        <category>centos</category>
        <category>linux</category>
        <category>apache优化</category>
        <category>apache隐藏index</category>
        <category>nginx优化</category>
        <category>nginx隐藏index</category>
        <category>日常</category>
        <category>seo</category>
        <category>优化</category>
        <category>摘要</category>
        <category>文章</category>
        <category>游记</category>
        <category>ca证书</category>
        <category>证书生成</category>
        <category>二战</category>
        <category>二战熊</category>
        <category>西伯利亚熊</category>
        <category>centos软件</category>
        <category>yum仓库</category>
        <category>软件</category>
        <category>centos安装chromium报错</category>
        <category>chromium安装报错</category>
        <category>svn</category>
        <category>代码提交</category>
        <category>deb</category>
        <category>deb/rpm互转</category>
        <category>deb转rpm</category>
        <category>rpm互转</category>
        <category>centos7安装steam</category>
        <category>centos安装steam</category>
        <category>centos安装吃鸡steam</category>
        <category>linux安装steam</category>
        <category>steam安装</category>
        <category>centos软件安装</category>
        <category>centos6.5</category>
        <category>linux系统</category>
        <category>centos7安装Redis Desktop Manager</category>
        <category>centos7安装redis桌面</category>
        <category>centos安装redis桌面</category>
        <category>linux安装centos7安装Redis Desktop Manager</category>
        <category>Redis Desktop Manager</category>
        <category>redis桌面</category>
        <category>centos7</category>
        <category>centos7-wine</category>
        <category>wine</category>
        <category>wine安装</category>
        <category>root密码</category>
        <category>root密码重置</category>
        <category>忘记root密码</category>
        <category>memcache</category>
        <category>memcached</category>
        <category>生活手记</category>
        <category>艾瑞可erik</category>
        <category>程序员</category>
        <category>开源云</category>
        <category>容器</category>
        <category>cncf</category>
        <category>监控</category>
        <category>composer</category>
        <category>composer部署</category>
        <category>内网部署composer</category>
        <category>cphalcon</category>
        <category>cphalcon报错</category>
        <category>css中的em</category>
        <category>响应式字体</category>
        <category>响应式布局样式</category>
        <category>proxy代理搭建</category>
        <category>代理服务器搭建</category>
        <category>proxy服务器</category>
        <category>查找</category>
        <category>排序</category>
        <category>快速排序</category>
        <category>冒泡排序</category>
        <category>二分查找</category>
        <category>datahub</category>
        <category>datahub安装</category>
        <category>deepin</category>
        <category>emergency</category>
        <category>emergency mode</category>
        <category>deepin黑屏</category>
        <category>开发语言</category>
        <category>开发</category>
        <category>清理系统</category>
        <category>清理c盘</category>
        <category>dns</category>
        <category>域名监测</category>
        <category>网站监测</category>
        <category>docker</category>
        <category>php安装</category>
        <category>杂谈</category>
        <category>ecshop</category>
        <category>框架</category>
        <category>beego数据分页</category>
        <category>分页</category>
        <category>elasticsearch</category>
        <category>elasticsearch搭建</category>
        <category>elasticsearch集群</category>
        <category>系统</category>
        <category>emm</category>
        <category>mdm</category>
        <category>phone</category>
        <category>手机</category>
        <category>新闻</category>
        <category>免费</category>
        <category>jenkis</category>
        <category>jenkis教程</category>
        <category>随笔吐槽</category>
        <category>算法</category>
        <category>ping</category>
        <category>ping服务器</category>
        <category>各大网站ping</category>
        <category>搜引擎ping服务器</category>
        <category>go获取ip</category>
        <category>im</category>
        <category>go中pdf生成</category>
        <category>go中限流</category>
        <category>go对接快递签名</category>
        <category>go时间获取</category>
        <category>go类型转换</category>
        <category>微服务</category>
        <category>go-zero</category>
        <category>rpc</category>
        <category>go加密</category>
        <category>go解密</category>
        <category>归</category>
        <category>吐槽</category>
        <category>php大全</category>
        <category>php资料</category>
        <category>hadoop</category>
        <category>hadoop2.8.0</category>
        <category>hadoop安装教程</category>
        <category>hadoop安装详细教程</category>
        <category>hadoop教程</category>
        <category>hadoop部署</category>
        <category>hadoop3</category>
        <category>hadoop配置</category>
        <category>log4j2</category>
        <category>数据库</category>
        <category>hbase安装教程</category>
        <category>hbase</category>
        <category>http1.1</category>
        <category>http2.0</category>
        <category>会话</category>
        <category>会话原理</category>
        <category>hyperf</category>
        <category>杂录</category>
        <category>新冠肺炎</category>
        <category>springboot</category>
        <category>springboot内网部署</category>
        <category>linux恢复</category>
        <category>linux误删</category>
        <category>linux命令</category>
        <category>linux命令汇总</category>
        <category>jenkins</category>
        <category>jetbtrains</category>
        <category>phpstorm</category>
        <category>搜索引擎</category>
        <category>图片</category>
        <category>素材</category>
        <category>jq</category>
        <category>jq侧边导航</category>
        <category>侧边导航</category>
        <category>js判断浏览器</category>
        <category>js判断浏览器版本</category>
        <category>判断浏览器</category>
        <category>浏览器版本判断</category>
        <category>获取浏览器信息</category>
        <category>kali右键汉化</category>
        <category>kali桌面右键汉化</category>
        <category>kali右键创建文件</category>
        <category>区块链</category>
        <category>lanmp</category>
        <category>php环境独立配置</category>
        <category>服务器</category>
        <category>劳动纠纷</category>
        <category>追缴工资</category>
        <category>申请仲裁</category>
        <category>flash</category>
        <category>表单</category>
        <category>linux报错</category>
        <category>linux中update-command-not-found</category>
        <category>linux运行命令报错</category>
        <category>web前端</category>
        <category>web优化</category>
        <category>蓝牙</category>
        <category>小程序蓝牙</category>
        <category>蓝牙连接</category>
        <category>撮合算法</category>
        <category>撮合</category>
        <category>php撮合算法</category>
        <category>maven搭建库</category>
        <category>maven</category>
        <category>maven内网库</category>
        <category>高级缓存配置</category>
        <category>mongodb</category>
        <category>mongodb权限</category>
        <category>可穿戴设备</category>
        <category>mysql</category>
        <category>分库分表</category>
        <category>mysql应对千万级</category>
        <category>mysql瓶颈</category>
        <category>mysql瓶颈解决办法</category>
        <category>redis</category>
        <category>数据一致性</category>
        <category>mysql消息</category>
        <category>mysql队列</category>
        <category>mysql高并发</category>
        <category>mysql存储</category>
        <category>mysql引擎</category>
        <category>mysql数据表设计选择</category>
        <category>mysql监控</category>
        <category>mysql性能</category>
        <category>内网支付</category>
        <category>内网穿透</category>
        <category>支付接口本地化开发</category>
        <category>本地挂网</category>
        <category>穿透</category>
        <category>go</category>
        <category>new和make的区别</category>
        <category>红包算法</category>
        <category>深圳劳动法服务部门</category>
        <category>非关系型数据库</category>
        <category>onethink</category>
        <category>oop</category>
        <category>php管理系统</category>
        <category>开放接口开发</category>
        <category>开源工具</category>
        <category>桌面共享工具</category>
        <category>openresty</category>
        <category>openvas</category>
        <category>openvas安装</category>
        <category>php架构</category>
        <category>php服务</category>
        <category>php服务设计</category>
        <category>php项目</category>
        <category>php架构设计</category>
        <category>php变量</category>
        <category>php超级全局变量</category>
        <category>php超级变量</category>
        <category>php基本类型</category>
        <category>php数据类型</category>
        <category>php设计模式</category>
        <category>php对接微信支付</category>
        <category>微信支付</category>
        <category>微信支付回调</category>
        <category>游戏</category>
        <category>php函数</category>
        <category>php随机数</category>
        <category>php获取闰年</category>
        <category>闰年.php时间</category>
        <category>php环境</category>
        <category>php集成环境</category>
        <category>服务器集成环境</category>
        <category>数组函数</category>
        <category>数组排序函数</category>
        <category>php数学函数</category>
        <category>php面试题</category>
        <category>php面向对象</category>
        <category>面向对象</category>
        <category>php-zookeeper</category>
        <category>zookeeper3.5.5</category>
        <category>php-zookeeper扩展</category>
        <category>php串口开发</category>
        <category>php倒计时</category>
        <category>php时间</category>
        <category>倒计时</category>
        <category>计算时间</category>
        <category>PHP数组</category>
        <category>字符串函数</category>
        <category>排序函数</category>
        <category>php的SPL</category>
        <category>SPL手册</category>
        <category>PHP算法</category>
        <category>php递归</category>
        <category>递归</category>
        <category>phpunit</category>
        <category>phpunit安装</category>
        <category>php过滤</category>
        <category>过滤</category>
        <category>foreach</category>
        <category>foreach报错</category>
        <category>php中foreach报错</category>
        <category>php中if</category>
        <category>php中if判断</category>
        <category>php的if</category>
        <category>php字符串</category>
        <category>php7中sphinx</category>
        <category>php7中sphinx扩展</category>
        <category>sphinx扩展</category>
        <category>plc</category>
        <category>python</category>
        <category>python库</category>
        <category>R语言</category>
        <category>a标签</category>
        <category>a标签虚线</category>
        <category>虚线框</category>
        <category>数据随机化</category>
        <category>redis总结</category>
        <category>redis命令</category>
        <category>redis监控</category>
        <category>redis锁</category>
        <category>redis分布式锁</category>
        <category>任意金额输入</category>
        <category>10元、5元、2元</category>
        <category>队列</category>
        <category>栈</category>
        <category>顺序表</category>
        <category>链表</category>
        <category>数据结构</category>
        <category>线性结构</category>
        <category>浏览器禁止操作视频</category>
        <category>禁止视频</category>
        <category>响应慢</category>
        <category>页面优化</category>
        <category>js编辑</category>
        <category>runjs</category>
        <category>在线编辑</category>
        <category>rust</category>
        <category>rust配置</category>
        <category>seajs</category>
        <category>session</category>
        <category>路由器烧录</category>
        <category>烧录</category>
        <category>极路由2烧录</category>
        <category>砖头烧录</category>
        <category>免费服务器</category>
        <category>空间</category>
        <category>资源</category>
        <category>深圳政府电话</category>
        <category>深圳电话</category>
        <category>特区电话</category>
        <category>居住证</category>
        <category>居住证签注</category>
        <category>深圳新居住证</category>
        <category>国庆骑行</category>
        <category>深圳珠海骑行</category>
        <category>骑行</category>
        <category>世界那么大</category>
        <category>css3</category>
        <category>css在线生成工具</category>
        <category>css工具</category>
        <category>php正则</category>
        <category>正则</category>
        <category>shodan</category>
        <category>黑谷歌</category>
        <category>黒帽搜素</category>
        <category>shopex</category>
        <category>可视化数据</category>
        <category>数据</category>
        <category>数据表</category>
        <category>socket通信</category>
        <category>socket多进程</category>
        <category>socket</category>
        <category>json</category>
        <category>json数组</category>
        <category>json解析</category>
        <category>大数据</category>
        <category>spark</category>
        <category>hive</category>
        <category>数据分析</category>
        <category>sql</category>
        <category>sql优化</category>
        <category>css3兼容360浏览器兼容模式</category>
        <category>css圆角</category>
        <category>结构体转map</category>
        <category>config</category>
        <category>thinkphp</category>
        <category>配置文件</category>
        <category>树</category>
        <category>二叉树</category>
        <category>js插件</category>
        <category>virtualbox</category>
        <category>hyper-v</category>
        <category>鸿蒙开发</category>
        <category>web自适应</category>
        <category>响应式布局</category>
        <category>响应式所有分辨率</category>
        <category>自适应布局</category>
        <category>自适应所有分辨率</category>
        <category>webman</category>
        <category>mysql设置超时，超时</category>
        <category>markdown</category>
        <category>wget</category>
        <category>wget抓取</category>
        <category>网站抓取</category>
        <category>我在</category>
        <category>wordpress</category>
        <category>wordpress标签</category>
        <category>域名合并</category>
        <category>wpscan</category>
        <category>usbrip</category>
        <category>无限极分类</category>
        <category>php无限极</category>
        <category>分类tree</category>
        <category>无限极分类树型</category>
        <category>xhprof</category>
        <category>laravel</category>
        <category>composer插件</category>
        <category>html</category>
        <category>响应式分辨率</category>
        <category>响应式调试</category>
        <category>自适应屏幕</category>
        <category>3D</category>
        <category>动画</category>
        <category>平台</category>
        <category>虚幻4引擎编辑</category>
        <category>携程</category>
        <category>携程攻击</category>
        <category>携程网站瘫痪</category>
        <category>物理删除</category>
        <category>生成唯一id</category>
        <category>生成id</category>
        <category>发邮件</category>
        <category>邮件函数</category>
        <category>储蓄卡免年费</category>
        <category>银行卡</category>
        <category>银行卡免年费</category>
        <category>composer安装</category>
        <category>composer配置</category>
        <category>项目创建composer</category>
        <category>mysql优化</category>
        <category>mysql读写优化</category>
        <category>数据库优化，mysql语句优化</category>
        <category>php加密</category>
        <category>php技术</category>
        <category>夕阳</category>
        <category>mysql函数</category>
        <category>php中mysql函数</category>
        <category>互联网时代</category>
        <category>开源技术</category>
        <category>web框架</category>
        <category>php抓取图片</category>
        <category>php批量抓取页面图片</category>
        <category>邮箱服务器</category>
        <category>正则表达式</category>
        <category>翻墙</category>
        <category>谷歌</category>
        <category>谷歌搜索</category>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/08/25/luetoot-app/</guid>
            <title>『uni-app、小程序』蓝牙连接、读写数据全过程</title>
            <link>https://erik.xyz/2025/08/25/luetoot-app/</link>
            <category>蓝牙</category>
            <category>小程序蓝牙</category>
            <category>蓝牙连接</category>
            <pubDate>Mon, 25 Aug 2025 15:42:00 +0800</pubDate>
            <description><![CDATA[ &lt;ul&gt;
&lt;li&gt;开发工具：HBuilder X 3.4.7.20220422&lt;/li&gt;
&lt;li&gt;uni-app + Vue3&lt;/li&gt;
&lt;li&gt;以安卓App的方式运行（iOS和小程序同理）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;思路&#34;&gt;&lt;a href=&#34;#思路&#34; class=&#34;headerlink&#34; title=&#34;思路&#34;&gt;&lt;/a&gt;思路&lt;/h3&gt;&lt;p&gt;蓝牙收发数据的逻辑和我们常用的 AJAX 进行的网络请求是有一丢丢不同的。&lt;br&gt;其中较大的区别是：蓝牙接收数据不是那么的稳定，相比起网络请求，蓝牙更容易出现丢包的情况。&lt;br&gt;在开发中，AJAX 发起的请求不管成功还是失败，浏览器基本都会给你一个答复。但 uni-app 提供的 api 来看，蓝牙接收数据会显得更加 “异步” 。&lt;br&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&#34;大致思路&#34;&gt;&lt;a href=&#34;#大致思路&#34; class=&#34;headerlink&#34; title=&#34;大致思路&#34;&gt;&lt;/a&gt;大致思路&lt;/h4&gt;&lt;p&gt;使用蓝牙进行数据传输的大概思路如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;初始化：打开蓝牙模块&lt;/li&gt;
&lt;li&gt;搜寻：检测附近存在的设备&lt;/li&gt;
&lt;li&gt;连接：找到目标设备进行&lt;/li&gt;
&lt;li&gt;监听：开启监听功能，接收其他设备传过来的数据&lt;/li&gt;
&lt;li&gt;发送指令：不管发送数据还是读取数据，都可以理解为向外发送指令&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;初始化阶段&#34;&gt;&lt;a href=&#34;#初始化阶段&#34; class=&#34;headerlink&#34; title=&#34;初始化阶段&#34;&gt;&lt;/a&gt;初始化阶段&lt;/h4&gt;&lt;p&gt;使用蓝牙之前，需要初始化蓝牙模块，这是最最最开始就要做的！&lt;/p&gt;
&lt;p&gt;使用 uni.openBluetoothAdapter 这个 api 就可以初始化蓝牙模块。其他&lt;br&gt;蓝牙相关 API 必须在 uni.openBluetoothAdapter 调用之后使用。否则 API 会返回错误（ errCode=10000 ）。&lt;/p&gt;
&lt;p&gt;作者：德育处主任&lt;br&gt;链接：&lt;a href=&#34;https://juejin.cn/post/7093171532318375950&#34;&gt;https://juejin.cn/post/7093171532318375950&lt;/a&gt;&lt;br&gt;来源：稀土掘金&lt;br&gt;著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。&lt;/p&gt;
&lt;p&gt;代码示例&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;initBlue&amp;quot;&amp;gt;初始化蓝牙&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;script setup&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【1】初始化蓝牙&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function initBlue() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.openBluetoothAdapter(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;初始化蓝牙成功&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;初始化蓝牙失败&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;如果你手机开启了蓝牙，点击页面上的按钮后，控制台就会输出如下内容&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&amp;quot;errMsg&amp;quot;:&amp;quot;openBluetoothAdapter:ok&amp;quot;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;如果手机没开启蓝牙，就会返回如下内容&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&amp;quot;errMsg&amp;quot;:&amp;quot;openBluetoothAdapter:fail not available&amp;quot;,&amp;quot;code&amp;quot;:10001&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;10001代表当前蓝牙适配器不可用。&lt;/p&gt;
&lt;p&gt;如果你的控制台能打印出 {“errMsg”:”openBluetoothAdapter:ok”} 证明第一步已经成功了。&lt;/p&gt;
&lt;p&gt;接下来可以开始搜索附近蓝牙设备。&lt;/p&gt;
&lt;h4 id=&#34;搜寻附近设备&#34;&gt;&lt;a href=&#34;#搜寻附近设备&#34; class=&#34;headerlink&#34; title=&#34;搜寻附近设备&#34;&gt;&lt;/a&gt;搜寻附近设备&lt;/h4&gt;&lt;p&gt;这一步需要2个 api 配合完成。所以可以分解成以下2步：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开启搜寻功能：uni.startBluetoothDevicesDiscovery&lt;/li&gt;
&lt;li&gt;监听搜寻到新设备：uni.onBluetoothDeviceFound&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;开发蓝牙相关功能时，操作逻辑更像是推送，所以“开启搜索”和“监听新设备”是分开操作的。&lt;/p&gt;
&lt;p&gt;uni.startBluetoothDevicesDiscovery 可以让设备开始搜索附近蓝牙设备，但这个方法比较耗费系统资源，建议在连接到设备之后就使用 uni.stopBluetoothDevicesDiscovery 停止继续搜索。&lt;/p&gt;
&lt;p&gt;uni.startBluetoothDevicesDiscovery 方法里可以传入一个对象，该对象接收几个参数，但初学的话我们只关注 success 和 fail。如果你的项目中硬件佬有提供 service 的 uuid 给你的话，你也可以在 services 里传入。其他参数可以查看官方文档的介绍。&lt;/p&gt;
&lt;p&gt;在使用 uni.startBluetoothDevicesDiscovery （开始搜索）后，可以使用 uni.onBluetoothDeviceFound 进行监听，这个方法里面接收一个回调函数。&lt;/p&gt;
&lt;p&gt;代码示例&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;scroll-view&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            scroll-y&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            class=&amp;quot;box&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;view class=&amp;quot;item&amp;quot; v-for=&amp;quot;item in blueDeviceList&amp;quot;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;lt;text&amp;gt;id: &amp;#123;&amp;#123; item.deviceId &amp;#125;&amp;#125;&amp;lt;/text&amp;gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;lt;text&amp;gt;name: &amp;#123;&amp;#123; item.name &amp;#125;&amp;#125;&amp;lt;/text&amp;gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;/scroll-view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;initBlue&amp;quot;&amp;gt;初始化蓝牙&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;discovery&amp;quot;&amp;gt;搜索附近蓝牙设备&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;script setup&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;import &amp;#123; ref &amp;#125; from &amp;#x27;vue&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 搜索到的蓝牙设备列表&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;const blueDeviceList = ref([])&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【1】初始化蓝牙&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function initBlue() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.openBluetoothAdapter(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;初始化蓝牙成功&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;初始化蓝牙失败&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【2】开始搜寻附近设备&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function discovery() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.startBluetoothDevicesDiscovery(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;开始搜索&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            // 开启监听回调&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.onBluetoothDeviceFound(found)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;搜索失败&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【3】找到新设备就触发该方法&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function found(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    blueDeviceList.value.push(res.devices[0])&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;.box &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    width: 100%;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    height: 400rpx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    box-sizing: border-box;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    margin-bottom: 20rpx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    border: 2px solid dodgerblue;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;.item &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    box-sizing: border-box;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    padding: 10rpx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    border-bottom: 1px solid #ccc;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;button &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    margin-bottom: 20rpx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;上面代码的逻辑是，如果开启 “寻找附近设备” 功能成功，接着就开启 “监听寻找到新设备的事件” 。&lt;/p&gt;
&lt;p&gt;搜索到的设备会返回以下数据：&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;devices&amp;quot;: [&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;deviceId&amp;quot;: &amp;quot;B4:10:7B:C4:83:14&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;name&amp;quot;: &amp;quot;蓝牙设备名&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;RSSI&amp;quot;: -58,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;localName&amp;quot;: &amp;quot;&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;advertisServiceUUIDs&amp;quot;: [&amp;quot;0000FFF0-0000-1000-8000-00805F9B34FB&amp;quot;],&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;advertisData&amp;quot;: &amp;#123;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;每监听到一个新的设备，我都会将其添加到 蓝牙设备列表(blueDeviceList) 里，最后讲这个列表的数据渲染到页面上。&lt;/p&gt;
&lt;h4 id=&#34;连接目标设备&#34;&gt;&lt;a href=&#34;#连接目标设备&#34; class=&#34;headerlink&#34; title=&#34;连接目标设备&#34;&gt;&lt;/a&gt;连接目标设备&lt;/h4&gt;&lt;p&gt;连接目标设备只需要1个 api 就能完成。但根据文档提示，我们连接后还需要关闭 “搜索附近设备” 的功能，这个很好理解，既然找到了，再继续找就是浪费资源。&lt;/p&gt;
&lt;p&gt;流程如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;获取设备ID：根据 uni.onBluetoothDeviceFound 回调，拿到设备ID&lt;/li&gt;
&lt;li&gt;连接设备：使用设备ID进行连接 uni.createBLEConnection&lt;/li&gt;
&lt;li&gt;停止搜索：uni.stopBluetoothDevicesDiscovery&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我给每条搜索到的蓝牙结果添加一个 click 事件，会向目标设备发送连接请求。&lt;br&gt;我的设备名称是 leihou ，所以我点击了这条。&lt;/p&gt;
&lt;p&gt;代码示例&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;97&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;98&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;99&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;100&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;101&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;102&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;103&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;104&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;105&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;106&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;107&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;108&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;109&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;110&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;111&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;112&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;113&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;114&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;115&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;116&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;117&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;118&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;119&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;120&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;121&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;scroll-view&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            scroll-y&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            class=&amp;quot;box&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;view class=&amp;quot;item&amp;quot; v-for=&amp;quot;item in blueDeviceList&amp;quot; @click=&amp;quot;connect(item)&amp;quot;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;lt;text&amp;gt;id: &amp;#123;&amp;#123; item.deviceId &amp;#125;&amp;#125;&amp;lt;/text&amp;gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;lt;text&amp;gt;name: &amp;#123;&amp;#123; item.name &amp;#125;&amp;#125;&amp;lt;/text&amp;gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;/scroll-view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;initBlue&amp;quot;&amp;gt;初始化蓝牙&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;discovery&amp;quot;&amp;gt;搜索附近蓝牙设备&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;script setup&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;import &amp;#123; ref &amp;#125; from &amp;#x27;vue&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 搜索到的蓝牙设备列表&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;const blueDeviceList = ref([])&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【1】初始化蓝牙&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function initBlue() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.openBluetoothAdapter(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;初始化蓝牙成功&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;初始化蓝牙失败&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【2】开始搜寻附近设备&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function discovery() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.startBluetoothDevicesDiscovery(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;开始搜索&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            // 开启监听回调&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.onBluetoothDeviceFound(found)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;搜索失败&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【3】找到新设备就触发该方法&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function found(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    blueDeviceList.value.push(res.devices[0])&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 蓝牙设备的id&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;const deviceId = ref(&amp;#x27;&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【4】连接设备&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function connect(data) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    console.log(data)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    deviceId.value = data.deviceId&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.createBLEConnection(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        deviceId: deviceId.value,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;连接成功&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            // 停止搜索&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            stopDiscovery()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;连接失败&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【5】停止搜索&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function stopDiscovery() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.stopBluetoothDevicesDiscovery(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;停止成功&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;停止失败&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;.box &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    width: 100%;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    height: 400rpx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    box-sizing: border-box;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    margin-bottom: 20rpx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    border: 2px solid dodgerblue;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;.item &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    box-sizing: border-box;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    padding: 10rpx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    border-bottom: 1px solid #ccc;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;button &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    margin-bottom: 20rpx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;连接成功后在控制台会输出&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&amp;quot;errMsg&amp;quot;:&amp;quot;createBLEConnection:ok&amp;quot;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;在连接成功后就立刻调用 uni.stopBluetoothDevicesDiscovery 方法停止继续搜索附近其他设备，停止成功后会输出&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&amp;quot;errMsg&amp;quot;:&amp;quot;stopBluetoothDevicesDiscovery:ok&amp;quot;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h4 id=&#34;监听&#34;&gt;&lt;a href=&#34;#监听&#34; class=&#34;headerlink&#34; title=&#34;监听&#34;&gt;&lt;/a&gt;监听&lt;/h4&gt;&lt;p&gt;在连接完设备后，就要先开启监听数据的功能。这样才能接收到发送读写指令后设备给你回调的信息。&lt;/p&gt;
&lt;p&gt;要开启监听，首先需要知道蓝牙设备提供了那些服务，然后通过服务获取特征值，特征值会告诉你哪个可读，哪个可写。最后根据特征值进行消息监听。&lt;/p&gt;
&lt;p&gt;步骤如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;获取蓝牙设备服务：uni.getBLEDeviceServices&lt;/li&gt;
&lt;li&gt;获取特征值：uni.getBLEDeviceCharacteristics&lt;/li&gt;
&lt;li&gt;开启消息监听：uni.notifyBLECharacteristicValueChange&lt;/li&gt;
&lt;li&gt;接收消息监听传来的数据：uni.onBLECharacteristicValueChange&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;正常情况下，硬件佬会提前把蓝牙设备的指定服务还有特征值告诉你。&lt;/p&gt;
&lt;p&gt;比如我这个设备的蓝牙服务是：0000FFE0-0000-1000-8000-00805F9B34FB&lt;/p&gt;
&lt;p&gt;特征值是：0000FFE1-0000-1000-8000-00805F9B34FB&lt;/p&gt;
&lt;h5 id=&#34;第一步，获取蓝牙服务&#34;&gt;&lt;a href=&#34;#第一步，获取蓝牙服务&#34; class=&#34;headerlink&#34; title=&#34;第一步，获取蓝牙服务&#34;&gt;&lt;/a&gt;第一步，获取蓝牙服务&lt;/h5&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;!-- 省略上一步的代码 --&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;getServices&amp;quot;&amp;gt;获取蓝牙服务&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;script setup&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;import &amp;#123; ref &amp;#125; from &amp;#x27;vue&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 省略上一步的代码……&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【6】获取服务&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function getServices() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.getBLEDeviceServices(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        deviceId: deviceId.value, // 设备ID，在上一步【4】里获取&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;此时点击按钮，将会获取到已连接设备的所有服务。&lt;/p&gt;
&lt;p&gt;我的设备有以下几个服务。你在工作中拿到的 服务uuid 和我的是不一样的，数量也不一定相同。&lt;/p&gt;
&lt;p&gt;可以发现，我拿到的结果里有 0000FFE0-0000-1000-8000-00805F9B34FB 这条服务。&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;services&amp;quot;: [&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;uuid&amp;quot;: &amp;quot;00001800-0000-1000-8000-00805F9B34FB&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;isPrimary&amp;quot;: true&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;, &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;uuid&amp;quot;: &amp;quot;00001801-0000-1000-8000-00805F9B34FB&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;isPrimary&amp;quot;: true&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;, &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;uuid&amp;quot;: &amp;quot;0000180A-0000-1000-8000-00805F9B34FB&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;isPrimary&amp;quot;: true&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;, &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;uuid&amp;quot;: &amp;quot;0000FFF0-0000-1000-8000-00805F9B34FB&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;isPrimary&amp;quot;: true&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;, &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;uuid&amp;quot;: &amp;quot;0000FFE0-0000-1000-8000-00805F9B34FB&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;isPrimary&amp;quot;: true&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;],&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;errMsg&amp;quot;: &amp;quot;getBLEDeviceServices:ok&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h5 id=&#34;第二步，获取指定服务的特征值&#34;&gt;&lt;a href=&#34;#第二步，获取指定服务的特征值&#34; class=&#34;headerlink&#34; title=&#34;第二步，获取指定服务的特征值&#34;&gt;&lt;/a&gt;第二步，获取指定服务的特征值&lt;/h5&gt;&lt;p&gt;获取特征值，需要传 设备ID 和 服务ID。&lt;/p&gt;
&lt;p&gt;在上两步我拿到了 设备ID 为 B4:10:7B:C4:83:14，服务ID 为 0000FFE0-0000-1000-8000-00805F9B34FB。&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;!-- 省略前面几步代码 --&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;getCharacteristics&amp;quot;&amp;gt;获取特征值&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;script setup&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;import &amp;#123; ref &amp;#125; from &amp;#x27;vue&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 省略前面几步代码&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【7】获取特征值&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function getCharacteristics() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.getBLEDeviceCharacteristics(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        deviceId: deviceId.value, // 设备ID，在【4】里获取到&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        serviceId: &amp;#x27;0000FFE0-0000-1000-8000-00805F9B34FB&amp;#x27;, // 服务UUID，在【6】里能获取到&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;最后成功输出&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;characteristics&amp;quot;: [&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;uuid&amp;quot;: &amp;quot;0000FFE1-0000-1000-8000-00805F9B34FB&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;properties&amp;quot;: &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;quot;read&amp;quot;: true,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;quot;write&amp;quot;: true,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;quot;notify&amp;quot;: true,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;quot;indicate&amp;quot;: false&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;],&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;errMsg&amp;quot;: &amp;quot;getBLEDeviceCharacteristics:ok&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;characteristics 字段里保存了该服务的所有特征值，我的设备这个服务只有1个特征值，并且读、写、消息推送都为 true。&lt;/p&gt;
&lt;p&gt;你的设备可能不止一条特征值，需要监听那条特征值这需要你和硬件佬协商的（通常也是硬件佬直接和你说要监听哪条）。&lt;/p&gt;
&lt;h5 id=&#34;第三、四步，开启消息监听-并-接收消息监听传来的数据&#34;&gt;&lt;a href=&#34;#第三、四步，开启消息监听-并-接收消息监听传来的数据&#34; class=&#34;headerlink&#34; title=&#34;第三、四步，开启消息监听 并 接收消息监听传来的数据&#34;&gt;&lt;/a&gt;第三、四步，开启消息监听 并 接收消息监听传来的数据&lt;/h5&gt;&lt;p&gt;根据已经拿到的 设备ID、服务ID、特征值，就可以开启对应的监听功能。&lt;/p&gt;
&lt;p&gt;使用 uni.notifyBLECharacteristicValueChange 开启消息监听；&lt;/p&gt;
&lt;p&gt;并在 uni.onBLECharacteristicValueChange 方法触发监听到的消息。&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;!-- 省略前面几步代码 --&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;notify&amp;quot;&amp;gt;开启消息监听&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;script setup&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;import &amp;#123; ref &amp;#125; from &amp;#x27;vue&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 省略前面几步代码&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【8】开启消息监听&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function notify() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.notifyBLECharacteristicValueChange(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        deviceId: deviceId.value, // 设备ID，在【4】里获取到&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        serviceId: &amp;#x27;0000FFE0-0000-1000-8000-00805F9B34FB&amp;#x27;, // 服务UUID，在【6】里能获取到&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        characteristicId: &amp;#x27;0000FFE1-0000-1000-8000-00805F9B34FB&amp;#x27;, // 特征值，在【7】里能获取到&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            // 接受消息的方法&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            listenValueChange()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// ArrayBuffer转16进度字符串示例&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function ab2hex(buffer) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  const hexArr = Array.prototype.map.call(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    new Uint8Array(buffer),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    function (bit) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      return (&amp;#x27;00&amp;#x27; + bit.toString(16)).slice(-2)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  )&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  return hexArr.join(&amp;#x27;&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 将16进制的内容转成我们看得懂的字符串内容&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function hexCharCodeToStr(hexCharCodeStr) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    var trimedStr = hexCharCodeStr.trim();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    var rawStr = trimedStr.substr(0, 2).toLowerCase() === &amp;quot;0x&amp;quot; ? trimedStr.substr(2) : trimedStr;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    var len = rawStr.length;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    if (len % 2 !== 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            alert(&amp;quot;存在非法字符!&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            return &amp;quot;&amp;quot;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    var curCharCode;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    var resultStr = [];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    for (var i = 0; i &amp;lt; len; i = i + 2) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            curCharCode = parseInt(rawStr.substr(i, 2), 16);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            resultStr.push(String.fromCharCode(curCharCode));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    return resultStr.join(&amp;quot;&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【9】监听消息变化&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function listenValueChange() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.onBLECharacteristicValueChange(res =&amp;gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        // 结果&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        // 结果里有个value值，该值为 ArrayBuffer 类型，所以在控制台无法用肉眼观察到，必须将该值转换为16进制&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        let resHex = ab2hex(res.value)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        console.log(resHex)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        // 最后将16进制转换为ascii码，就能看到对应的结果&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        let result = hexCharCodeToStr(resHex)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        console.log(result)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;listenValueChange 方法是用来接收设备传过来的消息。&lt;/p&gt;
&lt;p&gt;上面的例子中，res 的结果是&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;deviceId&amp;quot;: &amp;quot;B4:10:7B:C4:83:14&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;serviceId&amp;quot;: &amp;quot;0000FFE0-0000-1000-8000-00805F9B34FB&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;characteristicId&amp;quot;: &amp;quot;0000FFE1-0000-1000-8000-00805F9B34FB&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;value&amp;quot;: &amp;#123;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;设备传过来的内容就放在 value 字段里，但因为该字段的类型是 ArrayBuffer，所以无法在控制台用肉眼直接观察。于是就通过 ab2hex 方法将该值转成 16进制 ，最后再用 hexCharCodeToStr 方法将 16进制 转成 ASCII码。&lt;/p&gt;
&lt;p&gt;我从设备里发送一段字符串过来：leihou&lt;/p&gt;
&lt;p&gt;App端收到的数据转成 16进制 后的结果：6c6569686f75&lt;/p&gt;
&lt;p&gt;再从 16进制 转成 ASCII码 后的结果：leihou&lt;/p&gt;
&lt;h4 id=&#34;发送指令&#34;&gt;&lt;a href=&#34;#发送指令&#34; class=&#34;headerlink&#34; title=&#34;发送指令&#34;&gt;&lt;/a&gt;发送指令&lt;/h4&gt;&lt;p&gt;终于到最后一步了。&lt;/p&gt;
&lt;p&gt;从 uni-app 和 微信小程序 提供的蓝牙api 来看，发送指令只要有2个方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;uni.writeBLECharacteristicValue：向低功耗蓝牙设备特征值中写入二进制数据。&lt;/li&gt;
&lt;li&gt;uni.readBLECharacteristicValue：读取低功耗蓝牙设备的特征值的二进制数据值。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里需要理清一个概念，本节的内容为 “发送指令” ，也就是说，从你的app或小程序向其他蓝牙设备发送指令，而这个指令分2种情况，一种是你要发送一些数据给蓝牙设备，另一种情况是你叫蓝牙设备给你发点信息。&lt;/p&gt;
&lt;p&gt;uni.writeBLECharacteristicValue&lt;/p&gt;
&lt;p&gt;这两种情况我们需要分开讨论，先讲uni.writeBLECharacteristicValue 。&lt;/p&gt;
&lt;p&gt;uni.writeBLECharacteristicValue 从文档可以看出，这个 api 是可以发送一些数据给蓝牙设备，但发送的值要转成 ArrayBuffer 。&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;!-- 省略前面几步代码 --&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;send&amp;quot;&amp;gt;发送数据&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;script setup&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;import &amp;#123; ref &amp;#125; from &amp;#x27;vue&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 省略前面几步代码&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【10】发送数据&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function send() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    // 向蓝牙设备发送一个0x00的16进制数据&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    let msg = &amp;#x27;hello&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    const buffer = new ArrayBuffer(msg.length)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    const dataView = new DataView(buffer)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    // dataView.setUint8(0, 0)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    for (var i = 0; i &amp;lt; msg.length; i++) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      dataView.setUint8(i, msg.charAt(i).charCodeAt())&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.writeBLECharacteristicValue(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      deviceId: deviceId.value, // 设备ID，在【4】里获取到&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      serviceId: &amp;#x27;0000FFE0-0000-1000-8000-00805F9B34FB&amp;#x27;, // 服务UUID，在【6】里能获取到&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      characteristicId: &amp;#x27;0000FFE1-0000-1000-8000-00805F9B34FB&amp;#x27;, // 特征值，在【7】里能获取到&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      value: buffer,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;此时，如果 uni.writeBLECharacteristicValue 走 success ，证明你已经把数据向外成功发送了，但不代表设备一定就收到了。&lt;/p&gt;
&lt;p&gt;通常设备收到你发送过去的信息，会返回一条消息给你，而这个回调消息会在 uni.onBLECharacteristicValueChange 触发，也就是 第【9】步 那里。但这是蓝牙设备那边控制的，你作为前端佬，人家“已读不回”你也拿人家没办法。&lt;/p&gt;
&lt;p&gt;uni.readBLECharacteristicValue&lt;/p&gt;
&lt;p&gt;在 “监听” 部分，我们使用了 uni.getBLEDeviceCharacteristics 获取设备的特征值，我的设备提供的特征值支持 read ，所以可以使用 uni.readBLECharacteristicValue 向蓝牙设备发送一条 “读取” 指令。然后在 uni.onBLECharacteristicValueChange 里可以接收设备发送过来的数据。&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;!-- 省略前面几步代码 --&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;read&amp;quot;&amp;gt;读取数据&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;script setup&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;import &amp;#123; ref &amp;#125; from &amp;#x27;vue&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 省略前面几步代码&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【11】读取数据&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function read() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.readBLECharacteristicValue(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        deviceId: deviceId.value,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        serviceId: serviceId.value,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        characteristicId: characteristicId.value,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;读取指令发送成功&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;读取指令发送失败&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;使用 “读取” 的方式向设备发送指令，是不需要另外传值的。&lt;/p&gt;
&lt;p&gt;此时我的设备返回 00&lt;/p&gt;
&lt;p&gt;这个数据是硬件那边设置的。&lt;/p&gt;
&lt;p&gt;在日常工作中，uni.readBLECharacteristicValue 的作用主要是读取数据，但使用场景不算很多。&lt;/p&gt;
&lt;p&gt;我在工作中遇到的场景是：蓝牙设备提供了几个接口，而且传过来的数据比较大，比如传图片给app这边。我就会先用 uni.writeBLECharacteristicValue 告诉设备我现在需要取什么接口的数据，然后用 uni.readBLECharacteristicValue 发送读取数据的请求，如果数据量比较大，就要重复使用 uni.readBLECharacteristicValue 进行读取。比如上面的例子，我读第一次的时候返回 00 ，读第二次就返回 01 ……&lt;/p&gt;
&lt;p&gt;最后再提醒一下，uni.readBLECharacteristicValue 只负责发送读取的请求，并且里面的 success 和 fail 只是返回你本次发送请求的动作是否成功，至于对面的蓝牙设备有没有收到这个指令你是不清楚的。&lt;/p&gt;
&lt;p&gt;最后需要通过 uni.getBLEDeviceCharacteristics 监听设备传过来的数据。&lt;/p&gt;
&lt;p&gt;完整代码&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;97&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;98&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;99&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;100&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;101&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;102&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;103&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;104&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;105&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;106&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;107&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;108&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;109&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;110&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;111&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;112&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;113&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;114&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;115&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;116&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;117&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;118&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;119&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;120&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;121&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;122&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;123&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;124&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;125&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;126&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;127&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;128&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;129&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;130&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;131&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;132&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;133&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;134&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;135&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;136&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;137&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;138&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;139&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;140&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;141&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;142&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;143&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;144&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;145&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;146&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;147&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;148&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;149&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;150&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;151&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;152&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;153&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;154&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;155&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;156&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;157&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;158&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;159&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;160&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;161&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;162&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;163&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;164&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;165&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;166&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;167&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;168&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;169&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;170&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;171&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;172&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;173&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;174&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;175&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;176&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;177&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;178&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;179&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;180&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;181&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;182&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;183&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;184&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;185&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;186&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;187&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;188&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;189&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;190&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;191&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;192&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;193&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;194&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;195&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;196&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;197&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;198&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;199&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;200&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;201&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;202&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;203&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;204&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;205&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;206&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;207&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;208&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;209&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;210&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;211&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;212&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;213&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;214&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;215&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;216&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;217&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;218&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;219&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;220&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;221&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;222&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;223&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;224&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;225&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;226&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;227&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;228&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;229&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;230&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;231&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;232&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;233&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;234&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;235&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;236&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;237&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;238&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;239&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;240&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;241&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;242&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;243&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;244&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;245&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;246&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;247&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;248&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;249&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;250&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;251&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;252&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;253&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;254&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;255&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;256&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;257&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;258&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;259&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;260&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;261&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;262&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;263&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;264&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;265&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;266&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;267&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;268&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;269&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;270&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;271&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;272&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;273&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;274&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;275&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;276&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;277&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;278&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;279&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;280&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;281&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;282&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;283&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;284&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;285&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;286&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;287&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;288&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;289&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;290&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;291&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;292&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;293&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;294&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;295&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;296&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;297&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;298&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;299&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;300&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;301&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;302&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;303&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;304&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;305&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;306&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;307&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;308&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;309&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;310&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;311&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;312&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;313&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;314&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;315&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;316&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;317&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;318&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;319&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;320&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;321&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;322&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;323&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;324&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;325&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;326&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;327&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;328&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;329&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;330&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;331&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;scroll-view&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            scroll-y&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            class=&amp;quot;box&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;view class=&amp;quot;item&amp;quot; v-for=&amp;quot;item in blueDeviceList&amp;quot; @click=&amp;quot;connect(item)&amp;quot;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;lt;text&amp;gt;id: &amp;#123;&amp;#123; item.deviceId &amp;#125;&amp;#125;&amp;lt;/text&amp;gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;lt;view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;lt;text&amp;gt;name: &amp;#123;&amp;#123; item.name &amp;#125;&amp;#125;&amp;lt;/text&amp;gt;  &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;/scroll-view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;initBlue&amp;quot;&amp;gt;1 初始化蓝牙&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;discovery&amp;quot;&amp;gt;2 搜索附近蓝牙设备&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;getServices&amp;quot;&amp;gt;3 获取蓝牙服务&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;getCharacteristics&amp;quot;&amp;gt;4 获取特征值&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;notify&amp;quot;&amp;gt;5 开启消息监听&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;send&amp;quot;&amp;gt;6 发送数据&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;button @click=&amp;quot;read&amp;quot;&amp;gt;7 读取数据&amp;lt;/button&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;view class=&amp;quot;msg_x&amp;quot;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;view class=&amp;quot;msg_txt&amp;quot;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                监听到的内容：&amp;#123;&amp;#123; message &amp;#125;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;view class=&amp;quot;msg_hex&amp;quot;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                监听到的内容（十六进制）：&amp;#123;&amp;#123; messageHex &amp;#125;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;/view&amp;gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;/view&amp;gt; &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/view&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;script setup&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;import &amp;#123; ref &amp;#125; from &amp;#x27;vue&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 搜索到的蓝牙设备列表&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;const blueDeviceList = ref([])&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【1】初始化蓝牙&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function initBlue() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.openBluetoothAdapter(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;初始化蓝牙成功&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;初始化蓝牙失败&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【2】开始搜寻附近设备&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function discovery() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.startBluetoothDevicesDiscovery(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;开始搜索&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            // 开启监听回调&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.onBluetoothDeviceFound(found)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;搜索失败&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【3】找到新设备就触发该方法&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function found(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    blueDeviceList.value.push(res.devices[0])&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 蓝牙设备的id&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;const deviceId = ref(&amp;#x27;&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【4】连接设备&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function connect(data) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    console.log(data)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    deviceId.value = data.deviceId // 将获取到的设备ID存起来&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.createBLEConnection(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        deviceId: deviceId.value,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;连接成功&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            // 停止搜索&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            stopDiscovery()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.showToast(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                title: &amp;#x27;连接成功&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;连接失败&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.showToast(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                title: &amp;#x27;连接成功&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                icon: &amp;#x27;error&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【5】停止搜索&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function stopDiscovery() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.stopBluetoothDevicesDiscovery(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;停止成功&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(&amp;#x27;停止失败&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【6】获取服务&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function getServices() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    // 如果是自动链接的话，uni.getBLEDeviceServices方法建议使用setTimeout延迟1秒后再执行&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.getBLEDeviceServices(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        deviceId: deviceId.value,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res) // 可以在res里判断有没有硬件佬给你的服务&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.showToast(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                title: &amp;#x27;获取服务成功&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.showToast(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                title: &amp;#x27;获取服务失败&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                icon: &amp;#x27;error&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 硬件提供的服务id，开发中需要问硬件佬获取该id&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;const serviceId = ref(&amp;#x27;0000FFE0-0000-1000-8000-00805F9B34FB&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【7】获取特征值&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function getCharacteristics() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    // 如果是自动链接的话，uni.getBLEDeviceCharacteristics方法建议使用setTimeout延迟1秒后再执行&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.getBLEDeviceCharacteristics(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        deviceId: deviceId.value,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        serviceId: serviceId.value,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res) // 可以在此判断特征值是否支持读写等操作，特征值其实也需要提前向硬件佬索取的&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.showToast(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                title: &amp;#x27;获取特征值成功&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.showToast(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                title: &amp;#x27;获取特征值失败&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                icon: &amp;#x27;error&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;const characteristicId = ref(&amp;#x27;0000FFE1-0000-1000-8000-00805F9B34FB&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【8】开启消息监听&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function notify() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.notifyBLECharacteristicValueChange(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        deviceId: deviceId.value, // 设备id&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        serviceId: serviceId.value, // 监听指定的服务&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        characteristicId: characteristicId.value, // 监听对应的特征值&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            listenValueChange()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.showToast(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                title: &amp;#x27;已开启监听&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.showToast(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                title: &amp;#x27;监听失败&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                icon: &amp;#x27;error&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// ArrayBuffer转16进度字符串示例&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function ab2hex(buffer) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  const hexArr = Array.prototype.map.call(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    new Uint8Array(buffer),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    function (bit) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      return (&amp;#x27;00&amp;#x27; + bit.toString(16)).slice(-2)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  )&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  return hexArr.join(&amp;#x27;&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 将16进制的内容转成我们看得懂的字符串内容&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function hexCharCodeToStr(hexCharCodeStr) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    var trimedStr = hexCharCodeStr.trim();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    var rawStr = trimedStr.substr(0, 2).toLowerCase() === &amp;quot;0x&amp;quot; ? trimedStr.substr(2) : trimedStr;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    var len = rawStr.length;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    if (len % 2 !== 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            alert(&amp;quot;存在非法字符!&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            return &amp;quot;&amp;quot;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    var curCharCode;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    var resultStr = [];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    for (var i = 0; i &amp;lt; len; i = i + 2) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            curCharCode = parseInt(rawStr.substr(i, 2), 16);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            resultStr.push(String.fromCharCode(curCharCode));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    return resultStr.join(&amp;quot;&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 监听到的内容&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;const message = ref(&amp;#x27;&amp;#x27;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;const messageHex = ref(&amp;#x27;&amp;#x27;) // 十六进制&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【9】监听消息变化&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function listenValueChange() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.onBLECharacteristicValueChange(res =&amp;gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        let resHex = ab2hex(res.value)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        console.log(resHex)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        messageHex.value = resHex&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        let result = hexCharCodeToStr(resHex)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        console.log(String(result))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        message.value = String(result)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【10】发送数据&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function send() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    // 向蓝牙设备发送一个0x00的16进制数据&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    let msg = &amp;#x27;hello&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    const buffer = new ArrayBuffer(msg.length)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    const dataView = new DataView(buffer)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    // dataView.setUint8(0, 0)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    for (var i = 0; i &amp;lt; msg.length; i++) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      dataView.setUint8(i, msg.charAt(i).charCodeAt())&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.writeBLECharacteristicValue(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      deviceId: deviceId.value,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      serviceId: serviceId.value,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      characteristicId: characteristicId.value,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      value: buffer,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        console.log(&amp;#x27;writeBLECharacteristicValue success&amp;#x27;, res.errMsg)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.showToast(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                title: &amp;#x27;write指令发送成功&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.showToast(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                title: &amp;#x27;write指令发送失败&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                icon: &amp;#x27;error&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 【11】读取数据&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function read() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    uni.readBLECharacteristicValue(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        deviceId: deviceId.value,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        serviceId: serviceId.value,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        characteristicId: characteristicId.value,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        success(res) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.log(res)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.showToast(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                title: &amp;#x27;read指令发送成功&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        fail(err) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            console.error(err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            uni.showToast(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                title: &amp;#x27;read指令发送失败&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                icon: &amp;#x27;error&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;.box &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    width: 98%;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    height: 400rpx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    box-sizing: border-box;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    margin: 0 auto 20rpx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    border: 2px solid dodgerblue;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;.item &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    box-sizing: border-box;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    padding: 10rpx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    border-bottom: 1px solid #ccc;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;button &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    margin-bottom: 20rpx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;.msg_x &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    border: 2px solid seagreen;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    width: 98%;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    margin: 10rpx auto;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    box-sizing: border-box;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    padding: 20rpx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;​&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;.msg_x .msg_txt &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    margin-bottom: 20rpx;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;作者：德育处主任&lt;br&gt;链接：&lt;a href=&#34;https://juejin.cn/post/7093171532318375950&#34;&gt;https://juejin.cn/post/7093171532318375950&lt;/a&gt;&lt;br&gt;来源：稀土掘金&lt;br&gt;著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/08/18/development-scenarios-learning-directions/</guid>
            <title>开发语言应用场景与学习方向解析</title>
            <link>https://erik.xyz/2025/08/18/development-scenarios-learning-directions/</link>
            <category>开发语言</category>
            <category>开发</category>
            <pubDate>Mon, 18 Aug 2025 23:19:00 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;以下是针对主流开发语言（PHP、Go、Rust、C、C++、Java、C#、Qt）的应用场景、优劣势及学习方向的综合分析，结合技术特性和行业实践整理而成：&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;📊-一、语言对比概览表&#34;&gt;&lt;a href=&#34;#📊-一、语言对比概览表&#34; class=&#34;headerlink&#34; title=&#34;📊 一、语言对比概览表&#34;&gt;&lt;/a&gt;📊 &lt;strong&gt;一、语言对比概览表&lt;/strong&gt;&lt;/h3&gt;&lt;div class=&#34;table-container&#34;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;语言&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;主要应用场景&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;关键优势&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;典型劣势&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PHP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;中小型Web开发、CMS系统&lt;/td&gt;
&lt;td&gt;开发快、易入门、框架丰富&lt;/td&gt;
&lt;td&gt;性能中等、安全风险需管控&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Go&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;云原生、分布式服务、工具开发&lt;/td&gt;
&lt;td&gt;高并发、编译高效、部署简单&lt;/td&gt;
&lt;td&gt;生态较小、GUI支持弱&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rust&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;系统编程、安全敏感型基础设施&lt;/td&gt;
&lt;td&gt;内存安全、零成本抽象、高性能&lt;/td&gt;
&lt;td&gt;学习曲线陡、编译慢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;操作系统、嵌入式、硬件驱动&lt;/td&gt;
&lt;td&gt;极致性能、硬件级控制&lt;/td&gt;
&lt;td&gt;手动内存管理、易出安全漏洞&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C++&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;游戏引擎、高频交易、大型系统&lt;/td&gt;
&lt;td&gt;高性能、多范式支持、生态成熟&lt;/td&gt;
&lt;td&gt;复杂难精、迭代缓慢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Java&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;企业应用、Android开发、大数据&lt;/td&gt;
&lt;td&gt;跨平台、生态强大、稳定性高&lt;/td&gt;
&lt;td&gt;内存占用高、启动慢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C#&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unity游戏、企业桌面应用、IoT&lt;/td&gt;
&lt;td&gt;强类型系统、IDE强大、语法优雅&lt;/td&gt;
&lt;td&gt;跨平台能力弱于Java&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Qt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;跨平台GUI、工业控制界面&lt;/td&gt;
&lt;td&gt;界面美观、信号槽机制、跨平台一致&lt;/td&gt;
&lt;td&gt;库体积大、商业授权复杂&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;
&lt;h3 id=&#34;🔍-二、分语言详解&#34;&gt;&lt;a href=&#34;#🔍-二、分语言详解&#34; class=&#34;headerlink&#34; title=&#34;🔍 二、分语言详解&#34;&gt;&lt;/a&gt;🔍 &lt;strong&gt;二、分语言详解&lt;/strong&gt;&lt;/h3&gt;&lt;h4 id=&#34;1-PHP&#34;&gt;&lt;a href=&#34;#1-PHP&#34; class=&#34;headerlink&#34; title=&#34;1. PHP&#34;&gt;&lt;/a&gt;1. &lt;strong&gt;PHP&lt;/strong&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：动态网站（如WordPress）、电商平台（Magento）、API服务。  &lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;开发速度极快，语法简单，LAMP/LNMP生态成熟。  &lt;/li&gt;
&lt;li&gt;社区资源丰富（如Laravel框架）。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;劣势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;默认性能低于编译型语言，需配合Swoole扩展优化。  &lt;/li&gt;
&lt;li&gt;历史代码存在SQL注入等安全隐患，需严格遵循规范。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;学习方向&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;掌握现代框架（Laravel/Symfony），学习异步扩展（Swoole），强化安全实践（如PSR标准）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;2-Go&#34;&gt;&lt;a href=&#34;#2-Go&#34; class=&#34;headerlink&#34; title=&#34;2. Go&#34;&gt;&lt;/a&gt;2. &lt;strong&gt;Go&lt;/strong&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：Docker/Kubernetes云原生生态、微服务、高并发API。  &lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;Goroutine轻量级并发模型，编译为单二进制文件，部署便捷。  &lt;/li&gt;
&lt;li&gt;内置垃圾回收和标准库（如HTTP/JSON处理）。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;劣势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;依赖管理（go mod）仍待完善，缺乏泛型历史遗留问题（1.18后已支持）。  &lt;/li&gt;
&lt;li&gt;图形界面生态薄弱，不适合桌面GUI开发。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;学习方向&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;深入并发模型（Channel模式），学习云原生工具链（K8s+Grpc），掌握性能调优。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;3-Rust&#34;&gt;&lt;a href=&#34;#3-Rust&#34; class=&#34;headerlink&#34; title=&#34;3. Rust&#34;&gt;&lt;/a&gt;3. &lt;strong&gt;Rust&lt;/strong&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：操作系统（如Redox）、浏览器引擎（Servo）、区块链底层。  &lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;所有权机制保障内存/线程安全，零运行时开销媲美C++。  &lt;/li&gt;
&lt;li&gt;模式匹配和错误处理（Result/Option）提升代码健壮性。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;劣势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;学习曲线陡峭（生命周期标注难度高），编译时间长。  &lt;/li&gt;
&lt;li&gt;生态较新，企业级框架少于Java/C#。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;学习方向&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;精读《The Rust Book》，实战WebAssembly或系统工具开发，参与开源贡献。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;4-C&#34;&gt;&lt;a href=&#34;#4-C&#34; class=&#34;headerlink&#34; title=&#34;4. C&#34;&gt;&lt;/a&gt;4. &lt;strong&gt;C&lt;/strong&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：Linux内核、单片机固件、数据库引擎（如SQLite）。  &lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;无运行时依赖，直接操作硬件，性能极致优化。  &lt;/li&gt;
&lt;li&gt;语法简洁，是后续语言（C++/Go）的设计基础。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;劣势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;手动内存管理易导致泄漏/溢出，缺乏现代抽象机制。  &lt;/li&gt;
&lt;li&gt;项目规模扩大后维护成本激增。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;学习方向&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;深入指针与内存管理，学习Linux系统编程，掌握调试工具（Valgrind/GDB）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;5-C&#34;&gt;&lt;a href=&#34;#5-C&#34; class=&#34;headerlink&#34; title=&#34;5. C++&#34;&gt;&lt;/a&gt;5. &lt;strong&gt;C++&lt;/strong&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：游戏引擎（Unreal）、高频交易系统、CAD软件。  &lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;零成本抽象（模板元编程），多范式支持（OOP/泛型/函数式）。  &lt;/li&gt;
&lt;li&gt;STL提供高效数据结构，兼容C生态。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;劣势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;语法复杂（如移动语义），旧代码迭代困难（兼容性负担）。  &lt;/li&gt;
&lt;li&gt;安全漏洞风险（如缓冲区溢出）高于Java/C#。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;学习方向&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;掌握现代标准（C++17/20），学习性能优化（内存池/并发模型），熟悉Qt框架结合开发。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;6-Java&#34;&gt;&lt;a href=&#34;#6-Java&#34; class=&#34;headerlink&#34; title=&#34;6. Java&#34;&gt;&lt;/a&gt;6. &lt;strong&gt;Java&lt;/strong&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：企业级后台（Spring生态）、Android应用、Hadoop大数据。  &lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;JVM跨平台特性，GC减少手动内存负担，生态完备（Maven/Spring）。  &lt;/li&gt;
&lt;li&gt;多线程支持成熟（JUC包），监控工具（JMX/Arthas）完善。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;劣势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;启动慢，内存占用高（需堆配置优化），语法冗长。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;学习方向&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;精通JVM机制（类加载/GC算法），学习微服务框架（Spring Cloud），掌握异步编程（CompletableFuture）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;7-C&#34;&gt;&lt;a href=&#34;#7-C&#34; class=&#34;headerlink&#34; title=&#34;7. C#&#34;&gt;&lt;/a&gt;7. &lt;strong&gt;C#&lt;/strong&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：Unity3D游戏开发、Windows桌面应用（WPF）、工业IoT。  &lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;LINQ简化数据查询，异步编程（async/await）模型优雅。  &lt;/li&gt;
&lt;li&gt;Visual Studio提供顶级开发体验（调试/重构）。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;劣势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;跨平台能力弱于Java（.NET Core已改善但生态仍不足）。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;学习方向&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;深入.NET Core跨平台开发，学习Unity引擎整合，掌握设计模式（MVVM）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;8-Qt（C-框架）&#34;&gt;&lt;a href=&#34;#8-Qt（C-框架）&#34; class=&#34;headerlink&#34; title=&#34;8. Qt（C++框架）&#34;&gt;&lt;/a&gt;8. &lt;strong&gt;Qt（C++框架）&lt;/strong&gt;&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：工业控制界面、跨平台桌面软件（WPS/Linux桌面）。  &lt;ul&gt;
&lt;li&gt;&lt;strong&gt;优势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;信号槽机制简化事件处理，QML支持动态UI设计。  &lt;/li&gt;
&lt;li&gt;一次开发多平台部署（Windows/Linux/macOS）。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;劣势&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;库体积大（影响小型设备），商业项目需付费授权。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;学习方向&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;学习C++基础后专攻Qt Widgets/QML，掌握跨平台编译（CMake）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;💎-三、总结建议&#34;&gt;&lt;a href=&#34;#💎-三、总结建议&#34; class=&#34;headerlink&#34; title=&#34;💎 三、总结建议&#34;&gt;&lt;/a&gt;💎 &lt;strong&gt;三、总结建议&lt;/strong&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;追求开发效率&lt;/strong&gt;：选&lt;strong&gt;PHP&lt;/strong&gt;（Web快速迭代）或&lt;strong&gt;Go&lt;/strong&gt;（微服务）。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;需要高性能与安全&lt;/strong&gt;：用&lt;strong&gt;Rust&lt;/strong&gt;（系统层）或&lt;strong&gt;C++&lt;/strong&gt;（游戏/高频场景）。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;企业级/跨平台&lt;/strong&gt;：&lt;strong&gt;Java&lt;/strong&gt;（后台/Android）或&lt;strong&gt;C#&lt;/strong&gt;（Windows/Unity）。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;嵌入式/GUI&lt;/strong&gt;：&lt;strong&gt;C&lt;/strong&gt;（硬件层）搭配&lt;strong&gt;Qt&lt;/strong&gt;（界面层）。  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;学习优先级&lt;/strong&gt;：根据目标领域选择：  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Web开发&lt;/strong&gt;：PHP → Go → Rust（WASM）。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;系统/嵌入式&lt;/strong&gt;：C → C++ → Rust。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全栈/企业应用&lt;/strong&gt;：Java → C# → Go。  &lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;注：语言生态持续演进（如Go泛型、Rust异步），需结合最新技术动态调整学习重点。&lt;/p&gt;
&lt;/blockquote&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/07/25/spark-hive-hadoo-combination-guide/</guid>
            <title>Spark、Hive、Hadoop选择与组合使用指南</title>
            <link>https://erik.xyz/2025/07/25/spark-hive-hadoo-combination-guide/</link>
            <category>hadoop</category>
            <category>大数据</category>
            <category>spark</category>
            <category>hive</category>
            <category>数据分析</category>
            <pubDate>Fri, 25 Jul 2025 21:36:00 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;在构建大数据平台时，Spark、Hive 和 Hadoop 的关系是&lt;strong&gt;互补协作&lt;/strong&gt;而非互斥。选择哪种技术以及如何组合取决于你的具体需求（数据量、处理速度、查询复杂度、成本、团队技能）。以下是如何选择和结合使用的指南：&lt;/p&gt;
&lt;h2 id=&#34;核心定位与区别&#34;&gt;&lt;a href=&#34;#核心定位与区别&#34; class=&#34;headerlink&#34; title=&#34;核心定位与区别&#34;&gt;&lt;/a&gt;核心定位与区别&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Hadoop (主要指 HDFS + MapReduce/YARN):&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;定位：&lt;/strong&gt; 分布式存储(HDFS) + 基础资源管理和批处理框架(MapReduce/YARN)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优势：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;高可靠、高容错的分布式存储 (HDFS):&lt;/strong&gt; 存储海量数据的基石。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;成熟的资源管理 (YARN):&lt;/strong&gt; 协调集群资源（CPU, 内存）供上层应用（Spark, MapReduce, Hive on MR/Tez）使用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;极高的可扩展性和成本效益：&lt;/strong&gt; 能在廉价硬件上构建大规模集群。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;劣势：&lt;/strong&gt; MapReduce 计算模型&lt;strong&gt;速度慢&lt;/strong&gt;（尤其迭代计算、交互式查询），编程相对复杂。&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hive:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;定位：&lt;/strong&gt; 构建在 Hadoop 之上的&lt;strong&gt;数据仓库框架&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优势：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;类 SQL 接口 (HiveQL):&lt;/strong&gt; 极大降低了使用门槛，让熟悉 SQL 的分析师和工程师能处理大数据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;强大的元数据管理 (Metastore):&lt;/strong&gt; 管理表结构、分区、数据类型等，是数据治理的关键。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;批处理优化：&lt;/strong&gt; 将 SQL 查询转换为 MapReduce/Tez/Spark 作业执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;劣势：&lt;/strong&gt; 传统上基于 MapReduce/Tez，&lt;strong&gt;延迟较高&lt;/strong&gt;，不适合低延迟交互式查询或流处理（虽然有 LLAP 和 Hive on Spark 改进）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Spark:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;定位：&lt;/strong&gt; 统一&lt;strong&gt;的、高性能的分布式计算引擎&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优势：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;极快的速度：&lt;/strong&gt; 基于内存计算、DAG 执行引擎、高度优化的执行计划，比 MapReduce 快几个数量级。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;统一引擎：&lt;/strong&gt; 支持批处理(Spark SQL)、流处理(Spark Streaming, Structured Streaming)、机器学习(MLlib)、图计算(GraphX)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;易用的 API：&lt;/strong&gt; 提供 Scala, Java, Python, R 等多种语言的 API，开发效率高。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;灵活的部署：&lt;/strong&gt; 可以在 YARN (Hadoop), Mesos, Kubernetes 或 Standalone 模式上运行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;与 Hive 集成：&lt;/strong&gt; 可以直接读取 Hive Metastore 中的元数据，运行 HiveQL 查询（Spark SQL）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;劣势：&lt;/strong&gt; 对内存要求较高，纯内存计算成本可能高于基于磁盘的 MapReduce（但速度优势通常远超成本差异）；非常复杂的计算可能需要更精细的内存调优。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;如何选择？&#34;&gt;&lt;a href=&#34;#如何选择？&#34; class=&#34;headerlink&#34; title=&#34;如何选择？&#34;&gt;&lt;/a&gt;如何选择？&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;存储层：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HDFS 通常是默认和基石选择：&lt;/strong&gt; 如果你需要存储 PB 级甚至 EB 级数据，要求高容错、高可靠、成本效益，&lt;strong&gt;HDFS 几乎是必选项&lt;/strong&gt;。它是 Spark 和 Hive 最常见的数据来源和存储目的地。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;替代方案：&lt;/strong&gt; 云存储（S3, GCS, ADLS）越来越流行，它们提供无限扩展、按需付费、高可用性，并且 Spark/Hive 都支持直接读写。选择云存储通常能简化运维。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;计算引擎：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;需要极致的处理速度（批处理、流处理、迭代算法、机器学习）？&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;首选 Spark：&lt;/strong&gt; 它在绝大多数计算场景下都远快于 MapReduce/Tez。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;主要需求是使用熟悉的 SQL 进行批处理分析和报表，团队 SQL 技能强？&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hive 是很好的起点：&lt;/strong&gt; 利用其强大的元数据管理和 SQL 接口。但&lt;strong&gt;强烈建议将执行引擎配置为 Spark (Hive on Spark)&lt;/strong&gt;，而不是 MapReduce 或 Tez，以获得显著的性能提升。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spark SQL：&lt;/strong&gt; 也可以直接使用 Spark SQL 来执行 SQL 查询，它同样支持 HiveQL 语法（兼容 Hive Metastore），性能通常优于 Hive on Spark。如果你已经在用 Spark 做其他事情（如 ETL, ML），Spark SQL 是更统一的选择。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;需要亚秒级响应的交互式查询？&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hive LLAP：&lt;/strong&gt; Hive 的 Live Long and Process 模式提供内存缓存和守护进程，加速查询。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spark Thrift Server / JDBC/ODBC Server：&lt;/strong&gt; 允许 BI 工具通过标准接口连接 Spark SQL 进行交互式查询。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;专用交互式引擎：&lt;/strong&gt; 如 Presto, Impala, Druid 等（这些通常也依赖 HDFS/S3 存储和 Hive Metastore）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;需要实时/准实时流处理？&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Spark Structured Streaming：&lt;/strong&gt; 首选的统一引擎方案。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flink：&lt;/strong&gt; 另一个强大的流处理引擎（Spark 和 Flink 是当前流处理的主流）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;需要做机器学习或图计算？&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Spark MLlib / GraphX：&lt;/strong&gt; Spark 的内置库提供了丰富的功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;元数据管理：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hive Metastore (HMS) 是事实标准：&lt;/strong&gt; 无论你主要使用 Hive、Spark SQL 还是其他查询引擎（Presto, Impala），&lt;strong&gt;强烈建议使用 Hive Metastore&lt;/strong&gt; 来集中管理表结构、分区、数据类型、存储位置等元数据。这保证了不同引擎对数据有一致的视图，是数据治理的基础。Spark 原生支持读写 Hive Metastore。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;典型的组合使用模式&#34;&gt;&lt;a href=&#34;#典型的组合使用模式&#34; class=&#34;headerlink&#34; title=&#34;典型的组合使用模式&#34;&gt;&lt;/a&gt;典型的组合使用模式&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;经典批处理数据仓库 (Hive-Centric):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;存储:&lt;/strong&gt; HDFS 或 S3/GCS/ADLS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;元数据:&lt;/strong&gt; Hive Metastore&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ETL/计算：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;em&gt;传统/基础版：&lt;/em&gt; Hive on MapReduce/Tez (利用 HiveQL 编写 ETL 和查询逻辑)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;现代/高效版：* &lt;/strong&gt;Hive on Spark&lt;strong&gt; (HiveQL -&amp;gt; Spark Job) 或 &lt;/strong&gt;Spark SQL** (Spark API/SQL -&amp;gt; Spark Job)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查询：&lt;/strong&gt; Hive CLI/Beeline (执行 HiveQL)，Spark SQL Thrift Server (支持 BI 工具连接)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;场景：&lt;/strong&gt; 传统的 T+1 数据报表，大规模历史数据分析。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;高性能统一分析平台 (Spark-Centric):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;存储:&lt;/strong&gt; HDFS 或 S3/GCS/ADLS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;元数据:&lt;/strong&gt; Hive Metastore (Spark SQL 直接读写 HMS)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;计算：&lt;/strong&gt; &lt;strong&gt;Spark&lt;/strong&gt; 作为核心引擎&lt;ul&gt;
&lt;li&gt;&lt;em&gt;批处理 ETL:&lt;/em&gt; 使用 Spark DataFrame/Dataset API 或 Spark SQL 清洗、转换、加载数据到 Hive 表。&lt;/li&gt;
&lt;li&gt;&lt;em&gt;流处理:&lt;/em&gt; 使用 Spark Structured Streaming 处理 Kafka 等数据源，结果写入 Hive 表或直接供下游使用。&lt;/li&gt;
&lt;li&gt;&lt;em&gt;交互式查询:&lt;/em&gt; 通过 Spark Thrift Server 支持 BI 工具连接 Spark SQL。&lt;/li&gt;
&lt;li&gt;&lt;em&gt;机器学习:&lt;/em&gt; 使用 Spark MLlib 训练模型。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;资源管理:&lt;/strong&gt; YARN (Hadoop) 或 Kubernetes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;场景：&lt;/strong&gt; 需要融合批处理、流处理、机器学习、交互式查询等多种工作负载的现代数据平台。Hive Metastore 提供了统一的数据目录。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;云原生数据湖：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;存储:&lt;/strong&gt; S3, GCS, ADLS (对象存储)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;元数据:&lt;/strong&gt; Hive Metastore (托管服务如 AWS Glue Data Catalog 是 HMS 的兼容实现)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;计算：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Serverless SQL 查询：&lt;/em&gt; AWS Athena, Google BigQuery (直接查询 S3/GCS 数据，利用 Glue Catalog 或类似元数据)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;自定义处理：&lt;/em&gt; &lt;strong&gt;Spark&lt;/strong&gt; on EMR/Dataproc/Databricks/Synapse (进行复杂的 ETL、ML、流处理，读写对象存储和元数据服务)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;场景：&lt;/strong&gt; 利用云服务的弹性、按需付费特性，减少基础设施运维负担。Spark 处理复杂任务，Serverless SQL 处理即席查询。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;总结与关键建议&#34;&gt;&lt;a href=&#34;#总结与关键建议&#34; class=&#34;headerlink&#34; title=&#34;总结与关键建议&#34;&gt;&lt;/a&gt;总结与关键建议&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;HDFS/对象存储是基石：&lt;/strong&gt; 选择适合你的分布式存储（HDFS 或云存储）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hive Metastore 是核心枢纽：&lt;/strong&gt; &lt;strong&gt;务必使用&lt;/strong&gt; Hive Metastore 或兼容服务（如 AWS Glue）来统一管理元数据，这是打通不同计算引擎的关键。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spark 是首选计算引擎：&lt;/strong&gt; 对于绝大多数&lt;strong&gt;需要速度&lt;/strong&gt;的计算任务（批处理、流处理、ML、交互式SQL），&lt;strong&gt;优先选择 Spark&lt;/strong&gt; 替代传统的 MapReduce/Tez。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hive 的价值在于 SQL 接口和 Metastore：&lt;/strong&gt; 即使主要用 Spark，Hive 的 Metastore 必不可少，HiveQL 接口对于重度 SQL 用户依然有价值（但底层执行应配置为 Spark）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hadoop YARN 是可选的资源管理器：&lt;/strong&gt; Spark 可以运行在 YARN、Kubernetes 或 Standalone 上。如果已有 Hadoop 集群，YARN 是自然选择；新建集群，Kubernetes 越来越流行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;组合是常态：&lt;/strong&gt; 几乎没有大型平台只使用其中一种技术。典型组合是：&lt;code&gt;(HDFS/S3) + (Hive Metastore) + (Spark for Processing) + (可选 Hive for SQL interface / LLAP for interactive)&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;简而言之：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;存储数据用什么？&lt;/strong&gt; HDFS 或 云存储 (S3/GCS/ADLS)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;管理表结构用什么？&lt;/strong&gt; Hive Metastore (或云服务如 Glue Catalog)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跑 ETL、流处理、机器学习、快速 SQL 用什么？&lt;/strong&gt; Spark (Spark Core, Spark SQL, Structured Streaming, MLlib)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;想用熟悉的 SQL 做批处理分析，或者需要加速交互查询？&lt;/strong&gt; 可以使用 Hive (但配置 Hive on Spark 或 Hive LLAP)，或者直接用 Spark SQL + Thrift Server。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;根据你的具体场景（数据规模、延迟要求、处理类型、团队技能、预算、云或本地）来调整这些组件的权重和配置。对于新项目，以 Spark 为核心计算引擎，结合 Hive Metastore 和 HDFS/云存储，是最常见且高效的起点。&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/07/22/virtualbox-hyper-v/</guid>
            <title>virtualbox与hyper-v的冲突</title>
            <link>https://erik.xyz/2025/07/22/virtualbox-hyper-v/</link>
            <category>virtualbox</category>
            <category>hyper-v</category>
            <category>鸿蒙开发</category>
            <pubDate>Tue, 22 Jul 2025 17:10:00 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;因为最近在学鸿蒙开发，开发电脑一直用的deepin系统，DevEcoStudio不支持linux系统，用了win兼容引擎还是不行。没办法，只好装了virtualbox来运行win10。&lt;/p&gt;
&lt;p&gt;在virtualbox中安装好win10也装了，DevEcoStudio运行后，虚拟机一直提示hyper-v未开启。但是，明明开启了，怎么办？难打VT-X和hyper-v有冲突？查查资料，找到了解决办法。&lt;br&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;1.关闭 Win10 虚拟机，在 Deepin 终端执行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    VBoxManage modifyvm &amp;quot;虚拟机名称&amp;quot; --nested-hw-virt on 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;2.修改虚拟机设置&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;打开 VirtualBox 管理界面 → 选择 Win10 虚拟机 → 设置 → 系统 → 加速：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;半虚拟化接口 选择 Hyper-V4；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;勾选 启用嵌套分页（Nested Paging）；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;处理器数量 ≥ 2（若物理 CPU 核心足够）5。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;3.开启win的hyper-v。&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;pushd &amp;quot;%~dp0&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dir /b %SystemRoot%\servicing\Packages\*Hyper-V*.mum &amp;gt;hyper-v.txt&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;for /f %%i in (&amp;#x27;findstr /i . hyper-v.txt 2^&amp;gt;nul&amp;#x27;) do dism /online /norestart /add-package:&amp;quot;%SystemRoot%\servicing\Packages\%%i&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;del hyper-v.txt&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;Dism /online /enable-feature /featurename:Microsoft-Hyper-V-All /LimitAccess /ALL&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;br&gt;自己在win中创建txt文件，把代码复制进去，然后把后缀改成bat以管理原的方式运行。结束后重启就可以了。&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/07/11/plc-concepts-application-scenarios/</guid>
            <title>PLC基础概念与应用场景详解</title>
            <link>https://erik.xyz/2025/07/11/plc-concepts-application-scenarios/</link>
            <category>开发</category>
            <category>plc</category>
            <pubDate>Fri, 11 Jul 2025 22:12:00 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;自学PLC是完全可行的！关键在于&lt;strong&gt;系统化的学习路径+动手实践&lt;/strong&gt;。以下是为你整理的 &lt;strong&gt;「零基础自学PLC全攻略」&lt;/strong&gt;，结合多年工业自动化经验总结，帮你少走弯路：&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一、自学必备基础准备&#34;&gt;&lt;a href=&#34;#一、自学必备基础准备&#34; class=&#34;headerlink&#34; title=&#34;一、自学必备基础准备&#34;&gt;&lt;/a&gt;&lt;strong&gt;一、自学必备基础准备&lt;/strong&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;电工知识&lt;/strong&gt;（必须掌握）  &lt;ul&gt;
&lt;li&gt;看懂电路图、继电器控制原理（启保停/正反转）&lt;/li&gt;
&lt;li&gt;了解传感器（NPN/PNP接线）、接触器、开关电源等元件&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;计算机操作&lt;/strong&gt;  &lt;ul&gt;
&lt;li&gt;文件管理、软件安装、基础调试能力&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;逻辑思维能力&lt;/strong&gt;  &lt;ul&gt;
&lt;li&gt;能分析“IF…THEN…”条件逻辑&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;重要提示&lt;/strong&gt;：若电工基础薄弱，先学习 &lt;strong&gt;《电工上岗证》&lt;/strong&gt; 内容（推荐b站“电工大吴”系列视频）&lt;/p&gt;
&lt;h2 id=&#34;&#34;&gt;&lt;a href=&#34;#&#34; class=&#34;headerlink&#34; title=&#34;&#34;&gt;&lt;/a&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/h2&gt;&lt;/blockquote&gt;
&lt;h2 id=&#34;📚-二、分阶段学习路线（附资源）&#34;&gt;&lt;a href=&#34;#📚-二、分阶段学习路线（附资源）&#34; class=&#34;headerlink&#34; title=&#34;📚 二、分阶段学习路线（附资源）&#34;&gt;&lt;/a&gt;📚 &lt;strong&gt;二、分阶段学习路线（附资源）&lt;/strong&gt;&lt;/h2&gt;&lt;h3 id=&#34;▶-阶段1：PLC核心概念筑基（1-2周）&#34;&gt;&lt;a href=&#34;#▶-阶段1：PLC核心概念筑基（1-2周）&#34; class=&#34;headerlink&#34; title=&#34;▶ 阶段1：PLC核心概念筑基（1-2周）&#34;&gt;&lt;/a&gt;▶ &lt;strong&gt;阶段1：PLC核心概念筑基（1-2周）&lt;/strong&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;学习重点&lt;/strong&gt;：&lt;ul&gt;
&lt;li&gt;PLC工作原理（输入-处理-输出循环）&lt;/li&gt;
&lt;li&gt;硬件组成（CPU/IO模块/电源/总线）&lt;/li&gt;
&lt;li&gt;进制转换（二进制/十六进制）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;推荐资源&lt;/strong&gt;：&lt;ul&gt;
&lt;li&gt;书籍：《PLC编程从入门到精通》（廖常初著）&lt;/li&gt;
&lt;li&gt;视频：B站“工控自习室”《PLC原理10讲》（免费）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;▶-阶段2：编程语言实战（重点！3-6周）&#34;&gt;&lt;a href=&#34;#▶-阶段2：编程语言实战（重点！3-6周）&#34; class=&#34;headerlink&#34; title=&#34;▶ 阶段2：编程语言实战（重点！3-6周）&#34;&gt;&lt;/a&gt;▶ &lt;strong&gt;阶段2：编程语言实战（重点！3-6周）&lt;/strong&gt;&lt;/h3&gt;&lt;div class=&#34;table-container&#34;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;语言&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;学习建议&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;仿真软件&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;梯形图(LD)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;先掌握！80%基础项目用此语言&lt;/td&gt;
&lt;td&gt;西门子TIA Portal（博途）学习版&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;功能块(FBD)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;学习标准功能块（定时器/计数器）&lt;/td&gt;
&lt;td&gt;三菱GX Works2 模拟器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;结构化文本(ST)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;有C语言基础再学&lt;/td&gt;
&lt;td&gt;Codesys V3.5（免费）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;✨ &lt;strong&gt;关键练习&lt;/strong&gt;：&lt;br&gt;① 电机正反转控制&lt;br&gt;② 三级传送带联动&lt;br&gt;③ 水箱液位PID调节（模拟量）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;▶-阶段3：硬件实操（核心环节！）&#34;&gt;&lt;a href=&#34;#▶-阶段3：硬件实操（核心环节！）&#34; class=&#34;headerlink&#34; title=&#34;▶ 阶段3：硬件实操（核心环节！）&#34;&gt;&lt;/a&gt;▶ &lt;strong&gt;阶段3：硬件实操（核心环节！）&lt;/strong&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;低成本方案&lt;/strong&gt;（总预算＜500元）：&lt;pre class=&#34;mermaid&#34;&gt;  graph LR
  A[淘宝二手PLC] --&gt; B[三菱FX1S-20MR ≈200元]
  B --&gt; C[按钮开关套装 50元]
  C --&gt; D[继电器模块 30元]
  D --&gt; E[USB编程线 40元]&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;虚拟方案&lt;/strong&gt;（零硬件）：&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Factory IO&lt;/strong&gt;（3D仿真软件）+ &lt;strong&gt;TIA Portal&lt;/strong&gt; 联动实验&lt;/li&gt;
&lt;li&gt;模拟真实场景：传送带分拣/智能仓储/电梯控制&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;▶-阶段4：工业通信与高级应用（2-3个月）&#34;&gt;&lt;a href=&#34;#▶-阶段4：工业通信与高级应用（2-3个月）&#34; class=&#34;headerlink&#34; title=&#34;▶ 阶段4：工业通信与高级应用（2-3个月）&#34;&gt;&lt;/a&gt;▶ &lt;strong&gt;阶段4：工业通信与高级应用（2-3个月）&lt;/strong&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;通信协议&lt;/strong&gt;：Modbus RTU（必学）、CANopen&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HMI入门&lt;/strong&gt;：用威纶通MT8071IE做简易控制界面&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SCADA基础&lt;/strong&gt;：组态王/力控组态软件数据采集&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;🔧-三、避坑指南（血泪经验！）&#34;&gt;&lt;a href=&#34;#🔧-三、避坑指南（血泪经验！）&#34; class=&#34;headerlink&#34; title=&#34;🔧 三、避坑指南（血泪经验！）&#34;&gt;&lt;/a&gt;🔧 &lt;strong&gt;三、避坑指南（血泪经验！）&lt;/strong&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;错误路线&lt;/strong&gt;：一开始就钻研西门子S7-1500（复杂度高打击信心）&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;正确路线&lt;/strong&gt;：&lt;strong&gt;国产PLC入门（信捷/汇川）→ 三菱FX系列 → 西门子S7-1200&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;🚫 &lt;strong&gt;勿踩的坑&lt;/strong&gt;：&lt;ol&gt;
&lt;li&gt;不练案例直接看手册 → 效率极低&lt;/li&gt;
&lt;li&gt;忽视PLC接地 → 烧毁模块风险&lt;/li&gt;
&lt;li&gt;未备份程序直接修改 → 导致设备停机&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;🌐-四、持续进阶资源&#34;&gt;&lt;a href=&#34;#🌐-四、持续进阶资源&#34; class=&#34;headerlink&#34; title=&#34;🌐 四、持续进阶资源&#34;&gt;&lt;/a&gt;🌐 &lt;strong&gt;四、持续进阶资源&lt;/strong&gt;&lt;/h2&gt;&lt;div class=&#34;table-container&#34;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;类型&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;推荐资源&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;故障排查&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;《PLC故障诊断与维护》PDF + 抖音“工控老张说维修”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;行业案例&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;中华工控网论坛案例区（真实项目程序下载）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;认证体系&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;西门子S7-1200认证工程师（可在线考）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;开源项目&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GitHub搜索“PLC Project”学习产线控制源码&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h2 id=&#34;🚀-五、就业能力转化建议&#34;&gt;&lt;a href=&#34;#🚀-五、就业能力转化建议&#34; class=&#34;headerlink&#34; title=&#34;🚀 五、就业能力转化建议&#34;&gt;&lt;/a&gt;🚀 &lt;strong&gt;五、就业能力转化建议&lt;/strong&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;打造作品集&lt;/strong&gt;：&lt;ul&gt;
&lt;li&gt;用Factory IO录制控制仿真视频&lt;/li&gt;
&lt;li&gt;自制PLC控制台（淘宝钣金箱+元器件组装）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;参与项目&lt;/strong&gt;：&lt;ul&gt;
&lt;li&gt;接简单外包：设备改造/小型非标自动化（智造家平台）&lt;/li&gt;
&lt;li&gt;加入工控侠客行等社群做志愿者项目&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/07/09/php-architecture03/</guid>
            <title>php架构方面的设计与思考（三）</title>
            <link>https://erik.xyz/2025/07/09/php-architecture03/</link>
            <category>php架构</category>
            <category>php项目</category>
            <category>php架构设计</category>
            <pubDate>Wed, 09 Jul 2025 01:37:00 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;PHP微服务架构终极整合方案:&lt;br&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
  &lt;pre class=&#34;mermaid&#34;&gt;  graph TD
      A[客户端] --&gt; B{Kong网关}
      B --&gt;|路由| C[用户服务]
      B --&gt;|路由| D[订单服务]
      B --&gt;|路由| E[支付服务]

      C --&gt;|注册| F[Consul集群]
      D --&gt;|注册| F
      E --&gt;|注册| F

      C --&gt;|配置| G[Apollo]
      D --&gt;|配置| G
      E --&gt;|配置| G

      D --&gt;|消息| H[RabbitMQ集群]
      H --&gt; E
      H --&gt; I[通知服务]

      F --&gt; J[Prometheus]
      G --&gt; J
      H --&gt; J
      J --&gt; K[Grafana]

      L[Jaeger] --&gt; M[服务追踪]&lt;/pre&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;基础设施快速部署&lt;br&gt;Docker Compose 全栈配置&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;version: &amp;#x27;3.8&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;services:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  # 注册中心&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  consul-server:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    image: consul:1.15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ports: [&amp;quot;8500:8500&amp;quot;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    command: &amp;quot;agent -server -bootstrap-expect=1 -ui -client 0.0.0.0&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  # API网关&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  kong:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    image: kong:3.4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    depends_on: [consul-server]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    environment:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      KONG_DATABASE: &amp;quot;off&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      KONG_DECLARATIVE_CONFIG: &amp;quot;/etc/kong/kong.yml&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      KONG_PROXY_ACCESS_LOG: &amp;quot;/dev/stdout&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      KONG_ADMIN_ACCESS_LOG: &amp;quot;/dev/stdout&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      KONG_PROXY_ERROR_LOG: &amp;quot;/dev/stderr&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ports: [&amp;quot;8000:8000&amp;quot;, &amp;quot;8001:8001&amp;quot;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    volumes:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      - ./kong:/etc/kong&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  # 配置中心&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  apollo:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    image: apolloconfig/apollo-portal:2.1.0&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ports: [&amp;quot;8070:8070&amp;quot;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    environment:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      SPRING_DATASOURCE_URL: &amp;quot;jdbc:mysql://apollo-db:3306/ApolloPortalDB&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  # 消息队列&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  rabbitmq:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    image: rabbitmq:3.11-management&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ports: [&amp;quot;5672:5672&amp;quot;, &amp;quot;15672:15672&amp;quot;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    environment:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      RABBITMQ_DEFAULT_USER: &amp;quot;admin&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      RABBITMQ_DEFAULT_PASS: &amp;quot;secret&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  # 监控系统&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  prometheus:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    image: prom/prometheus:v2.47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ports: [&amp;quot;9090:9090&amp;quot;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    volumes:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      - ./prometheus.yml:/etc/prometheus/prometheus.yml&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;核心服务PHP实现包&lt;/p&gt;
&lt;p&gt; composer.json 关键依赖&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &amp;quot;require&amp;quot;: &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;php&amp;quot;: &amp;quot;&amp;gt;=8.2&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;ext-swoole&amp;quot;: &amp;quot;*&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;ext-redis&amp;quot;: &amp;quot;*&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;ext-amqp&amp;quot;: &amp;quot;*&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;sensiolabs/consul-php-sdk&amp;quot;: &amp;quot;^2.0&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;guzzlehttp/guzzle&amp;quot;: &amp;quot;^7.8&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;php-amqplib/php-amqplib&amp;quot;: &amp;quot;^3.2&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;resilience-php/resilience-php&amp;quot;: &amp;quot;^1.3&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;apolloconfig/apollo-client&amp;quot;: &amp;quot;^2.0&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;服务注册与发现完整实现&lt;/p&gt;
&lt;p&gt; 服务注册（bootstrap.php）&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;use SensioLabs\Consul\ServiceFactory;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$consul = new ServiceFactory([&amp;#x27;base_uri&amp;#x27; =&amp;gt; &amp;#x27;http://consul-server:8500&amp;#x27;]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 自动获取本机IP&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$ip = trim(shell_exec(&amp;quot;hostname -i&amp;quot;));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 注册服务&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$consul-&amp;gt;get(AgentInterface::class)-&amp;gt;registerService([&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;ID&amp;#x27; =&amp;gt; &amp;#x27;order-service-&amp;#x27;.gethostname(),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;Name&amp;#x27; =&amp;gt; &amp;#x27;order-service&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;Address&amp;#x27; =&amp;gt; $ip,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;Port&amp;#x27; =&amp;gt; 8000,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;Check&amp;#x27; =&amp;gt; [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;HTTP&amp;#x27; =&amp;gt; &amp;quot;http://&amp;#123;$ip&amp;#125;:8000/health&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;Interval&amp;#x27; =&amp;gt; &amp;#x27;5s&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;Timeout&amp;#x27; =&amp;gt; &amp;#x27;2s&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;DeregisterCriticalServiceAfter&amp;#x27; =&amp;gt; &amp;#x27;30s&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ],&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;Tags&amp;#x27; =&amp;gt; [&amp;#x27;v2&amp;#x27;, &amp;#x27;primary&amp;#x27;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 健康检查端点&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$app-&amp;gt;get(&amp;#x27;/health&amp;#x27;, function() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    check_database();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    check_redis();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    return json_response([&amp;#x27;status&amp;#x27; =&amp;gt; &amp;#x27;UP&amp;#x27;]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;服务发现与负载均衡&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;class ServiceDiscovery &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    private static $cache = [];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    private static $ttl = 5; // 缓存5秒&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    public static function getInstance(string $service): string &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $now = time();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        // 缓存有效期内直接返回&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        if (isset(self::$cache[$service]) &amp;amp;&amp;amp; &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $now - self::$cache[$service][&amp;#x27;timestamp&amp;#x27;] &amp;lt; self::$ttl) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            return self::selectInstance(self::$cache[$service][&amp;#x27;instances&amp;#x27;]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        // 从Consul获取新实例&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $instances = $consul-&amp;gt;getCatalog()-&amp;gt;service($service)-&amp;gt;json();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        self::$cache[$service] = [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;instances&amp;#x27; =&amp;gt; $instances,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;timestamp&amp;#x27; =&amp;gt; $now&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        return self::selectInstance($instances);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    private static function selectInstance(array $instances): string &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        // 加权随机算法&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $total = array_sum(array_column($instances, &amp;#x27;Weight&amp;#x27;));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $rand = mt_rand(1, $total);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $current = 0;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        foreach ($instances as $instance) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $current += $instance[&amp;#x27;Weight&amp;#x27;];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            if ($rand &amp;lt;= $current) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                return &amp;quot;http://&amp;#123;$instance[&amp;#x27;ServiceAddress&amp;#x27;]&amp;#125;:&amp;#123;$instance[&amp;#x27;ServicePort&amp;#x27;]&amp;#125;&amp;quot;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;统一配置中心接入&lt;/p&gt;
&lt;p&gt;Apollo配置监听&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;$apollo = new \ApolloClient\Client([&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;config_server&amp;#x27; =&amp;gt; &amp;#x27;http://apollo:8070&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;app_id&amp;#x27; =&amp;gt; &amp;#x27;order-service&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;cluster&amp;#x27; =&amp;gt; &amp;#x27;default&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 初始化配置&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$mysqlConfig = $apollo-&amp;gt;get(&amp;#x27;mysql&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;DB::connect($mysqlConfig);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 动态监听&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$apollo-&amp;gt;listen([&amp;#x27;mysql&amp;#x27;, &amp;#x27;redis&amp;#x27;], function($namespace, $config) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    switch ($namespace) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        case &amp;#x27;mysql&amp;#x27;:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            DB::reconnect($config);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            break;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        case &amp;#x27;redis&amp;#x27;:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            Redis::setConfig($config);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            break;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Logger::info(&amp;quot;Config updated: $namespace&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;服务通信完整方案&lt;/p&gt;
&lt;p&gt;同步调用（HTTP）&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;class HttpServiceClient &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    use CircuitBreaker;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    public function call(string $service, string $endpoint, array $data) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        return $this-&amp;gt;protect(function() use ($service, $endpoint, $data) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $baseUrl = ServiceDiscovery::getInstance($service);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $client = new GuzzleHttp\Client([&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#x27;base_uri&amp;#x27; =&amp;gt; $baseUrl,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#x27;timeout&amp;#x27; =&amp;gt; 2.0&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            return $client-&amp;gt;post($endpoint, [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#x27;json&amp;#x27; =&amp;gt; $data,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#x27;headers&amp;#x27; =&amp;gt; [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;#x27;X-Trace-Id&amp;#x27; =&amp;gt; Trace::getId()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                ]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;, function() &amp;#123; // 降级处理&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            return [&amp;#x27;status&amp;#x27; =&amp;gt; &amp;#x27;degraded&amp;#x27;];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;异步通信（RabbitMQ）&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;class EventPublisher &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    private $channel;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    public function __construct() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $conn = new AMQPStreamConnection(&amp;#x27;rabbitmq&amp;#x27;, 5672, &amp;#x27;admin&amp;#x27;, &amp;#x27;secret&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $this-&amp;gt;channel = $conn-&amp;gt;channel();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        // 声明死信交换器&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $this-&amp;gt;channel-&amp;gt;exchange_declare(&amp;#x27;dlx&amp;#x27;, &amp;#x27;direct&amp;#x27;, false, true);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $this-&amp;gt;channel-&amp;gt;queue_declare(&amp;#x27;dlq&amp;#x27;, false, true);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $this-&amp;gt;channel-&amp;gt;queue_bind(&amp;#x27;dlq&amp;#x27;, &amp;#x27;dlx&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    public function publish(string $event, array $data) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $this-&amp;gt;channel-&amp;gt;tx_select();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $message = new AMQPMessage(json_encode($data), [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#x27;delivery_mode&amp;#x27; =&amp;gt; AMQPMessage::DELIVERY_MODE_PERSISTENT,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#x27;message_id&amp;#x27; =&amp;gt; Uuid::uuid4(),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#x27;timestamp&amp;#x27; =&amp;gt; time()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            ]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $this-&amp;gt;channel-&amp;gt;basic_publish($message, &amp;#x27;events&amp;#x27;, $event);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            DB::table(&amp;#x27;outbox&amp;#x27;)-&amp;gt;insert([&amp;#x27;message_id&amp;#x27; =&amp;gt; $message-&amp;gt;get(&amp;#x27;message_id&amp;#x27;)]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $this-&amp;gt;channel-&amp;gt;tx_commit();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125; catch (Exception $e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $this-&amp;gt;channel-&amp;gt;tx_rollback();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            throw $e;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;服务降级熔断策略&lt;/p&gt;
&lt;p&gt;多级降级配置&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;class DegradeManager &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    private static $levels = [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;order-service&amp;#x27; =&amp;gt; [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;full&amp;#x27; =&amp;gt; [&amp;#x27;threshold&amp;#x27; =&amp;gt; 0.95, &amp;#x27;fallback&amp;#x27; =&amp;gt; &amp;#x27;cache&amp;#x27;],&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;cache&amp;#x27; =&amp;gt; [&amp;#x27;threshold&amp;#x27; =&amp;gt; 0.8, &amp;#x27;fallback&amp;#x27; =&amp;gt; &amp;#x27;readonly&amp;#x27;],&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;readonly&amp;#x27; =&amp;gt; [&amp;#x27;threshold&amp;#x27; =&amp;gt; 0.5, &amp;#x27;fallback&amp;#x27; =&amp;gt; &amp;#x27;static&amp;#x27;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    public static function handle(string $service, callable $func) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $status = self::getServiceStatus($service);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            switch ($status) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                case &amp;#x27;full&amp;#x27;:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    return $func();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                case &amp;#x27;cache&amp;#x27;:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    return Cache::remember(&amp;quot;fallback:$service&amp;quot;, 60, $func);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                case &amp;#x27;readonly&amp;#x27;:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    if ($_SERVER[&amp;#x27;REQUEST_METHOD&amp;#x27;] === &amp;#x27;GET&amp;#x27;) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        return $func();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    throw new DegradeException(&amp;#x27;只读模式&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                case &amp;#x27;static&amp;#x27;:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    return [&amp;#x27;status&amp;#x27; =&amp;gt; &amp;#x27;degraded&amp;#x27;];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125; catch (Exception $e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            self::recordFailure($service);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            return self::handle($service, $func); // 自动降级&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    private static function getServiceStatus(string $service): string &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $failureRate = Prometheus::getFailureRate($service);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        foreach (self::$levels[$service] as $level =&amp;gt; $config) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            if ($failureRate &amp;lt;= $config[&amp;#x27;threshold&amp;#x27;]) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                return $level;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        return &amp;#x27;static&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;监控与告警配置&lt;/p&gt;
&lt;p&gt;Prometheus指标收集&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;class Metrics &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    private static $counter;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    public static function init() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $registry = new CollectorRegistry(new InMemory());&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        self::$counter = $registry-&amp;gt;registerCounter(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;php&amp;#x27;, &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;http_requests_total&amp;#x27;, &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;Total HTTP requests&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            [&amp;#x27;method&amp;#x27;, &amp;#x27;endpoint&amp;#x27;, &amp;#x27;status&amp;#x27;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        // 暴露指标端点&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $app-&amp;gt;get(&amp;#x27;/metrics&amp;#x27;, function() use ($registry) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $renderer = new RenderTextFormat();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            return $renderer-&amp;gt;render($registry-&amp;gt;getMetricFamilySamples());&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    public static function countRequest($method, $path, $status) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        self::$counter-&amp;gt;inc([&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $method, &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            preg_replace(&amp;#x27;/\d+/&amp;#x27;, &amp;#x27;&amp;#123;id&amp;#125;&amp;#x27;, $path),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $status&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 在中间件中调用&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$app-&amp;gt;addMiddleware(function($req, $handler) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    $start = microtime(true);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    $response = $handler-&amp;gt;handle($req);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    $duration = microtime(true) - $start;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Metrics::countRequest(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $req-&amp;gt;getMethod(),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $req-&amp;gt;getUri()-&amp;gt;getPath(),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $response-&amp;gt;getStatusCode()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    return $response;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;生产环境部署建议&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;服务注册中心：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;部署3节点Consul集群&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;启用ACL和TLS加密&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;设置自动备份策略&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;API网关：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Kong集群 + Nginx负载均衡&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;启用JWT插件和速率限制&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;配置WAF规则防止攻击&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;配置中心：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Apollo多环境隔离（DEV/TEST/PROD）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;敏感配置加密存储&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;设置配置变更审批流程&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;消息队列：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;RabbitMQ镜像队列&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;设置合理的TTL和死信策略&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;监控队列积压情况&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;监控系统：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Prometheus联邦集群&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Grafana统一看板&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;关键指标告警（P99延迟&amp;gt;500ms，错误率&amp;gt;1%）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&#34;完整架构调用流程示例&#34;&gt;&lt;a href=&#34;#完整架构调用流程示例&#34; class=&#34;headerlink&#34; title=&#34;完整架构调用流程示例&#34;&gt;&lt;/a&gt;完整架构调用流程示例&lt;/h5&gt;&lt;ol&gt;
&lt;li&gt;客户端请求 → Kong网关（认证+限流）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;网关查询Consul获取订单服务实例&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;订单服务处理时：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;从Apollo获取当前配置&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;通过HTTP调用支付服务（带熔断）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;发送消息到RabbitMQ&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;支付服务消费消息后：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;更新数据库&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;记录Prometheus指标&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;推送结果到通知服务&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;全链路追踪数据上报Jaeger&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/07/06/php-architecture02/</guid>
            <title>php 架构方面的设计与思考（二）</title>
            <link>https://erik.xyz/2025/07/06/php-architecture02/</link>
            <category>php架构</category>
            <category>php项目</category>
            <category>php架构设计</category>
            <pubDate>Sun, 06 Jul 2025 23:45:22 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;基于之前的架构设计，进一步提供完整的可落地方案：&lt;/p&gt;
&lt;h4 id=&#34;一、服务注册中心增强实现（Consul-PHP）&#34;&gt;&lt;a href=&#34;#一、服务注册中心增强实现（Consul-PHP）&#34; class=&#34;headerlink&#34; title=&#34;一、服务注册中心增强实现（Consul + PHP）&#34;&gt;&lt;/a&gt;一、服务注册中心增强实现（Consul + PHP）&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;健康检查与自动注销&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;// 服务注册时增加健康检查端点&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$consul-&amp;gt;get(AgentInterface::class)-&amp;gt;registerService([&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;ID&amp;#x27; =&amp;gt; &amp;#x27;payment-service-&amp;#x27;.uniqid(),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;Name&amp;#x27; =&amp;gt; &amp;#x27;payment-service&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;Tags&amp;#x27; =&amp;gt; [&amp;#x27;primary&amp;#x27;, &amp;#x27;v2&amp;#x27;],&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;Address&amp;#x27; =&amp;gt; get_current_ip(),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;Port&amp;#x27; =&amp;gt; 8000,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;Check&amp;#x27; =&amp;gt; [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;HTTP&amp;#x27; =&amp;gt; &amp;#x27;http://&amp;#x27;.get_current_ip().&amp;#x27;:8000/health&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;Interval&amp;#x27; =&amp;gt; &amp;#x27;5s&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;Timeout&amp;#x27; =&amp;gt; &amp;#x27;2s&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;DeregisterCriticalServiceAfter&amp;#x27; =&amp;gt; &amp;#x27;30s&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// PHP健康检查端点实现&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$app-&amp;gt;get(&amp;#x27;/health&amp;#x27;, function() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    return json_encode([&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;status&amp;#x27; =&amp;gt; &amp;#x27;UP&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;db_connected&amp;#x27; =&amp;gt; DB::connection()-&amp;gt;getPdo() ? true : false,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;redis_connected&amp;#x27; =&amp;gt; Redis::ping() ? true : false,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;load&amp;#x27; =&amp;gt; sys_getloadavg()[0]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;
&lt;ol&gt;
&lt;li&gt;服务发现负载均衡&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;class ServiceLocator &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    private static $services = [];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    public static function getInstance($serviceName) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        if (!isset(self::$services[$serviceName]) || count(self::$services[$serviceName]) == 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $instances = $consul-&amp;gt;getCatalog()-&amp;gt;service($serviceName)-&amp;gt;json();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            self::$services[$serviceName] = array_map(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                fn($s) =&amp;gt; new Instance($s[&amp;#x27;ServiceAddress&amp;#x27;], $s[&amp;#x27;ServicePort&amp;#x27;]),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                $instances&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        // 使用加权随机算法选择实例&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $totalWeight = array_sum(array_map(fn($i) =&amp;gt; $i-&amp;gt;weight, self::$services[$serviceName]));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $rand = mt_rand(1, $totalWeight);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        foreach (self::$services[$serviceName] as $instance) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $rand -= $instance-&amp;gt;weight;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            if ($rand &amp;lt;= 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                return $instance;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    // 定时刷新服务列表&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    public static function refresh() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        swoole_timer_tick(30000, function() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            foreach (array_keys(self::$services) as $name) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                $instances = $consul-&amp;gt;getCatalog()-&amp;gt;service($name)-&amp;gt;json();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                self::$services[$name] = $instances;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&#34;二、API-网关深度集成（Kong-OpenResty）&#34;&gt;&lt;a href=&#34;#二、API-网关深度集成（Kong-OpenResty）&#34; class=&#34;headerlink&#34; title=&#34;二、API 网关深度集成（Kong + OpenResty）&#34;&gt;&lt;/a&gt;二、API 网关深度集成（Kong + OpenResty）&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;动态路由配置&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;#lua&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;-- kong/plugins/dynamic-router/handler.lua&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;local DynamicRouterHandler = &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    PRIORITY = 1000,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    VERSION = &amp;quot;1.0&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;function DynamicRouterHandler:access(conf)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    -- 从Consul获取最新服务实例&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    local res, err = kong.request.make(&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        method = &amp;quot;GET&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        url = &amp;quot;http://consul:8500/v1/catalog/service/&amp;quot; .. kong.request.get_header(&amp;quot;X-Service-Name&amp;quot;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    if not res then&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        kong.log.err(&amp;quot;Consul request failed: &amp;quot;, err)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        return kong.response.exit(503)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    end&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    local instances = cjson.decode(res.body)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    if #instances == 0 then&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        return kong.response.exit(404, &amp;#123; message = &amp;quot;Service unavailable&amp;quot; &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    end&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    -- 更新上游目标&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    local ok, err = kong.admin_api.post(&amp;quot;/upstreams/service-&amp;quot;..conf.service_name..&amp;quot;/targets&amp;quot;, &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        target = instances[1].ServiceAddress..&amp;quot;:&amp;quot;..instances[1].ServicePort,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        weight = 100&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;return DynamicRouterHandler&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;PHP 插件开发示例（JWT 验证）&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;// kong/plugins/jwt-validator/validate.php&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$token = $_SERVER[&amp;#x27;HTTP_AUTHORIZATION&amp;#x27;] ?? &amp;#x27;&amp;#x27;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;if (!preg_match(&amp;#x27;/Bearer\s+(.*)$/i&amp;#x27;, $token, $matches)) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    header(&amp;#x27;HTTP/1.1 401 Unauthorized&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    exit;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$jwt = $matches[1];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$userInfo = AuthService::validateJWT($jwt);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;if (!$userInfo) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    header(&amp;#x27;HTTP/1.1 403 Forbidden&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    exit;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 将用户信息传递给上游服务&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;header(&amp;#x27;X-User-Id: &amp;#x27; . $userInfo[&amp;#x27;id&amp;#x27;]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;header(&amp;#x27;X-User-Roles: &amp;#x27; . implode(&amp;#x27;,&amp;#x27;, $userInfo[&amp;#x27;roles&amp;#x27;]));&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h4 id=&#34;三、配置中心完整方案（Apollo-PHP）&#34;&gt;&lt;a href=&#34;#三、配置中心完整方案（Apollo-PHP）&#34; class=&#34;headerlink&#34; title=&#34;三、配置中心完整方案（Apollo + PHP）&#34;&gt;&lt;/a&gt;三、配置中心完整方案（Apollo + PHP）&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;配置监听长轮询&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;class ApolloWatcher &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    private $client;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    private $callbacks = [];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    public function __construct() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $this-&amp;gt;client = new \ApolloClient\Client([...]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $this-&amp;gt;startWatch();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    public function watch($namespace, callable $callback) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $this-&amp;gt;callbacks[$namespace][] = $callback;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    private function startWatch() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        swoole_timer_tick(1000, function() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            foreach ($this-&amp;gt;callbacks as $namespace =&amp;gt; $cbs) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                $notifications = $this-&amp;gt;client-&amp;gt;getNotifications([$namespace]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                if ($notifications[$namespace] &amp;gt; $this-&amp;gt;versions[$namespace] ?? 0) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    $config = $this-&amp;gt;client-&amp;gt;getConfig($namespace);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    foreach ($cbs as $cb) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                        $cb($config);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    $this-&amp;gt;versions[$namespace] = $notifications[$namespace];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 使用示例&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$watcher = new ApolloWatcher();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$watcher-&amp;gt;watch(&amp;#x27;application&amp;#x27;, function($config) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Redis::set(&amp;#x27;app_config&amp;#x27;, json_encode($config));&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;配置加密存储&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;class SecureConfig &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    private $key;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    public function __construct() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $this-&amp;gt;key = file_get_contents(&amp;#x27;/etc/apollo/key.pem&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    public function get($key) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $encrypted = Apollo::get($key);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        return openssl_decrypt(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            base64_decode($encrypted),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#x27;aes-256-cbc&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $this-&amp;gt;key,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            0,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            substr($this-&amp;gt;key, 0, 16)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h4 id=&#34;四、服务总线可靠消息方案（RabbitMQ）&#34;&gt;&lt;a href=&#34;#四、服务总线可靠消息方案（RabbitMQ）&#34; class=&#34;headerlink&#34; title=&#34;四、服务总线可靠消息方案（RabbitMQ）&#34;&gt;&lt;/a&gt;四、服务总线可靠消息方案（RabbitMQ）&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;消息事务与确认&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;// 生产者端&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$channel-&amp;gt;tx_select();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    $msg = new AMQPMessage($data, [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;delivery_mode&amp;#x27; =&amp;gt; AMQPMessage::DELIVERY_MODE_PERSISTENT,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;message_id&amp;#x27; =&amp;gt; uniqid()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    $channel-&amp;gt;basic_publish($msg, &amp;#x27;orders&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    DB::table(&amp;#x27;outbox&amp;#x27;)-&amp;gt;insert([&amp;#x27;message_id&amp;#x27; =&amp;gt; $msg-&amp;gt;get(&amp;#x27;message_id&amp;#x27;)]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    $channel-&amp;gt;tx_commit();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125; catch (Exception $e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    $channel-&amp;gt;tx_rollback();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    Metrics::counter(&amp;#x27;publish_failed&amp;#x27;)-&amp;gt;inc();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 消费者端&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$channel-&amp;gt;basic_consume(&amp;#x27;orders&amp;#x27;, &amp;#x27;&amp;#x27;, false, false, false, false,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    function($msg) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        try &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            processOrder($msg-&amp;gt;body);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $msg-&amp;gt;ack();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            // 幂等处理&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            if (!DB::table(&amp;#x27;processed_messages&amp;#x27;)-&amp;gt;where(&amp;#x27;msg_id&amp;#x27;, $msg-&amp;gt;get(&amp;#x27;message_id&amp;#x27;))-&amp;gt;exists()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                DB::table(&amp;#x27;processed_messages&amp;#x27;)-&amp;gt;insert([&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;#x27;msg_id&amp;#x27; =&amp;gt; $msg-&amp;gt;get(&amp;#x27;message_id&amp;#x27;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;#x27;processed_at&amp;#x27; =&amp;gt; now()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                ]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125; catch (Exception $e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $msg-&amp;gt;nack(true); // 重新入队&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;死信队列配置&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;// 声明死信交换器&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$channel-&amp;gt;exchange_declare(&amp;#x27;dlx&amp;#x27;, &amp;#x27;direct&amp;#x27;, false, true, false);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$channel-&amp;gt;queue_declare(&amp;#x27;dlq&amp;#x27;, false, true, false, false);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$channel-&amp;gt;queue_bind(&amp;#x27;dlq&amp;#x27;, &amp;#x27;dlx&amp;#x27;, &amp;#x27;dead&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 主队列配置&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$channel-&amp;gt;queue_declare(&amp;#x27;orders&amp;#x27;, false, true, false, false, false, [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;x-dead-letter-exchange&amp;#x27; =&amp;gt; &amp;#x27;dlx&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;x-dead-letter-routing-key&amp;#x27; =&amp;gt; &amp;#x27;dead&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;x-message-ttl&amp;#x27; =&amp;gt; 600000 // 10分钟后进入DLQ&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;]);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h4 id=&#34;五、服务降级完整策略&#34;&gt;&lt;a href=&#34;#五、服务降级完整策略&#34; class=&#34;headerlink&#34; title=&#34;五、服务降级完整策略&#34;&gt;&lt;/a&gt;五、服务降级完整策略&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;多级降级方案&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;class OrderService &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    const DEGRADE_LEVELS = [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;full&amp;#x27; =&amp;gt; 0,    // 正常服务&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;readonly&amp;#x27; =&amp;gt; 1, // 只读模式&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;cache&amp;#x27; =&amp;gt; 2,    // 返回缓存数据&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;static&amp;#x27; =&amp;gt; 3    // 返回静态页面&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    public function getOrder($id) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $level = CircuitBreaker::getStatus(&amp;#x27;order-service&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        switch ($level) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            case self::DEGRADE_LEVELS[&amp;#x27;full&amp;#x27;]:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                return DB::table(&amp;#x27;orders&amp;#x27;)-&amp;gt;find($id);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            case self::DEGRADE_LEVELS[&amp;#x27;readonly&amp;#x27;]:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                if (request()-&amp;gt;isMethod(&amp;#x27;GET&amp;#x27;)) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    return DB::table(&amp;#x27;orders&amp;#x27;)-&amp;gt;find($id);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                throw new DegradeException(&amp;#x27;只读模式&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            case self::DEGRADE_LEVELS[&amp;#x27;cache&amp;#x27;]:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                return Cache::remember(&amp;quot;order:$id&amp;quot;, 60, function() use ($id) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    return DB::table(&amp;#x27;orders&amp;#x27;)-&amp;gt;find($id) ?? [];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            case self::DEGRADE_LEVELS[&amp;#x27;static&amp;#x27;]:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                return [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;#x27;id&amp;#x27; =&amp;gt; $id,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;#x27;status&amp;#x27; =&amp;gt; &amp;#x27;degraded&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                    &amp;#x27;message&amp;#x27; =&amp;gt; &amp;#x27;系统繁忙，请稍后查看订单状态&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                ];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;自动降级规则配置&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;# degradation-rules.yaml&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;services:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  order-service:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    failure_threshold: 50%  # 失败率阈值&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    slow_call_threshold: 2000ms  # 慢调用阈值&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    minimum_calls: 20  # 最小调用量&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    sliding_window: 60s  # 统计窗口&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    wait_duration: 300s  # 半开状态等待时间&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    fallbacks:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      - condition: &amp;quot;error instanceof TimeoutException&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        action: &amp;quot;return cachedData()&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      - condition: &amp;quot;error instanceof DBException&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        action: &amp;quot;switchToReadonlyMode()&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;六、部署架构优化方案&#34;&gt;&lt;a href=&#34;#六、部署架构优化方案&#34; class=&#34;headerlink&#34; title=&#34;六、部署架构优化方案&#34;&gt;&lt;/a&gt;六、部署架构优化方案&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Kubernetes 部署模板&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;# deployment.yaml&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;apiVersion: apps/v1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;kind: Deployment&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;metadata:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  name: order-service&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;spec:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  replicas: 3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  strategy:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    rollingUpdate:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      maxSurge: 1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      maxUnavailable: 0&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  template:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    spec:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      containers:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      - name: php&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        image: my-registry/order-service:v1.2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ports:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        - containerPort: 8000&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        livenessProbe:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          httpGet:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            path: /health&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            port: 8000&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          initialDelaySeconds: 30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          periodSeconds: 10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        readinessProbe:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          httpGet:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            path: /ready&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            port: 8000&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          initialDelaySeconds: 5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          periodSeconds: 5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        envFrom:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        - configMapRef:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            name: apollo-config&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;---&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;# service.yaml&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;apiVersion: v1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;kind: Service&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;metadata:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  name: order-service&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  annotations:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    consul.hashicorp.com/service-name: order-service&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;spec:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  ports:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  - port: 8000&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    targetPort: 8000&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  selector:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    app: order-service&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;监控告警配置&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;# prometheus-rules.yml&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;groups:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;- name: php-services&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  rules:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  - alert: HighErrorRate&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    expr: sum(rate(http_requests_total&amp;#123;status=~&amp;quot;5..&amp;quot;&amp;#125;[1m])) by (service) / sum(rate(http_requests_total[1m])) by (service) &amp;gt; 0.05&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    for: 5m&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    labels:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      severity: critical&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    annotations:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      summary: &amp;quot;High error rate on &amp;#123;&amp;#123; $labels.service &amp;#125;&amp;#125;&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      description: &amp;quot;Error rate is &amp;#123;&amp;#123; $value &amp;#125;&amp;#125;&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  - alert: ServiceDown&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    expr: up&amp;#123;job=&amp;quot;php-services&amp;quot;&amp;#125; == 0&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    for: 1m&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    labels:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      severity: critical&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    annotations:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      summary: &amp;quot;&amp;#123;&amp;#123; $labels.instance &amp;#125;&amp;#125; is down&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;七、开发环境工具链&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;本地调试方案：&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;# 使用docker-compose启动依赖服务&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;docker-compose -f dev-env.yml up consul rabbitmq apollo&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;# 使用Xdebug远程调试&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;PHP_IDE_CONFIG=&amp;quot;serverName=my-service&amp;quot; php -dxdebug.mode=debug \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;-dxdebug.client_host=host.docker.internal \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;-dxdebug.client_port=9003 \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;-dxdebug.start_with_request=yes \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;artisan serve&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;API 文档生成：&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;/**&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * @OA\Post(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *     path=&amp;quot;/orders&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *     tags=&amp;#123;&amp;quot;Orders&amp;quot;&amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *     @OA\RequestBody(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *         @OA\JsonContent(ref=&amp;quot;#/components/schemas/OrderRequest&amp;quot;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *     ),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *     @OA\Response(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *         response=201,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *         description=&amp;quot;Order created&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *         @OA\JsonContent(ref=&amp;quot;#/components/schemas/Order&amp;quot;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *     ),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *     @OA\Response(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *         response=503,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *         description=&amp;quot;Service degraded&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *         @OA\JsonContent(ref=&amp;quot;#/components/schemas/Error&amp;quot;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; *     )&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; * )&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;public function createOrder(Request $request) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    // ...&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;性能测试工具：&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;# 使用wrk进行压力测试&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;wrk -t4 -c100 -d60s --latency http://localhost:8000/api/orders&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;# 生成火焰图&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;php -dxdebug.profiler_enable=1 -dxdebug.profiler_output_dir=/tmp \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;-dxdebug.profiler_output_name=cachegrind.out.%p artisan serve&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;关键优化点总结&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;服务发现：采用 Consul 集群+健康检查+客户端缓存，实现秒级服务状态更新&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;消息可靠：通过事务消息+死信队列+幂等处理保障消息不丢失&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;配置管理：Apollo 长轮询+加密存储+版本回溯实现安全配置中心&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;降级策略：多级降级（全功能 → 只读 → 缓存 → 静态）保障核心链路&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;可观测性：Prometheus 指标+Jaeger 追踪+ELK 日志三位一体监控&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/07/05/php-architecture01/</guid>
            <title>php架构方面的设计与思考（一）</title>
            <link>https://erik.xyz/2025/07/05/php-architecture01/</link>
            <category>php架构</category>
            <category>php服务</category>
            <category>php服务设计</category>
            <pubDate>Sat, 05 Jul 2025 11:00:00 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;PHP的微服务架构设计，涵盖服务注册中心、服务调度、服务降级、服务网关、服务配置、服务总线等核心模块的完整梳理和实现方案：&lt;/p&gt;
&lt;pre class=&#34;mermaid&#34;&gt;graph TD
    A[服务网关] --&gt; B[服务注册中心]
    A --&gt; C[服务配置中心]
    D[服务提供者] --&gt; B
    E[服务消费者] --&gt; B
    F[服务总线] --&gt; D
    F --&gt; E
    G[服务调度器] --&gt; F
    H[降级管理器] --&gt; C&lt;/pre&gt;

&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;
&lt;h4 id=&#34;一、架构核心模块与实现技术选型&#34;&gt;&lt;a href=&#34;#一、架构核心模块与实现技术选型&#34; class=&#34;headerlink&#34; title=&#34;一、架构核心模块与实现技术选型&#34;&gt;&lt;/a&gt;一、架构核心模块与实现技术选型&lt;/h4&gt;&lt;div class=&#34;table-container&#34;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:left&#34;&gt;模块&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;推荐技术&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;PHP对接方式&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;特点&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;服务注册中心&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Consul、Nacos&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;HTTP API/PHP客户端库&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;支持健康检查、服务发现&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;服务网关&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Kong、APISIX&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Nginx代理/PHP插件&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;动态路由、限流、认证&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;服务配置中心&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Apollo、Nacos&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;HTTP长轮询/PHP SDK&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;配置热更新、版本管理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;服务总线&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;RabbitMQ、Kafka&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;PHP扩展(php-amqplib/rdkafka)&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;异步解耦、可靠消息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;服务调度&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Workerman/Swoole&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;原生PHP支持&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;协程调度、高性能&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;服务降级&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;ResiliencePHP&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Composer库&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;熔断、限流、降级&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;分布式追踪&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Jaeger、Zipkin&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;OpenTracing-PHP&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;全链路监控&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h4 id=&#34;二、各模块具体实现方案&#34;&gt;&lt;a href=&#34;#二、各模块具体实现方案&#34; class=&#34;headerlink&#34; title=&#34;二、各模块具体实现方案&#34;&gt;&lt;/a&gt;二、各模块具体实现方案&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;服务注册中心（Consul + PHP）&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;// 安装Consul PHP客户端：composer require sensiolabs/consul-php-sdk&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;use SensioLabs\Consul\ServiceFactory;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;use SensioLabs\Consul\Services\AgentInterface;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$consul = new ServiceFactory([&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;base_uri&amp;#x27; =&amp;gt; &amp;#x27;http://consul-server:8500&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 服务注册&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$consul-&amp;gt;get(AgentInterface::class)-&amp;gt;registerService([&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;ID&amp;#x27; =&amp;gt; &amp;#x27;order-service-001&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;Name&amp;#x27; =&amp;gt; &amp;#x27;order-service&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;Address&amp;#x27; =&amp;gt; &amp;#x27;192.168.1.101&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;Port&amp;#x27; =&amp;gt; 8000,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;Check&amp;#x27; =&amp;gt; [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;HTTP&amp;#x27; =&amp;gt; &amp;#x27;http://192.168.1.101:8000/health&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#x27;Interval&amp;#x27; =&amp;gt; &amp;#x27;10s&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 服务发现&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$services = $consul-&amp;gt;getCatalog()-&amp;gt;service(&amp;#x27;order-service&amp;#x27;)-&amp;gt;json();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$instances = array_map(fn($s) =&amp;gt; &amp;quot;&amp;#123;$s[&amp;#x27;ServiceAddress&amp;#x27;]&amp;#125;:&amp;#123;$s[&amp;#x27;ServicePort&amp;#x27;]&amp;#125;&amp;quot;, $services);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;API网关（Kong + PHP）&lt;/p&gt;
 &lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;# Kong配置示例&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  location /api/ &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    access_by_lua_block &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      -- PHP认证插件&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      local res = ngx.location.capture(&amp;quot;/auth.php?token=&amp;quot;..ngx.var.arg_token)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      if res.status ~= 200 then&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        ngx.exit(401)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      end&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    proxy_pass http://php-upstream;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    proxy_set_header X-Service-Name $route_name;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/li&gt;
&lt;li&gt;配置中心（Apollo + PHP） &lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;// 使用apollo-client-php&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$client = new \ApolloClient\Client([&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;config_server&amp;#x27; =&amp;gt; &amp;#x27;http://apollo-config:8080&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;app_id&amp;#x27; =&amp;gt; &amp;#x27;user-service&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;cluster&amp;#x27; =&amp;gt; &amp;#x27;default&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 获取配置&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$mysqlConfig = $client-&amp;gt;get(&amp;#x27;mysql.connection&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 监听配置变更&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$client-&amp;gt;listen(function($key, $value) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    if ($key === &amp;#x27;feature_toggle&amp;#x27;) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        FeatureToggle::update($value);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/li&gt;
&lt;li&gt;服务总线（RabbitMQ + PHP） &lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;// 生产者&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$connection = new AMQPStreamConnection(&amp;#x27;rabbitmq&amp;#x27;, 5672, &amp;#x27;guest&amp;#x27;, &amp;#x27;guest&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$channel = $connection-&amp;gt;channel();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$channel-&amp;gt;queue_declare(&amp;#x27;order_events&amp;#x27;, false, true, false, false);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$msg = new AMQPMessage(json_encode([&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;event&amp;#x27; =&amp;gt; &amp;#x27;ORDER_CREATED&amp;#x27;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;data&amp;#x27; =&amp;gt; [&amp;#x27;id&amp;#x27; =&amp;gt; 123]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;]), [&amp;#x27;delivery_mode&amp;#x27; =&amp;gt; 2]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$channel-&amp;gt;basic_publish($msg, &amp;#x27;&amp;#x27;, &amp;#x27;order_events&amp;#x27;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 消费者&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$channel-&amp;gt;basic_consume(&amp;#x27;order_events&amp;#x27;, &amp;#x27;&amp;#x27;, false, false, false, false, &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    function($msg) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $event = json_decode($msg-&amp;gt;body, true);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        EventDispatcher::dispatch($event);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $msg-&amp;gt;ack();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/li&gt;
&lt;li&gt;服务降级（ResiliencePHP） &lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;use Resilience\Retry;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;use Resilience\CircuitBreaker;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 熔断器配置&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$circuitBreaker = CircuitBreaker::builder()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    -&amp;gt;withFailureRateThreshold(50)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    -&amp;gt;withWaitDuration(10000)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    -&amp;gt;build();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 带熔断的重试机制&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$result = Retry::with(Retry::maxAttempts(3))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    -&amp;gt;decorate($circuitBreaker)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    -&amp;gt;run(function() use ($service) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        return $service-&amp;gt;callApi();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;, function($e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        // 降级处理&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        return [&amp;#x27;status&amp;#x27; =&amp;gt; &amp;#x27;degraded&amp;#x27;, &amp;#x27;data&amp;#x27; =&amp;gt; cached_data()];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/li&gt;
&lt;li&gt;分布式调度（Swoole协程） &lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;use Swoole\Coroutine;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 并发调用多个服务&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;Coroutine\run(function() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    $results = [];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    $wg = new Coroutine\WaitGroup();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    $services = [&amp;#x27;inventory&amp;#x27;, &amp;#x27;payment&amp;#x27;, &amp;#x27;notification&amp;#x27;];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    foreach ($services as $service) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        $wg-&amp;gt;add();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        go(function() use ($service, &amp;amp;$results, $wg) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $results[$service] = callService($service);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            $wg-&amp;gt;done();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    $wg-&amp;gt;wait();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    // 处理聚合结果&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    processResults($results);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;三、完整架构部署方案&#34;&gt;&lt;a href=&#34;#三、完整架构部署方案&#34; class=&#34;headerlink&#34; title=&#34;三、完整架构部署方案&#34;&gt;&lt;/a&gt;三、完整架构部署方案&lt;/h4&gt;&lt;pre class=&#34;mermaid&#34;&gt;graph LR
    A[客户端] --&gt; B[Kong网关]
    B --&gt; C[认证服务]
    B --&gt; D[用户服务]
    B --&gt; E[订单服务]
    B --&gt; F[支付服务]

    subgraph 微服务集群
        C --&gt;|注册| H[Consul]
        D --&gt;|注册| H
        E --&gt;|注册| H
        F --&gt;|注册| H

        C --&gt;|配置| I[Apollo]
        D --&gt;|配置| I
        E --&gt;|配置| I
        F --&gt;|配置| I

        E --&gt;|消息| J[RabbitMQ]
        J --&gt; F
        J --&gt; K[通知服务]
    end

    H --&gt; L[Prometheus监控]
    I --&gt; L
    J --&gt; L
    L --&gt; M[Grafana]&lt;/pre&gt;

&lt;h4 id=&#34;四、关键组件安装与配置&#34;&gt;&lt;a href=&#34;#四、关键组件安装与配置&#34; class=&#34;headerlink&#34; title=&#34;四、关键组件安装与配置&#34;&gt;&lt;/a&gt;四、关键组件安装与配置&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;基础设施部署（Docker示例） &lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;# docker-compose.yml&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;version: &amp;#x27;3&amp;#x27;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;services:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  consul:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    image: consul:latest&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ports:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      - &amp;quot;8500:8500&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  kong:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    image: kong:latest&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    depends_on:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      - postgres&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ports:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      - &amp;quot;8000:8000&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      - &amp;quot;8001:8001&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  apollo:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    image: apolloconfig/apollo-portal&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ports:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      - &amp;quot;8070:8070&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  rabbitmq:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    image: rabbitmq:management&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ports:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      - &amp;quot;5672:5672&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;      - &amp;quot;15672:15672&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PHP服务Dockerfile&lt;/p&gt;
 &lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;FROM php:8.2-fpm&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;# 安装必要扩展&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    libzip-dev \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    librabbitmq-dev \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;amp;&amp;amp; docker-php-ext-install zip pdo_mysql \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;amp;&amp;amp; pecl install amqp \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;amp;&amp;amp; docker-php-ext-enable amqp&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;# 安装Composer&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;COPY --from=composer:latest /usr/bin/composer /usr/bin/composer&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;WORKDIR /var/www&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;COPY . .&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;RUN composer install --no-dev --optimize-autoloader&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;五、生产环境建议&#34;&gt;&lt;a href=&#34;#五、生产环境建议&#34; class=&#34;headerlink&#34; title=&#34;五、生产环境建议&#34;&gt;&lt;/a&gt;五、生产环境建议&lt;/h4&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;服务注册中心：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;使用Consul集群（至少3节点）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;开启ACL安全控制&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;集成Prometheus监控&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;网关层：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Kong集群 + Nginx负载均衡&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;启用JWT认证插件&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;配置速率限制（1000rps/服务）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;配置管理：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Apollo多环境配置（DEV/TEST/PROD）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;敏感配置加密存储&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;配置变更审计日志&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;服务通信：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;同步调用：HTTP/2 + Protobuf&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;异步消息：RabbitMQ镜像队列&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;服务网格：Istio（可选）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;可观测性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;日志：ELK收集Nginx/PHP日志&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;指标：Prometheus收集服务指标&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;追踪：Jaeger实现全链路追踪&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;六、PHP微服务开发工具包&#34;&gt;&lt;a href=&#34;#六、PHP微服务开发工具包&#34; class=&#34;headerlink&#34; title=&#34;六、PHP微服务开发工具包&#34;&gt;&lt;/a&gt;六、PHP微服务开发工具包&lt;/h4&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;composer require:&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  sensiolabs/consul-php-sdk     # Consul客户端&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  guzzlehttp/guzzle             # HTTP客户端&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  php-amqplib/php-amqplib       # RabbitMQ&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  kwn/php-jaeger-client         # Jaeger追踪&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  resilience-php/resilience-php # 熔断降级&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;  ext-swoole                    # 协程调度&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt; ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/06/27/emm-mdm-systems/</guid>
            <title>EMM与MDM系统详解</title>
            <link>https://erik.xyz/2025/06/27/emm-mdm-systems/</link>
            <category>系统</category>
            <category>emm</category>
            <category>mdm</category>
            <pubDate>Fri, 27 Jun 2025 10:12:00 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;EMM（Enterprise Mobility Management，企业移动化管理）和MDM（Mobile Device Management，移动设备管理）是企业用于安全管理移动设备、应用及数据的核心技术体系。下面从定义、功能、演进和应用场景等方面进行系统说明：&lt;/p&gt;
&lt;p&gt;📱 一、核心概念&lt;br&gt;MDM（移动设备管理）&lt;br&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;定义：专注于移动设备（如手机、平板）的全生命周期管理，包括注册、配置、监控、远程控制（如锁定/擦除）及停用146。&lt;/p&gt;
&lt;p&gt;适用场景：适用于企业统一配发的设备（如物流手持终端、公司手机），需完全控制硬件和操作系统39。&lt;/p&gt;
&lt;p&gt;EMM（企业移动化管理）&lt;/p&gt;
&lt;p&gt;定义：MDM的扩展，是一套综合解决方案，除设备管理外，还包含移动应用管理（MAM）、移动内容管理（MCM）、移动身份管理（MIM）等组件，目标是在保障安全的前提下提升移动办公效率189。&lt;/p&gt;
&lt;p&gt;核心能力：支持BYOD（自带设备办公），通过“容器化”技术隔离企业数据与个人数据，实现细粒度控制（如仅擦除企业应用数据）59。&lt;/p&gt;
&lt;p&gt;⚙️ 二、核心功能组件&lt;br&gt;EMM通过以下模块协同工作：&lt;/p&gt;
&lt;p&gt;MDM：设备注册、策略配置（密码复杂度、加密）、远程擦除16。&lt;/p&gt;
&lt;p&gt;MAM（移动应用管理）：&lt;/p&gt;
&lt;p&gt;企业应用商店分发、静默安装/更新；&lt;/p&gt;
&lt;p&gt;控制应用权限（如禁止截屏/粘贴）19。&lt;/p&gt;
&lt;p&gt;MCM（移动内容管理）：&lt;/p&gt;
&lt;p&gt;安全分发企业文件（如合同、报表）；&lt;/p&gt;
&lt;p&gt;数据加密存储，限制分享至非授权应用18。&lt;/p&gt;
&lt;p&gt;MIM（移动身份管理）：&lt;/p&gt;
&lt;p&gt;单点登录（SSO）、多因素认证；&lt;/p&gt;
&lt;p&gt;基于角色/设备状态动态调整访问权限18。&lt;/p&gt;
&lt;p&gt;📊 三、MDM与EMM的区别&lt;br&gt;维度    MDM    EMM&lt;br&gt;管理对象    设备硬件及操作系统    设备+应用+数据+身份&lt;br&gt;控制粒度    设备级（全盘控制）    应用/数据级（如仅管理企业容器）&lt;br&gt;适用场景    企业统一配发设备（COBO/COPE）    BYOD（员工个人设备）为主&lt;br&gt;隐私兼容性    低（需完全控制设备）    高（隔离企业数据，保护个人隐私）&lt;br&gt;演进关系    EMM的基础组件    MDM的扩展与升级&lt;br&gt;示例：在BYOD场景中，EMM允许员工用个人手机访问企业邮箱，但仅对企业邮箱App内的数据加密并远程擦除，不影响个人照片等隐私内容59。&lt;/p&gt;
&lt;p&gt;⏳ 四、技术演进：从MDM到EMM&lt;br&gt;移动管理技术经历了三个阶段：&lt;/p&gt;
&lt;p&gt;设备驱动阶段（2007-2012）：MDM主导，功能限于设备注册、密码策略、远程擦除210。&lt;/p&gt;
&lt;p&gt;应用管理阶段（2011-2013）：MAM兴起，引入沙箱/容器隔离企业应用数据，适应BYOD需求25。&lt;/p&gt;
&lt;p&gt;内容与身份融合阶段（2013至今）：&lt;/p&gt;
&lt;p&gt;EMM整合MCM/MIM，实现基于角色、位置、设备状态的动态策略（如“出差时禁止访问财务系统”）10；&lt;/p&gt;
&lt;p&gt;向UEM（统一端点管理）演进，覆盖PC、IoT等更多终端35。&lt;/p&gt;
&lt;p&gt;🏢 五、企业应用价值&lt;br&gt;安全提升：防止数据泄露（如阻断企业文件保存至个人云盘）、抵御越狱/root设备风险16。&lt;/p&gt;
&lt;p&gt;效率优化：&lt;/p&gt;
&lt;p&gt;自动分发应用（如销售团队统一安装CRM工具）；&lt;/p&gt;
&lt;p&gt;远程故障排查，减少现场支持成本9。&lt;/p&gt;
&lt;p&gt;合规支持：满足GDPR等法规要求，审计日志记录敏感操作68。&lt;/p&gt;
&lt;p&gt;💎 总结&lt;br&gt;MDM是移动管理的起点，聚焦设备控制；EMM是更成熟的解决方案，通过应用/数据/身份的精细管理，在安全与员工体验间取得平衡。随着远程办公普及，EMM已成为企业移动化转型的核心基础设施，并持续向跨终端、智能化的UEM方向演进358。&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/06/10/php-game-fish/</guid>
            <title>php开发捕鱼游戏的构思</title>
            <link>https://erik.xyz/2025/06/10/php-game-fish/</link>
            <category>php</category>
            <category>游戏</category>
            <pubDate>Tue, 10 Jun 2025 23:09:00 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;使用PHP开发捕鱼游戏涉及前后端协作，以下是核心实现思路和代码示例：&lt;/p&gt;
&lt;h3 id=&#34;一、系统设计&#34;&gt;&lt;a href=&#34;#一、系统设计&#34; class=&#34;headerlink&#34; title=&#34;一、系统设计&#34;&gt;&lt;/a&gt;一、系统设计&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;前端&lt;/strong&gt;：HTML5 Canvas + JavaScript 绘制游戏场景&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;后端&lt;/strong&gt;：PHP 处理游戏逻辑和数据库操作&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据库&lt;/strong&gt;：MySQL 存储玩家数据&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;通信&lt;/strong&gt;：AJAX 实现前后端交互&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;二、核心功能实现&#34;&gt;&lt;a href=&#34;#二、核心功能实现&#34; class=&#34;headerlink&#34; title=&#34;二、核心功能实现&#34;&gt;&lt;/a&gt;二、核心功能实现&lt;/h3&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;
&lt;h4 id=&#34;1-数据库设计（MySQL）&#34;&gt;&lt;a href=&#34;#1-数据库设计（MySQL）&#34; class=&#34;headerlink&#34; title=&#34;1. 数据库设计（MySQL）&#34;&gt;&lt;/a&gt;1. 数据库设计（MySQL）&lt;/h4&gt;&lt;figure class=&#34;highlight sql&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;CREATE TABLE&lt;/span&gt; players (&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    id &lt;span class=&#34;type&#34;&gt;INT&lt;/span&gt; AUTO_INCREMENT &lt;span class=&#34;keyword&#34;&gt;PRIMARY KEY&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    username &lt;span class=&#34;type&#34;&gt;VARCHAR&lt;/span&gt;(&lt;span class=&#34;number&#34;&gt;50&lt;/span&gt;) &lt;span class=&#34;keyword&#34;&gt;UNIQUE&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    password &lt;span class=&#34;type&#34;&gt;VARCHAR&lt;/span&gt;(&lt;span class=&#34;number&#34;&gt;255&lt;/span&gt;),&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    gold &lt;span class=&#34;type&#34;&gt;INT&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;DEFAULT&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;1000&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    score &lt;span class=&#34;type&#34;&gt;INT&lt;/span&gt; &lt;span class=&#34;keyword&#34;&gt;DEFAULT&lt;/span&gt; &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;);&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h4 id=&#34;2-PHP后端逻辑（结合前端交互）&#34;&gt;&lt;a href=&#34;#2-PHP后端逻辑（结合前端交互）&#34; class=&#34;headerlink&#34; title=&#34;2. PHP后端逻辑（结合前端交互）&#34;&gt;&lt;/a&gt;2. PHP后端逻辑（结合前端交互）&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;游戏核心文件：&lt;code&gt;game.php&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;figure class=&#34;highlight php&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;&amp;lt;?php&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;title function_ invoke__&#34;&gt;session_start&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// 连接数据库&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;variable&#34;&gt;$db&lt;/span&gt; = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title function_ invoke__&#34;&gt;mysqli&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;#x27;localhost&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;string&#34;&gt;&amp;#x27;username&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;string&#34;&gt;&amp;#x27;password&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;string&#34;&gt;&amp;#x27;fishing_game&amp;#x27;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;class&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;FishingGame&lt;/span&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;$db&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;__construct&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;variable&#34;&gt;$db&lt;/span&gt;&lt;/span&gt;) &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;variable language_&#34;&gt;$this&lt;/span&gt;-&amp;gt;db = &lt;span class=&#34;variable&#34;&gt;$db&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// 捕鱼逻辑&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;catchFish&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;variable&#34;&gt;$playerId&lt;/span&gt;, &lt;span class=&#34;variable&#34;&gt;$cannonType&lt;/span&gt;, &lt;span class=&#34;variable&#34;&gt;$fishType&lt;/span&gt;&lt;/span&gt;) &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;variable&#34;&gt;$cost&lt;/span&gt; = &lt;span class=&#34;variable language_&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span class=&#34;title function_ invoke__&#34;&gt;getCannonCost&lt;/span&gt;(&lt;span class=&#34;variable&#34;&gt;$cannonType&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;variable&#34;&gt;$reward&lt;/span&gt; = &lt;span class=&#34;variable language_&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span class=&#34;title function_ invoke__&#34;&gt;getFishReward&lt;/span&gt;(&lt;span class=&#34;variable&#34;&gt;$fishType&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// 扣除金币&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;variable language_&#34;&gt;$this&lt;/span&gt;-&amp;gt;db-&amp;gt;&lt;span class=&#34;title function_ invoke__&#34;&gt;query&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;quot;UPDATE players SET gold = gold - &lt;span class=&#34;subst&#34;&gt;$cost&lt;/span&gt; WHERE id = &lt;span class=&#34;subst&#34;&gt;$playerId&lt;/span&gt;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;comment&#34;&gt;// 捕获判定（简化版概率计算）&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;variable&#34;&gt;$success&lt;/span&gt; = (&lt;span class=&#34;title function_ invoke__&#34;&gt;rand&lt;/span&gt;(&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;100&lt;/span&gt;) &amp;gt; &lt;span class=&#34;number&#34;&gt;30&lt;/span&gt;); &lt;span class=&#34;comment&#34;&gt;// 70%成功率&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt;(&lt;span class=&#34;variable&#34;&gt;$success&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;variable language_&#34;&gt;$this&lt;/span&gt;-&amp;gt;db-&amp;gt;&lt;span class=&#34;title function_ invoke__&#34;&gt;query&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;quot;UPDATE players SET gold = gold + &lt;span class=&#34;subst&#34;&gt;$reward&lt;/span&gt;, score = score + &lt;span class=&#34;subst&#34;&gt;$reward&lt;/span&gt; WHERE id = &lt;span class=&#34;subst&#34;&gt;$playerId&lt;/span&gt;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; [&lt;span class=&#34;string&#34;&gt;&amp;#x27;success&amp;#x27;&lt;/span&gt; =&amp;gt; &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;, &lt;span class=&#34;string&#34;&gt;&amp;#x27;reward&amp;#x27;&lt;/span&gt; =&amp;gt; &lt;span class=&#34;variable&#34;&gt;$reward&lt;/span&gt;];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; [&lt;span class=&#34;string&#34;&gt;&amp;#x27;success&amp;#x27;&lt;/span&gt; =&amp;gt; &lt;span class=&#34;literal&#34;&gt;false&lt;/span&gt;];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;getCannonCost&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;variable&#34;&gt;$type&lt;/span&gt;&lt;/span&gt;) &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;variable&#34;&gt;$costs&lt;/span&gt; = [&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt; =&amp;gt; &lt;span class=&#34;number&#34;&gt;5&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt; =&amp;gt; &lt;span class=&#34;number&#34;&gt;10&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;3&lt;/span&gt; =&amp;gt; &lt;span class=&#34;number&#34;&gt;20&lt;/span&gt;]; &lt;span class=&#34;comment&#34;&gt;// 炮弹类型对应消耗&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;$costs&lt;/span&gt;[&lt;span class=&#34;variable&#34;&gt;$type&lt;/span&gt;] ?? &lt;span class=&#34;number&#34;&gt;5&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;title&#34;&gt;getFishReward&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;span class=&#34;variable&#34;&gt;$type&lt;/span&gt;&lt;/span&gt;) &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;variable&#34;&gt;$rewards&lt;/span&gt; = [&lt;span class=&#34;number&#34;&gt;1&lt;/span&gt; =&amp;gt; &lt;span class=&#34;number&#34;&gt;10&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt; =&amp;gt; &lt;span class=&#34;number&#34;&gt;25&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;3&lt;/span&gt; =&amp;gt; &lt;span class=&#34;number&#34;&gt;50&lt;/span&gt;]; &lt;span class=&#34;comment&#34;&gt;// 鱼类奖励&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;variable&#34;&gt;$rewards&lt;/span&gt;[&lt;span class=&#34;variable&#34;&gt;$type&lt;/span&gt;] ?? &lt;span class=&#34;number&#34;&gt;10&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// 实例化游戏&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;variable&#34;&gt;$game&lt;/span&gt; = &lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;FishingGame&lt;/span&gt;(&lt;span class=&#34;variable&#34;&gt;$db&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// 处理AJAX请求&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt; (&lt;span class=&#34;variable&#34;&gt;$_SERVER&lt;/span&gt;[&lt;span class=&#34;string&#34;&gt;&amp;#x27;REQUEST_METHOD&amp;#x27;&lt;/span&gt;] === &lt;span class=&#34;string&#34;&gt;&amp;#x27;POST&amp;#x27;&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;variable&#34;&gt;$playerId&lt;/span&gt; = &lt;span class=&#34;variable&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span class=&#34;string&#34;&gt;&amp;#x27;player_id&amp;#x27;&lt;/span&gt;];&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;variable&#34;&gt;$data&lt;/span&gt; = &lt;span class=&#34;title function_ invoke__&#34;&gt;json_decode&lt;/span&gt;(&lt;span class=&#34;title function_ invoke__&#34;&gt;file_get_contents&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;#x27;php://input&amp;#x27;&lt;/span&gt;), &lt;span class=&#34;literal&#34;&gt;true&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt;(&lt;span class=&#34;keyword&#34;&gt;isset&lt;/span&gt;(&lt;span class=&#34;variable&#34;&gt;$data&lt;/span&gt;[&lt;span class=&#34;string&#34;&gt;&amp;#x27;action&amp;#x27;&lt;/span&gt;]) &amp;amp;&amp;amp; &lt;span class=&#34;variable&#34;&gt;$data&lt;/span&gt;[&lt;span class=&#34;string&#34;&gt;&amp;#x27;action&amp;#x27;&lt;/span&gt;] === &lt;span class=&#34;string&#34;&gt;&amp;#x27;catch&amp;#x27;&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;variable&#34;&gt;$response&lt;/span&gt; = &lt;span class=&#34;variable&#34;&gt;$game&lt;/span&gt;-&amp;gt;&lt;span class=&#34;title function_ invoke__&#34;&gt;catchFish&lt;/span&gt;(&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;variable&#34;&gt;$playerId&lt;/span&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;variable&#34;&gt;$data&lt;/span&gt;[&lt;span class=&#34;string&#34;&gt;&amp;#x27;cannon&amp;#x27;&lt;/span&gt;],&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &lt;span class=&#34;variable&#34;&gt;$data&lt;/span&gt;[&lt;span class=&#34;string&#34;&gt;&amp;#x27;fishType&amp;#x27;&lt;/span&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        );&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;title function_ invoke__&#34;&gt;header&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;#x27;Content-Type: application/json&amp;#x27;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;title function_ invoke__&#34;&gt;json_encode&lt;/span&gt;(&lt;span class=&#34;variable&#34;&gt;$response&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;exit&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;?&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;h4 id=&#34;3-前端核心实现（JavaScript）&#34;&gt;&lt;a href=&#34;#3-前端核心实现（JavaScript）&#34; class=&#34;headerlink&#34; title=&#34;3. 前端核心实现（JavaScript）&#34;&gt;&lt;/a&gt;3. 前端核心实现（JavaScript）&lt;/h4&gt;&lt;figure class=&#34;highlight html&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;97&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;98&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;99&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;100&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;101&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;102&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;103&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;104&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;105&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;106&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;107&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;108&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;canvas&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;id&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;gameCanvas&amp;quot;&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;width&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;800&amp;quot;&lt;/span&gt; &lt;span class=&#34;attr&#34;&gt;height&lt;/span&gt;=&lt;span class=&#34;string&#34;&gt;&amp;quot;600&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;canvas&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;&lt;span class=&#34;name&#34;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;language-javascript&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;&lt;span class=&#34;comment&#34;&gt;// 游戏配置&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;variable constant_&#34;&gt;FISH_TYPES&lt;/span&gt; = &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;: &amp;#123; &lt;span class=&#34;attr&#34;&gt;speed&lt;/span&gt;: &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;attr&#34;&gt;color&lt;/span&gt;: &lt;span class=&#34;string&#34;&gt;&amp;#x27;#FF9900&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;attr&#34;&gt;size&lt;/span&gt;: &lt;span class=&#34;number&#34;&gt;30&lt;/span&gt; &amp;#125;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;: &amp;#123; &lt;span class=&#34;attr&#34;&gt;speed&lt;/span&gt;: &lt;span class=&#34;number&#34;&gt;3&lt;/span&gt;, &lt;span class=&#34;attr&#34;&gt;color&lt;/span&gt;: &lt;span class=&#34;string&#34;&gt;&amp;#x27;#00CCFF&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;attr&#34;&gt;size&lt;/span&gt;: &lt;span class=&#34;number&#34;&gt;50&lt;/span&gt; &amp;#125;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &lt;span class=&#34;number&#34;&gt;3&lt;/span&gt;: &amp;#123; &lt;span class=&#34;attr&#34;&gt;speed&lt;/span&gt;: &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;, &lt;span class=&#34;attr&#34;&gt;color&lt;/span&gt;: &lt;span class=&#34;string&#34;&gt;&amp;#x27;#FF66CC&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;attr&#34;&gt;size&lt;/span&gt;: &lt;span class=&#34;number&#34;&gt;80&lt;/span&gt; &amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;&amp;#125;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;FishingGame&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &lt;span class=&#34;title function_&#34;&gt;constructor&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;canvas&lt;/span&gt; = &lt;span class=&#34;variable language_&#34;&gt;document&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;getElementById&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;#x27;gameCanvas&amp;#x27;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;ctx&lt;/span&gt; = &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;canvas&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;getContext&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;#x27;2d&amp;#x27;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;fishes&lt;/span&gt; = [];&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;player&lt;/span&gt; = &amp;#123; &lt;span class=&#34;attr&#34;&gt;gold&lt;/span&gt;: &lt;span class=&#34;number&#34;&gt;1000&lt;/span&gt;, &lt;span class=&#34;attr&#34;&gt;score&lt;/span&gt;: &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt; &amp;#125;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;init&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &lt;span class=&#34;title function_&#34;&gt;init&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;generateFish&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;gameLoop&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;canvas&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;addEventListener&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;#x27;click&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;handleClick&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;bind&lt;/span&gt;(&lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;));&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &lt;span class=&#34;title function_&#34;&gt;generateFish&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// 随机生成鱼群&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;built_in&#34;&gt;setInterval&lt;/span&gt;(&lt;span class=&#34;function&#34;&gt;() =&amp;gt;&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; type = &lt;span class=&#34;title class_&#34;&gt;Math&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;floor&lt;/span&gt;(&lt;span class=&#34;title class_&#34;&gt;Math&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;random&lt;/span&gt;() * &lt;span class=&#34;number&#34;&gt;3&lt;/span&gt;) + &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;fishes&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;push&lt;/span&gt;(&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;        type,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;        &lt;span class=&#34;attr&#34;&gt;x&lt;/span&gt;: -&lt;span class=&#34;number&#34;&gt;50&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;        &lt;span class=&#34;attr&#34;&gt;y&lt;/span&gt;: &lt;span class=&#34;title class_&#34;&gt;Math&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;random&lt;/span&gt;() * &lt;span class=&#34;number&#34;&gt;500&lt;/span&gt; + &lt;span class=&#34;number&#34;&gt;50&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;        ...&lt;span class=&#34;variable constant_&#34;&gt;FISH_TYPES&lt;/span&gt;[type]&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &amp;#125;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &amp;#125;, &lt;span class=&#34;number&#34;&gt;2000&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &lt;span class=&#34;title function_&#34;&gt;handleClick&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;e&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; rect = &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;canvas&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;getBoundingClientRect&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; clickX = e.&lt;span class=&#34;property&#34;&gt;clientX&lt;/span&gt; - rect.&lt;span class=&#34;property&#34;&gt;left&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; clickY = e.&lt;span class=&#34;property&#34;&gt;clientY&lt;/span&gt; - rect.&lt;span class=&#34;property&#34;&gt;top&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// 检测点击到的鱼&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; cannonType = &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;; &lt;span class=&#34;comment&#34;&gt;// 默认炮弹类型&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;for&lt;/span&gt;(&lt;span class=&#34;keyword&#34;&gt;let&lt;/span&gt; i = &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;fishes&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;length&lt;/span&gt; - &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;; i &amp;gt;= &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;; i--) &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; fish = &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;fishes&lt;/span&gt;[i];&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; distance = &lt;span class=&#34;title class_&#34;&gt;Math&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;sqrt&lt;/span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;        &lt;span class=&#34;title class_&#34;&gt;Math&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;pow&lt;/span&gt;(fish.&lt;span class=&#34;property&#34;&gt;x&lt;/span&gt; - clickX, &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;) + &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;        &lt;span class=&#34;title class_&#34;&gt;Math&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;pow&lt;/span&gt;(fish.&lt;span class=&#34;property&#34;&gt;y&lt;/span&gt; - clickY, &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      );&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt;(distance &amp;lt; fish.&lt;span class=&#34;property&#34;&gt;size&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;        &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;sendCatchRequest&lt;/span&gt;(fish.&lt;span class=&#34;property&#34;&gt;type&lt;/span&gt;, cannonType);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;        &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;fishes&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;splice&lt;/span&gt;(i, &lt;span class=&#34;number&#34;&gt;1&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;        &lt;span class=&#34;keyword&#34;&gt;break&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &lt;span class=&#34;keyword&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;sendCatchRequest&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;fishType, cannonType&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; response = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;title function_&#34;&gt;fetch&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;#x27;game.php&amp;#x27;&lt;/span&gt;, &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;attr&#34;&gt;method&lt;/span&gt;: &lt;span class=&#34;string&#34;&gt;&amp;#x27;POST&amp;#x27;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;attr&#34;&gt;headers&lt;/span&gt;: &amp;#123; &lt;span class=&#34;string&#34;&gt;&amp;#x27;Content-Type&amp;#x27;&lt;/span&gt;: &lt;span class=&#34;string&#34;&gt;&amp;#x27;application/json&amp;#x27;&lt;/span&gt; &amp;#125;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;attr&#34;&gt;body&lt;/span&gt;: &lt;span class=&#34;title class_&#34;&gt;JSON&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;stringify&lt;/span&gt;(&amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;        &lt;span class=&#34;attr&#34;&gt;action&lt;/span&gt;: &lt;span class=&#34;string&#34;&gt;&amp;#x27;catch&amp;#x27;&lt;/span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;        &lt;span class=&#34;attr&#34;&gt;fishType&lt;/span&gt;: fishType,&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;        &lt;span class=&#34;attr&#34;&gt;cannon&lt;/span&gt;: cannonType&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &amp;#125;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &amp;#125;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;const&lt;/span&gt; result = &lt;span class=&#34;keyword&#34;&gt;await&lt;/span&gt; response.&lt;span class=&#34;title function_&#34;&gt;json&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt;(result.&lt;span class=&#34;property&#34;&gt;success&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;player&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;gold&lt;/span&gt; += result.&lt;span class=&#34;property&#34;&gt;reward&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;player&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;score&lt;/span&gt; += result.&lt;span class=&#34;property&#34;&gt;reward&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;showEffect&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;&amp;#x27;+&amp;#x27;&lt;/span&gt;+result.&lt;span class=&#34;property&#34;&gt;reward&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &lt;span class=&#34;title function_&#34;&gt;gameLoop&lt;/span&gt;(&lt;span class=&#34;params&#34;&gt;&lt;/span&gt;) &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;ctx&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;clearRect&lt;/span&gt;(&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;800&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;600&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// 绘制背景&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;ctx&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;fillStyle&lt;/span&gt; = &lt;span class=&#34;string&#34;&gt;&amp;#x27;#3399FF&amp;#x27;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;ctx&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;fillRect&lt;/span&gt;(&lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;800&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;600&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// 更新并绘制鱼&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;fishes&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;forEach&lt;/span&gt;(&lt;span class=&#34;function&#34;&gt;&lt;span class=&#34;params&#34;&gt;fish&lt;/span&gt; =&amp;gt;&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      fish.&lt;span class=&#34;property&#34;&gt;x&lt;/span&gt; += fish.&lt;span class=&#34;property&#34;&gt;speed&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;keyword&#34;&gt;if&lt;/span&gt;(fish.&lt;span class=&#34;property&#34;&gt;x&lt;/span&gt; &amp;gt; &lt;span class=&#34;number&#34;&gt;850&lt;/span&gt;) fish.&lt;span class=&#34;property&#34;&gt;x&lt;/span&gt; = -&lt;span class=&#34;number&#34;&gt;50&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;ctx&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;beginPath&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;ctx&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;fillStyle&lt;/span&gt; = fish.&lt;span class=&#34;property&#34;&gt;color&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;ctx&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;arc&lt;/span&gt;(fish.&lt;span class=&#34;property&#34;&gt;x&lt;/span&gt;, fish.&lt;span class=&#34;property&#34;&gt;y&lt;/span&gt;, fish.&lt;span class=&#34;property&#34;&gt;size&lt;/span&gt;/&lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;0&lt;/span&gt;, &lt;span class=&#34;title class_&#34;&gt;Math&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;PI&lt;/span&gt; * &lt;span class=&#34;number&#34;&gt;2&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;      &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;ctx&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;fill&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &amp;#125;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;comment&#34;&gt;// 绘制UI&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;ctx&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;fillStyle&lt;/span&gt; = &lt;span class=&#34;string&#34;&gt;&amp;#x27;#000&amp;#x27;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;ctx&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;font&lt;/span&gt; = &lt;span class=&#34;string&#34;&gt;&amp;#x27;20px Arial&amp;#x27;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;ctx&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;fillText&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;`金币: &lt;span class=&#34;subst&#34;&gt;$&amp;#123;&lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.player.gold&amp;#125;&lt;/span&gt;`&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;20&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;30&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;property&#34;&gt;ctx&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;fillText&lt;/span&gt;(&lt;span class=&#34;string&#34;&gt;`分数: &lt;span class=&#34;subst&#34;&gt;$&amp;#123;&lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.player.score&amp;#125;&lt;/span&gt;`&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;20&lt;/span&gt;, &lt;span class=&#34;number&#34;&gt;60&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;    &lt;span class=&#34;title function_&#34;&gt;requestAnimationFrame&lt;/span&gt;(&lt;span class=&#34;function&#34;&gt;() =&amp;gt;&lt;/span&gt; &lt;span class=&#34;variable language_&#34;&gt;this&lt;/span&gt;.&lt;span class=&#34;title function_&#34;&gt;gameLoop&lt;/span&gt;());&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;  &amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;&amp;#125;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;FishingGame&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;language-javascript&#34;&gt;&lt;/span&gt;&lt;span class=&#34;tag&#34;&gt;&amp;lt;/&lt;span class=&#34;name&#34;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&#34;三、游戏功能扩展建议&#34;&gt;&lt;a href=&#34;#三、游戏功能扩展建议&#34; class=&#34;headerlink&#34; title=&#34;三、游戏功能扩展建议&#34;&gt;&lt;/a&gt;三、游戏功能扩展建议&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;高级功能&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;添加多种武器系统（激光炮、渔网）&lt;/li&gt;
&lt;li&gt;实现鱼群特殊行为（BOSS鱼、鱼群迁徙）&lt;/li&gt;
&lt;li&gt;道具系统（金币加倍、冰冻效果）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;性能优化&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WebSocket实现实时通信&lt;/li&gt;
&lt;li&gt;对象池重用鱼对象&lt;/li&gt;
&lt;li&gt;精灵图代替纯色绘制&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;安全增强&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;炮弹消耗验证&lt;/li&gt;
&lt;li&gt;频率限制（防作弊）&lt;/li&gt;
&lt;li&gt;数据加密传输&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;商业化功能&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;内购金币系统&lt;/li&gt;
&lt;li&gt;每日任务奖励&lt;/li&gt;
&lt;li&gt;玩家排行榜&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;四、部署注意事项&#34;&gt;&lt;a href=&#34;#四、部署注意事项&#34; class=&#34;headerlink&#34; title=&#34;四、部署注意事项&#34;&gt;&lt;/a&gt;四、部署注意事项&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;使用PHP 7.4+ 获取最佳性能&lt;/li&gt;
&lt;li&gt;配置OPCache加速PHP执行&lt;/li&gt;
&lt;li&gt;前端资源使用CDN加速&lt;/li&gt;
&lt;li&gt;定期备份玩家数据&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;完整实现需包含用户系统（注册/登录）、游戏商城、社交功能等模块。实际开发中建议使用游戏引擎如Phaser.js替代原生Canvas API以提高开发效率。&lt;/p&gt;
&lt;/blockquote&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/05/26/webman-hyperf-change/</guid>
            <title>webman和hyperf对比解析</title>
            <link>https://erik.xyz/2025/05/26/webman-hyperf-change/</link>
            <category>php</category>
            <category>hyperf</category>
            <category>webman</category>
            <pubDate>Mon, 26 May 2025 21:55:00 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;好的，我们来深入探讨一下 &lt;code&gt;webman&lt;/code&gt;、&lt;code&gt;Hyperf&lt;/code&gt; 的底层原理以及 PHP 中注解（Annotations）的实现原理。&lt;/p&gt;
&lt;h2 id=&#34;1-webman-底层原理&#34;&gt;&lt;a href=&#34;#1-webman-底层原理&#34; class=&#34;headerlink&#34; title=&#34;1. webman 底层原理&#34;&gt;&lt;/a&gt;1. webman 底层原理&lt;/h2&gt;&lt;p&gt;&lt;code&gt;webman&lt;/code&gt; 的核心设计理念是 &lt;strong&gt;轻量、高性能、易用&lt;/strong&gt;。它构建在强大的异步事件驱动网络库 &lt;strong&gt;Workerman&lt;/strong&gt; 之上，充分利用了 PHP 的 CLI（命令行接口）模式和事件循环机制，摒弃了传统的 PHP-FPM 模式，从而实现了高性能和高并发。&lt;/p&gt;
&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;
&lt;h3 id=&#34;核心原理剖析&#34;&gt;&lt;a href=&#34;#核心原理剖析&#34; class=&#34;headerlink&#34; title=&#34;核心原理剖析&#34;&gt;&lt;/a&gt;核心原理剖析&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;基于 Workerman：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;事件驱动：&lt;/strong&gt; Workerman 使用 &lt;code&gt;libevent&lt;/code&gt; (默认)、&lt;code&gt;event&lt;/code&gt; 或 &lt;code&gt;swoole&lt;/code&gt; 扩展提供的事件循环库。它在一个或多个主进程中创建和管理一个或多个事件循环 (&lt;code&gt;event loop&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;非阻塞 I/O：&lt;/strong&gt; 所有网络操作（TCP/UDP/Unix Socket 监听和连接）都是非阻塞的。当一个连接上有数据可读、可写或发生错误时，事件循环会触发注册的回调函数进行处理。这避免了为每个连接创建线程或进程的巨大开销。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多进程模型：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;主进程 (Master)：&lt;/strong&gt; 负责解析配置、创建监听套接字、管理子进程（Worker 进程）、监控子进程状态（如崩溃重启）、处理信号（如 reload, stop, status）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Worker 进程 (Worker)：&lt;/strong&gt; 真正处理业务逻辑的进程。主进程 &lt;code&gt;fork&lt;/code&gt; 出多个 Worker 子进程。&lt;strong&gt;默认情况下，这些 Worker 进程是常驻内存的&lt;/strong&gt;。每个 Worker 进程都独立运行着自己的事件循环，处理分配给它的连接请求。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;进程间通信 (IPC)：&lt;/strong&gt; Master 和 Worker 之间通常通过 Unix Socket 管道进行通信（例如发送 reload 信号、状态查询）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;协议支持：&lt;/strong&gt; Workerman 内置了对 HTTP、WebSocket、TCP、UDP 等协议的支持，并能自定义协议。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;webman 在 Workerman 上的封装：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PSR 兼容：&lt;/strong&gt; 提供了符合 PSR-7 (HTTP 消息接口)、PSR-15 (HTTP 中间件)、PSR-11 (容器) 等标准的实现，使得开发者可以使用熟悉的中间件模式和依赖注入。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;路由：&lt;/strong&gt; 提供灵活的路由配置（如文件路由 &lt;code&gt;route.php&lt;/code&gt;），将 HTTP 请求映射到对应的控制器方法或闭包。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中间件：&lt;/strong&gt; 实现了洋葱圈模型的中间件机制，方便处理请求前、后的逻辑（如鉴权、日志、跨域）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;控制器：&lt;/strong&gt; 组织业务逻辑代码。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;视图：&lt;/strong&gt; 支持简单的模板渲染。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据库/Redis：&lt;/strong&gt; 提供便捷的封装，但需要注意&lt;strong&gt;在默认 Worker 进程模型下，数据库连接不能像 Swoole 协程那样自动释放/重用，需要开发者自行管理连接的生命周期&lt;/strong&gt;（通常在一个请求处理周期内创建和关闭，或使用连接池）。webman 官方提供了 &lt;code&gt;webman/redis-queue&lt;/code&gt; 等插件来帮助管理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文件监控与热更新：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;php webman start&lt;/code&gt; 默认启动时带 &lt;code&gt;-w&lt;/code&gt; 参数开启监控。&lt;/li&gt;
&lt;li&gt;Workerman 利用 Linux 内核的 &lt;code&gt;inotify&lt;/code&gt; 机制（或 macOS 的 &lt;code&gt;kqueue&lt;/code&gt;）监控项目文件（&lt;code&gt;.php&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt; 等）的更改。&lt;/li&gt;
&lt;li&gt;当检测到文件修改，主进程会向所有 Worker 进程发送 &lt;code&gt;SIGUSR1&lt;/code&gt; 信号。&lt;/li&gt;
&lt;li&gt;Worker 进程收到信号后，安全地退出当前正在处理的请求（如果有），然后优雅地重启自身（重新加载修改后的代码），而主进程保持运行并重新 &lt;code&gt;fork&lt;/code&gt; 新的 Worker 进程。这个过程&lt;strong&gt;非常快速&lt;/strong&gt;，用户几乎感知不到服务中断，实现了“热更新”。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;高性能关键点：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;常驻内存：&lt;/strong&gt; 最大的优势。框架核心、业务代码、配置等在 Worker 进程启动时加载一次，驻留在内存中。处理后续请求时&lt;strong&gt;无需重复初始化框架、加载核心文件、连接数据库（如果连接池管理得当）&lt;/strong&gt;，极大减少了开销。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;事件驱动 &amp;amp; 非阻塞：&lt;/strong&gt; 高效处理高并发连接，尤其适合 I/O 密集型场景。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;轻量级：&lt;/strong&gt; 框架本身代码量少，抽象层薄，开销小。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;总结 webman 原理：&lt;/strong&gt; 利用 Workerman 的事件驱动、非阻塞 I/O 和多进程模型提供高性能网络基础。webman 在其上构建了一个符合现代 PHP 开发规范（PSR）的轻量级 Web 框架，并通过文件监控实现了便捷的热更新。其核心优势在于常驻内存带来的性能提升和 Workerman 的稳定性。&lt;/p&gt;
&lt;h2 id=&#34;2-Hyperf-底层原理&#34;&gt;&lt;a href=&#34;#2-Hyperf-底层原理&#34; class=&#34;headerlink&#34; title=&#34;2. Hyperf 底层原理&#34;&gt;&lt;/a&gt;2. Hyperf 底层原理&lt;/h2&gt;&lt;p&gt;&lt;code&gt;Hyperf&lt;/code&gt; 是一个 &lt;strong&gt;高性能、高灵活性的企业级协程框架&lt;/strong&gt;。它的核心建立在 &lt;strong&gt;Swoole&lt;/strong&gt; 扩展之上，深度利用了 Swoole 提供的 &lt;strong&gt;协程&lt;/strong&gt; 能力来实现高性能和高并发，并引入了大量 Java Spring Cloud 等框架的设计理念（如依赖注入、AOP、注解驱动）。&lt;/p&gt;
&lt;h3 id=&#34;核心原理剖析-1&#34;&gt;&lt;a href=&#34;#核心原理剖析-1&#34; class=&#34;headerlink&#34; title=&#34;核心原理剖析&#34;&gt;&lt;/a&gt;核心原理剖析&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;基于 Swoole：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;协程：&lt;/strong&gt; Swoole 的核心能力。协程是用户态的轻量级线程，由框架或运行时自身调度，切换成本极低（通常只有函数调用的开销）。Hyperf &lt;strong&gt;深度拥抱协程&lt;/strong&gt;，几乎所有组件（HTTP Server、Database Client、Redis Client、RPC Client/Server、AMQP 等）都设计为&lt;strong&gt;协程安全&lt;/strong&gt;或&lt;strong&gt;协程友好&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;协程调度：&lt;/strong&gt; Swoole 提供了协程调度器。当一个协程遇到 I/O 操作（如数据库查询、网络请求、文件读写）时，它会自动挂起（&lt;code&gt;yield&lt;/code&gt;），将 CPU 让给其他就绪的协程。当 I/O 操作完成，调度器会恢复（&lt;code&gt;resume&lt;/code&gt;）该协程继续执行。这使得&lt;strong&gt;单进程内可以并发处理成千上万个连接/任务&lt;/strong&gt;，且代码逻辑依然是&lt;strong&gt;顺序编写&lt;/strong&gt;（异步回调的 &lt;code&gt;callback hell&lt;/code&gt; 问题得到极大缓解）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;事件驱动：&lt;/strong&gt; 底层仍然是事件驱动（基于 &lt;code&gt;epoll&lt;/code&gt;/&lt;code&gt;kqueue&lt;/code&gt; 等），Swoole 的事件循环驱动着协程的调度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server：&lt;/strong&gt; 提供高性能的 HTTP Server、WebSocket Server、TCP/UDP Server 等。Hyperf 主要使用 HTTP Server。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hyperf 在 Swoole 上的高级封装与架构：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;强大的依赖注入容器 (DI Container)：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;是整个框架的基石（基于 &lt;code&gt;hyperf/di&lt;/code&gt; 组件）。&lt;/li&gt;
&lt;li&gt;实现了 &lt;code&gt;PSR-11&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;支持自动装配（Autowiring）、接口绑定实现、构造函数注入、属性注入、方法注入。&lt;/li&gt;
&lt;li&gt;管理着应用中几乎所有对象的生命周期（单例、原型等）。&lt;/li&gt;
&lt;li&gt;是 AOP 和注解驱动实现的基础。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;注解驱动开发 (Annotation-Driven Development)：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Hyperf &lt;strong&gt;重度依赖注解&lt;/strong&gt;来配置路由、定义中间件、声明 AOP 切面、标记定时任务、配置依赖注入、定义 RPC 服务等。&lt;/li&gt;
&lt;li&gt;框架启动时（或在 &lt;code&gt;Worker&lt;/code&gt; 进程启动时，取决于注解作用域），会通过&lt;strong&gt;反射&lt;/strong&gt;扫描代码，解析类、方法、属性上的注解，收集元数据，并动态生成代理类或进行相应的配置注册（如将路由信息注册到路由器）。&lt;/li&gt;
&lt;li&gt;极大提高了开发效率和代码的可读性、可维护性（配置紧贴代码）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;面向切面编程 (AOP)：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;基于 DI 容器和动态代理实现。&lt;/li&gt;
&lt;li&gt;允许开发者定义“切面”（&lt;code&gt;Aspect&lt;/code&gt;）类，其中包含“通知”（&lt;code&gt;Advice&lt;/code&gt; - 如 &lt;code&gt;@Before&lt;/code&gt;, &lt;code&gt;@After&lt;/code&gt;, &lt;code&gt;@Around&lt;/code&gt;）和“切入点”（&lt;code&gt;Pointcut&lt;/code&gt; - 通过注解或表达式指定哪些类的哪些方法需要被切入）。&lt;/li&gt;
&lt;li&gt;框架在运行时，会为匹配 &lt;code&gt;Pointcut&lt;/code&gt; 的目标类&lt;strong&gt;生成代理类&lt;/strong&gt;。当调用目标方法时，实际上是调用代理类的方法，代理类会按顺序执行相关的 &lt;code&gt;Advice&lt;/code&gt; 逻辑（如日志记录、性能监控、事务管理、缓存处理、权限校验等），然后再调用或环绕调用原始目标方法。&lt;/li&gt;
&lt;li&gt;实现了横切关注点（Cross-Cutting Concerns）与核心业务逻辑的解耦。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;协程上下文管理：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;由于协程是轻量级且并发执行的，传统的全局变量、单例模式在协程环境下可能不安全（一个协程修改会影响其他协程）。&lt;/li&gt;
&lt;li&gt;Hyperf 提供了 &lt;code&gt;hyperf/context&lt;/code&gt; 组件，利用 Swoole 的协程 API (&lt;code&gt;Swoole\Coroutine::getContext()&lt;/code&gt;) 实现&lt;strong&gt;协程级别的上下文隔离&lt;/strong&gt;。&lt;code&gt;Context&lt;/code&gt; 类允许安全地在同一个协程内存储和获取数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;连接池：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;为昂贵的资源（如数据库连接、Redis 连接、HTTP 客户端连接）提供池化管理。&lt;/li&gt;
&lt;li&gt;当协程需要资源时，从池中获取；使用完毕后，归还到池中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免频繁创建和销毁连接的开销&lt;/strong&gt;，极大提升性能，并且&lt;strong&gt;天然适配协程模型&lt;/strong&gt;（每个协程使用独立的连接，避免并发问题）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;组件化与异步非阻塞客户端：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Hyperf 提供了大量开箱即用的高性能协程组件：数据库 (&lt;code&gt;hyperf/database&lt;/code&gt; - 基于 Eloquent/Doctrine, 带连接池)、Redis (&lt;code&gt;hyperf/redis&lt;/code&gt; - 带连接池)、缓存、队列 (&lt;code&gt;hyperf/async-queue&lt;/code&gt;)、RPC (&lt;code&gt;hyperf/json-rpc&lt;/code&gt;)、服务注册与发现 (&lt;code&gt;hyperf/service-governance&lt;/code&gt;)、配置中心 (&lt;code&gt;hyperf/config&lt;/code&gt;)、分布式追踪 (&lt;code&gt;hyperf/tracer&lt;/code&gt;)、限流熔断 (&lt;code&gt;hyperf/rate-limit&lt;/code&gt;, &lt;code&gt;hyperf/circuit-breaker&lt;/code&gt;)、GraphQL、gRPC、AMQP、WebSocket 等。&lt;/li&gt;
&lt;li&gt;这些客户端底层都使用 Swoole 提供的协程 Client 或自行实现的协程化 Client，确保所有 I/O 操作都是&lt;strong&gt;异步非阻塞&lt;/strong&gt;的，能够被协程调度器挂起和恢复。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;进程模型：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;主进程 (Master)：&lt;/strong&gt; 管理服务生命周期。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manager 进程：&lt;/strong&gt; 管理 Worker/TaskWorker 进程（创建、回收）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Worker 进程：&lt;/strong&gt; 处理网络请求（HTTP, WebSocket, TCP 等）。&lt;strong&gt;每个 Worker 进程是一个独立的协程调度单元&lt;/strong&gt;，内部可以并发运行大量协程处理请求。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TaskWorker 进程 (可选)：&lt;/strong&gt; 专门处理耗时较长的同步阻塞任务（如某些复杂计算、调用不支持协程的库）。Worker 进程通过 &lt;code&gt;task()&lt;/code&gt; 投递任务到 TaskWorker。Hyperf 也提供了基于消息队列 (&lt;code&gt;async-queue&lt;/code&gt;) 的异步任务处理方案。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;热更新：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;原理类似 webman/Workerman，使用 &lt;code&gt;inotify&lt;/code&gt;/&lt;code&gt;kqueue&lt;/code&gt; 监控文件变化。&lt;/li&gt;
&lt;li&gt;向 Worker 进程发送信号 (&lt;code&gt;SIGUSR1&lt;/code&gt; 或 &lt;code&gt;SIGTERM&lt;/code&gt;) 通知其优雅重启（完成当前请求后退出，主进程重新拉起新 Worker 加载新代码）。Hyperf 的 DI 容器和代理类生成机制使得热更新相对可靠。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;总结 Hyperf 原理：&lt;/strong&gt; 深度集成 Swoole 协程，构建了一个高性能、企业级的微服务框架。其核心在于强大的依赖注入容器、基于注解的元编程和配置、AOP 解耦横切关注点、完善的协程上下文和连接池管理，以及大量开箱即用的高性能协程组件。它更适合构建复杂的、分布式的高并发系统（如微服务架构）。&lt;/p&gt;
&lt;h2 id=&#34;3-PHP-注解-Annotations-原理&#34;&gt;&lt;a href=&#34;#3-PHP-注解-Annotations-原理&#34; class=&#34;headerlink&#34; title=&#34;3. PHP 注解 (Annotations) 原理&#34;&gt;&lt;/a&gt;3. PHP 注解 (Annotations) 原理&lt;/h2&gt;&lt;p&gt;注解（PHP 8 之前常称为 DocBlock Annotations，PHP 8 引入了原生 Attributes）是一种&lt;strong&gt;元数据&lt;/strong&gt;机制。它允许你将结构化的信息（元数据）附加到代码元素（类、方法、属性、函数、参数）上。这些信息本身&lt;strong&gt;不会直接影响代码的逻辑执行&lt;/strong&gt;，但可以被&lt;strong&gt;外部工具（如框架、库、IDE、文档生成器）在运行时或编译时读取并利用&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;实现原理-重点在框架如何利用&#34;&gt;&lt;a href=&#34;#实现原理-重点在框架如何利用&#34; class=&#34;headerlink&#34; title=&#34;实现原理 (重点在框架如何利用)&#34;&gt;&lt;/a&gt;实现原理 (重点在框架如何利用)&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PHP 8 之前 (DocBlock Annotations)：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;存储位置：&lt;/strong&gt; 元数据以特定格式的注释块（&lt;code&gt;/** ... */&lt;/code&gt;）形式写在代码元素的上方。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;格式：&lt;/strong&gt; 遵循 PHPDoc 标准，但框架会定义自己的特殊标签（如 &lt;code&gt;@Route&lt;/code&gt;, &lt;code&gt;@Inject&lt;/code&gt;, &lt;code&gt;@Cacheable&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解析：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;反射 (Reflection)：&lt;/strong&gt; 框架利用 PHP 的反射 API (&lt;code&gt;ReflectionClass&lt;/code&gt;, &lt;code&gt;ReflectionMethod&lt;/code&gt;, &lt;code&gt;ReflectionProperty&lt;/code&gt;) 获取代码元素（类、方法、属性）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;获取 DocComment：&lt;/strong&gt; 通过反射对象的 &lt;code&gt;getDocComment()&lt;/code&gt; 方法获取该元素上的文档注释字符串。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解析字符串：&lt;/strong&gt; 框架需要编写自己的解析器（或使用库如 &lt;code&gt;doctrine/annotations&lt;/code&gt;）来解析这个字符串。解析器：&lt;ul&gt;
&lt;li&gt;识别以 &lt;code&gt;@&lt;/code&gt; 开头的标记（Tags）。&lt;/li&gt;
&lt;li&gt;解析标记后的参数（可能是简单的字符串、键值对、数组、甚至嵌套结构）。&lt;/li&gt;
&lt;li&gt;将解析结果转换为结构化的数据（通常是数组或特定的注解对象）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;处理：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;启动时扫描：&lt;/strong&gt; 框架通常在启动阶段（或首次请求时）扫描指定的目录或命名空间下的所有 PHP 文件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反射与解析：&lt;/strong&gt; 对扫描到的类、方法、属性使用反射获取 DocComment 并进行解析。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;元数据收集：&lt;/strong&gt; 将解析得到的结构化注解信息收集并存储起来（例如，将 &lt;code&gt;@Route&lt;/code&gt; 信息注册到路由表中；将 &lt;code&gt;@Inject&lt;/code&gt; 信息用于 DI 容器的自动装配配置）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运行时利用：&lt;/strong&gt; 在处理请求或执行特定逻辑时，框架会根据之前收集的注解元数据来指导行为（如根据路由注解匹配控制器方法；根据缓存注解决定是否从缓存读取数据）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PHP 8 及之后 (原生 Attributes)：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;语言级支持：&lt;/strong&gt; PHP 8 将注解作为&lt;strong&gt;一级语言特性&lt;/strong&gt;引入，称为 &lt;strong&gt;Attributes&lt;/strong&gt;。它们不再是注释，而是&lt;strong&gt;正式的语法结构&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;定义：&lt;/strong&gt; 使用 &lt;code&gt;#[...]&lt;/code&gt; 语法定义。Attribute 本身就是一个普通的 PHP 类（通常继承自 &lt;code&gt;\Attribute&lt;/code&gt;），可以有自己的构造函数、属性和方法，用于定义和验证元数据的结构。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反射获取：&lt;/strong&gt; 反射 API 新增了 &lt;code&gt;getAttributes()&lt;/code&gt; 方法（例如 &lt;code&gt;ReflectionClass::getAttributes()&lt;/code&gt;）。这个方法返回一个 &lt;code&gt;ReflectionAttribute&lt;/code&gt; 对象数组。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;实例化：&lt;/strong&gt; 可以通过 &lt;code&gt;ReflectionAttribute::newInstance()&lt;/code&gt; 方法实例化 Attribute 类对象（如果定义了类），传入的参数就是在 Attribute 定义时提供的参数。这样就得到了一个&lt;strong&gt;强类型的、结构化的注解对象&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优势：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;性能：&lt;/strong&gt; 原生语法，解析速度比解析 DocComment 字符串快得多。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;验证：&lt;/strong&gt; Attribute 类可以定义构造函数参数类型，PHP 引擎会在定义时就进行参数类型检查，避免运行时解析错误。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;结构清晰：&lt;/strong&gt; 强类型对象比解析字符串得到的数组更清晰、更安全。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IDE 支持：&lt;/strong&gt; IDE 能更好地识别、自动补全和检查 Attributes。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;框架处理流程 (类似 DocBlock，但更高效安全)：&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;启动扫描目录/命名空间。&lt;/li&gt;
&lt;li&gt;使用反射 API 获取类/方法/属性。&lt;/li&gt;
&lt;li&gt;调用 &lt;code&gt;getAttributes()&lt;/code&gt; 获取附加的 Attribute 列表。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;newInstance()&lt;/code&gt; 实例化需要的 Attribute 对象。&lt;/li&gt;
&lt;li&gt;收集这些 Attribute 对象包含的元数据。&lt;/li&gt;
&lt;li&gt;根据元数据配置框架（注册路由、配置 DI、定义 AOP 切入点等）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;总结注解原理：&lt;/strong&gt; 无论是旧的 DocBlock 还是新的原生 Attribute，注解的本质都是&lt;strong&gt;附加在代码元素上的元数据&lt;/strong&gt;。框架通过 &lt;strong&gt;反射机制&lt;/strong&gt; 在&lt;strong&gt;启动阶段（或按需）&lt;/strong&gt; 扫描代码，&lt;strong&gt;解析&lt;/strong&gt;这些元数据，将其转换为&lt;strong&gt;结构化信息&lt;/strong&gt;，并&lt;strong&gt;利用这些信息来动态配置框架行为、生成代理代码（AOP）或指导运行时逻辑&lt;/strong&gt;。原生 Attributes 在性能、类型安全性和开发体验上带来了显著提升。像 Hyperf 这类现代框架已全面转向使用原生 Attributes。&lt;/p&gt;
&lt;h2 id=&#34;webman-vs-Hyperf-核心原理对比摘要&#34;&gt;&lt;a href=&#34;#webman-vs-Hyperf-核心原理对比摘要&#34; class=&#34;headerlink&#34; title=&#34;webman vs Hyperf 核心原理对比摘要&#34;&gt;&lt;/a&gt;webman vs Hyperf 核心原理对比摘要&lt;/h2&gt;&lt;div class=&#34;table-container&#34;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&#34;text-align:left&#34;&gt;特性&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;webman&lt;/th&gt;
&lt;th style=&#34;text-align:left&#34;&gt;Hyperf&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;核心引擎&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Workerman (事件驱动, 多进程)&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Swoole (事件驱动 + &lt;strong&gt;协程&lt;/strong&gt;, 多进程)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;编程模型&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;事件回调 + 传统同步逻辑 (需自行管理阻塞I/O)&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;协程&lt;/strong&gt; (同步写法, 异步非阻塞I/O)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;性能关键&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;常驻内存 (减少初始化开销)&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;常驻内存 + &lt;strong&gt;协程&lt;/strong&gt; (高并发, 低资源消耗)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;连接管理&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;需手动管理或借助插件/连接池&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;内置协程连接池&lt;/strong&gt; (DB, Redis, HTTP Client等)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;核心机制&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;轻量路由、中间件、PSR 封装&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;强大的DI容器&lt;/strong&gt;、&lt;strong&gt;注解驱动&lt;/strong&gt;、&lt;strong&gt;AOP&lt;/strong&gt;、丰富组件化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;并发能力&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;高 (多进程)&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;极高&lt;/strong&gt; (多进程 + 单进程内高协程并发)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;复杂度&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;轻量简单&lt;/strong&gt;，学习曲线平缓&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;功能强大复杂&lt;/strong&gt;，学习曲线较陡 (需理解DI, AOP, 协程)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;定位&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;高性能 HTTP API / 简单实时应用&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;企业级、微服务、分布式系统、复杂高并发应用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;热更新&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;支持 (基于 inotify/kqueue)&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;支持 (基于 inotify/kqueue)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&#34;text-align:left&#34;&gt;&lt;strong&gt;代表技术&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;文件监听、Workerman API&lt;/td&gt;
&lt;td style=&#34;text-align:left&#34;&gt;Attributes、AOP、依赖注入、连接池、服务治理&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/04/11/python-backstage-project/</guid>
            <title>python后端管理框架有哪些</title>
            <link>https://erik.xyz/2025/04/11/python-backstage-project/</link>
            <category>python</category>
            <pubDate>Fri, 11 Apr 2025 21:24:00 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;Python 后端管理框架主要用于快速构建后台管理系统（如数据管理、用户权限控制等）。以下是常见的 Python 后端管理框架分类及代表工具：&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;1-基于-Django-的框架&#34;&gt;&lt;a href=&#34;#1-基于-Django-的框架&#34; class=&#34;headerlink&#34; title=&#34;1. 基于 Django 的框架&#34;&gt;&lt;/a&gt;&lt;strong&gt;1. 基于 Django 的框架&lt;/strong&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Django Admin&lt;/strong&gt;  &lt;ul&gt;
&lt;li&gt;Django 内置的管理后台，开箱即用。  &lt;/li&gt;
&lt;li&gt;支持 ORM 自动生成 CRUD 界面，适合快速开发。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺点&lt;/strong&gt;：默认界面较简陋（可通过插件美化）。  &lt;figure class=&#34;highlight python&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;# 示例：注册模型到 Admin&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;from&lt;/span&gt; django.contrib &lt;span class=&#34;keyword&#34;&gt;import&lt;/span&gt; admin&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;from&lt;/span&gt; .models &lt;span class=&#34;keyword&#34;&gt;import&lt;/span&gt; Product&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;meta&#34;&gt;@admin.register(&lt;span class=&#34;params&#34;&gt;Product&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;ProductAdmin&lt;/span&gt;(admin.ModelAdmin):&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    list_display = (&lt;span class=&#34;string&#34;&gt;&amp;#x27;name&amp;#x27;&lt;/span&gt;, &lt;span class=&#34;string&#34;&gt;&amp;#x27;price&amp;#x27;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Django Jet&lt;/strong&gt;  &lt;ul&gt;
&lt;li&gt;现代化的 Django Admin 主题（支持响应式布局）。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Django Grappelli&lt;/strong&gt;  &lt;ul&gt;
&lt;li&gt;经典的美化扩展，提供更友好的 UI。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Django Suit&lt;/strong&gt;  &lt;ul&gt;
&lt;li&gt;另一个流行的 Admin 主题（需付费）。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;2-基于-Flask-的框架&#34;&gt;&lt;a href=&#34;#2-基于-Flask-的框架&#34; class=&#34;headerlink&#34; title=&#34;2. 基于 Flask 的框架&#34;&gt;&lt;/a&gt;&lt;strong&gt;2. 基于 Flask 的框架&lt;/strong&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flask-Admin&lt;/strong&gt;  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;轻量级、高度可定制，支持多种数据库后端（SQLAlchemy、MongoDB 等）。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：灵活性高，适合中大型项目。  &lt;figure class=&#34;highlight python&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;# 示例：集成 Flask-Admin&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;from&lt;/span&gt; flask &lt;span class=&#34;keyword&#34;&gt;import&lt;/span&gt; Flask&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;from&lt;/span&gt; flask_sqlalchemy &lt;span class=&#34;keyword&#34;&gt;import&lt;/span&gt; SQLAlchemy&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;from&lt;/span&gt; flask_admin &lt;span class=&#34;keyword&#34;&gt;import&lt;/span&gt; Admin&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;from&lt;/span&gt; flask_admin.contrib.sqla &lt;span class=&#34;keyword&#34;&gt;import&lt;/span&gt; ModelView&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;app = Flask(__name__)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;db = SQLAlchemy(app)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;admin = Admin(app, name=&lt;span class=&#34;string&#34;&gt;&amp;#x27;管理后台&amp;#x27;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;title class_&#34;&gt;Product&lt;/span&gt;(db.Model):&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &lt;span class=&#34;built_in&#34;&gt;id&lt;/span&gt; = db.Column(db.Integer, primary_key=&lt;span class=&#34;literal&#34;&gt;True&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    name = db.Column(db.String(&lt;span class=&#34;number&#34;&gt;80&lt;/span&gt;))&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;admin.add_view(ModelView(Product, db.session))&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flask-AppBuilder&lt;/strong&gt;  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;快速生成 CRUD 界面，内置 RBAC 权限控制。  &lt;/li&gt;
&lt;li&gt;提供图表、表单生成等高级功能。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;3-独立-通用框架&#34;&gt;&lt;a href=&#34;#3-独立-通用框架&#34; class=&#34;headerlink&#34; title=&#34;3. 独立/通用框架&#34;&gt;&lt;/a&gt;&lt;strong&gt;3. 独立/通用框架&lt;/strong&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SQLAlchemy-Admin&lt;/strong&gt;  &lt;ul&gt;
&lt;li&gt;基于 SQLAlchemy 的独立管理界面，不依赖 Web 框架。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WTForms-Alchemy&lt;/strong&gt;  &lt;ul&gt;
&lt;li&gt;自动从 SQLAlchemy 模型生成表单（常与 Flask-Admin 配合使用）。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;4-基于-FastAPI-的框架&#34;&gt;&lt;a href=&#34;#4-基于-FastAPI-的框架&#34; class=&#34;headerlink&#34; title=&#34;4. 基于 FastAPI 的框架&#34;&gt;&lt;/a&gt;&lt;strong&gt;4. 基于 FastAPI 的框架&lt;/strong&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;FastAPI Admin&lt;/strong&gt;  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;异步支持，适合高性能场景。  &lt;/li&gt;
&lt;li&gt;提供模型管理、文件上传等功能。  &lt;figure class=&#34;highlight python&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;comment&#34;&gt;# 示例：FastAPI-Admin 基础使用&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;from&lt;/span&gt; fastapi &lt;span class=&#34;keyword&#34;&gt;import&lt;/span&gt; FastAPI&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;from&lt;/span&gt; fastapi_admin.app &lt;span class=&#34;keyword&#34;&gt;import&lt;/span&gt; app &lt;span class=&#34;keyword&#34;&gt;as&lt;/span&gt; admin_app&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;keyword&#34;&gt;from&lt;/span&gt; fastapi_admin.providers.login &lt;span class=&#34;keyword&#34;&gt;import&lt;/span&gt; UsernamePasswordProvider&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;app = FastAPI()&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;admin_app.add_provider(UsernamePasswordProvider())&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;app.mount(&lt;span class=&#34;string&#34;&gt;&amp;quot;/admin&amp;quot;&lt;/span&gt;, admin_app)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SQLModel-Admin&lt;/strong&gt;  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基于 SQLModel（FastAPI 官方 ORM）的管理界面。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;5-低代码-可视化工具&#34;&gt;&lt;a href=&#34;#5-低代码-可视化工具&#34; class=&#34;headerlink&#34; title=&#34;5. 低代码/可视化工具&#34;&gt;&lt;/a&gt;&lt;strong&gt;5. 低代码/可视化工具&lt;/strong&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AppSmith / ToolJet&lt;/strong&gt;  &lt;ul&gt;
&lt;li&gt;支持连接数据库/API，通过拖拽生成管理界面（非纯 Python，但可集成）。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Django Builder&lt;/strong&gt;  &lt;ul&gt;
&lt;li&gt;可视化构建 Django 后台的第三方工具。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;选择建议&#34;&gt;&lt;a href=&#34;#选择建议&#34; class=&#34;headerlink&#34; title=&#34;选择建议&#34;&gt;&lt;/a&gt;&lt;strong&gt;选择建议&lt;/strong&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;快速原型&lt;/strong&gt;：用 Django Admin（Django 项目）或 Flask-AppBuilder（Flask 项目）。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高定制化&lt;/strong&gt;：选 Flask-Admin 或手动扩展 Django Admin。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;现代异步架构&lt;/strong&gt;：选 FastAPI Admin。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;低代码需求&lt;/strong&gt;：AppSmith/ToolJet + Python API。  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;根据项目需求和技术栈选择合适的工具，能大幅提升后台管理系统的开发效率！&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/03/22/github-open-project-im/</guid>
            <title>github开源的即时通讯项目</title>
            <link>https://erik.xyz/2025/03/22/github-open-project-im/</link>
            <category>php</category>
            <category>im</category>
            <pubDate>Sat, 22 Mar 2025 10:01:00 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;以下是 GitHub 上当前活跃且功能强大的开源即时通讯（IM）项目，涵盖企业级解决方案、轻量级应用和创新型平台，适合不同开发需求：&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;🔧-一、企业级解决方案（高可用、可扩展）&#34;&gt;&lt;a href=&#34;#🔧-一、企业级解决方案（高可用、可扩展）&#34; class=&#34;headerlink&#34; title=&#34;🔧 一、企业级解决方案（高可用、可扩展）&#34;&gt;&lt;/a&gt;🔧 &lt;strong&gt;一、企业级解决方案（高可用、可扩展）&lt;/strong&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;OpenIM&lt;/strong&gt;  &lt;ul&gt;
&lt;li&gt;&lt;strong&gt;技术栈&lt;/strong&gt;：Go 语言服务端，支持 Android/iOS/Web/Flutter 等全平台 SDK 。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;核心特性&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;私有化部署，数据完全自控，支持集群扩展至亿级用户 。  &lt;/li&gt;
&lt;li&gt;消息必达（100% 可靠性），支持单聊、群聊（10 万人超大群）、已读回执、消息撤回等 。  &lt;/li&gt;
&lt;li&gt;集成组织架构、工作圈、音视频通话，适合政企办公场景 。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：替代商业 IM 云服务（如融云、环信），降低长期成本 。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;项目地址&lt;/strong&gt;：&lt;a href=&#34;https://github.com/OpenIMSDK&#34;&gt;GitHub - OpenIMSDK&lt;/a&gt; | 🌟 &lt;strong&gt;Star 9k+&lt;/strong&gt; 。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;
&lt;hr&gt;
&lt;h3 id=&#34;🖥️-二、轻量级跨平台应用&#34;&gt;&lt;a href=&#34;#🖥️-二、轻量级跨平台应用&#34; class=&#34;headerlink&#34; title=&#34;🖥️ 二、轻量级跨平台应用&#34;&gt;&lt;/a&gt;🖥️ &lt;strong&gt;二、轻量级跨平台应用&lt;/strong&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;HuLa&lt;/strong&gt;  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;技术栈&lt;/strong&gt;：基于 Tauri（Rust 跨平台框架） + Vue 3 + TypeScript，支持 Windows/macOS/Linux 。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;核心特性&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;轻量高效（资源占用低），支持文字、语音、视频通话及文件传输 。  &lt;/li&gt;
&lt;li&gt;内置主题定制、表情包、群组管理，界面简洁易用 。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：个人开发者快速构建桌面端聊天应用。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;项目地址&lt;/strong&gt;：&lt;a href=&#34;https://github.com/HuLaSpark/HuLa&#34;&gt;GitHub - HuLaSpark&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tailchat&lt;/strong&gt;  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;技术栈&lt;/strong&gt;：React + TypeScript + Node.js，插件化架构（类似 Discord）。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;核心特性&lt;/strong&gt;：  &lt;ul&gt;
&lt;li&gt;高度可扩展的插件系统（30+ 官方插件），支持身份组权限管理、自定义面板 。  &lt;/li&gt;
&lt;li&gt;多端同步（Web/移动/桌面），集成开放平台（机器人、第三方登录）。  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适用场景&lt;/strong&gt;：社区协作、游戏社交等需定制化功能的场景。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;项目地址&lt;/strong&gt;：&lt;a href=&#34;https://github.com/msgbyte/tailchat&#34;&gt;GitHub - Tailchat&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&#34;🔌-三、垂直场景与集成方案&#34;&gt;&lt;a href=&#34;#🔌-三、垂直场景与集成方案&#34; class=&#34;headerlink&#34; title=&#34;🔌 三、垂直场景与集成方案&#34;&gt;&lt;/a&gt;🔌 &lt;strong&gt;三、垂直场景与集成方案&lt;/strong&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;基于环信 SDK 的社交应用&lt;/strong&gt;  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;代表项目&lt;/strong&gt;：高仿微信、图忆（LBS 社交）、美肤 GO（电商客服）。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特点&lt;/strong&gt;：提供完整 UI 和业务逻辑源码，适合快速集成社交功能。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;项目示例&lt;/strong&gt;：&lt;a href=&#34;https://github.com/CameloeAnthony/Perfect_IM_SNS&#34;&gt;GitHub - 环信 Demo 合集&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;FreeTube（隐私向 IM 扩展）&lt;/strong&gt;  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;技术栈&lt;/strong&gt;：JavaScript 桌面应用，专注隐私保护 。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;特色&lt;/strong&gt;：无广告、无追踪，本地存储用户数据，适合安全敏感场景。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;项目地址&lt;/strong&gt;：&lt;a href=&#34;https://github.com/FreeTubeApp/FreeTube&#34;&gt;GitHub - FreeTube&lt;/a&gt; | 🌟 Star 15k+。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&#34;📊-核心项目对比&#34;&gt;&lt;a href=&#34;#📊-核心项目对比&#34; class=&#34;headerlink&#34; title=&#34;📊 核心项目对比&#34;&gt;&lt;/a&gt;📊 &lt;strong&gt;核心项目对比&lt;/strong&gt;&lt;/h3&gt;&lt;div class=&#34;table-container&#34;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;项目&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;技术优势&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;适用场景&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;部署复杂度&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenIM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;高并发、企业级功能全&lt;/td&gt;
&lt;td&gt;政务/大型企业 IM&lt;/td&gt;
&lt;td&gt;中高（需 Docker）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HuLa&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;轻量跨平台、开发快捷&lt;/td&gt;
&lt;td&gt;个人/小团队桌面应用&lt;/td&gt;
&lt;td&gt;低&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tailchat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;插件化、高度可定制&lt;/td&gt;
&lt;td&gt;社区/游戏社交平台&lt;/td&gt;
&lt;td&gt;中&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;hr&gt;
&lt;h3 id=&#34;💡-四、选型建议&#34;&gt;&lt;a href=&#34;#💡-四、选型建议&#34; class=&#34;headerlink&#34; title=&#34;💡 四、选型建议&#34;&gt;&lt;/a&gt;💡 &lt;strong&gt;四、选型建议&lt;/strong&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;追求企业级稳定性&lt;/strong&gt;：选择 &lt;strong&gt;OpenIM&lt;/strong&gt;，支持私有化部署和国产化需求 。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;快速开发桌面端应用&lt;/strong&gt;：&lt;strong&gt;HuLa&lt;/strong&gt; 的现代前端技术栈（Vue3 + Tauri）可大幅提升效率 。  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;需要高度定制化&lt;/strong&gt;：&lt;strong&gt;Tailchat&lt;/strong&gt; 的插件机制允许自由扩展功能 。  &lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 更多项目源码和部署指南可访问对应 GitHub 仓库。若需替代商业 IM 服务（如融云、环信），OpenIM 的私有化方案能显著降低成本并提升数据安全性 。&lt;/p&gt;
&lt;/blockquote&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/02/13/open-source-esktop-sharing-tool/</guid>
            <title>开源桌面共享工具</title>
            <link>https://erik.xyz/2025/02/13/open-source-esktop-sharing-tool/</link>
            <category>工具</category>
            <category>开源工具</category>
            <category>桌面共享工具</category>
            <pubDate>Thu, 13 Feb 2025 21:35:00 +0800</pubDate>
            <description><![CDATA[ &lt;h2 id=&#34;1-xrdp&#34;&gt;&lt;a href=&#34;#1-xrdp&#34; class=&#34;headerlink&#34; title=&#34;1. xrdp&#34;&gt;&lt;/a&gt;1. &lt;a href=&#34;https://github.com/neutrinolabs/xrdp&#34;&gt;xrdp&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：xrdp 是一个开源的远程桌面协议 (RDP) 服务器，允许 Linux 系统通过 Windows 的 RDP 客户端远程访问。它与 Microsoft RDP 协议兼容，能够让你从 Windows 客户端连接到 Linux 系统。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安装&lt;/strong&gt;：通常可以通过 Linux 的包管理器安装，例如在 Ubuntu 上使用命令：&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;sudo apt install xrdp&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;2-VNC-Virtual-Network-Computing&#34;&gt;&lt;a href=&#34;#2-VNC-Virtual-Network-Computing&#34; class=&#34;headerlink&#34; title=&#34;2. VNC (Virtual Network Computing)&#34;&gt;&lt;/a&gt;2. VNC (Virtual Network Computing)&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：VNC 是一种图形桌面共享系统，支持跨平台远程访问。常见的开源 VNC 实现有：&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TightVNC&lt;/strong&gt;：轻量级且性能优化。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TigerVNC&lt;/strong&gt;：增强了性能和安全性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RealVNC&lt;/strong&gt;：提供商业和开源版本。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安装&lt;/strong&gt;：可以通过包管理器（如 &lt;code&gt;apt&lt;/code&gt;）安装，或通过下载其源代码来安装。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;3-NoMachine&#34;&gt;&lt;a href=&#34;#3-NoMachine&#34; class=&#34;headerlink&#34; title=&#34;3. NoMachine&#34;&gt;&lt;/a&gt;3. &lt;a href=&#34;https://www.nomachine.com/&#34;&gt;NoMachine&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：NoMachine 是一款开源远程桌面解决方案，支持跨平台远程访问。它具有高性能和较低延迟，支持音视频流和文件传输等功能。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安装&lt;/strong&gt;：可以直接从官网下载安装包，也可以通过官方的开源版本进行安装。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;4-Remmina&#34;&gt;&lt;a href=&#34;#4-Remmina&#34; class=&#34;headerlink&#34; title=&#34;4. Remmina&#34;&gt;&lt;/a&gt;4. &lt;a href=&#34;https://github.com/FreeRDP/Remmina&#34;&gt;Remmina&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：Remmina 是一个支持多种远程桌面协议的客户端，支持 RDP、VNC、SSH 等多种协议。它的设计非常适合 Linux 用户，但也支持其他操作系统。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安装&lt;/strong&gt;：大多数 Linux 发行版都可以通过包管理器安装，例如：&lt;figure class=&#34;highlight bash&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;sudo&lt;/span&gt; apt install remmina&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;5-FreeRDP&#34;&gt;&lt;a href=&#34;#5-FreeRDP&#34; class=&#34;headerlink&#34; title=&#34;5. FreeRDP&#34;&gt;&lt;/a&gt;5. &lt;a href=&#34;https://github.com/FreeRDP/FreeRDP&#34;&gt;FreeRDP&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：FreeRDP 是一个开源的远程桌面协议实现，可以用作 RDP 客户端和服务器。它支持 Windows 和 Linux 系统之间的远程访问，并且非常轻量级。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安装&lt;/strong&gt;：可以通过包管理器进行安装，例如：&lt;figure class=&#34;highlight bash&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;built_in&#34;&gt;sudo&lt;/span&gt; apt install freerdp2-x11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;6-WayVNC&#34;&gt;&lt;a href=&#34;#6-WayVNC&#34; class=&#34;headerlink&#34; title=&#34;6. WayVNC&#34;&gt;&lt;/a&gt;6. WayVNC&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：WayVNC 是专门为 Wayland 兼容的 Linux 系统设计的 VNC 服务器，适用于那些使用 Wayland 显示服务器的系统。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安装&lt;/strong&gt;：可以通过源码编译安装，或查找相关的 Linux 包。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;7-Guacamole&#34;&gt;&lt;a href=&#34;#7-Guacamole&#34; class=&#34;headerlink&#34; title=&#34;7. Guacamole&#34;&gt;&lt;/a&gt;7. &lt;a href=&#34;http://guacamole.apache.org/&#34;&gt;Guacamole&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;功能&lt;/strong&gt;：Guacamole 是一个基于 Web 的远程桌面客户端，支持 RDP、VNC 和 SSH。你可以通过浏览器访问并远程控制计算机，安装较为复杂，通常需要配置 Web 服务器。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安装&lt;/strong&gt;：需要在服务器端安装 Guacamole，并配置相应的 Web 服务器。它支持各种操作系统。&lt;/li&gt;
&lt;/ul&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/02/05/baimasi/</guid>
            <title>白马寺</title>
            <link>https://erik.xyz/2025/02/05/baimasi/</link>
            <category>随笔</category>
            <pubDate>Wed, 05 Feb 2025 13:16:00 +0800</pubDate>
            <description><![CDATA[ &lt;pre&gt;&lt;code&gt;      白马寺

千年风霜斑驳目，新出寰宇客来慕。
佛丘已非今朝时，独得枯木逢春时。
&lt;/code&gt;&lt;/pre&gt; ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/01/12/composer-intranet-deployment/</guid>
            <title>composer内网部署</title>
            <link>https://erik.xyz/2025/01/12/composer-intranet-deployment/</link>
            <category>composer</category>
            <category>composer部署</category>
            <category>内网部署composer</category>
            <pubDate>Sun, 12 Jan 2025 22:01:00 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;在内网环境中部署 Composer（PHP的依赖管理工具），通常是为了避免因外网访问限制而导致的依赖下载问题。内网部署 Composer 主要涉及配置一个 私有 Composer 仓库，或者使用 本地代理镜像 来加速和管理依赖。&lt;/p&gt;
&lt;h3 id=&#34;1-使用-composer-json-设置依赖&#34;&gt;&lt;a href=&#34;#1-使用-composer-json-设置依赖&#34; class=&#34;headerlink&#34; title=&#34;1. 使用 composer.json 设置依赖&#34;&gt;&lt;/a&gt;&lt;b&gt;1. 使用 composer.json 设置依赖&lt;/b&gt;&lt;/h3&gt;&lt;p&gt;首先，确保你的 composer.json 文件包含所有项目依赖，并正确配置了包的版本和来源。&lt;br&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;name&amp;quot;: &amp;quot;vendor/project&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;require&amp;quot;: &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;quot;monolog/monolog&amp;quot;: &amp;quot;^2.0&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;h3 id=&#34;2-使用代理镜像&#34;&gt;&lt;a href=&#34;#2-使用代理镜像&#34; class=&#34;headerlink&#34; title=&#34;2. 使用代理镜像&#34;&gt;&lt;/a&gt;&lt;b&gt;2. 使用代理镜像&lt;/b&gt;&lt;/h3&gt;&lt;p&gt;由于内网无法直接访问 Composer 官方的包仓库，常见的做法是使用国内镜像或搭建代理服务器，常见的方案包括使用 阿里云 Composer 镜像 或 私有镜像仓库。&lt;/p&gt;
&lt;p&gt; &lt;b&gt;2.1 使用国内镜像源&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;你可以通过配置 Composer 使用国内镜像源来加速依赖包的下载。在 Composer 中，你可以使用下面的命令设置国内镜像：&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;composer config repo.packagist composer https://mirrors.aliyun.com/composer/&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;br&gt;这个命令将 packagist.org 源更换为阿里云的镜像源。&lt;/p&gt;
&lt;p&gt;你还可以通过在 composer.json 文件中进行设置，强制使用镜像源：&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;repositories&amp;quot;: [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;quot;type&amp;quot;: &amp;quot;composer&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;quot;url&amp;quot;: &amp;quot;https://mirrors.aliyun.com/composer/&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;这样每次执行 composer install 时，都会从阿里云的镜像源下载依赖。&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2.2 配置自建 Composer 镜像代理&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;如果你想在企业内部完全控制包管理，可以搭建自己的 Composer 镜像代理。常见的选择包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Satis：一个官方提供的工具，用来搭建私有的 Composer 仓库。&lt;/li&gt;
&lt;li&gt;Private Packagist：一个商业解决方案，专门用于在私有环境中管理 Composer 包。&lt;/li&gt;
&lt;li&gt;Sinopia (npm 仓库代理工具)：可以作为私有的 Composer 仓库代理使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;2.2.1 使用 Satis 搭建私有 Composer 仓库&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Satis 是 Composer 官方提供的一个轻量级工具，专门用于创建私有的 Composer 仓库。通过使用 Satis，你可以将内网环境下常用的依赖缓存下来，并提供给项目中使用。&lt;/p&gt;
&lt;p&gt;步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;安装 Satis：&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;使用 Composer 安装 Satis：&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;composer require composer/satis&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;配置 Satis：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在你的服务器上创建一个 satis.json 配置文件，指定你希望托管的包源。&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;name&amp;quot;: &amp;quot;my-private-repo&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;homepage&amp;quot;: &amp;quot;https://example.com/packages&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;repositories&amp;quot;: [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;quot;type&amp;quot;: &amp;quot;vcs&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;quot;url&amp;quot;: &amp;quot;https://github.com/some/package&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;quot;type&amp;quot;: &amp;quot;composer&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;quot;url&amp;quot;: &amp;quot;https://packagist.org&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ],&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;output-dir&amp;quot;: &amp;quot;/path/to/output&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;生成静态资源：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;运行以下命令生成静态的 Composer 包：&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;php bin/satis build satis.json /path/to/output&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;提供访问：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;你可以通过 Web 服务器（如 Nginx 或 Apache）提供访问静态资源。这样，其他项目就能通过你的内部 Satis 仓库获取依赖了。&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2.2.2 配置 Composer 使用私有仓库&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;在 composer.json 中配置私有仓库（例如，你的公司内部搭建的 Satis 仓库）。&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;quot;repositories&amp;quot;: [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;quot;type&amp;quot;: &amp;quot;composer&amp;quot;,&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;quot;url&amp;quot;: &amp;quot;http://your-internal-repository.com&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    ]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;这样，Composer 会从你配置的私有仓库中拉取依赖。&lt;/p&gt;
&lt;h3 id=&#34;3-离线安装-Composer-依赖&#34;&gt;&lt;a href=&#34;#3-离线安装-Composer-依赖&#34; class=&#34;headerlink&#34; title=&#34;3. 离线安装 Composer 依赖&#34;&gt;&lt;/a&gt;&lt;b&gt;3. 离线安装 Composer 依赖&lt;/b&gt;&lt;/h3&gt;&lt;p&gt;如果内网无法访问外部网络，也可以考虑在有外网访问权限的机器上下载依赖包，然后将它们导入到内网机器中进行安装。&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3.1  在有外网的机器上下载依赖&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;在能够访问外网的机器上执行 composer install。&lt;br&gt;下载完成后，将 vendor 目录及 composer.lock 文件拷贝到内网服务器相同位置。&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3.2 使用 —prefer-dist 参数&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;你还可以使用 composer install —prefer-dist 来下载 .tar.gz 或 .zip 格式的包，这样可以方便地进行离线安装。&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3.3 配置 COMPOSER_HOME&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;如果你希望在内网机器上使用本地缓存的包，可以设置 COMPOSER_HOME 环境变量来指定 Composer 缓存目录。&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;export COMPOSER_HOME=/path/to/composer/cache&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;然后，将缓存目录复制到内网机器，确保 Composer 能够使用这些缓存。&lt;/p&gt;
&lt;h3 id=&#34;4-其他考虑&#34;&gt;&lt;a href=&#34;#4-其他考虑&#34; class=&#34;headerlink&#34; title=&#34;4. 其他考虑&#34;&gt;&lt;/a&gt;&lt;b&gt;4. 其他考虑&lt;/b&gt;&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;代理配置：如果内网可以通过 HTTP 代理访问外网，确保 Composer 配置了代理。在 composer.json 中配置代理，或者使用环境变量来指定代理：&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;export HTTP_PROXY=http://proxy.example.com:8080&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;export HTTPS_PROXY=http://proxy.example.com:8080&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;私有包的权限：如果你使用了私有 Composer 仓库或 GitHub 仓库，记得配置好认证方式（如使用 auth.json 文件存储 GitHub Token）。&lt;/li&gt;
&lt;/ul&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/01/10/agricultural-detection-system/</guid>
            <title>农业检测系统设计</title>
            <link>https://erik.xyz/2025/01/10/agricultural-detection-system/</link>
            <category>农业系统</category>
            <category>系统设计</category>
            <pubDate>Fri, 10 Jan 2025 22:34:00 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;&lt;b&gt;农业检测系统设计&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;根据你的需求，我们将设计一个智能农业系统，通过多种技术手段实现温湿度、光照度监测、设备控制和远程管理。系统的核心功能包括：智能节点监控环境数据、手动和自动控制模式、通过 LoRa 和 4G 通信传输数据、以及通过手机或电脑端进行远程控制。&lt;br&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&#34;1-系统模块概述&#34;&gt;&lt;a href=&#34;#1-系统模块概述&#34; class=&#34;headerlink&#34; title=&#34;1. 系统模块概述&#34;&gt;&lt;/a&gt;&lt;b&gt;1. 系统模块概述&lt;/b&gt;&lt;/h4&gt;&lt;p&gt;该农业检测系统包括以下几个主要模块：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;智能节点（传感器与控制单元）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;获取温湿度、光照度传感器数据。&lt;/li&gt;
&lt;li&gt;控制风扇的启停，显示当前风扇状态。&lt;/li&gt;
&lt;li&gt;支持手动和自动模式切换。&lt;/li&gt;
&lt;li&gt;显示温湿度、光照度、风扇状态和模式状态。&lt;/li&gt;
&lt;li&gt;通过 LoRa 发送数据到 4G 节点。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;4G 网关节点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;接收来自智能节点的传感器数据。&lt;/li&gt;
&lt;li&gt;将数据通过 4G 网络上传至 MOTT 服务器。&lt;/li&gt;
&lt;li&gt;在显示屏上显示各项传感器数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;MOTT 服务器：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;存储从 4G 节点发送来的数据。&lt;/li&gt;
&lt;li&gt;提供远程访问接口。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;应用软件（电脑端或手机端）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;显示温湿度、光照度、风扇状态、手动/自动模式等数据。&lt;/li&gt;
&lt;li&gt;提供登录、手动控制、自动控制等功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;2-系统功能设计&#34;&gt;&lt;a href=&#34;#2-系统功能设计&#34; class=&#34;headerlink&#34; title=&#34;2.系统功能设计&#34;&gt;&lt;/a&gt;&lt;b&gt;2.系统功能设计&lt;/b&gt;&lt;/h4&gt;&lt;p&gt;2.1 智能节点功能&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;传感器数据采集：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;温湿度传感器：实时获取温度和湿度数据。&lt;/li&gt;
&lt;li&gt;光照度传感器：实时获取光照强度数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;模式切换：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;手动模式：允许用户通过按键手动启动或停止风扇。&lt;/li&gt;
&lt;li&gt;自动模式：当温度和光照度超过设定阈值时，自动启停风扇。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;风扇控制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在手动模式下，用户通过按键控制风扇开关，并通过绿色 LED 指示灯显示风扇状态。&lt;/li&gt;
&lt;li&gt;在自动模式下，系统会根据设定的温度和光照度阈值自动控制风扇，并使用绿灯和红灯进行状态指示。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;数据传输：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 LoRa 无线传输模块将传感器数据和风扇状态上传至 4G 网关节点。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;显示屏：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;显示当前温湿度、光照度、模式状态和风扇状态。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;2.2 4G 网关节点功能&lt;/b&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;数据接收：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过 LoRa 接收智能节点上传的传感器数据（温度、湿度、光照度、风扇状态、手动/自动模式状态）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;4G 网络传输：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将智能节点的数据通过 4G 网络上传至 MOTT 服务器。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;显示屏显示：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 4G 网关节点的显示屏上展示温湿度、光照度、模式状态和风扇状态。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;2.3 MOTT 服务器功能&lt;/b&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;数据存储：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;存储来自 4G 网关节点的温湿度、光照度、风扇状态等数据。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;远程管理接口：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;提供 RESTful API 或 WebSocket 接口，供客户端应用访问。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;2.4 应用软件功能&lt;/b&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用户登录：&lt;ul&gt;
&lt;li&gt;提供登录界面，用户通过输入用户名和密码登录（用户名：admin，密码：admin123）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;手动控制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;登录成功后，用户可通过界面手动启停风扇，操作时触发红灯和蜂鸣器的提示。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;自动控制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户可以设定温度和光照度的阈值，智能节点进入自动模式后，系统根据传感器数据自动控制风扇开关，并通过 LED 指示灯显示状态。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;显示功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实时显示温度、湿度、光照度、风扇状态、手动/自动模式状态。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;3-原型图设计&#34;&gt;&lt;a href=&#34;#3-原型图设计&#34; class=&#34;headerlink&#34; title=&#34;3. 原型图设计&#34;&gt;&lt;/a&gt;&lt;b&gt;3. 原型图设计&lt;/b&gt;&lt;/h4&gt;&lt;p&gt;3.1 智能节点原型图&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;+-------------------------+&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|    温湿度传感器          |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                         |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|    光照度传感器          |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                         |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|    按键面板 (5个按键)    |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                         |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|    显示屏 (LCD)          |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                         |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|    风扇控制 (LED/蜂鸣器) |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;+-------------------------+&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;显示屏：显示温度、湿度、光照度、当前模式（手动/自动）和风扇状态。&lt;/li&gt;
&lt;li&gt;按键面板：5个按键，其中包括：&lt;ul&gt;
&lt;li&gt;切换模式按键（手动/自动）&lt;/li&gt;
&lt;li&gt;启动风扇的控制按键&lt;/li&gt;
&lt;li&gt;停止风扇的控制按键&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;LED 灯：指示风扇是否正在运行（绿色为运行，红色为停止）。&lt;/li&gt;
&lt;li&gt;蜂鸣器：在风扇运行时以 1Hz 的频率发出声音。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;3.2 4G 网关节点原型图&lt;/b&gt;&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;+-------------------------+&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|    LoRa 接收模块        |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|    (接收数据)           |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                         |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|    显示屏 (LCD)          |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|                         |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|    4G 网络模块          |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|    (上传数据)           |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;+-------------------------+&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;显示屏：显示温湿度、光照度、模式状态和风扇状态。&lt;/li&gt;
&lt;li&gt;LoRa 接收模块：接收智能节点发送的传感器数据。&lt;/li&gt;
&lt;li&gt;4G 网络模块：将接收到的数据上传到 MOTT 服务器。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;3.3 应用软件原型图（电脑端或手机端）&lt;/b&gt;&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;+-------------------------------------------------------+&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| 登录界面                                               |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|   - 用户名输入框                                        |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|   - 密码输入框                                          |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|   - 登录按钮                                            |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;+-------------------------------------------------------+&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;| 主界面                                                 |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|   - 实时显示温湿度、光照度、风扇状态、手动/自动模式   |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|   - 手动控制按钮 (启动/停止风扇)                       |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|   - 自动模式阈值设置 (温度、光照度)                    |&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;|   - 状态显示：当前模式（手动/自动）、风扇状态（开/关）|&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;+-------------------------------------------------------+&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h4 id=&#34;4-系统设计步骤&#34;&gt;&lt;a href=&#34;#4-系统设计步骤&#34; class=&#34;headerlink&#34; title=&#34;4. 系统设计步骤&#34;&gt;&lt;/a&gt;&lt;b&gt;4. 系统设计步骤&lt;/b&gt;&lt;/h4&gt;&lt;p&gt;&lt;b&gt;4.1 硬件设计与开发&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;选择适当的传感器：温湿度传感器（如 DHT22）、光照度传感器、风扇控制模块、LED 指示灯、蜂鸣器。&lt;/li&gt;
&lt;li&gt;智能节点电路设计：设计电源管理电路，确保所有传感器和控制组件能正常工作。&lt;/li&gt;
&lt;li&gt;LoRa 模块集成：将 LoRa 模块集成到智能节点中，实现数据的无线传输。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;4.2 软件开发&lt;/b&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;智能节点程序：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;编写代码获取传感器数据。&lt;/li&gt;
&lt;li&gt;控制风扇及 LED 指示灯。&lt;/li&gt;
&lt;li&gt;实现手动与自动模式的切换。&lt;/li&gt;
&lt;li&gt;实现 LoRa 数据发送功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;4G 网关节点程序：&lt;ul&gt;
&lt;li&gt;接收 LoRa 数据并通过 4G 网络上传至 MOTT 服务器。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;MOTT 服务器开发：&lt;ul&gt;
&lt;li&gt;提供数据存储和远程管理接口（API）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;应用软件开发：&lt;ul&gt;
&lt;li&gt;实现用户登录、手动控制、自动控制、数据展示等功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;4.3 系统集成与测试&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;硬件集成：将传感器、LoRa 模块、显示屏等硬件连接至智能节点和 4G 网关节点。&lt;/li&gt;
&lt;li&gt;功能测试：测试手动模式、自动模式、数据传输功能等。&lt;/li&gt;
&lt;li&gt;性能优化：确保系统在实时数据传输和控制中的稳定性。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;4.4 部署与维护&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;部署智能节点和 4G 网关：将系统部署到实际农业环境中，进行现场测试和调试。&lt;/li&gt;
&lt;li&gt;维护与更新：定期检查系统，进行必要的功能更新和优化。&lt;/li&gt;
&lt;/ul&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2025/01/03/php-serial-port-development/</guid>
            <title>php串口开发</title>
            <link>https://erik.xyz/2025/01/03/php-serial-port-development/</link>
            <category>php</category>
            <category>php串口开发</category>
            <pubDate>Fri, 03 Jan 2025 15:30:00 +0800</pubDate>
            <description><![CDATA[ &lt;p&gt;使用外置设备，通过串口发送和接收数据。那么，就要在php端有个串口的操作代码。&lt;br&gt;PHP 的 dio 扩展（Direct I/O）提供了对底层 I/O 操作的访问，包括串口通信。通过 dio 扩展，你可以直接操作串口设备文件（如 /dev/ttyUSB0 或 COM1）来实现串口通信。&lt;br&gt; &lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 id=&#34;1-安装-dio-扩展&#34;&gt;&lt;a href=&#34;#1-安装-dio-扩展&#34; class=&#34;headerlink&#34; title=&#34;1. 安装 dio 扩展&#34;&gt;&lt;/a&gt;1. 安装 dio 扩展&lt;/h3&gt;&lt;p&gt;dio 扩展是 PHP 的一个 PECL 扩展。你可以通过以下步骤安装：&lt;/p&gt;
&lt;p&gt;在 Linux 上安装：&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;sudo apt-get install php-dev  # 安装 PHP 开发工具&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;sudo pecl install dio         # 安装 dio 扩展&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;安装完成后，在 php.ini 文件中启用扩展：&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;extension=dio.so&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h3 id=&#34;2-使用-dio-实现串口通信&#34;&gt;&lt;a href=&#34;#2-使用-dio-实现串口通信&#34; class=&#34;headerlink&#34; title=&#34;2. 使用 dio 实现串口通信&#34;&gt;&lt;/a&gt;2. 使用 dio 实现串口通信&lt;/h3&gt;&lt;p&gt;以下是一个使用 dio 扩展实现串口通信的示例代码：&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 串口设备路径&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$device = &amp;#x27;/dev/pts/4&amp;#x27;; // Linux&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// $device = &amp;#x27;COM1&amp;#x27;;      // Windows&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 打开串口设备&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$fd = dio_open($device, O_RDWR | O_NOCTTY | O_NONBLOCK);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;if (!$fd) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    die(&amp;quot;无法打开串口设备: $device\n&amp;quot;);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 配置串口参数&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dio_tcsetattr($fd, [&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;baud&amp;#x27; =&amp;gt; 9600,          // 波特率&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;bits&amp;#x27; =&amp;gt; 8,             // 数据位&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;stop&amp;#x27; =&amp;gt; 1,             // 停止位&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;parity&amp;#x27; =&amp;gt; 0,           // 校验位 (0: none, 1: odd, 2: even)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;#x27;flow_control&amp;#x27; =&amp;gt; 0,     // 流控制 (0: none, 1: hardware)&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;]);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 发送数据到串口&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$message = &amp;quot;你好我在https://erik.xyz上出生了！&amp;quot;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dio_write($fd, $message);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;echo &amp;quot;已发送: $message&amp;quot;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 从串口读取数据&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$data = dio_read($fd, 1024); // 读取最多 1024 字节&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;echo &amp;quot;已接收: $data\n&amp;quot;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 关闭串口&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;dio_close($fd);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;?&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;h3 id=&#34;3-代码说明&#34;&gt;&lt;a href=&#34;#3-代码说明&#34; class=&#34;headerlink&#34; title=&#34;3. 代码说明&#34;&gt;&lt;/a&gt;3. 代码说明&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;dio_open: 打开串口设备文件。O_RDWR 表示以读写模式打开，O_NOCTTY 表示不将设备作为控制终端，O_NONBLOCK 表示非阻塞模式。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dio_tcsetattr: 配置串口参数，包括波特率、数据位、停止位、校验位和流控制。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dio_write: 向串口写入数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dio_read: 从串口读取数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dio_close: 关闭串口设备。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;4-串口参数配置&#34;&gt;&lt;a href=&#34;#4-串口参数配置&#34; class=&#34;headerlink&#34; title=&#34;4. 串口参数配置&#34;&gt;&lt;/a&gt;4. 串口参数配置&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;dio_tcsetattr 的配置选项：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;baud: 波特率（如 9600、19200、38400、57600、115200）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;bits: 数据位（通常为 8）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;stop: 停止位（1 或 2）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;parity: 校验位（0: 无校验，1: 奇校验，2: 偶校验）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;flow_control: 流控制（0: 无流控制，1: 硬件流控制）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;那么这时候需要测试一下代码。那总不能真的找个串口设备吧，然而虚拟串口真香。&#34;&gt;&lt;a href=&#34;#那么这时候需要测试一下代码。那总不能真的找个串口设备吧，然而虚拟串口真香。&#34; class=&#34;headerlink&#34; title=&#34;那么这时候需要测试一下代码。那总不能真的找个串口设备吧，然而虚拟串口真香。&#34;&gt;&lt;/a&gt;那么这时候需要测试一下代码。那总不能真的找个串口设备吧，然而虚拟串口真香。&lt;/h2&gt;&lt;h3 id=&#34;5-在Linux中使用-socat-模拟虚拟串口&#34;&gt;&lt;a href=&#34;#5-在Linux中使用-socat-模拟虚拟串口&#34; class=&#34;headerlink&#34; title=&#34;5.在Linux中使用 socat 模拟虚拟串口&#34;&gt;&lt;/a&gt;5.在Linux中使用 socat 模拟虚拟串口&lt;/h3&gt;&lt;p&gt;socat 是一个强大的工具，可以创建虚拟串口对。&lt;/p&gt;
&lt;p&gt;安装 socat：&lt;br&gt;在Debian/Ubuntu系统上：&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;sudo apt update&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;sudo apt install socat&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;创建虚拟串口对：&lt;br&gt;运行以下命令创建一对虚拟串口：&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;socat -d -d pty,raw,echo=0 pty,raw,echo=0&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;运行后如图：&lt;br&gt;&lt;img src=&#34;/img/2024/20250103151316.png&#34; alt=&#34;https://erik.xyz&#34;&gt;&lt;br&gt;这样可以看到出现两个虚拟串口。&lt;br&gt;把上面的php代码放到文件中运行一下:&lt;br&gt;&lt;img src=&#34;/img/2024/20250103151620.png&#34; alt=&#34;https://erik.xyz&#34;&gt;&lt;/p&gt;
&lt;p&gt;同时新开个窗口执行：&lt;code&gt;cat /dev/pts/5&lt;/code&gt;来读取串口数据。&lt;br&gt;如下图：&lt;br&gt;&lt;img src=&#34;/img/2024/20250103151927.png&#34; alt=&#34;https://erik.xyz&#34;&gt;&lt;/p&gt;
&lt;p&gt;这里是发送数据，那接收数据怎么看呢。&lt;br&gt;那就在代码上改造一下加个for：&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;// 从串口读取数据&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$data = dio_read($fd, 1024); // 读取最多 1024 字节&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;echo &amp;quot;已接收: $data\n&amp;quot;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;br&gt;这里改造主要是测试用，实际上不需要。&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;//测试接收&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;for($i=0;$i&amp;lt;20;$i++)&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    sleep(3);&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;// 从串口读取数据&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;$data = dio_read($fd, 1024); // 读取最多 1024 字节&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;echo &amp;quot;已接收: $data\n&amp;quot;;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;br&gt;那么，再次运行php代码,然后在新窗口运行&lt;code&gt;echo &amp;quot;欢迎你出生在https://erik.xyz&amp;quot; &amp;gt; /dev/pts/5&lt;/code&gt;来发送信息，你会看到如图的接收：&lt;br&gt;&lt;img src=&#34;/img/2024/20250103152421.png&#34; alt=&#34;https://erik.xyz&#34;&gt;&lt;/p&gt;
&lt;p&gt;到这里，发送和接收串口已经好了。&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2024/12/18/maven-intranet-library/</guid>
            <title>maven内网库</title>
            <link>https://erik.xyz/2024/12/18/maven-intranet-library/</link>
            <category>maven搭建库</category>
            <category>maven</category>
            <category>maven内网库</category>
            <pubDate>Wed, 18 Dec 2024 20:55:00 +0800</pubDate>
            <description><![CDATA[ &lt;h4 id=&#34;1-搭建-Maven-私有仓库&#34;&gt;&lt;a href=&#34;#1-搭建-Maven-私有仓库&#34; class=&#34;headerlink&#34; title=&#34;1. 搭建 Maven 私有仓库&#34;&gt;&lt;/a&gt;1. 搭建 Maven 私有仓库&lt;/h4&gt;&lt;p&gt;首先，需要在内网环境中搭建一个 Maven 仓库，常用的私有 Maven 仓库工具有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nexus Repository：Sonatype Nexus 是最流行的私有 Maven 仓库管理工具。&lt;/li&gt;
&lt;li&gt;Artifactory：JFrog Artifactory 是另一种流行的构建管理工具，提供了私有仓库的支持。&lt;/li&gt;
&lt;li&gt;Apache Archiva：Apache Archiva 也是一个支持 Maven 的仓库管理工具。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下是搭建 Nexus Repository 的简单步骤：&lt;br&gt;&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;1.1 安装 Nexus Repository&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;下载 Nexus： 访问 Nexus Repository 下载页面 下载 Nexus OSS 版本。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;解压并启动： 解压下载的压缩包并启动 Nexus。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;cd /opt/nexus/bin&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;./nexus start&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;访问 Nexus UI： 打开浏览器，访问 Nexus 的默认地址：&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;http://localhost:8081&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;默认用户名为 admin，密码为 admin123，可以在首次登录后修改密码。&lt;/p&gt;
&lt;p&gt;1.2 配置 Maven 仓库&lt;/p&gt;
&lt;p&gt;在 Nexus UI 中，你可以创建一个新的 Maven 仓库。创建仓库后，你可以上传公司的内部依赖、插件和构建工件。&lt;/p&gt;
&lt;h4 id=&#34;2-配置-Maven-使用内网仓库&#34;&gt;&lt;a href=&#34;#2-配置-Maven-使用内网仓库&#34; class=&#34;headerlink&#34; title=&#34;2. 配置 Maven 使用内网仓库&#34;&gt;&lt;/a&gt;2. 配置 Maven 使用内网仓库&lt;/h4&gt;&lt;p&gt;配置 Maven 使用内网仓库需要修改 settings.xml 文件。&lt;/p&gt;
&lt;p&gt;2.1 修改 settings.xml&lt;/p&gt;
&lt;p&gt;在 Maven 的 settings.xml 文件中，配置私有仓库的地址和认证信息。settings.xml 文件通常位于 ~/.m2/ 目录下（用户级别配置）或者 ${MAVEN_HOME}/conf/ 目录下（全局配置）。&lt;/p&gt;
&lt;p&gt;以下是配置内网 Maven 仓库的示例：&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;40&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;settings xmlns=&amp;quot;http://maven.apache.org/SETTINGS/1.0.0&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;          xsi:schemaLocation=&amp;quot;http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd&amp;quot;&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;mirrors&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;!-- 配置私有仓库镜像 --&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;mirror&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;id&amp;gt;nexus&amp;lt;/id&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;mirrorOf&amp;gt;external:http://central&amp;lt;/mirrorOf&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;url&amp;gt;http://your-nexus-server:8081/repository/maven-public/&amp;lt;/url&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;blocked&amp;gt;false&amp;lt;/blocked&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;/mirror&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/mirrors&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;repositories&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;repository&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;id&amp;gt;internal-repo&amp;lt;/id&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;url&amp;gt;http://your-nexus-server:8081/repository/maven-releases/&amp;lt;/url&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;snapshots&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;                &amp;lt;enabled&amp;gt;false&amp;lt;/enabled&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;/snapshots&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;/repository&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/repositories&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;pluginRepositories&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;pluginRepository&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;id&amp;gt;internal-plugins&amp;lt;/id&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;url&amp;gt;http://your-nexus-server:8081/repository/maven-plugins/&amp;lt;/url&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;/pluginRepository&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/pluginRepositories&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;servers&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;!-- 配置 Maven 仓库认证 --&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;server&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;id&amp;gt;nexus&amp;lt;/id&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;username&amp;gt;your-nexus-username&amp;lt;/username&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;            &amp;lt;password&amp;gt;your-nexus-password&amp;lt;/password&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;/server&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/servers&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/settings&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;2.2 配置镜像和仓库&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;镜像（Mirror）：在 &lt;mirrors&gt; 标签中配置私有仓库的 URL，将 Maven 的中央仓库或其他公共仓库的请求代理到私有仓库中。通过 mirrorOf 配置来选择代理哪些仓库（external:&lt;a href=&#34;http://central&#34;&gt;http://central&lt;/a&gt; 表示代理所有外部仓库）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;仓库（Repository）：在 &lt;repositories&gt; 和 &lt;pluginRepositories&gt; 标签中配置你的内网仓库的 URL。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;认证（Server Authentication）：在 &lt;servers&gt; 标签中配置内网仓库的认证信息（如果仓库需要认证）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;3-配置项目使用内网仓库&#34;&gt;&lt;a href=&#34;#3-配置项目使用内网仓库&#34; class=&#34;headerlink&#34; title=&#34;3. 配置项目使用内网仓库&#34;&gt;&lt;/a&gt;3. 配置项目使用内网仓库&lt;/h4&gt;&lt;p&gt;在项目的 pom.xml 文件中，通常不需要额外配置仓库，因为 Maven 会使用 settings.xml 中配置的内网仓库。但是如果需要强制指定某个仓库，可以在 pom.xml 中配置 &lt;repositories&gt; 标签：&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;repositories&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;repository&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;id&amp;gt;nexus-repo&amp;lt;/id&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;url&amp;gt;http://your-nexus-server:8081/repository/maven-releases/&amp;lt;/url&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/repository&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/repositories&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h4 id=&#34;4-上传和下载依赖&#34;&gt;&lt;a href=&#34;#4-上传和下载依赖&#34; class=&#34;headerlink&#34; title=&#34;4. 上传和下载依赖&#34;&gt;&lt;/a&gt;4. 上传和下载依赖&lt;/h4&gt;&lt;p&gt;4.1 上传依赖到内网仓库&lt;br&gt;你可以通过 Maven 命令将本地构建的 JAR 文件上传到内网仓库。例如，将某个 JAR 上传到 Nexus：&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;mvn deploy:deploy-file \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    -DgroupId=com.example \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    -DartifactId=my-artifact \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    -Dversion=1.0.0 \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    -Dpackaging=jar \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    -Dfile=path/to/your-artifact.jar \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    -DrepositoryId=nexus \&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    -Durl=http://your-nexus-server:8081/repository/maven-releases/&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;4.2 从内网仓库下载依赖&lt;/p&gt;
&lt;p&gt;配置好内网仓库后，Maven 会自动从内网仓库下载依赖。如果仓库中没有该依赖，Maven 会尝试从其他配置的仓库下载。&lt;/p&gt;
&lt;h4 id=&#34;5-使用私有仓库中的依赖&#34;&gt;&lt;a href=&#34;#5-使用私有仓库中的依赖&#34; class=&#34;headerlink&#34; title=&#34;5. 使用私有仓库中的依赖&#34;&gt;&lt;/a&gt;5. 使用私有仓库中的依赖&lt;/h4&gt;&lt;p&gt;一旦仓库配置好，Maven 将会从配置的内网仓库下载依赖。你可以在项目的 pom.xml 中正常引用依赖，Maven 会自动从私有仓库中拉取。&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;dependency&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;groupId&amp;gt;com.example&amp;lt;/groupId&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;artifactId&amp;gt;my-artifact&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;        &amp;lt;version&amp;gt;1.0.0&amp;lt;/version&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;    &amp;lt;/dependency&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;h4 id=&#34;6-配置镜像以提高构建速度（可选）&#34;&gt;&lt;a href=&#34;#6-配置镜像以提高构建速度（可选）&#34; class=&#34;headerlink&#34; title=&#34;6. 配置镜像以提高构建速度（可选）&#34;&gt;&lt;/a&gt;6. 配置镜像以提高构建速度（可选）&lt;/h4&gt;&lt;p&gt;为了提高构建速度，你可以配置 settings.xml 来使用私有仓库作为 Maven 的默认镜像，确保所有的构建依赖都从私有仓库中拉取，避免每次访问外部仓库，降低构建时间。&lt;/p&gt;
 ]]></description>
        </item>
        <item>
            <guid isPermalink="true">https://erik.xyz/2024/12/12/intranet-spring-boot-install/</guid>
            <title>spring boot内网部署</title>
            <link>https://erik.xyz/2024/12/12/intranet-spring-boot-install/</link>
            <category>springboot</category>
            <category>springboot内网部署</category>
            <pubDate>Thu, 12 Dec 2024 18:22:18 +0800</pubDate>
            <description><![CDATA[ &lt;h4 id=&#34;1-准备环境&#34;&gt;&lt;a href=&#34;#1-准备环境&#34; class=&#34;headerlink&#34; title=&#34;1. 准备环境&#34;&gt;&lt;/a&gt;1. 准备环境&lt;/h4&gt;&lt;p&gt;确保内网中的服务器或机器具备运行 Spring Boot 应用的基本环境：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JDK：确保服务器安装了合适版本的 JDK（通常建议使用 Java 8 及以上版本）。&lt;/li&gt;
&lt;li&gt;Maven/Gradle：根据项目使用的构建工具安装 Maven 或 Gradle。&lt;/li&gt;
&lt;li&gt;数据库：如果应用需要连接数据库，确保数据库在内网中可访问，且连接配置正确。&lt;/li&gt;
&lt;/ul&gt;
&lt;span id=&#34;more&#34;&gt;&lt;/span&gt;
&lt;h4 id=&#34;2-编译-Spring-Boot-应用&#34;&gt;&lt;a href=&#34;#2-编译-Spring-Boot-应用&#34; class=&#34;headerlink&#34; title=&#34;2. 编译 Spring Boot 应用&#34;&gt;&lt;/a&gt;2. 编译 Spring Boot 应用&lt;/h4&gt;&lt;p&gt;首先，你需要编译你的 Spring Boot 应用，生成可执行的 JAR 文件。可以通过以下命令在项目目录下执行：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Maven 构建命令：&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;mvn clean package&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;Gradle 构建命令：&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;./gradlew build&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;这会在 target/ 或 build/libs/ 目录下生成一个可执行的 JAR 文件，通常名为 your-application-name.jar。&lt;/p&gt;
&lt;h4 id=&#34;3-传输-JAR-到内网服务器&#34;&gt;&lt;a href=&#34;#3-传输-JAR-到内网服务器&#34; class=&#34;headerlink&#34; title=&#34;3. 传输 JAR 到内网服务器&#34;&gt;&lt;/a&gt;3. 传输 JAR 到内网服务器&lt;/h4&gt;&lt;p&gt;将生成的 JAR 文件上传到内网的目标服务器。可以使用各种文件传输工具，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SCP（如果服务器支持 SSH）：&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;scp your-application-name.jar user@server-ip:/path/to/deploy/&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;FTP 或 SFTP（如果有配置 FTP 服务）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;4-配置-Spring-Boot-应用&#34;&gt;&lt;a href=&#34;#4-配置-Spring-Boot-应用&#34; class=&#34;headerlink&#34; title=&#34;4. 配置 Spring Boot 应用&#34;&gt;&lt;/a&gt;4. 配置 Spring Boot 应用&lt;/h4&gt;&lt;p&gt;在内网部署时，你可能需要根据环境修改配置文件，比如 application.properties 或 application.yml。常见的配置项包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据库连接信息（spring.datasource.url、spring.datasource.username 等）。&lt;/li&gt;
&lt;li&gt;日志配置。&lt;/li&gt;
&lt;li&gt;服务端口（server.port）。&lt;/li&gt;
&lt;li&gt;安全设置（如禁用外部访问，或者设置访问白名单等）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如，修改 application.properties 中的数据库配置：&lt;br&gt;&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;spring.datasource.url=jdbc:mysql://localhost:3306/your_db&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;spring.datasource.username=db_user&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;spring.datasource.password=db_password&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
&lt;h4 id=&#34;5-启动-Spring-Boot-应用&#34;&gt;&lt;a href=&#34;#5-启动-Spring-Boot-应用&#34; class=&#34;headerlink&#34; title=&#34;5. 启动 Spring Boot 应用&#34;&gt;&lt;/a&gt;5. 启动 Spring Boot 应用&lt;/h4&gt;&lt;p&gt;在内网服务器上，使用以下命令启动 Spring Boot 应用：&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;java -jar your-application-name.jar&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;如果你希望应用在后台运行，可以使用 nohup 或者类似的工具：&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;nohup java -jar your-application-name.jar &amp;gt; output.log 2&amp;gt;&amp;amp;1 &amp;amp;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;这会将输出重定向到 output.log 文件，并让应用在后台运行。&lt;/p&gt;
&lt;h4 id=&#34;6-配置防火墙和网络&#34;&gt;&lt;a href=&#34;#6-配置防火墙和网络&#34; class=&#34;headerlink&#34; title=&#34;6. 配置防火墙和网络&#34;&gt;&lt;/a&gt;6. 配置防火墙和网络&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;确保服务器的防火墙允许访问应用所绑定的端口（默认是 8080）。如果使用其他端口，可以在防火墙中配置允许访问该端口。&lt;/li&gt;
&lt;li&gt;如果 Spring Boot 应用需要通过内网的特定 IP 地址或域名访问，确保 DNS 或 hosts 配置正确。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;7-监控和日志&#34;&gt;&lt;a href=&#34;#7-监控和日志&#34; class=&#34;headerlink&#34; title=&#34;7. 监控和日志&#34;&gt;&lt;/a&gt;7. 监控和日志&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;日志：Spring Boot 应用会将日志输出到控制台，你可以将日志配置为输出到文件中进行持久化存储。常见做法是在 application.properties 或 application.yml 中设置日志路径：&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;logging.file.name=/path/to/logs/application.log&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;监控：可以使用 Spring Boot 的 Actuator 或其他监控工具（如 Prometheus 和 Grafana）来监控应用的运行状态。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;8-设置开机启动（可选）&#34;&gt;&lt;a href=&#34;#8-设置开机启动（可选）&#34; class=&#34;headerlink&#34; title=&#34;8. 设置开机启动（可选）&#34;&gt;&lt;/a&gt;8. 设置开机启动（可选）&lt;/h4&gt;&lt;p&gt;如果希望应用在服务器重启时自动启动，可以使用 systemd（Linux 系统）或配置为 Windows 服务。&lt;/p&gt;
&lt;p&gt;Linux 系统 (Systemd)&lt;/p&gt;
&lt;p&gt;创建一个 systemd 服务文件，如 /etc/systemd/system/yourapp.service：&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;12&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;[Unit]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;Description=Spring Boot Application&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;After=network.target&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;[Service]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;User=your_user&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;ExecStart=/usr/bin/java -jar /path/to/your-application-name.jar&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;SuccessExitStatus=143&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;Restart=always&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;[Install]&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;WantedBy=multi-user.target&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
&lt;p&gt;然后启用并启动服务：&lt;/p&gt;
&lt;figure class=&#34;highlight plaintext&#34;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&#34;gutter&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&#34;code&#34;&gt;&lt;pre&gt;&lt;span class=&#34;line&#34;&gt;sudo systemctl enable yourapp&lt;/span&gt;&lt;br&gt;&lt;span class=&#34;line&#34;&gt;sudo systemctl start yourapp&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt; ]]></description>
        </item>
    </channel>
</rss>