-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
545 lines (511 loc) · 56.1 KB
/
search.xml
File metadata and controls
545 lines (511 loc) · 56.1 KB
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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>10亿的订单数据用mysql怎么存储</title>
<url>/2021/06/17/%E9%9D%A2%E8%AF%95%E9%A2%98/10%E4%BA%BF%E7%9A%84%E8%AE%A2%E5%8D%95%E6%95%B0%E6%8D%AE%E7%94%A8mysql%E6%80%8E%E4%B9%88%E5%AD%98%E5%82%A8/</url>
<content><![CDATA[<p>很显然一个库一张表是不可能的<br><a id="more"></a><br>需要对表做水平切分,一张1千万,需要100张才能存10个亿</p>
]]></content>
<categories>
<category>面试题</category>
</categories>
<tags>
<tag>mysql</tag>
</tags>
</entry>
<entry>
<title>ElasticSearch 实现合并相似款商品</title>
<url>/2019/03/19/ElasticSearch/elasticsearch%20%E5%AE%9E%E7%8E%B0%E5%90%88%E5%B9%B6%E7%9B%B8%E4%BC%BC%E6%AC%BE%E5%95%86%E5%93%81/</url>
<content><![CDATA[<blockquote>
<p>需求背景</p>
</blockquote>
<p>商品列表页,有个合并相似款的筛选条件,何为相似款,商品同一档口,货号,价格,被称为相似款,原来已经有实现了合并功能,定时执行脚本,用mysql分组,查出合并后要显示的商品,设置缓存,es 构建的时候,读取缓存进行判断,存在设置合并字段为 1, 不存在设置为0,这样就可以为合并筛选提供查询条件,但是有个缺点,只能在全部商品中进行合并,不能根据条件进行筛选再合并,<br>偏偏产品经理就提出了要根据其它筛选条件进行合并的需求,这就意味着合并功能需要推翻重构,很麻烦,但天无绝人之路。<br><a id="more"></a></p>
<blockquote>
<p>探索一,尝试直接用 ES 进行多字段分组</p>
</blockquote>
<figure class="highlight xquery"><table><tr><td class="code"><pre><code class="hljs undefined"><br>利用 aggs top_hits , 直接进行分组查询<br><br>GET index/type/_search<br>{<br> <span class="hljs-string">"query"</span>: {<br> <span class="hljs-string">"match"</span>: {}<br> },<br> <span class="hljs-string">"aggs"</span>: {<br> <span class="hljs-string">"group"</span>: {<br> <span class="hljs-string">"terms"</span>: {<br> <span class="hljs-string">"field"</span>: <span class="hljs-string">"group_field"</span>,<br> <span class="hljs-string">"size"</span>: <span class="hljs-number">10</span><br> },<br> <span class="hljs-string">"aggs"</span>: {<br> <span class="hljs-string">"data"</span>: {<br> <span class="hljs-string">"top_hits"</span>: {}<br> }<br> }<br> }<br> }<br>}<br><br>问题:很明显,只能按一个字段进行分组,满足不了<br></code></pre></td></tr></table></figure>
<figure class="highlight routeros"><table><tr><td class="code"><pre><code class="hljs undefined"><br>利用<span class="hljs-built_in"> script </span>实现多字段分组查询<br><br><span class="hljs-builtin-name">GET</span> /product/goods/_search<br>{<br> <span class="hljs-string">"size"</span>: 0, <br> <span class="hljs-string">"aggs"</span>: {<br> <span class="hljs-string">"group"</span>: {<br> <span class="hljs-string">"terms"</span>: {<br> <span class="hljs-string">"script"</span>: <span class="hljs-string">"doc['sid'].value + doc['shop_dangkou_no'].value + doc['item_price_pifa']"</span><br> },<br> <span class="hljs-string">"aggs"</span>: {<br> <span class="hljs-string">"data"</span>: {<br> <span class="hljs-string">"top_hits"</span>: {<br><br> }<br> }<br> } <br> }<br> },<br> <span class="hljs-string">"query"</span>: {<br> <span class="hljs-string">"bool"</span>: {<br> <span class="hljs-string">"must"</span>: [<br><br> ]<br> }<br> }<br>}<br><br>问题:分组无法进行分页,大数据量分组查询占用内存较高<br></code></pre></td></tr></table></figure>
<blockquote>
<p>探索二, 尝试使用字段折叠功能</p>
</blockquote>
<figure class="highlight xquery"><table><tr><td class="code"><pre><code class="hljs undefined">{<br> <span class="hljs-string">"query"</span> : {<br> <br> },<br> <span class="hljs-string">"collapse"</span> : {<br> <span class="hljs-string">"field"</span> : <span class="hljs-string">"same_goods.keyword"</span> <br> }<br>}<br><br><br>经实践,完美的解决了分组,分页功能<br><br>问题:返回的total是query的总数,无法得知字段折叠后的总数量,分页还是有问题<br></code></pre></td></tr></table></figure>
<figure class="highlight xquery"><table><tr><td class="code"><pre><code class="hljs undefined"><br>分组统计一下<br><br>{<br> <span class="hljs-string">"query"</span> : {<br> <br> },<br> <span class="hljs-string">"aggs"</span>: {<br> <span class="hljs-string">"group"</span>: {<br> <span class="hljs-string">"cardinality"</span>: {<br> <span class="hljs-string">"field"</span>: <span class="hljs-string">"same_goods.keyword"</span><br> }<br> }<br> }<br><br>}<br><br>完美,解决总数量分页的问题<br></code></pre></td></tr></table></figure>
<blockquote>
<p>方案落实</p>
</blockquote>
<p>到此,利用字段折叠功能,可实现多字段合并,只需要构建一个字段,数据结构如下:</p>
<figure class="highlight dns"><table><tr><td class="code"><pre><code class="hljs undefined">same_goods: <span class="hljs-number">12345678</span>:<span class="hljs-number">23456543</span>:<span class="hljs-number">34456765</span><br><br>把相似的商品ID用冒号拼接,就可以用字段折叠筛选,后面如果有其它业务,需要知道宝贝的相似款,也方便查询<br></code></pre></td></tr></table></figure>
<blockquote>
<p>前台功能解决了,那如何构建这个字段呢</p>
</blockquote>
<p>mysql 只能分组无法,把相似款的商品都查出来,只能用程序,要根据档口,货号,价格字段进行分组,然后拼成上面的数据格式,设置到redis并设置过期时间定时更新,key为当前的商品ID, 只设置有相似款的宝贝,其它没有相似款的宝贝不需要设置</p>
<p>好了代码撸完,准备上线… 问题来了…</p>
<p>问题:一条sql查出所有上架的商品,执行时间十几秒,宝贝表几百万数据,进行全表扫描,实在没办法10分钟跑一次勉强也能接受,最忍无可忍的就是,程序遍历了一百多万的数据赋值给新数组,脚本直接把内存给跑满了,尝试优化脚本,效果不佳只能换种思路了</p>
<p>优化方案:</p>
<p>拆,以档口为单位进行查询,档口ID可以并中索引,查询速度相当的快,查询数据量少,程序分组拼接数据结构,就不影响性能了,写一个合并的控制器接口,接口档口ID参数(数组形式批量处理,查几个档口速度还是很快的),收到参数后,再丢异步任务去执行,把档口的相似款设置到reids, 如果数据有变商品被下架了,key不再更新会自动过期,程序开了20个worker同时处理,每一次调用10个档口,再写一个定时执行的脚本,查询所有档口,批量调用合并接口,经测试2分钟就跑完所有档口,内存观察比原来节省了90%开销</p>
]]></content>
<categories>
<category>ElasticSearch</category>
</categories>
</entry>
<entry>
<title>ElasticSearch 重建索引</title>
<url>/2019/03/14/ElasticSearch/ElasticSearch-%E9%87%8D%E5%BB%BA%E7%B4%A2%E5%BC%95/</url>
<content><![CDATA[<blockquote>
<p>创建新的索引和字段类型</p>
<p>设置索引副本数量</p>
</blockquote>
<p>导入数据时,要设置副本为0,数据导入完成后,记得副本设置为1</p>
<a id="more"></a>
<figure class="highlight xquery"><table><tr><td class="code"><pre><code class="hljs undefined">PUT my_index_v2/_settings<br>{<br><br> <span class="hljs-string">"settings"</span> : {<br> <span class="hljs-string">"index"</span> : {<br> <span class="hljs-string">"number_of_replicas"</span> : <span class="hljs-string">"1"</span><br> }<br> }<br>}<br></code></pre></td></tr></table></figure>
<blockquote>
<p>设置刷新时间禁用</p>
</blockquote>
<figure class="highlight awk"><table><tr><td class="code"><pre><code class="hljs undefined">默认为<span class="hljs-number">1</span>S<br><br>禁用刷新<br>PUT <span class="hljs-regexp">/my_index_v2/</span>_settings<br>{<br> <span class="hljs-string">"refresh_interval"</span>: <span class="hljs-string">"-1"</span><br>}<br><br><br>恢复<br>PUT <span class="hljs-regexp">/my_index_v2/</span>_settings<br>{<br> <span class="hljs-string">"refresh_interval"</span>: <span class="hljs-string">"1s"</span><br>}<br></code></pre></td></tr></table></figure>
<blockquote>
<p>reindex 开始重建索引</p>
</blockquote>
<p>数据量大一般会超时,但是任务还在执行,可以查看任务</p>
<figure class="highlight xquery"><table><tr><td class="code"><pre><code class="hljs undefined">POST _reindex<br>{<br> <span class="hljs-string">"source"</span>: {<br> <span class="hljs-string">"index"</span>: <span class="hljs-string">"my_index"</span><br> },<br> <span class="hljs-string">"dest"</span>: {<br> <span class="hljs-string">"index"</span>: <span class="hljs-string">"my_index_v2"</span><br> }<br>}<br></code></pre></td></tr></table></figure>
<blockquote>
<p>查看任务</p>
</blockquote>
<figure class="highlight routeros"><table><tr><td class="code"><pre><code class="hljs undefined"><span class="hljs-builtin-name">GET</span> _tasks?<span class="hljs-attribute">actions</span>=indices:data/write/reindex<br></code></pre></td></tr></table></figure>
<blockquote>
<p>取消任务</p>
</blockquote>
<figure class="highlight sqf"><table><tr><td class="code"><pre><code class="hljs undefined">POST <span class="hljs-variable">_tasks</span>/task_id:<span class="hljs-number">1</span>/<span class="hljs-variable">_cancel</span><br></code></pre></td></tr></table></figure>
<blockquote>
<p>创建索引别名</p>
</blockquote>
<p>最好一开始就设计好索引别名,如果怕索引被删除,字段类型自动生成,可为索引名称设置模板</p>
<figure class="highlight armasm"><table><tr><td class="code"><pre><code class="hljs undefined"><span class="hljs-symbol">PUT</span> /my_index_v2/_<span class="hljs-meta">alias</span>/my_index<br></code></pre></td></tr></table></figure>
<blockquote>
<p>查看别名</p>
</blockquote>
<figure class="highlight armasm"><table><tr><td class="code"><pre><code class="hljs undefined"><span class="hljs-symbol">GET</span> /my_index_v2/_<span class="hljs-meta">alias</span><span class="hljs-comment">/*<br><br>GET /*/</span>_<span class="hljs-meta">alias</span>/my_index<br></code></pre></td></tr></table></figure>
<blockquote>
<p>程序批量导入</p>
</blockquote>
<p>使用 scroll(游标) 批量查询,再批量导入</p>
<p>先根据条件初始化查询,拿到第一指数据和scroll id, 再根据 scroll id 一批一批的查询(好像越查越慢),再用bulk api批量写入</p>
]]></content>
<categories>
<category>ElasticSearch</category>
</categories>
</entry>
<entry>
<title>ElasticSearch 深度分页问题</title>
<url>/2019/03/14/ElasticSearch/ElasticSearch-%E6%B7%B1%E5%BA%A6%E5%88%86%E9%A1%B5%E9%97%AE%E9%A2%98/</url>
<content><![CDATA[<p>ES 默认分页,超出10000条会报</p>
<figure class="highlight coffeescript"><table><tr><td class="code"><pre><code class="hljs undefined">Result <span class="hljs-built_in">window</span> <span class="hljs-keyword">is</span> too large<br></code></pre></td></tr></table></figure>
<a id="more"></a>
<blockquote>
<p>解决问题</p>
</blockquote>
<figure class="highlight awk"><table><tr><td class="code"><pre><code class="hljs undefined">curl -XPUT http:<span class="hljs-regexp">//</span><span class="hljs-number">127.0</span>.<span class="hljs-number">0.1</span>:<span class="hljs-number">9200</span><span class="hljs-regexp">/my_index/</span>_settings -d <span class="hljs-string">'{ "index" : { "max_result_window" : 500000}}'</span><br><br>或用kibana<br><br>PUT <span class="hljs-regexp">/goods-weight-v2/</span>_settings<br>{<br> <span class="hljs-string">"index"</span>: {<br> <span class="hljs-string">"max_result_window"</span>: <span class="hljs-number">10000</span><br> }<br>}<br></code></pre></td></tr></table></figure>
<p>修改 max_result_window 的值</p>
<blockquote>
<p>原因</p>
</blockquote>
<p>ElasticSearch的默认深度分页机制有结不足,比如有6000条数据,当你仅想取第5000到6000条数据的时候,ES也会将前5000条数据加载到内存当中,所以ES为了避免用户的过大分页请求造成ES服务所在机器内存溢出,默认对深度分页的条数进行了限制,默认的最大条数是10000条,导致当获取第10000条数据的时候报Result window is too large异常。</p>
<blockquote>
<p>注意事项</p>
</blockquote>
<p>需要注意的问题,窗口值调大了后,虽然请求到分页的数据条数更多了,但它是用牺牲更多的内存、CPU资源来换取的。要考虑业务场景中过大的分页请求,是否会造成集群服务的内存溢出。</p>
]]></content>
<categories>
<category>ElasticSearch</category>
</categories>
</entry>
<entry>
<title>Linux 如何释放磁盘空间</title>
<url>/2019/03/14/Linux/Linux%20%E5%A6%82%E4%BD%95%E6%AD%A3%E7%A1%AE%E9%87%8A%E6%94%BE%E7%A3%81%E7%9B%98%E7%A9%BA%E9%97%B4/</url>
<content><![CDATA[<blockquote>
<p>正确清除日志姿势</p>
</blockquote>
<figure class="highlight sqf"><table><tr><td class="code"><pre><code class="hljs undefined"><span class="hljs-built_in">echo</span> <span class="hljs-string">''</span> > test.<span class="hljs-built_in">log</span><br></code></pre></td></tr></table></figure>
<blockquote>
<p> 查找已删除文件被占用</p>
</blockquote>
<a id="more"></a>
<figure class="highlight 1c"><table><tr><td class="code"><pre><code class="hljs undefined">lsof <span class="hljs-string">| grep deleted</span><br></code></pre></td></tr></table></figure>
<blockquote>
<p>释放空间</p>
</blockquote>
<p>kill 掉进程,或重启应用服务</p>
<figure class="highlight perl"><table><tr><td class="code"><pre><code class="hljs undefined">ps -aux | <span class="hljs-keyword">grep</span> pid<br><br><span class="hljs-keyword">kill</span> -<span class="hljs-number">9</span> pid1 pid2<br></code></pre></td></tr></table></figure>
<p>如果进程 kill 不掉,试试一次kill所有pid,或进程名</p>
]]></content>
<categories>
<category>Linux</category>
</categories>
</entry>
<entry>
<title>mysql大数据分库和分表 php解决方案</title>
<url>/2017/03/18/Mysql/mysql%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%88%86%E5%BA%93%E5%92%8C%E5%88%86%E8%A1%A8-php%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/</url>
<content><![CDATA[<p>1,主从复制,读写分离</p>
<p>对主库修改数据,查询使用从库。一主多从,来降低数据库读取压力。</p>
<p>2,分库分表</p>
<p>根据实体业务来分库,分表。如,根据数据的活跃性,根据用户uid等。</p>
<p>3,mysql 不同存储引擎区别</p>
<p>InnoDB 用于数据完整性/写性能要求比较高的应用. MyISAM 适合查询应用。</p>
<a id="more"></a>
<p>分表是分散数据库压力的好方法。</p>
<p>分表,最直白的意思,就是将一个表结构分为多个表,然后,可以再同一个库里,也可以放到不同的库。</p>
<p>当然,首先要知道什么情况下,才需要分表。个人觉得单表记录条数达到百万到千万级别时就要使用分表了。</p>
<p>1,分表的分类</p>
<p>1>纵向分表</p>
<p>将本来可以在同一个表的内容,人为划分为多个表。(所谓的本来,是指按照关系型数据库的第三范式要求,是应该在同一个表的。)</p>
<p>分表理由:根据数据的活跃度进行分离,(因为不同活跃的数据,处理方式是不同的)</p>
<p>案例:</p>
<p>对于一个博客系统,文章标题,作者,分类,创建时间等,是变化频率慢,查询次数多,而且最好有很好的实时性的数据,我们把它叫做冷数据。而博客的浏览量,回复数等,类似的统计信息,或者别的变化频率比较高的数据,我们把它叫做活跃数据。所以,在进行数据库结构设计的时候,就应该考虑分表,首先是纵向分表的处理。</p>
<p>这样纵向分表后:</p>
<p>首先存储引擎的使用不同,冷数据使用MyIsam 可以有更好的查询数据。活跃数据,可以使用Innodb ,可以有更好的更新速度。</p>
<p>其次,对冷数据进行更多的从库配置,因为更多的操作时查询,这样来加快查询速度。对热数据,可以相对有更多的主库的横向分表处理。</p>
<p>其实,对于一些特殊的活跃数据,也可以考虑使用memcache ,redis</p>
<p>之类的缓存,等累计到一定量再去更新数据库。或者mongodb 一类的nosql 数据库,这里只是举例,就先不说这个。</p>
<p>2>横向分表</p>
<p>字面意思,就可以看出来,是把大的表结构,横向切割为同样结构的不同表,如,用户信息表,user_1,user_2 等。表结构是完全一样,但是,根据某些特定的规则来划分的表,如根据用户ID来取模划分。</p>
<p>分表理由:根据数据量的规模来划分,保证单表的容量不会太大,从而来保证单表的查询等处理能力。</p>
<p>案例:同上面的例子,博客系统。当博客的量达到很大时候,就应该采取横向分割来降低每个单表的压力,来提升性能。例如博客的冷数据表,假如分为100个表,当同时有100万个用户在浏览时,如果是单表的话,会进行100万次请求,而现在分表后,就可能是每个表进行1万个数据的请求(因为,不可能绝对的平均,只是假设),这样压力就降低了很多很多。</p>
<p>1,背景:一个地址薄的应用程序,设计的用户量为2亿,统计出每个用户的地址薄为30个左右,整个数据量为60亿,使用mysql数据库(亿级数据库设计)<br>计划分为:1000个表,100个库</p>
<p>2,分库分表代码</p>
<pre><code>private <span class="keyword">function</span> getDbNo(<span class="variable">$email</span>)
{
<span class="variable">$m</span> = md5(<span class="variable">$email</span>);
<span class="variable">$n</span> = hexdec(substr(<span class="variable">$m</span>, <span class="number">0</span>, <span class="number">16</span>));
<span class="variable">$tableNo</span> = fmod(<span class="variable">$n</span>, <span class="number">1000</span>);
<span class="variable">$dbNo</span> = <span class="variable">$tableNo</span> % <span class="number">100</span>;
return array(<span class="variable">$dbNo</span>, <span class="variable">$tableNo</span>);
}
</code></pre><p>3,配合的连接访问代码</p>
<pre><code>require_once ‘Db/Config.php’;
class Db_Adapter
{
const MASTER = <span class="number">0</span>;
const SLAVE = <span class="number">1</span>;
private static <span class="variable">$instances</span> = array();
private <span class="variable">$conf</span> = array();
private <span class="variable">$conns</span> = array();
private <span class="variable">$conn</span> = NULL;
private <span class="variable">$stmt</span> = NULL;
public function __construct(<span class="variable">$conf</span>)
{
<span class="variable">$this-</span>>conf = <span class="variable">$conf</span>;
}
public function execute(<span class="variable">$sql</span>, <span class="variable">$params</span>)
{
<span class="variable">$cmd</span> = substr(strtolower(trim(<span class="variable">$sql</span>)), <span class="number">0</span>, <span class="number">6</span>);
if (<span class="variable">$cmd</span> == ‘select’) {
<span class="variable">$conn</span> = <span class="variable">$this-</span>>getConn(self::SLAVE);
} else {
<span class="variable">$conn</span> = <span class="variable">$this-</span>>getConn(self::MASTER);
}
<span class="variable">$conn-</span>>prepare(<span class="variable">$sql</span>);
<span class="variable">$stmt</span> = <span class="variable">$conn-</span>>execute(<span class="variable">$params</span>);
<span class="variable">$this-</span>>conn = <span class="variable">$conn</span>;
<span class="variable">$this-</span>>stmt = <span class="variable">$stmt</span>;
}
public function fetch()
{
return <span class="variable">$this-</span>>stmt->fetch();
}
public function fetchAll()
{
return <span class="variable">$this-</span>>stmt->fetchAll();
}
public function lastInsertId(<span class="variable">$name</span> = NULL)
{
return <span class="variable">$this-</span>>conn->lastInsertId(<span class="variable">$name</span>);
}
public function rowCount()
{
return <span class="variable">$this-</span>>stmt->rowCount();
}
private function getConn(<span class="variable">$type</span>)
{
if (<span class="variable">$type</span> == self::SLAVE && isset(<span class="variable">$this-</span>>conf[self::SLAVE])) {
<span class="variable">$id</span> = <span class="number">0</span>;
} else {
<span class="variable">$id</span> = <span class="number">1</span>;
}
if (!isset(<span class="variable">$this-</span>>conns[<span class="variable">$id</span>])) {
<span class="variable">$conf</span> = <span class="variable">$this-</span>>conf[<span class="variable">$id</span>];
<span class="variable">$this-</span>>conns[<span class="variable">$id</span>] = new PDO(
<span class="variable">$conf</span>[<span class="string">'dsn'</span>], <span class="variable">$conf</span>[<span class="string">'user'</span>], <span class="variable">$conf</span>[<span class="string">'pass'</span>],
self::dbOptions);
}
return <span class="variable">$this-</span>>conns[<span class="variable">$id</span>];
}
public static function getInstance(<span class="variable">$dbName</span>, <span class="variable">$dbNo</span> = <span class="number">0</span>)
{
<span class="variable">$key</span> = <span class="variable">$dbName</span> . ‘<span class="number">_</span>’ . <span class="variable">$dbNo</span>;
if (!isset(self::<span class="variable">$instances</span>[<span class="variable">$key</span>])) {
<span class="variable">$conf</span> = Db_Config::getConfig(<span class="variable">$dbName</span>, <span class="variable">$dbNo</span>); //连接配置参数
self::<span class="variable">$instances</span>[<span class="variable">$key</span>] = new self(<span class="variable">$conf</span>);
}
return self::<span class="variable">$instances</span>[<span class="variable">$key</span>];
}
}
</code></pre><p>4,潜在问题</p>
<p>如果某个表中的那些用户的地址薄联系人超多,如每个人1000个,则可能出现该表超大,需要把该表区分为子表,暂时没有配置中心来处理该情况。<br>(若真的出现该情况,在连接参数这个地方继续作一次hash)。</p>
]]></content>
<categories>
<category>PHP</category>
</categories>
<tags>
<tag>分库</tag>
<tag>分表</tag>
</tags>
</entry>
<entry>
<title>Javascript 面向对象编程(一):封装</title>
<url>/2017/01/20/Javascript/Javascript-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%BC%96%E7%A8%8B%EF%BC%88%E4%B8%80%EF%BC%89%EF%BC%9A%E5%B0%81%E8%A3%85/</url>
<content><![CDATA[<p>Javascript是一种基于对象(object-based)的语言,我们遇到的所有东西几乎都是对象。但是,它又不是一种真正的面向对象编程(OOP)语言,因为它的语法中没有class(类)。</p>
<p>那么,如果我们要把”属性”(property)和”方法”(method),封装成一个对象,甚至要从原型对象生成一个实例对象,我们应该怎么做呢?<br><a id="more"></a></p>
<h3 id="一、_生成实例对象的原始模式">一、 生成实例对象的原始模式</h3><p>假定我们把猫看成一个对象,它有”名字”和”颜色”两个属性。</p>
<pre><code><span class="variable"><span class="keyword">var</span> cat</span> = {
name: <span class="string">""</span>,
color: <span class="string">""</span>
}
</code></pre><p>现在,我们需要根据这个原型对象的规格(schema),生成两个实例对象。</p>
<pre><code><span class="variable"><span class="keyword">var</span> cat1</span> = {};
cat1.name = <span class="string">"大毛"</span>;
cat1.color = <span class="string">"黄色"</span>;
<span class="variable"><span class="keyword">var</span> cat2</span> = {};
cat2.name = <span class="string">"二毛"</span>;
cat2.color = <span class="string">"黑色"</span>;
</code></pre><p>这就是最简单的封装了,把两个属性封装在一个对象里面。</p>
<p>但是,这样的写法有两个缺点,一是如果多生成几个实例,写起来就非常麻烦;二是实例与原型之间,没有任何办法,可以看出有什么联系。</p>
<h3 id="二、_原始模式的改进">二、 原始模式的改进</h3><p>我们可以写一个函数,解决代码重复的问题。</p>
<pre><code><span class="function"><span class="keyword">function</span></span> cat(<span class="keyword">name</span>,color) {
<span class="keyword">return</span> {
<span class="keyword">name</span>:<span class="keyword">name</span>,
color:color
}
}
</code></pre><p>然后生成实例对象,就等于是在调用函数:</p>
<pre><code><span class="variable"><span class="keyword">var</span> cat1</span> = Cat(<span class="string">"大毛"</span>,<span class="string">"黄色"</span>);
<span class="variable"><span class="keyword">var</span> cat2</span> = Cat(<span class="string">"二毛"</span>,<span class="string">"黑色"</span>);
</code></pre><p>这种方法的问题依然是,cat1和cat2之间没有内在的联系,不能反映出它们是同一个原型对象的实例。</p>
<h3 id="三、_构造函数模式">三、 构造函数模式</h3><p>所谓”构造函数”,其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。</p>
<p>比如,猫的原型对象现在可以这样写,</p>
<pre><code><span class="function"><span class="keyword">function</span> <span class="title">cat</span><span class="params">(name, color)</span></span>{
<span class="keyword">this</span>.name = name;
<span class="keyword">this</span>.color = color;
}
</code></pre><p>我们现在就可以生成实例对象了。</p>
<pre><code><span class="keyword">var</span> cat1 = <span class="keyword">new</span> cat(<span class="string">"大毛"</span>,<span class="string">"黄色"</span>);
<span class="keyword">var</span> cat2 = <span class="keyword">new</span> cat(<span class="string">"二毛"</span>,<span class="string">"黑色"</span>);
alert(cat1.name); <span class="comment">// "大毛"</span>
alert(cat2.color); <span class="comment">// "黑色"</span>
</code></pre><p>这时cat1和cat2会自动含有一个constructor属性,指向它们的构造函数。</p>
<pre><code>alert(cat1.constructor == cat); <span class="regexp">//</span> <span class="literal">true</span>
alert(cat2.constructor == cat); <span class="regexp">//</span> <span class="literal">true</span>
</code></pre><p>Javascript还提供了一个instanceof运算符,验证原型对象与实例对象之间的关系。</p>
<pre><code>alert(cat1 <span class="keyword">instanceof</span> cat); <span class="comment">// true</span>
alert(cat2 <span class="keyword">instanceof</span> cat); <span class="comment">// true</span>
</code></pre><h3 id="四、构造函数模式的问题">四、构造函数模式的问题</h3><p>构造函数方法很好用,但是存在一个浪费内存的问题。</p>
<p>请看,我们现在为cat对象添加一个不变的属性type,再添加一个方法eat。那么,原型对象cat就变成了下面这样:</p>
<pre><code><span class="function"><span class="keyword">function</span> <span class="title">cat</span><span class="params">(name, color)</span></span>{
<span class="keyword">this</span>.name = name;
<span class="keyword">this</span>.color = color;
<span class="keyword">this</span>.type = <span class="string">"猫科动物"</span>;
<span class="keyword">this</span>.eat = <span class="function"><span class="keyword">function</span><span class="params">()</span></span>{alert(<span class="string">"吃老鼠"</span>);};
}
</code></pre><p>还是采用同样的方法,生成实例:</p>
<pre><code><span class="keyword">var</span> cat1 = <span class="built_in">new</span> cat(<span class="string">"大毛"</span>,<span class="string">"黄色"</span>);
<span class="keyword">var</span> cat2 = <span class="built_in">new</span> cat (<span class="string">"二毛"</span>,<span class="string">"黑色"</span>);
alert(cat1.<span class="keyword">type</span>); <span class="comment">// "猫科动物"</span>
cat1.eat(); <span class="comment">// "吃老鼠"</span>
</code></pre><p>表面上好像没什么问题,但是实际上这样做,有一个很大的弊端。那就是对于每一个实例对象,type属性和eat()方法都是一模一样的内容,每一次生成一个实例,都必须为重复的内容,多占用一些内存。这样既不环保,也缺乏效率。</p>
<pre><code>alert(cat1.eat == cat2.eat); <span class="regexp">//</span> <span class="literal">false</span>
</code></pre><p>所以我们要让type属性和eat()方法在内存中只生成一次,然后所有实例都指向那个内存地址。</p>
<h3 id="五、_Prototype模式">五、 Prototype模式</h3><p>Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。</p>
<p>这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。</p>
<pre><code><span class="function"><span class="keyword">function</span> <span class="title">cat</span><span class="params">(name, color)</span></span>{
<span class="keyword">this</span>.name = name;
<span class="keyword">this</span>.color = color;
}
cat.prototype.type = <span class="string">"猫科动物"</span>;
cat.prototype.eat = <span class="function"><span class="keyword">function</span><span class="params">()</span></span>{alert(<span class="string">"吃老鼠"</span>)};
</code></pre><p>然后,生成实例。</p>
<pre><code><span class="keyword">var</span> cat1 = <span class="built_in">new</span> cat(<span class="string">"大毛"</span>, <span class="string">"黄色"</span>);
<span class="keyword">var</span> cat2 = <span class="built_in">new</span> cat(<span class="string">"二毛"</span>, <span class="string">"黑色"</span>);
alert(cat1.<span class="keyword">type</span>); <span class="comment">// "猫科动物"</span>
cat2.eat(); <span class="comment">// "吃老鼠"</span>
</code></pre><p>这时,所有实例的type属性和eat()方法,其实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。</p>
<pre><code>alert(cat1.eat == cat2.eat); <span class="regexp">//</span> <span class="literal">true</span>
</code></pre><h3 id="六、_Prototype模式的验证方法">六、 Prototype模式的验证方法</h3><p>为了配合prototype属性,Javascript定义了一些辅助方法,帮助我们使用它。</p>
<p>1、isPrototypeOf()<br>这个方法用来判断,某个proptotype对象和某个实例之间的关系。</p>
<pre><code>alert<span class="list">(<span class="keyword">cat</span>.prototype.isPrototypeOf<span class="list">(<span class="keyword">cat1</span>)</span>)</span><span class="comment">; // true</span>
alert<span class="list">(<span class="keyword">cat</span>.prototype.isPrototypeOf<span class="list">(<span class="keyword">cat2</span>)</span>)</span><span class="comment">; // true</span>
</code></pre><p>2、hasOwnProperty()<br>每个实例对象都有一个hasOwnProperty()方法,用来判断某一个属性到底是本地属性,还是继承自prototype对象的属性。</p>
<pre><code>alert<span class="list">(<span class="keyword">cat1</span>.hasOwnProperty<span class="list">(<span class="string">"name"</span>)</span>)</span><span class="comment">; // true</span>
alert<span class="list">(<span class="keyword">cat1</span>.hasOwnProperty<span class="list">(<span class="string">"type"</span>)</span>)</span><span class="comment">; // false</span>
</code></pre><p>3、in运算符<br>in运算符可以用来判断,某个实例是否含有某个属性,不管是不是本地属性。</p>
<pre><code>alert(<span class="string">"name"</span> <span class="keyword">in</span> cat1); <span class="regexp">//</span> <span class="keyword">true</span>
alert(<span class="string">"type"</span> <span class="keyword">in</span> cat2); <span class="regexp">//</span> <span class="keyword">true</span>
</code></pre><p>in运算符还可以用来遍历某个对象的所有属性。</p>
<pre><code><span class="keyword">for</span>(<span class="keyword">var</span> <span class="keyword">prop</span> <span class="keyword">in</span> cat1) {
alert(<span class="string">"cat1["</span>+<span class="keyword">prop</span>+<span class="string">"]="</span>+cat1[<span class="keyword">prop</span>]);
}
</code></pre>]]></content>
<categories>
<category>Javascript</category>
</categories>
<tags>
<tag>Javascript</tag>
</tags>
</entry>
<entry>
<title>何为JWT</title>
<url>/2017/01/19/PHP/%E4%BD%95%E4%B8%BAJWT/</url>
<content><![CDATA[<h3 id="什么是JWT">什么是JWT</h3><blockquote>
<p>Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。<br><a id="more"></a></p>
<h3 id="起源">起源</h3></blockquote>
<p>说起JWT,我们应该来谈一谈基于token的认证和传统的session认证的区别。</p>
<h3 id="传统的session认证">传统的session认证</h3><p>我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。</p>
<p>但是这种基于session的认证使应用本身很难得到扩展,随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,而这时候基于session认证应用的问题就会暴露出来.</p>
<h3 id="基于session认证所显露的问题">基于session认证所显露的问题</h3><p>Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。</p>
<p>扩展性: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。</p>
<p>CSRF: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。</p>
<h3 id="基于token的鉴权机制">基于token的鉴权机制</h3><p>基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。</p>
<p>流程上是这样的:</p>
<p>用户使用用户名密码来请求服务器<br>服务器进行验证用户的信息<br>服务器通过验证发送给用户一个token<br>客户端存储token,并在每次请求时附送上这个token值<br>服务端验证token值,并返回数据<br>这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了Access-Control-Allow-Origin: *。</p>
<p>那么我们现在回到JWT的主题上。</p>
<h3 id="JWT长什么样?">JWT长什么样?</h3><p>JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:</p>
<pre><code>eyJhbGciOiJIUzI<span class="number">1</span><span class="label">NiIsInR5</span>cCI<span class="number">6</span>IkpX<span class="title">VCJ9</span>.eyJzdWIiOiIxMj<span class="title">M0</span><span class="label">NTY3</span>ODkwIiwibmFtZSI<span class="number">6</span>Ikpva<span class="keyword">G4</span>gR<span class="keyword">G9</span>lIiwiYWRtaW<span class="number">4</span>iO<span class="label">nRydWV9</span>.TJVA<span class="number">95</span><span class="keyword">Or</span><span class="title">M7</span>E<span class="number">2</span>cBab<span class="number">30</span>RMHrHDcEfxjoYZgeFO<span class="label">NFh7</span>HgQ
</code></pre><h3 id="JWT的构成">JWT的构成</h3><p>第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).</p>
<p>header</p>
<p>jwt的头部承载两部分信息:</p>
<p>声明类型,这里是jwt<br>声明加密的算法 通常直接使用 HMAC SHA256<br>完整的头部就像下面这样的JSON:</p>
<pre><code>{
<span class="string">'typ'</span>: <span class="string">'JWT'</span>,
<span class="string">'alg'</span>: <span class="string">'HS256'</span>
}
</code></pre><p>然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.</p>
<pre><code>eyJ<span class="number">0</span>eXAiOiJKV<span class="number">1</span>QiLCJhbGciOiJIUzI<span class="number">1</span><span class="label">NiJ9</span>
</code></pre><p>playload</p>
<p>载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分</p>
<p>标准中注册的声明<br>公共的声明<br>私有的声明<br>标准中注册的声明 (建议但不强制使用) :</p>
<p>iss: jwt签发者<br>sub: jwt所面向的用户<br>aud: 接收jwt的一方<br>exp: jwt的过期时间,这个过期时间必须要大于签发时间<br>nbf: 定义在什么时间之前,该jwt都是不可用的.<br>iat: jwt的签发时间<br>jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。<br>公共的声明 :<br>公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.</p>
<p>私有的声明 :<br>私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。</p>
<p>定义一个payload:</p>
<pre><code>{
"<span class="attribute">sub</span>": <span class="value"><span class="string">"1234567890"</span></span>,
"<span class="attribute">name</span>": <span class="value"><span class="string">"John Doe"</span></span>,
"<span class="attribute">admin</span>": <span class="value"><span class="literal">true</span>
</span>}
</code></pre><p>然后将其进行base64加密,得到Jwt的第二部分。</p>
<pre><code>eyJzdWIiOiIxMj<span class="title">M0</span><span class="label">NTY3</span>ODkwIiwibmFtZSI<span class="number">6</span>Ikpva<span class="keyword">G4</span>gR<span class="keyword">G9</span>lIiwiYWRtaW<span class="number">4</span>iO<span class="label">nRydWV9</span>
</code></pre><p>signature</p>
<p>jwt的第三部分是一个签证信息,这个签证信息由三部分组成:</p>
<p>header (base64后的)<br>payload (base64后的)<br>secret<br>这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。</p>
<pre><code><span class="comment">// javascript</span>
<span class="built_in">var</span> encodedString = base64UrlEncode(<span class="keyword">header</span>) + <span class="string">'.'</span> + base64UrlEncode(payload);
<span class="built_in">var</span> signature = HMACSHA256(encodedString, <span class="string">'secret'</span>); <span class="comment">// TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ</span>
</code></pre><p>将这三部分用.连接成一个完整的字符串,构成了最终的jwt:</p>
<pre><code>eyJhbGciOiJIUzI<span class="number">1</span><span class="label">NiIsInR5</span>cCI<span class="number">6</span>IkpX<span class="title">VCJ9</span>.eyJzdWIiOiIxMj<span class="title">M0</span><span class="label">NTY3</span>ODkwIiwibmFtZSI<span class="number">6</span>Ikpva<span class="keyword">G4</span>gR<span class="keyword">G9</span>lIiwiYWRtaW<span class="number">4</span>iO<span class="label">nRydWV9</span>.TJVA<span class="number">95</span><span class="keyword">Or</span><span class="title">M7</span>E<span class="number">2</span>cBab<span class="number">30</span>RMHrHDcEfxjoYZgeFO<span class="label">NFh7</span>HgQ
</code></pre><p>注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。</p>
<h3 id="如何应用">如何应用</h3><p>一般是在请求头里加入Authorization,并加上Bearer标注:</p>
<pre><code>fetch(<span class="string">'api/user/1'</span>, {
headers: {
<span class="string">'Authorization'</span>: <span class="string">'Bearer '</span> + token
}
})
</code></pre><p>服务端会验证token,如果验证通过就会返回相应的资源。整个流程就是这样的:</p>
<h3 id="总结">总结</h3><p>优点</p>
<p>因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。<br>因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。<br>便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。<br>它不需要在服务端保存会话信息, 所以它易于应用的扩展</p>
<p>安全相关</p>
<p>不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。<br>保护好secret私钥,该私钥非常重要。<br>如果可以,请使用https协议</p>
]]></content>
<categories>
<category>PHP</category>
</categories>
<tags>
<tag>JWT</tag>
</tags>
</entry>
<entry>
<title>Restful Api 的理解</title>
<url>/2017/01/18/PHP/Restful-Api-%E7%9A%84%E7%90%86%E8%A7%A3/</url>
<content><![CDATA[<ol>
<li>什么是REST?</li>
</ol>
<p>REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移。 它首次出现在2000年Roy Fielding的博士论文中,Roy Fielding是HTTP规范的主要编写者之一。 他在论文中提到:”我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。REST指的是一组架构约束条件和原则。” 如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。<br>REST本身并没有创造新的技术、组件或服务,而隐藏在RESTful背后的理念就是使用Web的现有特征和能力, 更好地使用现有Web标准中的一些准则和约束。虽然REST本身受Web技术的影响很深, 但是理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST相关的实例。 所以我们这里描述的REST也是通过HTTP实现的REST。<br><a id="more"></a></p>
<ol>
<li>理解RESTful</li>
</ol>
<p>要理解RESTful架构,需要理解Representational State Transfer这个词组到底是什么意思,它的每一个词都有些什么涵义。<br>下面我们结合REST原则,围绕资源展开讨论,从资源的定义、获取、表述、关联、状态变迁等角度,列举一些关键概念并加以解释。<br>资源与URI<br>统一资源接口<br>资源的表述<br>资源的链接<br>状态的转移</p>
<ol>
<li>1 资源与URI</li>
</ol>
<p>REST全称是表述性状态转移,那究竟指的是什么的表述? 其实指的就是资源。任何事物,只要有被引用到的必要,它就是一个资源。资源可以是实体(例如手机号码),也可以只是一个抽象概念(例如价值) 。下面是一些资源的例子:<br>某用户的手机号码<br>某用户的个人信息<br>最多用户订购的GPRS套餐<br>两个产品之间的依赖关系<br>某用户可以办理的优惠套餐<br>某手机号码的潜在价值<br>要让一个资源可以被识别,需要有个唯一标识,在Web中这个唯一标识就是URI(Uniform Resource Identifier)。<br>URI既可以看成是资源的地址,也可以看成是资源的名称。如果某些信息没有使用URI来表示,那它就不能算是一个资源, 只能算是资源的一些信息而已。URI的设计应该遵循可寻址性原则,具有自描述性,需要在形式上给人以直觉上的关联。这里以github网站为例,给出一些还算不错的URI:<br><a href="https://github.com/git" target="_blank" rel="noopener">https://github.com/git</a><br><a href="https://github.com/git/git" target="_blank" rel="noopener">https://github.com/git/git</a><br><a href="https://github.com/git/git/blob/master/block-sha1/sha1.h" target="_blank" rel="noopener">https://github.com/git/git/blob/master/block-sha1/sha1.h</a><br><a href="https://github.com/git/git/commit/e3af72cdafab5993d18fae056f87e1d675913d08" target="_blank" rel="noopener">https://github.com/git/git/commit/e3af72cdafab5993d18fae056f87e1d675913d08</a><br><a href="https://github.com/git/git/pulls" target="_blank" rel="noopener">https://github.com/git/git/pulls</a><br><a href="https://github.com/git/git/pulls?state=closed" target="_blank" rel="noopener">https://github.com/git/git/pulls?state=closed</a><br><a href="https://github.com/git/git/compare/master…next" target="_blank" rel="noopener">https://github.com/git/git/compare/master…next</a><br>下面让我们来看看URI设计上的一些技巧:<br>使用<em>或-来让URI可读性更好<br>曾经Web上的URI都是冰冷的数字或者无意义的字符串,但现在越来越多的网站使用</em>或-来分隔一些单词,让URI看上去更为人性化。 例如国内比较出名的开源中国社区,它上面的新闻地址就采用这种风格, 如<a href="http://www.oschina.net/news/38119/oschina-translate-reward-plan。" target="_blank" rel="noopener">http://www.oschina.net/news/38119/oschina-translate-reward-plan。</a><br>使用/来表示资源的层级关系<br>例如上述/git/git/commit/e3af72cdafab5993d18fae056f87e1d675913d08就表示了一个多级的资源, 指的是git用户的git项目的某次提交记录,又例如/orders/2012/10可以用来表示2012年10月的订单记录。<br>使用?用来过滤资源<br>很多人只是把?简单的当做是参数的传递,很容易造成URI过于复杂、难以理解。可以把?用于对资源的过滤, 例如/git/git/pulls用来表示git项目的所有推入请求,而/pulls?state=closed用来表示git项目中已经关闭的推入请求, 这种URL通常对应的是一些特定条件的查询结果或算法运算结果。<br>,或;可以用来表示同级资源的关系<br>有时候我们需要表示同级资源的关系时,可以使用,或;来进行分割。例如哪天github可以比较某个文件在随意两次提交记录之间的差异,或许可以使用/git/git /block-sha1/sha1.h/compare/e3af72cdafab5993d18fae056f87e1d675913d08;bd63e61bdf38e872d5215c07b264dcc16e4febca作为URI。 不过,现在github是使用…来做这个事情的,例如/git/git/compare/master…next。</p>
<ol>
<li>2 统一资源接口</li>
</ol>
<p>RESTful架构应该遵循统一接口原则,统一接口包含了一组受限的预定义的操作,不论什么样的资源,都是通过使用相同的接口进行资源的访问。接口应该使用标准的HTTP方法如GET,PUT和POST,并遵循这些方法的语义。<br>如果按照HTTP方法的语义来暴露资源,那么接口将会拥有安全性和幂等性的特性,例如GET和HEAD请求都是安全的, 无论请求多少次,都不会改变服务器状态。而GET、HEAD、PUT和DELETE请求都是幂等的,无论对资源操作多少次, 结果总是一样的,后面的请求并不会产生比第一次更多的影响。<br>下面列出了GET,DELETE,PUT和POST的典型用法:</p>
<p>GET<br>安全且幂等<br>获取表示<br>变更时获取表示(缓存)<br>200(OK) - 表示已在响应中发出<br>204(无内容) - 资源有空表示<br>301(Moved Permanently) - 资源的URI已被更新<br>303(See Other) - 其他(如,负载均衡)<br>304(not modified)- 资源未更改(缓存)<br>400 (bad request)- 指代坏请求(如,参数错误)<br>404 (not found)- 资源不存在<br>406 (not acceptable)- 服务端不支持所需表示<br>500 (internal server error)- 通用错误响应<br>503 (Service Unavailable)- 服务端当前无法处理请求<br>POST<br>不安全且不幂等<br>使用服务端管理的(自动产生)的实例号创建资源<br>创建子资源<br>部分更新资源<br>如果没有被修改,则不过更新资源(乐观锁)<br>200(OK)- 如果现有资源已被更改<br>201(created)- 如果新资源被创建<br>202(accepted)- 已接受处理请求但尚未完成(异步处理)<br>301(Moved Permanently)- 资源的URI被更新<br>303(See Other)- 其他(如,负载均衡)<br>400(bad request)- 指代坏请求<br>404 (not found)- 资源不存在<br>406 (not acceptable)- 服务端不支持所需表示<br>409 (conflict)- 通用冲突<br>412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)<br>415 (unsupported media type)- 接受到的表示不受支持<br>500 (internal server error)- 通用错误响应<br>503 (Service Unavailable)- 服务当前无法处理请求<br>PUT<br>不安全但幂等<br>用客户端管理的实例号创建一个资源<br>通过替换的方式更新资源<br>如果未被修改,则更新资源(乐观锁)<br>200 (OK)- 如果已存在资源被更改<br>201 (created)- 如果新资源被创建<br>301(Moved Permanently)- 资源的URI已更改<br>303 (See Other)- 其他(如,负载均衡)<br>400 (bad request)- 指代坏请求<br>404 (not found)- 资源不存在<br>406 (not acceptable)- 服务端不支持所需表示<br>409 (conflict)- 通用冲突<br>412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)<br>415 (unsupported media type)- 接受到的表示不受支持<br>500 (internal server error)- 通用错误响应<br>503 (Service Unavailable)- 服务当前无法处理请求<br>DELETE<br>不安全但幂等<br>删除资源<br>200 (OK)- 资源已被删除<br>301 (Moved Permanently)- 资源的URI已更改<br>303 (See Other)- 其他,如负载均衡<br>400 (bad request)- 指代坏请求<br>404 (not found)- 资源不存在<br>409 (conflict)- 通用冲突<br>500 (internal server error)- 通用错误响应<br>503 (Service Unavailable)- 服务端当前无法处理请求</p>
]]></content>
<categories>
<category>PHP</category>
</categories>
<tags>
<tag>restful</tag>
<tag>api</tag>
</tags>
</entry>
<entry>
<title>PHP排序算法</title>
<url>/2016/09/25/%E7%AE%97%E6%B3%95/PHP%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95/</url>
<content><![CDATA[<p>排序算法有以下几种:</p>
<p>冒泡排序、直接插入排序、希尔排序、选择排序、堆排序,归并排序</p>
<p>以下数组为例子<br>$arr = array(23, 15, 43, 25, 54, 2, 6, 82, 11, 5, 21, 32, 65);<br><a id="more"></a><br>冒泡排序</p>
<pre><code><span class="keyword">function</span> maopao(<span class="variable">$arr</span>)
{
<span class="variable">$length</span> = count(<span class="variable">$arr</span>);
for (<span class="variable">$i</span> = <span class="number">0</span>; <span class="variable">$i</span> < <span class="variable">$length</span>; <span class="variable">$i</span>++) {
for (<span class="variable">$j</span> = <span class="variable">$i</span> + <span class="number">1</span>; <span class="variable">$j</span> < <span class="variable">$length</span>; <span class="variable">$j</span>++) {
if (<span class="variable">$arr</span>[<span class="variable">$i</span>] > <span class="variable">$arr</span>[<span class="variable">$j</span>]) {
<span class="variable">$temp</span> = <span class="variable">$arr</span>[<span class="variable">$j</span>];
<span class="variable">$arr</span>[<span class="variable">$j</span>] = <span class="variable">$arr</span>[<span class="variable">$i</span>];
<span class="variable">$arr</span>[<span class="variable">$i</span>] = <span class="variable">$temp</span>;
}
}
}
return <span class="variable">$arr</span>;
}
var_dump(maopao(<span class="variable">$arr</span>));
</code></pre><p>快速排序</p>
<pre><code><span class="keyword">function</span> kspx(<span class="variable">$demo</span>_<span class="keyword">array</span>)
{
<span class="variable">$length</span> = count(<span class="variable">$demo</span>_array);
if (<span class="variable">$length</span> <= <span class="number">1</span>) {
return <span class="variable">$demo</span>_array;
}
<span class="variable">$base</span> = <span class="variable">$demo</span>_array[<span class="number">0</span>];
<span class="variable">$left</span> = [];
<span class="variable">$right</span> = [];
for (<span class="variable">$i</span> = <span class="number">1</span>; <span class="variable">$i</span> < <span class="variable">$length</span>; <span class="variable">$i</span>++) {
if (<span class="variable">$base</span> > <span class="variable">$demo</span>_array[<span class="variable">$i</span>]) {
<span class="variable">$left</span>[] = <span class="variable">$demo</span>_array[<span class="variable">$i</span>];
} else {
<span class="variable">$right</span>[] = <span class="variable">$demo</span>_array[<span class="variable">$i</span>];
}
}
<span class="variable">$left</span> = kspx(<span class="variable">$left</span>);
<span class="variable">$right</span> = kspx(<span class="variable">$right</span>);
return array_merge(<span class="variable">$left</span>, [<span class="variable">$base</span>], <span class="variable">$right</span>);
}
var_dump(kspx(<span class="variable">$arr</span>));
</code></pre>]]></content>
<categories>
<category>算法</category>
</categories>
<tags>
<tag>PHP</tag>
<tag>算法</tag>
</tags>
</entry>
</search>