-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrss.xml
1918 lines (1914 loc) · 309 KB
/
rss.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>艾瑞可erik • Posts by "undefined" categories</title>
<link>https://erik.xyz</link>
<description>一只PHP开发的程序猿,偶尔做做运维、Goland、Python、Java、摄影、画画、写作、顺便睡觉,反正整站都搞过。</description>
<language>zh-CN</language>
<pubDate>Wed, 05 Feb 2025 13:16:00 +0800</pubDate>
<lastBuildDate>Wed, 05 Feb 2025 13:16:00 +0800</lastBuildDate>
<category>日志</category>
<category>工具</category>
<category>jquery</category>
<category>jquery插件</category>
<category>php扩展</category>
<category>php技巧</category>
<category>php类库</category>
<category>php资源</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>转载</category>
<category>php库</category>
<category>nginx</category>
<category>互联</category>
<category>项目实战</category>
<category>javascript</category>
<category>php应用</category>
<category>浮点</category>
<category>漏洞</category>
<category>负载均衡</category>
<category>kali</category>
<category>kali安装</category>
<category>http</category>
<category>http服务器</category>
<category>农业系统</category>
<category>系统设计</category>
<category>克莱姆法则</category>
<category>行列式</category>
<category>n阶行列式</category>
<category>支付系统</category>
<category>支付</category>
<category>apache优化</category>
<category>apache隐藏index</category>
<category>nginx优化</category>
<category>nginx隐藏index</category>
<category>centos</category>
<category>linux</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>清理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>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>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>hadoop</category>
<category>hadoop2.8.0</category>
<category>hadoop安装教程</category>
<category>hadoop安装详细教程</category>
<category>hadoop教程</category>
<category>hadoop部署</category>
<category>php大全</category>
<category>php资料</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>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>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>微信支付</category>
<category>微信支付回调</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>面向对象</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>python</category>
<category>python库</category>
<category>a标签</category>
<category>a标签虚线</category>
<category>虚线框</category>
<category>R语言</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>js编辑</category>
<category>runjs</category>
<category>在线编辑</category>
<category>响应慢</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>json</category>
<category>json数组</category>
<category>json解析</category>
<category>socket通信</category>
<category>socket多进程</category>
<category>socket</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>web自适应</category>
<category>响应式布局</category>
<category>响应式所有分辨率</category>
<category>自适应布局</category>
<category>自适应所有分辨率</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>webman</category>
<category>laravel</category>
<category>composer插件</category>
<category>html</category>
<category>响应式分辨率</category>
<category>响应式调试</category>
<category>自适应屏幕</category>
<category>携程</category>
<category>携程攻击</category>
<category>携程网站瘫痪</category>
<category>物理删除</category>
<category>3D</category>
<category>动画</category>
<category>平台</category>
<category>游戏</category>
<category>虚幻4引擎编辑</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/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[ <pre><code> 白马寺
千年风霜斑驳目,新出寰宇客来慕。
佛丘已非今朝时,独得枯木逢春时。
</code></pre> ]]></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[ <p>在内网环境中部署 Composer(PHP的依赖管理工具),通常是为了避免因外网访问限制而导致的依赖下载问题。内网部署 Composer 主要涉及配置一个 私有 Composer 仓库,或者使用 本地代理镜像 来加速和管理依赖。</p>
<h3 id="1-使用-composer-json-设置依赖"><a href="#1-使用-composer-json-设置依赖" class="headerlink" title="1. 使用 composer.json 设置依赖"></a><b>1. 使用 composer.json 设置依赖</b></h3><p>首先,确保你的 composer.json 文件包含所有项目依赖,并正确配置了包的版本和来源。<br><span id="more"></span><br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line"> &quot;name&quot;: &quot;vendor/project&quot;,</span><br><span class="line"> &quot;require&quot;: &#123;</span><br><span class="line"> &quot;monolog/monolog&quot;: &quot;^2.0&quot;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="2-使用代理镜像"><a href="#2-使用代理镜像" class="headerlink" title="2. 使用代理镜像"></a><b>2. 使用代理镜像</b></h3><p>由于内网无法直接访问 Composer 官方的包仓库,常见的做法是使用国内镜像或搭建代理服务器,常见的方案包括使用 阿里云 Composer 镜像 或 私有镜像仓库。</p>
<p> <b>2.1 使用国内镜像源</b></p>
<p>你可以通过配置 Composer 使用国内镜像源来加速依赖包的下载。在 Composer 中,你可以使用下面的命令设置国内镜像:<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">composer config repo.packagist composer https://mirrors.aliyun.com/composer/</span><br></pre></td></tr></table></figure><br>这个命令将 packagist.org 源更换为阿里云的镜像源。</p>
<p>你还可以通过在 composer.json 文件中进行设置,强制使用镜像源:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line"> &quot;repositories&quot;: [</span><br><span class="line"> &#123;</span><br><span class="line"> &quot;type&quot;: &quot;composer&quot;,</span><br><span class="line"> &quot;url&quot;: &quot;https://mirrors.aliyun.com/composer/&quot;</span><br><span class="line"> &#125;</span><br><span class="line"> ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这样每次执行 composer install 时,都会从阿里云的镜像源下载依赖。</p>
<p><b>2.2 配置自建 Composer 镜像代理</b></p>
<p>如果你想在企业内部完全控制包管理,可以搭建自己的 Composer 镜像代理。常见的选择包括:</p>
<ul>
<li>Satis:一个官方提供的工具,用来搭建私有的 Composer 仓库。</li>
<li>Private Packagist:一个商业解决方案,专门用于在私有环境中管理 Composer 包。</li>
<li>Sinopia (npm 仓库代理工具):可以作为私有的 Composer 仓库代理使用。</li>
</ul>
<p><b>2.2.1 使用 Satis 搭建私有 Composer 仓库</b></p>
<p>Satis 是 Composer 官方提供的一个轻量级工具,专门用于创建私有的 Composer 仓库。通过使用 Satis,你可以将内网环境下常用的依赖缓存下来,并提供给项目中使用。</p>
<p>步骤:</p>
<ol>
<li>安装 Satis:</li>
</ol>
<p>使用 Composer 安装 Satis:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">composer require composer/satis</span><br></pre></td></tr></table></figure>
<ul>
<li>配置 Satis:</li>
</ul>
<p>在你的服务器上创建一个 satis.json 配置文件,指定你希望托管的包源。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line"> &quot;name&quot;: &quot;my-private-repo&quot;,</span><br><span class="line"> &quot;homepage&quot;: &quot;https://example.com/packages&quot;,</span><br><span class="line"> &quot;repositories&quot;: [</span><br><span class="line"> &#123;</span><br><span class="line"> &quot;type&quot;: &quot;vcs&quot;,</span><br><span class="line"> &quot;url&quot;: &quot;https://github.com/some/package&quot;</span><br><span class="line"> &#125;,</span><br><span class="line"> &#123;</span><br><span class="line"> &quot;type&quot;: &quot;composer&quot;,</span><br><span class="line"> &quot;url&quot;: &quot;https://packagist.org&quot;</span><br><span class="line"> &#125;</span><br><span class="line"> ],</span><br><span class="line"> &quot;output-dir&quot;: &quot;/path/to/output&quot;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>生成静态资源:</li>
</ul>
<p>运行以下命令生成静态的 Composer 包:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">php bin/satis build satis.json /path/to/output</span><br></pre></td></tr></table></figure>
<ul>
<li>提供访问:</li>
</ul>
<p>你可以通过 Web 服务器(如 Nginx 或 Apache)提供访问静态资源。这样,其他项目就能通过你的内部 Satis 仓库获取依赖了。</p>
<p><b>2.2.2 配置 Composer 使用私有仓库</b></p>
<p>在 composer.json 中配置私有仓库(例如,你的公司内部搭建的 Satis 仓库)。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line"> &quot;repositories&quot;: [</span><br><span class="line"> &#123;</span><br><span class="line"> &quot;type&quot;: &quot;composer&quot;,</span><br><span class="line"> &quot;url&quot;: &quot;http://your-internal-repository.com&quot;</span><br><span class="line"> &#125;</span><br><span class="line"> ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这样,Composer 会从你配置的私有仓库中拉取依赖。</p>
<h3 id="3-离线安装-Composer-依赖"><a href="#3-离线安装-Composer-依赖" class="headerlink" title="3. 离线安装 Composer 依赖"></a><b>3. 离线安装 Composer 依赖</b></h3><p>如果内网无法访问外部网络,也可以考虑在有外网访问权限的机器上下载依赖包,然后将它们导入到内网机器中进行安装。</p>
<p><b>3.1 在有外网的机器上下载依赖</b></p>
<p>在能够访问外网的机器上执行 composer install。<br>下载完成后,将 vendor 目录及 composer.lock 文件拷贝到内网服务器相同位置。</p>
<p><b>3.2 使用 —prefer-dist 参数</b></p>
<p>你还可以使用 composer install —prefer-dist 来下载 .tar.gz 或 .zip 格式的包,这样可以方便地进行离线安装。</p>
<p><b>3.3 配置 COMPOSER_HOME</b></p>
<p>如果你希望在内网机器上使用本地缓存的包,可以设置 COMPOSER_HOME 环境变量来指定 Composer 缓存目录。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">export COMPOSER_HOME=/path/to/composer/cache</span><br></pre></td></tr></table></figure>
<p>然后,将缓存目录复制到内网机器,确保 Composer 能够使用这些缓存。</p>
<h3 id="4-其他考虑"><a href="#4-其他考虑" class="headerlink" title="4. 其他考虑"></a><b>4. 其他考虑</b></h3><ul>
<li>代理配置:如果内网可以通过 HTTP 代理访问外网,确保 Composer 配置了代理。在 composer.json 中配置代理,或者使用环境变量来指定代理:</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">export HTTP_PROXY=http://proxy.example.com:8080</span><br><span class="line">export HTTPS_PROXY=http://proxy.example.com:8080</span><br></pre></td></tr></table></figure>
<ul>
<li>私有包的权限:如果你使用了私有 Composer 仓库或 GitHub 仓库,记得配置好认证方式(如使用 auth.json 文件存储 GitHub Token)。</li>
</ul>
]]></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[ <p><b>农业检测系统设计</b></p>
<p>根据你的需求,我们将设计一个智能农业系统,通过多种技术手段实现温湿度、光照度监测、设备控制和远程管理。系统的核心功能包括:智能节点监控环境数据、手动和自动控制模式、通过 LoRa 和 4G 通信传输数据、以及通过手机或电脑端进行远程控制。<br><span id="more"></span></p>
<h4 id="1-系统模块概述"><a href="#1-系统模块概述" class="headerlink" title="1. 系统模块概述"></a><b>1. 系统模块概述</b></h4><p>该农业检测系统包括以下几个主要模块:</p>
<ol>
<li><p>智能节点(传感器与控制单元):</p>
<ul>
<li>获取温湿度、光照度传感器数据。</li>
<li>控制风扇的启停,显示当前风扇状态。</li>
<li>支持手动和自动模式切换。</li>
<li>显示温湿度、光照度、风扇状态和模式状态。</li>
<li>通过 LoRa 发送数据到 4G 节点。</li>
</ul>
</li>
<li><p>4G 网关节点:</p>
<ul>
<li>接收来自智能节点的传感器数据。</li>
<li>将数据通过 4G 网络上传至 MOTT 服务器。</li>
<li>在显示屏上显示各项传感器数据。</li>
</ul>
</li>
</ol>
<ul>
<li><p>MOTT 服务器:</p>
<ul>
<li>存储从 4G 节点发送来的数据。</li>
<li>提供远程访问接口。</li>
</ul>
</li>
<li><p>应用软件(电脑端或手机端):</p>
<ul>
<li>显示温湿度、光照度、风扇状态、手动/自动模式等数据。</li>
<li>提供登录、手动控制、自动控制等功能。</li>
</ul>
</li>
</ul>
<h4 id="2-系统功能设计"><a href="#2-系统功能设计" class="headerlink" title="2.系统功能设计"></a><b>2.系统功能设计</b></h4><p>2.1 智能节点功能</p>
<ol>
<li><p>传感器数据采集:</p>
<ul>
<li>温湿度传感器:实时获取温度和湿度数据。</li>
<li>光照度传感器:实时获取光照强度数据。</li>
</ul>
</li>
</ol>
<ul>
<li><p>模式切换:</p>
<ul>
<li>手动模式:允许用户通过按键手动启动或停止风扇。</li>
<li>自动模式:当温度和光照度超过设定阈值时,自动启停风扇。</li>
</ul>
</li>
<li><p>风扇控制:</p>
<ul>
<li>在手动模式下,用户通过按键控制风扇开关,并通过绿色 LED 指示灯显示风扇状态。</li>
<li>在自动模式下,系统会根据设定的温度和光照度阈值自动控制风扇,并使用绿灯和红灯进行状态指示。</li>
</ul>
</li>
<li><p>数据传输:</p>
<ul>
<li>使用 LoRa 无线传输模块将传感器数据和风扇状态上传至 4G 网关节点。</li>
</ul>
</li>
<li><p>显示屏:</p>
<ul>
<li>显示当前温湿度、光照度、模式状态和风扇状态。</li>
</ul>
</li>
</ul>
<p><b>2.2 4G 网关节点功能</b></p>
<ol>
<li><p>数据接收:</p>
<ul>
<li>通过 LoRa 接收智能节点上传的传感器数据(温度、湿度、光照度、风扇状态、手动/自动模式状态)。</li>
</ul>
</li>
</ol>
<ul>
<li><p>4G 网络传输:</p>
<ul>
<li>将智能节点的数据通过 4G 网络上传至 MOTT 服务器。</li>
</ul>
</li>
<li><p>显示屏显示:</p>
<ul>
<li>在 4G 网关节点的显示屏上展示温湿度、光照度、模式状态和风扇状态。</li>
</ul>
</li>
</ul>
<p><b>2.3 MOTT 服务器功能</b></p>
<ol>
<li><p>数据存储:</p>
<ul>
<li>存储来自 4G 网关节点的温湿度、光照度、风扇状态等数据。</li>
</ul>
</li>
</ol>
<ul>
<li><p>远程管理接口:</p>
<ul>
<li>提供 RESTful API 或 WebSocket 接口,供客户端应用访问。</li>
</ul>
</li>
</ul>
<p><b>2.4 应用软件功能</b></p>
<ol>
<li>用户登录:<ul>
<li>提供登录界面,用户通过输入用户名和密码登录(用户名:admin,密码:admin123)。</li>
</ul>
</li>
</ol>
<ul>
<li><p>手动控制:</p>
<ul>
<li>登录成功后,用户可通过界面手动启停风扇,操作时触发红灯和蜂鸣器的提示。</li>
</ul>
</li>
<li><p>自动控制:</p>
<ul>
<li>用户可以设定温度和光照度的阈值,智能节点进入自动模式后,系统根据传感器数据自动控制风扇开关,并通过 LED 指示灯显示状态。</li>
</ul>
</li>
<li><p>显示功能:</p>
<ul>
<li>实时显示温度、湿度、光照度、风扇状态、手动/自动模式状态。</li>
</ul>
</li>
</ul>
<h4 id="3-原型图设计"><a href="#3-原型图设计" class="headerlink" title="3. 原型图设计"></a><b>3. 原型图设计</b></h4><p>3.1 智能节点原型图<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">+-------------------------+</span><br><span class="line">| 温湿度传感器 |</span><br><span class="line">| |</span><br><span class="line">| 光照度传感器 |</span><br><span class="line">| |</span><br><span class="line">| 按键面板 (5个按键) |</span><br><span class="line">| |</span><br><span class="line">| 显示屏 (LCD) |</span><br><span class="line">| |</span><br><span class="line">| 风扇控制 (LED/蜂鸣器) |</span><br><span class="line">+-------------------------+</span><br></pre></td></tr></table></figure></p>
<ul>
<li>显示屏:显示温度、湿度、光照度、当前模式(手动/自动)和风扇状态。</li>
<li>按键面板:5个按键,其中包括:<ul>
<li>切换模式按键(手动/自动)</li>
<li>启动风扇的控制按键</li>
<li>停止风扇的控制按键</li>
</ul>
</li>
<li>LED 灯:指示风扇是否正在运行(绿色为运行,红色为停止)。</li>
<li>蜂鸣器:在风扇运行时以 1Hz 的频率发出声音。</li>
</ul>
<p><b>3.2 4G 网关节点原型图</b></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">+-------------------------+</span><br><span class="line">| LoRa 接收模块 |</span><br><span class="line">| (接收数据) |</span><br><span class="line">| |</span><br><span class="line">| 显示屏 (LCD) |</span><br><span class="line">| |</span><br><span class="line">| 4G 网络模块 |</span><br><span class="line">| (上传数据) |</span><br><span class="line">+-------------------------+</span><br></pre></td></tr></table></figure>
<ul>
<li>显示屏:显示温湿度、光照度、模式状态和风扇状态。</li>
<li>LoRa 接收模块:接收智能节点发送的传感器数据。</li>
<li>4G 网络模块:将接收到的数据上传到 MOTT 服务器。</li>
</ul>
<p><b>3.3 应用软件原型图(电脑端或手机端)</b></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">+-------------------------------------------------------+</span><br><span class="line">| 登录界面 |</span><br><span class="line">| - 用户名输入框 |</span><br><span class="line">| - 密码输入框 |</span><br><span class="line">| - 登录按钮 |</span><br><span class="line">+-------------------------------------------------------+</span><br><span class="line">| 主界面 |</span><br><span class="line">| - 实时显示温湿度、光照度、风扇状态、手动/自动模式 |</span><br><span class="line">| - 手动控制按钮 (启动/停止风扇) |</span><br><span class="line">| - 自动模式阈值设置 (温度、光照度) |</span><br><span class="line">| - 状态显示:当前模式(手动/自动)、风扇状态(开/关)|</span><br><span class="line">+-------------------------------------------------------+</span><br></pre></td></tr></table></figure>
<h4 id="4-系统设计步骤"><a href="#4-系统设计步骤" class="headerlink" title="4. 系统设计步骤"></a><b>4. 系统设计步骤</b></h4><p><b>4.1 硬件设计与开发</b></p>
<ul>
<li>选择适当的传感器:温湿度传感器(如 DHT22)、光照度传感器、风扇控制模块、LED 指示灯、蜂鸣器。</li>
<li>智能节点电路设计:设计电源管理电路,确保所有传感器和控制组件能正常工作。</li>
<li>LoRa 模块集成:将 LoRa 模块集成到智能节点中,实现数据的无线传输。</li>
</ul>
<p><b>4.2 软件开发</b></p>
<ol>
<li><p>智能节点程序:</p>
<ul>
<li>编写代码获取传感器数据。</li>
<li>控制风扇及 LED 指示灯。</li>
<li>实现手动与自动模式的切换。</li>
<li>实现 LoRa 数据发送功能。</li>
</ul>
</li>
</ol>
<ul>
<li>4G 网关节点程序:<ul>
<li>接收 LoRa 数据并通过 4G 网络上传至 MOTT 服务器。</li>
</ul>
</li>
<li>MOTT 服务器开发:<ul>
<li>提供数据存储和远程管理接口(API)。</li>
</ul>
</li>
<li>应用软件开发:<ul>
<li>实现用户登录、手动控制、自动控制、数据展示等功能。</li>
</ul>
</li>
</ul>
<p><b>4.3 系统集成与测试</b></p>
<ul>
<li>硬件集成:将传感器、LoRa 模块、显示屏等硬件连接至智能节点和 4G 网关节点。</li>
<li>功能测试:测试手动模式、自动模式、数据传输功能等。</li>
<li>性能优化:确保系统在实时数据传输和控制中的稳定性。</li>
</ul>
<p><b>4.4 部署与维护</b></p>
<ul>
<li>部署智能节点和 4G 网关:将系统部署到实际农业环境中,进行现场测试和调试。</li>
<li>维护与更新:定期检查系统,进行必要的功能更新和优化。</li>
</ul>
]]></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[ <p>使用外置设备,通过串口发送和接收数据。那么,就要在php端有个串口的操作代码。<br>PHP 的 dio 扩展(Direct I/O)提供了对底层 I/O 操作的访问,包括串口通信。通过 dio 扩展,你可以直接操作串口设备文件(如 /dev/ttyUSB0 或 COM1)来实现串口通信。<br> <span id="more"></span></p>
<h3 id="1-安装-dio-扩展"><a href="#1-安装-dio-扩展" class="headerlink" title="1. 安装 dio 扩展"></a>1. 安装 dio 扩展</h3><p>dio 扩展是 PHP 的一个 PECL 扩展。你可以通过以下步骤安装:</p>
<p>在 Linux 上安装:<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get install php-dev # 安装 PHP 开发工具</span><br><span class="line">sudo pecl install dio # 安装 dio 扩展</span><br><span class="line"></span><br></pre></td></tr></table></figure></p>
<p>安装完成后,在 php.ini 文件中启用扩展:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">extension=dio.so</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3 id="2-使用-dio-实现串口通信"><a href="#2-使用-dio-实现串口通信" class="headerlink" title="2. 使用 dio 实现串口通信"></a>2. 使用 dio 实现串口通信</h3><p>以下是一个使用 dio 扩展实现串口通信的示例代码:<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">&lt;?php</span><br><span class="line">// 串口设备路径</span><br><span class="line">$device = &#x27;/dev/pts/4&#x27;; // Linux</span><br><span class="line">// $device = &#x27;COM1&#x27;; // Windows</span><br><span class="line"></span><br><span class="line">// 打开串口设备</span><br><span class="line">$fd = dio_open($device, O_RDWR | O_NOCTTY | O_NONBLOCK);</span><br><span class="line"></span><br><span class="line">if (!$fd) &#123;</span><br><span class="line"> die(&quot;无法打开串口设备: $device\n&quot;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// 配置串口参数</span><br><span class="line">dio_tcsetattr($fd, [</span><br><span class="line"> &#x27;baud&#x27; =&gt; 9600, // 波特率</span><br><span class="line"> &#x27;bits&#x27; =&gt; 8, // 数据位</span><br><span class="line"> &#x27;stop&#x27; =&gt; 1, // 停止位</span><br><span class="line"> &#x27;parity&#x27; =&gt; 0, // 校验位 (0: none, 1: odd, 2: even)</span><br><span class="line"> &#x27;flow_control&#x27; =&gt; 0, // 流控制 (0: none, 1: hardware)</span><br><span class="line">]);</span><br><span class="line"></span><br><span class="line">// 发送数据到串口</span><br><span class="line">$message = &quot;你好我在https://erik.xyz上出生了!&quot;;</span><br><span class="line">dio_write($fd, $message);</span><br><span class="line">echo &quot;已发送: $message&quot;;</span><br><span class="line"></span><br><span class="line">// 从串口读取数据</span><br><span class="line">$data = dio_read($fd, 1024); // 读取最多 1024 字节</span><br><span class="line">echo &quot;已接收: $data\n&quot;;</span><br><span class="line"></span><br><span class="line">// 关闭串口</span><br><span class="line">dio_close($fd);</span><br><span class="line">?&gt;</span><br><span class="line"></span><br></pre></td></tr></table></figure></p>
<h3 id="3-代码说明"><a href="#3-代码说明" class="headerlink" title="3. 代码说明"></a>3. 代码说明</h3><ul>
<li><p>dio_open: 打开串口设备文件。O_RDWR 表示以读写模式打开,O_NOCTTY 表示不将设备作为控制终端,O_NONBLOCK 表示非阻塞模式。</p>
</li>
<li><p>dio_tcsetattr: 配置串口参数,包括波特率、数据位、停止位、校验位和流控制。</p>
</li>
<li><p>dio_write: 向串口写入数据。</p>
</li>
<li><p>dio_read: 从串口读取数据。</p>
</li>
<li><p>dio_close: 关闭串口设备。</p>
</li>
</ul>
<h3 id="4-串口参数配置"><a href="#4-串口参数配置" class="headerlink" title="4. 串口参数配置"></a>4. 串口参数配置</h3><ul>
<li><p>dio_tcsetattr 的配置选项:</p>
</li>
<li><p>baud: 波特率(如 9600、19200、38400、57600、115200)。</p>
</li>
<li><p>bits: 数据位(通常为 8)。</p>
</li>
<li><p>stop: 停止位(1 或 2)。</p>
</li>
<li><p>parity: 校验位(0: 无校验,1: 奇校验,2: 偶校验)。</p>
</li>
<li><p>flow_control: 流控制(0: 无流控制,1: 硬件流控制)。</p>
</li>
</ul>
<h2 id="那么这时候需要测试一下代码。那总不能真的找个串口设备吧,然而虚拟串口真香。"><a href="#那么这时候需要测试一下代码。那总不能真的找个串口设备吧,然而虚拟串口真香。" class="headerlink" title="那么这时候需要测试一下代码。那总不能真的找个串口设备吧,然而虚拟串口真香。"></a>那么这时候需要测试一下代码。那总不能真的找个串口设备吧,然而虚拟串口真香。</h2><h3 id="5-在Linux中使用-socat-模拟虚拟串口"><a href="#5-在Linux中使用-socat-模拟虚拟串口" class="headerlink" title="5.在Linux中使用 socat 模拟虚拟串口"></a>5.在Linux中使用 socat 模拟虚拟串口</h3><p>socat 是一个强大的工具,可以创建虚拟串口对。</p>
<p>安装 socat:<br>在Debian/Ubuntu系统上:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo apt update</span><br><span class="line">sudo apt install socat</span><br></pre></td></tr></table></figure>
<p>创建虚拟串口对:<br>运行以下命令创建一对虚拟串口:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">socat -d -d pty,raw,echo=0 pty,raw,echo=0</span><br></pre></td></tr></table></figure>
<p>运行后如图:<br><img src="/img/2024/20250103151316.png" alt="https://erik.xyz"><br>这样可以看到出现两个虚拟串口。<br>把上面的php代码放到文件中运行一下:<br><img src="/img/2024/20250103151620.png" alt="https://erik.xyz"></p>
<p>同时新开个窗口执行:<code>cat /dev/pts/5</code>来读取串口数据。<br>如下图:<br><img src="/img/2024/20250103151927.png" alt="https://erik.xyz"></p>
<p>这里是发送数据,那接收数据怎么看呢。<br>那就在代码上改造一下加个for:<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">// 从串口读取数据</span><br><span class="line">$data = dio_read($fd, 1024); // 读取最多 1024 字节</span><br><span class="line">echo &quot;已接收: $data\n&quot;;</span><br></pre></td></tr></table></figure><br>这里改造主要是测试用,实际上不需要。<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">//测试接收</span><br><span class="line">for($i=0;$i&lt;20;$i++)&#123;</span><br><span class="line"> sleep(3);</span><br><span class="line">// 从串口读取数据</span><br><span class="line">$data = dio_read($fd, 1024); // 读取最多 1024 字节</span><br><span class="line">echo &quot;已接收: $data\n&quot;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>那么,再次运行php代码,然后在新窗口运行<code>echo &quot;欢迎你出生在https://erik.xyz&quot; &gt; /dev/pts/5</code>来发送信息,你会看到如图的接收:<br><img src="/img/2024/20250103152421.png" alt="https://erik.xyz"></p>
<p>到这里,发送和接收串口已经好了。</p>
]]></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[ <h4 id="1-搭建-Maven-私有仓库"><a href="#1-搭建-Maven-私有仓库" class="headerlink" title="1. 搭建 Maven 私有仓库"></a>1. 搭建 Maven 私有仓库</h4><p>首先,需要在内网环境中搭建一个 Maven 仓库,常用的私有 Maven 仓库工具有:</p>
<ul>
<li>Nexus Repository:Sonatype Nexus 是最流行的私有 Maven 仓库管理工具。</li>
<li>Artifactory:JFrog Artifactory 是另一种流行的构建管理工具,提供了私有仓库的支持。</li>
<li>Apache Archiva:Apache Archiva 也是一个支持 Maven 的仓库管理工具。</li>
</ul>
<p>以下是搭建 Nexus Repository 的简单步骤:<br><span id="more"></span></p>
<p>1.1 安装 Nexus Repository</p>
<ul>
<li><p>下载 Nexus: 访问 Nexus Repository 下载页面 下载 Nexus OSS 版本。</p>
</li>
<li><p>解压并启动: 解压下载的压缩包并启动 Nexus。</p>
</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">cd /opt/nexus/bin</span><br><span class="line">./nexus start</span><br></pre></td></tr></table></figure>
<ul>
<li>访问 Nexus UI: 打开浏览器,访问 Nexus 的默认地址:</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://localhost:8081</span><br></pre></td></tr></table></figure>
<p>默认用户名为 admin,密码为 admin123,可以在首次登录后修改密码。</p>
<p>1.2 配置 Maven 仓库</p>
<p>在 Nexus UI 中,你可以创建一个新的 Maven 仓库。创建仓库后,你可以上传公司的内部依赖、插件和构建工件。</p>
<h4 id="2-配置-Maven-使用内网仓库"><a href="#2-配置-Maven-使用内网仓库" class="headerlink" title="2. 配置 Maven 使用内网仓库"></a>2. 配置 Maven 使用内网仓库</h4><p>配置 Maven 使用内网仓库需要修改 settings.xml 文件。</p>
<p>2.1 修改 settings.xml</p>
<p>在 Maven 的 settings.xml 文件中,配置私有仓库的地址和认证信息。settings.xml 文件通常位于 ~/.m2/ 目录下(用户级别配置)或者 ${MAVEN_HOME}/conf/ 目录下(全局配置)。</p>
<p>以下是配置内网 Maven 仓库的示例:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">&lt;settings xmlns=&quot;http://maven.apache.org/SETTINGS/1.0.0&quot;</span><br><span class="line"> xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span><br><span class="line"> xsi:schemaLocation=&quot;http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd&quot;&gt;</span><br><span class="line"></span><br><span class="line"> &lt;mirrors&gt;</span><br><span class="line"> &lt;!-- 配置私有仓库镜像 --&gt;</span><br><span class="line"> &lt;mirror&gt;</span><br><span class="line"> &lt;id&gt;nexus&lt;/id&gt;</span><br><span class="line"> &lt;mirrorOf&gt;external:http://central&lt;/mirrorOf&gt;</span><br><span class="line"> &lt;url&gt;http://your-nexus-server:8081/repository/maven-public/&lt;/url&gt;</span><br><span class="line"> &lt;blocked&gt;false&lt;/blocked&gt;</span><br><span class="line"> &lt;/mirror&gt;</span><br><span class="line"> &lt;/mirrors&gt;</span><br><span class="line"></span><br><span class="line"> &lt;repositories&gt;</span><br><span class="line"> &lt;repository&gt;</span><br><span class="line"> &lt;id&gt;internal-repo&lt;/id&gt;</span><br><span class="line"> &lt;url&gt;http://your-nexus-server:8081/repository/maven-releases/&lt;/url&gt;</span><br><span class="line"> &lt;snapshots&gt;</span><br><span class="line"> &lt;enabled&gt;false&lt;/enabled&gt;</span><br><span class="line"> &lt;/snapshots&gt;</span><br><span class="line"> &lt;/repository&gt;</span><br><span class="line"> &lt;/repositories&gt;</span><br><span class="line"></span><br><span class="line"> &lt;pluginRepositories&gt;</span><br><span class="line"> &lt;pluginRepository&gt;</span><br><span class="line"> &lt;id&gt;internal-plugins&lt;/id&gt;</span><br><span class="line"> &lt;url&gt;http://your-nexus-server:8081/repository/maven-plugins/&lt;/url&gt;</span><br><span class="line"> &lt;/pluginRepository&gt;</span><br><span class="line"> &lt;/pluginRepositories&gt;</span><br><span class="line"></span><br><span class="line"> &lt;servers&gt;</span><br><span class="line"> &lt;!-- 配置 Maven 仓库认证 --&gt;</span><br><span class="line"> &lt;server&gt;</span><br><span class="line"> &lt;id&gt;nexus&lt;/id&gt;</span><br><span class="line"> &lt;username&gt;your-nexus-username&lt;/username&gt;</span><br><span class="line"> &lt;password&gt;your-nexus-password&lt;/password&gt;</span><br><span class="line"> &lt;/server&gt;</span><br><span class="line"> &lt;/servers&gt;</span><br><span class="line">&lt;/settings&gt;</span><br></pre></td></tr></table></figure>
<p>2.2 配置镜像和仓库</p>
<ul>
<li><p>镜像(Mirror):在 <mirrors> 标签中配置私有仓库的 URL,将 Maven 的中央仓库或其他公共仓库的请求代理到私有仓库中。通过 mirrorOf 配置来选择代理哪些仓库(external:<a href="http://central">http://central</a> 表示代理所有外部仓库)。</p>
</li>
<li><p>仓库(Repository):在 <repositories> 和 <pluginRepositories> 标签中配置你的内网仓库的 URL。</p>
</li>
<li><p>认证(Server Authentication):在 <servers> 标签中配置内网仓库的认证信息(如果仓库需要认证)。</p>
</li>
</ul>
<h4 id="3-配置项目使用内网仓库"><a href="#3-配置项目使用内网仓库" class="headerlink" title="3. 配置项目使用内网仓库"></a>3. 配置项目使用内网仓库</h4><p>在项目的 pom.xml 文件中,通常不需要额外配置仓库,因为 Maven 会使用 settings.xml 中配置的内网仓库。但是如果需要强制指定某个仓库,可以在 pom.xml 中配置 <repositories> 标签:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;repositories&gt;</span><br><span class="line"> &lt;repository&gt;</span><br><span class="line"> &lt;id&gt;nexus-repo&lt;/id&gt;</span><br><span class="line"> &lt;url&gt;http://your-nexus-server:8081/repository/maven-releases/&lt;/url&gt;</span><br><span class="line"> &lt;/repository&gt;</span><br><span class="line">&lt;/repositories&gt;</span><br></pre></td></tr></table></figure>
<h4 id="4-上传和下载依赖"><a href="#4-上传和下载依赖" class="headerlink" title="4. 上传和下载依赖"></a>4. 上传和下载依赖</h4><p>4.1 上传依赖到内网仓库<br>你可以通过 Maven 命令将本地构建的 JAR 文件上传到内网仓库。例如,将某个 JAR 上传到 Nexus:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">mvn deploy:deploy-file \</span><br><span class="line"> -DgroupId=com.example \</span><br><span class="line"> -DartifactId=my-artifact \</span><br><span class="line"> -Dversion=1.0.0 \</span><br><span class="line"> -Dpackaging=jar \</span><br><span class="line"> -Dfile=path/to/your-artifact.jar \</span><br><span class="line"> -DrepositoryId=nexus \</span><br><span class="line"> -Durl=http://your-nexus-server:8081/repository/maven-releases/</span><br></pre></td></tr></table></figure>
<p>4.2 从内网仓库下载依赖</p>
<p>配置好内网仓库后,Maven 会自动从内网仓库下载依赖。如果仓库中没有该依赖,Maven 会尝试从其他配置的仓库下载。</p>
<h4 id="5-使用私有仓库中的依赖"><a href="#5-使用私有仓库中的依赖" class="headerlink" title="5. 使用私有仓库中的依赖"></a>5. 使用私有仓库中的依赖</h4><p>一旦仓库配置好,Maven 将会从配置的内网仓库下载依赖。你可以在项目的 pom.xml 中正常引用依赖,Maven 会自动从私有仓库中拉取。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&lt;dependencies&gt;</span><br><span class="line"> &lt;dependency&gt;</span><br><span class="line"> &lt;groupId&gt;com.example&lt;/groupId&gt;</span><br><span class="line"> &lt;artifactId&gt;my-artifact&lt;/artifactId&gt;</span><br><span class="line"> &lt;version&gt;1.0.0&lt;/version&gt;</span><br><span class="line"> &lt;/dependency&gt;</span><br><span class="line">&lt;/dependencies&gt;</span><br></pre></td></tr></table></figure>
<h4 id="6-配置镜像以提高构建速度(可选)"><a href="#6-配置镜像以提高构建速度(可选)" class="headerlink" title="6. 配置镜像以提高构建速度(可选)"></a>6. 配置镜像以提高构建速度(可选)</h4><p>为了提高构建速度,你可以配置 settings.xml 来使用私有仓库作为 Maven 的默认镜像,确保所有的构建依赖都从私有仓库中拉取,避免每次访问外部仓库,降低构建时间。</p>
]]></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[ <h4 id="1-准备环境"><a href="#1-准备环境" class="headerlink" title="1. 准备环境"></a>1. 准备环境</h4><p>确保内网中的服务器或机器具备运行 Spring Boot 应用的基本环境:</p>
<ul>
<li>JDK:确保服务器安装了合适版本的 JDK(通常建议使用 Java 8 及以上版本)。</li>
<li>Maven/Gradle:根据项目使用的构建工具安装 Maven 或 Gradle。</li>
<li>数据库:如果应用需要连接数据库,确保数据库在内网中可访问,且连接配置正确。</li>
</ul>
<span id="more"></span>
<h4 id="2-编译-Spring-Boot-应用"><a href="#2-编译-Spring-Boot-应用" class="headerlink" title="2. 编译 Spring Boot 应用"></a>2. 编译 Spring Boot 应用</h4><p>首先,你需要编译你的 Spring Boot 应用,生成可执行的 JAR 文件。可以通过以下命令在项目目录下执行:</p>
<ul>
<li>Maven 构建命令:</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mvn clean package</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<ul>
<li>Gradle 构建命令:</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./gradlew build</span><br></pre></td></tr></table></figure>
<p>这会在 target/ 或 build/libs/ 目录下生成一个可执行的 JAR 文件,通常名为 your-application-name.jar。</p>
<h4 id="3-传输-JAR-到内网服务器"><a href="#3-传输-JAR-到内网服务器" class="headerlink" title="3. 传输 JAR 到内网服务器"></a>3. 传输 JAR 到内网服务器</h4><p>将生成的 JAR 文件上传到内网的目标服务器。可以使用各种文件传输工具,比如:</p>
<ul>
<li>SCP(如果服务器支持 SSH):</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scp your-application-name.jar user@server-ip:/path/to/deploy/</span><br></pre></td></tr></table></figure>
<ul>
<li>FTP 或 SFTP(如果有配置 FTP 服务)。</li>
</ul>
<h4 id="4-配置-Spring-Boot-应用"><a href="#4-配置-Spring-Boot-应用" class="headerlink" title="4. 配置 Spring Boot 应用"></a>4. 配置 Spring Boot 应用</h4><p>在内网部署时,你可能需要根据环境修改配置文件,比如 application.properties 或 application.yml。常见的配置项包括:</p>
<ul>
<li>数据库连接信息(spring.datasource.url、spring.datasource.username 等)。</li>
<li>日志配置。</li>
<li>服务端口(server.port)。</li>
<li>安全设置(如禁用外部访问,或者设置访问白名单等)。</li>
</ul>
<p>例如,修改 application.properties 中的数据库配置:<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">spring.datasource.url=jdbc:mysql://localhost:3306/your_db</span><br><span class="line">spring.datasource.username=db_user</span><br><span class="line">spring.datasource.password=db_password</span><br></pre></td></tr></table></figure></p>
<h4 id="5-启动-Spring-Boot-应用"><a href="#5-启动-Spring-Boot-应用" class="headerlink" title="5. 启动 Spring Boot 应用"></a>5. 启动 Spring Boot 应用</h4><p>在内网服务器上,使用以下命令启动 Spring Boot 应用:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java -jar your-application-name.jar</span><br></pre></td></tr></table></figure>
<p>如果你希望应用在后台运行,可以使用 nohup 或者类似的工具:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nohup java -jar your-application-name.jar &gt; output.log 2&gt;&amp;1 &amp;</span><br></pre></td></tr></table></figure>
<p>这会将输出重定向到 output.log 文件,并让应用在后台运行。</p>
<h4 id="6-配置防火墙和网络"><a href="#6-配置防火墙和网络" class="headerlink" title="6. 配置防火墙和网络"></a>6. 配置防火墙和网络</h4><ul>
<li>确保服务器的防火墙允许访问应用所绑定的端口(默认是 8080)。如果使用其他端口,可以在防火墙中配置允许访问该端口。</li>
<li>如果 Spring Boot 应用需要通过内网的特定 IP 地址或域名访问,确保 DNS 或 hosts 配置正确。</li>
</ul>
<h4 id="7-监控和日志"><a href="#7-监控和日志" class="headerlink" title="7. 监控和日志"></a>7. 监控和日志</h4><ul>
<li>日志:Spring Boot 应用会将日志输出到控制台,你可以将日志配置为输出到文件中进行持久化存储。常见做法是在 application.properties 或 application.yml 中设置日志路径:</li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">logging.file.name=/path/to/logs/application.log</span><br></pre></td></tr></table></figure>
<ul>
<li>监控:可以使用 Spring Boot 的 Actuator 或其他监控工具(如 Prometheus 和 Grafana)来监控应用的运行状态。</li>
</ul>
<h4 id="8-设置开机启动(可选)"><a href="#8-设置开机启动(可选)" class="headerlink" title="8. 设置开机启动(可选)"></a>8. 设置开机启动(可选)</h4><p>如果希望应用在服务器重启时自动启动,可以使用 systemd(Linux 系统)或配置为 Windows 服务。</p>
<p>Linux 系统 (Systemd)</p>
<p>创建一个 systemd 服务文件,如 /etc/systemd/system/yourapp.service:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">[Unit]</span><br><span class="line">Description=Spring Boot Application</span><br><span class="line">After=network.target</span><br><span class="line"></span><br><span class="line">[Service]</span><br><span class="line">User=your_user</span><br><span class="line">ExecStart=/usr/bin/java -jar /path/to/your-application-name.jar</span><br><span class="line">SuccessExitStatus=143</span><br><span class="line">Restart=always</span><br><span class="line"></span><br><span class="line">[Install]</span><br><span class="line">WantedBy=multi-user.target</span><br></pre></td></tr></table></figure>
<p>然后启用并启动服务:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl enable yourapp</span><br><span class="line">sudo systemctl start yourapp</span><br></pre></td></tr></table></figure> ]]></description>
</item>
<item>
<guid isPermalink="true">https://erik.xyz/2024/11/15/deepin-not-login/</guid>
<title>deepin无法登录报错emergency mode</title>
<link>https://erik.xyz/2024/11/15/deepin-not-login/</link>
<category>deepin</category>
<category>emergency</category>
<category>emergency mode</category>
<category>deepin黑屏</category>
<pubDate>Fri, 15 Nov 2024 10:56:00 +0800</pubDate>
<description><![CDATA[ <p>由于系统重启后,新安装的微信一直弹出框,卡在那难受。立马强制重启,开机后就一直显示You are in emergency mode……一堆东西,意思是说让进入root用户,查看报错并修复。</p>
<p> 有点头大了。</p>
<p> 系统命令可以显示,图形界面不显示。<br> <span id="more"></span><br> 果断拿出安装系统的u盘,使用u盘启动进入安装界面,按ctrl+alt+f4进入u盘命令界面。输入startx进入u盘图形界面。这下可以看到电脑的挂着盘。根据挂着的盘一个个找根目录盘。找到后使用u盘系统的命令窗口:<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo vi /etc/fstab</span><br><span class="line"></span><br></pre></td></tr></table></figure></p>
<p> 注释掉home目录,重启系统。</p>
<p> 重启后会显示个登录图形界面。这个是假的,没用。按ctrl+alt+f2进入命令界面。<br> 使用账号密码登录命令界面。</p>
<p> 打开/etc/fstab文件取消home注释。(这里要看一下home挂载路径,比如我的/dev/sda6)</p>
<p> 使用以下命令修复:<br> <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo e2fsck /dev/sda6</span><br></pre></td></tr></table></figure></p>
<p> 你会看到一堆修复,一个个确认后,等待修复完成就重启系统。</p>
]]></description>
</item>
<item>
<guid isPermalink="true">https://erik.xyz/2024/11/11/mysql-redis-consistency/</guid>
<title>如何下保证MySQL数据库与Redis缓存数据一致性?</title>
<link>https://erik.xyz/2024/11/11/mysql-redis-consistency/</link>
<category>mysql</category>
<category>redis</category>
<category>数据一致性</category>
<pubDate>Mon, 11 Nov 2024 09:58:00 +0800</pubDate>
<description><![CDATA[ <p>有时候感觉MySQL我们懂了,Redis我们懂了,但是面试的时候一直答不好,经常被难住,问题在哪呢?</p>
<p>答案是:面试官考的不是专项能力,而是多项技术结合应用能力。</p>
<p>就拿<strong>并发场景下如何保证MySQL与Redis缓存一致性?</strong>这个面试官常见的拷打考点举例。</p>
<p>对于读多写少并且要求高性能的业务逻辑,我们通常在应用服务器访问MySQL数据库的中间加上一层<strong>Redis缓存层</strong>,以提高数据的查询效率,减轻MySQL数据库的压力,避免在MySQL出现性能瓶颈。<br><span id="more"></span><br><img src="/img/2024/2024111101.png" alt="https://erik.xyz"></p>
<p>该问题,如果在数据存储后,只读场景下是不会出现MySQL与Redis缓存的一致性问题的,所以真正需要考虑的是<strong>并发读写场景</strong>下的数据一致性问题。</p>
<p>如果我们不加分析,单独利用MySQL和Redis的知识进行回答并发场景下如何保证MySQL与Redis缓存一致性?很难把这个问题回答好,因为看起来很简单的方案实际上是漏洞百出的。</p>
<h4 id="简单方案下的漏洞百出"><a href="#简单方案下的漏洞百出" class="headerlink" title="简单方案下的漏洞百出"></a>简单方案下的漏洞百出</h4><p>我们先看下简单的更新数据库、删除缓存和更新缓存方案下,会出现什么问题?</p>
<p><img src="/img/2024/2024111102.png" alt="https://erik.xyz"></p>
<h4 id="更新缓存,再更新数据库"><a href="#更新缓存,再更新数据库" class="headerlink" title="更新缓存,再更新数据库"></a>更新缓存,再更新数据库</h4><p>先说结论:不考虑。</p>
<p>原因是更新缓存成功后,数据库可能更新失败,出现数据库为旧值,缓存为新值。导致后续的所有的读请求,在缓存未过期或缓存未重新正确更新的情况下,会一直保持了数据的完全不一致!并且当前数据库中的值为旧值,而业务数据的正确性应该以数据库的为准。</p>
<p>那么如果更新缓存成功后,数据库可能更新失败,我们<strong>重新更新缓存</strong>是不是可以了?</p>
<p><img src="/img/2024/2024111103.png" alt="https://erik.xyz"></p>
<p>抛开需要重新更新缓存时,要单表或多表重新查询数据,再更新数据带来的性能问题,还可能期间有数据变更再次陷入脏数据的情况。实际上仍然还是会出现并发一致性问题。</p>
<p>只要缓存进行了更新,后续的读请求<strong>在更新数据库前、更新数据库失败并准备更新缓存前</strong>,基本上都能命中缓存情况,而这时返回的数据都是未落库的脏数据。</p>
<p><img src="/img/2024/2024111104.png" alt="https://erik.xyz"></p>
<h4 id="更新数据库,再更新缓存"><a href="#更新数据库,再更新缓存" class="headerlink" title="更新数据库,再更新缓存"></a>更新数据库,再更新缓存</h4><p>不考虑。</p>
<p>原因是当数据库更新成功后,缓存更新失败,出现数据库为最新值,缓存为旧值。导致后续的所有的读请求,在缓存未过期或缓存未重新正确更新的情况下,会一直保持了数据的完全不一致!</p>
<p><img src="/img/2024/2024111105.png" alt="https://erik.xyz"></p>
<p>该方案就算在更新数据库、更新缓存都成功的情况下,还是会存在并发引发的一致性问题,如下图所示(点击图片查看大图):<br><img src="/img/2024/2024111106.png" alt="https://erik.xyz"></p>
<p>可以看到在并发多写多读的场景下数据存在的不一致性问题。</p>
<h4 id="先删除缓存,再更新数据库"><a href="#先删除缓存,再更新数据库" class="headerlink" title="先删除缓存,再更新数据库"></a>先删除缓存,再更新数据库</h4><p>不考虑,但是通过使用<strong>延时双删策略</strong>后可以考虑。</p>
<p>采用“<strong>先删除缓存,再更新数据库</strong>”的方案是一种常见的方法来尝试解决这个问题的策略。</p>
<p>这种方法逻辑较为简单,易于理解和实现,理论上删除旧缓存后,下次读取时将从数据库获取最新数据。</p>
<p>但在并发的极端情况下,删除缓存成功后,如果再有大量的并发请求进来,那么便会直接请求到数据库中,对数据库造成巨大的压力。而且此方案还是可能会发生数据不一致性问题。</p>
<p><img src="/img/2024/2024111107.png" alt="https://erik.xyz"></p>
<p>通过上图发现在删除缓存后,如果有并发读请求1.1进来,那么查询缓存肯定是不存在,则去读取数据库,但因为此时更新数据库x=10的操作2.更新数据库还未完成,所以读取到的仍然是旧值x=5并设置缓存后,在2.更新数据库完成后,数据是新值10,而缓存是旧值,造成了数据不一致的问题。</p>
<p>对此我们可以先进行一波的小优化,那就是<strong>延时双删策略</strong>。即在更新数据库之后,先延迟等待一下(等待时间参考该读请求的响应时间+几十毫秒),再继续删除缓存。这样做的目的是确保读请求结束(已经在1.2读库中读取到了旧数据,后续会在该请求中更新缓存),写请求可以删除读请求造成的缓存脏数据,保证再删除缓存之后的所有读请求都能读到最新值。</p>
<p><img src="/img/2024/2024111108.png" alt="https://erik.xyz"></p>
<p>可以看出此优化方案关键点在于等待多长时间后,再次删除缓存尤为重要,但是这个时间都是根据历史查询请求的响应时间判断的,实际情况会有浮动。这也导致如果等待的延时时间过短,则仍然会出现数据不一致的情况;等待延迟时间过长,则导致延迟期间出现数据不一致的时间变长。</p>
<p>另外<strong>延时双删策略</strong>还需要考虑如果再次删除缓存失败的情况如何处理?</p>
<p>因为删除失败将导致后续的所有的读请求,在缓存未过期或缓存未重新正确更新的情况下,会一直保持了数据的完全不一致!这个在下文的技术优化方案继续讨论。</p>
<h4 id="先更新数据库,再删除缓存"><a href="#先更新数据库,再删除缓存" class="headerlink" title="先更新数据库,再删除缓存"></a>先更新数据库,再删除缓存</h4><p>比较推荐。</p>
<p>采用的“先更新数据库,再删除缓存”策略,跟“先删除缓存,再更新数据库”中我们进行<strong>延时双删策略</strong>的小优化基本一样,仍然需要考虑删除缓存失败的情况如何处理。</p>
<p>单纯从“先更新数据库,再删除缓存”和“先删除缓存,再更新数据库”对比起来。在大多数情况下,“先更新数据库,再删除缓存”被认为是一个更好的选择,原因如下:</p>
<p>1.<strong>数据的一致性</strong>:这种方法更倾向于保持数据的最终一致性,即使缓存删除失败,也能保证数据的一致性不会长期受损。</p>
<p>2.<strong>用户体验</strong>:在“先删除缓存,再更新数据库”的情况下,如果数据库更新失败,用户可能会一直看到旧数据,直到缓存过期。相比之下,“先更新数据库,再删除缓存”可以在某种程度上避免这种情况。</p>
<p>但该方案同样也会出现数据不一致性问题,如下图所示。</p>
<p><img src="/img/2024/2024111109.png" alt="https://erik.xyz"></p>
<p>当数据库的数据被更新后,缓存也被删除。接下来的出现读请求3.1和写请求3.2同时进来。</p>
<p>读请求先读了缓存发现缓存无命中,则查询数据库并在准备更新缓存时,3.2写请求已经完成了数据的更新和删除缓存的动作,之后3.1读请求才更新了缓存。最后导致了数据库中的值未新值,缓存中的值为旧值。</p>
<h4 id="优化后方案"><a href="#优化后方案" class="headerlink" title="优化后方案"></a>优化后方案</h4><p>从上面的简单方案方案中,似乎没有一种方案真正能解决并发场景下MySQL数据与Redis缓存数据一致性的问题。</p>
<p>这里有个说明下,如果业务要求必须要满足<strong>强一致性</strong>,那么不管如何优化缓存策略,都无法满足,而最好的办法是不用缓存。</p>
<p>强一致性:它要求系统写入什么,读出来的也会是什么,用户体验好,但实现起来往往对系统的性能影响大。</p>
<p>解决方案是读写串行化,而此方案会大大增加系统的处理效率,吞吐量也会大大降低。</p>
<p>另外在大型分布式系统中,其实分布式事务大多数情况都不会使用,因为维护成本太高了、复杂度也高。所以在分布式系统,我们一般都会推崇最终一致性,即这种一致性级别约束了系统在写入成功后,不承诺立即可以读到写入的值,也不承诺多久之后数据能够达到一致,但会尽可能地保证到某个时间级别(比如秒级别)后,数据能够达到一致状态。</p>
<p>现在我们接着继续优化..</p>
<h4 id="延迟双删策略-重试机制"><a href="#延迟双删策略-重试机制" class="headerlink" title="延迟双删策略+重试机制"></a>延迟双删策略+重试机制</h4><p>从上面简单方案下的漏洞百出下的先删除缓存,再更新数据库中,我们可以看出来其实<strong>延迟双删策略</strong>,算是融合“先删除缓存,再更新数据库”和“先更新数据库,再删除缓存”的策略,可以解决大部分的数据一致性的业务逻辑处理问题。</p>
<p>但我们前面还遗留了一个待解决的问题:如果再次<strong>删除缓存失败的情况如何处理</strong>?</p>
<p>——-当然是补救去继续删除这个缓存Key了,而补救方法则是<strong>重试</strong>。</p>
<p><strong>重试机制</strong>可以在当前中启动新协程(Golang中属于用户态的轻量级线程)中进行重试;也可以放到消息队列中进行重试;还可以是先启动新协程重试3次,重试失败后继续放到消息队列中重试,如下图展示的是放到消息队列中进行重试。</p>
<p>新协程中进行重试需要注意的是使用的新上下文context.Background(),而不是当前请求的上下文。</p>
<p>一般消息队列会支持高可靠性的队列,例如 RabbitMQ、Kafka 等。这些消息队列提供了非常强的消息传递、异步处理和持久化功能,可以有效地解决数据同步的问题。</p>
<p><img src="/img/2024/2024111110.png" alt="https://erik.xyz"></p>
<p>此方案仍然存在一些需要,如:选择合适的延迟等待时间进行删除缓存;协程中重试删除缓存次数、间隔时间;消息队列中删除失败缓存失败后是否需要重试等。</p>
<h4 id="读取binlog异步删除缓存"><a href="#读取binlog异步删除缓存" class="headerlink" title="读取binlog异步删除缓存"></a>读取binlog异步删除缓存</h4><p>重试删除缓存机制还可以吧,就是会造成好多业务代码入侵。</p>
<p>其实,还可以这样优化:</p>
<p>1.通过Canal将binlog日志采集发送到MQ队列来异步淘汰key。</p>
<p>2.删除缓存的应用程序通过ACK手动机制确认处理这条更新消息,删除缓存,保证数据缓存一致性。</p>
<p><img src="/img/2024/2024111111.png" alt="https://erik.xyz"></p>
<p>异步淘汰key相比于等新对比缓存数据并更新会简单一些,因为可能一份缓存数据涉及多张表的数据查询、聚合、排序等。</p>
<p>尽管该方案看起来也不错了,但是因为引入额外的组件(如Canal、消息队列)复杂性增加了也不少,需要维护和监控这些组件的运行状态,保证组件运行正常。</p>
<h4 id="定时任务"><a href="#定时任务" class="headerlink" title="定时任务"></a>定时任务</h4><p>在某些业务场景的需求下,也可以通过定时任务的方式进行 Redis 和 MySQL 的数据同步。</p>
<p>具体做法是通过定时任务从 Redis 中读取数据,然后跟 MySQL 中的数据进行比对,如果 Redis 中数据有变化,则进行同步。</p>
<p><img src="/img/2024/2024111112.png" alt="https://erik.xyz"></p>