-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
499 lines (292 loc) · 447 KB
/
atom.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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Frank's 技术世界</title>
<icon>https://www.gravatar.com/avatar/a4b62dd9a6da9d9dde198721ec9fe171</icon>
<subtitle>潜伏在黎明之前,混迹于互联网江湖。</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://www.frankfeekr.cn/"/>
<updated>2021-10-23T15:38:16.124Z</updated>
<id>http://www.frankfeekr.cn/</id>
<author>
<name>Frank Lam</name>
<email>[email protected]</email>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>手把手教你,使用 nvm 管理不同版本的 node</title>
<link href="http://www.frankfeekr.cn/2021/10/23/nvm-node-manager/"/>
<id>http://www.frankfeekr.cn/2021/10/23/nvm-node-manager/</id>
<published>2021-10-23T15:16:19.000Z</published>
<updated>2021-10-23T15:38:16.124Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文已 Centos7 为例</p></blockquote><ol><li>通过 Git 下载 nvm</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone git://github.com/creationix/nvm.git ~/nvm</span><br></pre></td></tr></table></figure><ol start="2"><li>下载完成后加入系统环境</li></ol><figure class="highlight shell"><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">echo "source ~/nvm/nvm.sh" >> ~/.bashrc</span><br><span class="line">source ~/.bashrc</span><br></pre></td></tr></table></figure><ol start="3"><li>查看 NVM 版本list</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nvm list-romote</span><br></pre></td></tr></table></figure><ol start="4"><li>安装需要的node版本</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nvm install v13.14.0</span><br></pre></td></tr></table></figure><ol start="5"><li>查看当前机器已安装版本号</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nvm list</span><br></pre></td></tr></table></figure><ol start="6"><li>切换node版本</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nvm use v13.14.0</span><br></pre></td></tr></table></figure><ol start="7"><li>设置默认的node版本</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nvm <span class="built_in">alias</span> default v13.14.0</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote>
<p>本文已 Centos7 为例</p>
</blockquote>
<ol>
<li>通过 Git 下载 nvm</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gut
</summary>
</entry>
<entry>
<title>Spring 快速集成分布式定时器</title>
<link href="http://www.frankfeekr.cn/2021/10/23/spring-integration-shedlock/"/>
<id>http://www.frankfeekr.cn/2021/10/23/spring-integration-shedlock/</id>
<published>2021-10-23T08:25:33.000Z</published>
<updated>2021-10-23T15:38:16.125Z</updated>
<content type="html"><![CDATA[<h2 id="前言">前言</h2><p>在业务生产中,定时器 Scheduler 的使用频率非常高。Spring 也为我们提供了默认的定时器。只需要加上 <code>@Scheduled</code> 和 <code>@EnableScheduling</code> 两个注解,即可快速运行。在单实例的服务中,官方的提供的定时任务可以非常方便的使用。</p><p>但是在如今分布式多实例的环境中,使用这种定时任务,则每个实例都定时并发执行,做着相同的事情,这必然不是我们想要的效果。这时你一定会想办法,让定时任务每次只在一个服务中运行,例如:每个服务通过配置文件来做为定时的开关、或是通过数据库实现分布式锁,这都是非常不错的选择。</p><p>本文将介绍一个更方便的组件,在 SpringBoot 工程中,你仅需要通过少量的代码即可实现分布式定时器功能。</p><p>还等什么呢,即刻开始!</p><h2 id="shedlock-简介">ShedLock 简介</h2><p>ShedLock 的作用,确保任务在同一时刻最多执行一次。如果一个任务正在一个节点上执行,则它将获得一个锁,该锁将阻止从另一个节点(或线程)执行同一任务。如果一个任务已经在一个节点上执行,则在其他节点上的执行不会等待,只需跳过它即可 。</p><p>ShedLock 使用 Mongo,JDBC 数据库,Redis,Hazelcast,ZooKeeper 或其他外部存储进行协调,即通过外部存储来实现锁机制。</p><p>官方传送门:<a href="https://github.com/lukas-krecan/ShedLock#jdbctemplate" target="_blank" rel="noopener">lukas-krecan/ShedLock: Distributed lock for your scheduled tasks</a></p><h2 id="shedlock-快速集成">ShedLock 快速集成</h2><p>本文将以 shedlock + mysql 为例,在 SpringBoot 中快速集成分布式定时任务。如果想</p><p>**核心思想:**通过对多个实例公共的数据库的 <code>shedlock</code> 表进行添加数据库锁,使得同一个定时任务在同一个时间点只有一个实例执行。</p><h3 id="1-引入依赖">1. 引入依赖</h3><p>这里引入了 shedlock-spring / shedlock-provider-redis-spring 两个依赖,shedlock-provider 也提供了丰富的 Lock Providers,例如:Redis、JdbcTemplate、Mongo 等等</p><figure class="highlight xml"><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></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>net.javacrumbs.shedlock<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>shedlock-spring<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>4.29.0<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>net.javacrumbs.shedlock<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>shedlock-provider-jdbc-template<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>4.29.0<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure><h3 id="2-配置数据库连接信息">2. 配置数据库连接信息</h3><p><strong>resources/application.yml</strong></p><figure class="highlight yaml"><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 class="attr">spring:</span></span><br><span class="line"><span class="attr"> datasource:</span></span><br><span class="line"><span class="attr"> url:</span> <span class="attr">jdbc:mysql://127.0.0.1:3306/vote_app?useSSL=false&serverTimezone=UTC&characterEncoding=UTF8</span></span><br><span class="line"><span class="attr"> username:</span> <span class="string">******</span></span><br><span class="line"><span class="attr"> password:</span> <span class="string">******</span></span><br><span class="line"><span class="attr"> driver-class-name:</span> <span class="string">com.mysql.cj.jdbc.Driver</span></span><br><span class="line"><span class="attr"> type:</span> <span class="string">com.mysql.cj.jdbc.MysqlDataSource</span></span><br></pre></td></tr></table></figure><p>如果 SpringBoot 工程已经集成了 MySQL 则可以跳过这一步。</p><h3 id="3-创建-mysql-数据表">3. 创建 MySQL 数据表</h3><figure class="highlight sql"><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 class="comment"># MySQL, MariaDB</span></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> shedlock(<span class="keyword">name</span> <span class="built_in">VARCHAR</span>(<span class="number">64</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span>, lock_until <span class="built_in">TIMESTAMP</span>(<span class="number">3</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line"> locked_at <span class="built_in">TIMESTAMP</span>(<span class="number">3</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> <span class="keyword">DEFAULT</span> <span class="keyword">CURRENT_TIMESTAMP</span>(<span class="number">3</span>), locked_by <span class="built_in">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span>, PRIMARY <span class="keyword">KEY</span> (<span class="keyword">name</span>));</span><br></pre></td></tr></table></figure><h3 id="4-shedlockconfig-配置类-配置-lockprovider">4. ShedLockConfig 配置类,配置 lockProvider</h3><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> net.javacrumbs.shedlock.core.LockProvider;</span><br><span class="line"><span class="keyword">import</span> net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;</span><br><span class="line"><span class="keyword">import</span> net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Bean;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration;</span><br><span class="line"><span class="keyword">import</span> org.springframework.jdbc.core.JdbcTemplate;</span><br><span class="line"><span class="keyword">import</span> org.springframework.scheduling.annotation.EnableScheduling;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.sql.DataSource;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Shedlock 配置类</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> [email protected]</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2021/10/23</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 标识该类为配置类</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="comment">// 开启定时器</span></span><br><span class="line"><span class="meta">@EnableScheduling</span></span><br><span class="line"><span class="comment">// 开启定时任务锁,指定一个默认的锁的时间30秒</span></span><br><span class="line"><span class="meta">@EnableSchedulerLock</span>(defaultLockAtMostFor = <span class="string">"PT30S"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ShedlockJdbcConfig</span> </span>{</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 配置锁的提供者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@Bean</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> LockProvider <span class="title">lockProvider</span><span class="params">(DataSource dataSource)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> JdbcTemplateLockProvider(</span><br><span class="line"> JdbcTemplateLockProvider.Configuration.builder()</span><br><span class="line"> .withJdbcTemplate(<span class="keyword">new</span> JdbcTemplate(dataSource))</span><br><span class="line"> .withTableName(<span class="string">"system_shedlock"</span>) <span class="comment">// 这里可以指定你的 MySQL 锁表,默认表名为:shedlock</span></span><br><span class="line"> .usingDbTime()</span><br><span class="line"> .build()</span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="5-mainapplication-启动类配置">5. MainApplication 启动类配置</h3><figure class="highlight java"><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"><span class="meta">@EnableScheduling</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainApplication</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> SpringApplication.run(LatticyApplication.class, args);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="6-创建定时任务">6. 创建定时任务</h3><figure class="highlight java"><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><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> lombok.extern.slf4j.Slf4j;</span><br><span class="line"><span class="keyword">import</span> net.javacrumbs.shedlock.spring.annotation.SchedulerLock;</span><br><span class="line"><span class="keyword">import</span> org.springframework.scheduling.annotation.Scheduled;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 开启分布式锁定时任务</span></span><br><span class="line"><span class="comment"> * 在线 cron 生成工具:https://www.bejson.com/othertools/cron/</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> [email protected]</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2021/10/23 16:55:12</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Slf</span>4j</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TaskJobDemo</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Integer count = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// @SchedulerLock的作用是保证当前定时任务的方法执行时获得锁,忽略其他相同任务的执行</span></span><br><span class="line"> <span class="comment">// name必须要指定,ShedLock就是根据这个name进行相同任务判定的</span></span><br><span class="line"> <span class="comment">// name:定时任务的名字,就是数据库中的主键(name)</span></span><br><span class="line"> <span class="comment">// lockAtMostFor:锁的最大时间单位为毫秒</span></span><br><span class="line"> <span class="comment">// lockAtLeastFor:锁的最小时间单位为毫秒</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 任务1每5秒执行一次</span></span><br><span class="line"><span class="comment"> * lockAtLeastFor:虽然,定时任务是每隔5秒执行一次,但是,分布式锁定义的是:每次任务要锁住20秒,20秒是持有锁的最小时间,必须等20秒后才释放锁,并且确保在20秒钟内,该任务不会运行超过1次;</span></span><br><span class="line"><span class="comment"> * lockAtMostFor:锁最大持有时间30秒,表示最多锁定30秒钟,主要用于防止执行任务的节点挂掉(即使这个节点挂掉,在30秒钟后,锁也被释放),一般将其设置为明显大于任务的最大执行时长;如果任务运行时间超过该值(即任务30秒钟没有执行完),则该任务可能被重复执行。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@Scheduled</span>(cron = <span class="string">"0/5 * * * * ? "</span>)</span><br><span class="line"> <span class="meta">@SchedulerLock</span>(name = <span class="string">"testJob1"</span>, lockAtLeastFor = <span class="string">"20000"</span>, lockAtMostFor = <span class="string">"30000"</span>)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">scheduledTask1</span><span class="params">()</span> </span>{</span><br><span class="line"> log.info(Thread.currentThread().getName() + <span class="string">"->>>任务1执行第:"</span> + (count++) + <span class="string">"次"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 任务 2 每 5 秒执行一次</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@Scheduled</span>(cron = <span class="string">"0/5 * * * * ?"</span>)</span><br><span class="line"> <span class="meta">@SchedulerLock</span>(name = <span class="string">"shedlock-demo"</span>)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">scheduledTask2</span><span class="params">()</span> </span>{</span><br><span class="line"> log.info(Thread.currentThread().getName() + <span class="string">"->>>任务2执行第:"</span> + (count++) + <span class="string">"次"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>@SchedulerLock</strong> 注解有五个参数</p><ul><li>name:定时任务的名字,就是数据库中的内个主键</li><li>lockAtMostFor:锁的最大时间单位为毫秒</li><li>lockAtMostForString:最大时间的字符串形式,例如:PT30S 代表30秒</li><li>lockAtLeastFor:锁的最小时间单位为毫秒</li><li>lockAtLeastForString:最小时间的字符串形式</li></ul><h3 id="7-让你的定时任务并行执行">* 7. 让你的定时任务并行执行</h3><p>这里有个小坑,默认 schedule 是单线程。如果你在多个函数上使用了 @Scheduled,定时任务是顺序执行,只有等定时任务 1 执行完成才执行任务 2。若其中一个定时任务阻塞,会影响其他的定时任务。因此,我们必须对定时任务进行配置,使定时任务互相不干扰。</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.springframework.context.annotation.Configuration;</span><br><span class="line"><span class="keyword">import</span> org.springframework.scheduling.annotation.SchedulingConfigurer;</span><br><span class="line"><span class="keyword">import</span> org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;</span><br><span class="line"><span class="keyword">import</span> org.springframework.scheduling.config.ScheduledTaskRegistrar;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.Executors;</span><br><span class="line"><span class="comment">//配置自定义线程池</span></span><br><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ScheduleConfig</span> <span class="keyword">implements</span> <span class="title">SchedulingConfigurer</span> </span>{</span><br><span class="line"><span class="comment">// @Override</span></span><br><span class="line"><span class="comment">// public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {</span></span><br><span class="line"><span class="comment">// taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));</span></span><br><span class="line"><span class="comment">// }</span></span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">configureTasks</span><span class="params">(ScheduledTaskRegistrar taskRegistrar)</span> </span>{</span><br><span class="line"> taskRegistrar.setScheduler(<span class="keyword">this</span>.getTaskScheduler());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> ThreadPoolTaskScheduler <span class="title">getTaskScheduler</span><span class="params">()</span> </span>{</span><br><span class="line"> ThreadPoolTaskScheduler taskScheduler = <span class="keyword">new</span> ThreadPoolTaskScheduler();</span><br><span class="line"> taskScheduler.setPoolSize(<span class="number">5</span>);</span><br><span class="line"> taskScheduler.setThreadNamePrefix(<span class="string">"schedule-pool-"</span>);</span><br><span class="line"> taskScheduler.afterPropertiesSet();</span><br><span class="line"> <span class="keyword">return</span> taskScheduler;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>至此 @Scheduled 可以并发执行了,最高并发度是 20,但是同一个 @Schedule 不会并发执行。</p><h2 id="参考资料">参考资料</h2><ul><li>官方文档:<a href="https://github.com/lukas-krecan/ShedLock" target="_blank" rel="noopener">lukas-krecan/ShedLock: Distributed lock for your scheduled tasks</a></li><li>shedlock-demo:<a href="https://github.com/frank-lam/shedlock-demo" target="_blank" rel="noopener">frank-lam/shedlock-demo</a></li><li><a href="https://www.cnblogs.com/gutousu/p/10235160.html" target="_blank" rel="noopener">用shedlock实现分布式定时任务锁 - 骨头酥 - 博客园</a></li><li><a href="https://www.codenong.com/cs105991176/" target="_blank" rel="noopener">spring Boot 线程池异步执行多个定时任务 | 码农家园</a></li><li><a href="https://juejin.cn/post/6964947403128438821" target="_blank" rel="noopener">Spring Boot集成ShedLock分布式定时任务实例 - 掘金</a></li></ul>]]></content>
<summary type="html">
<h2 id="前言">前言</h2>
<p>在业务生产中,定时器 Scheduler 的使用频率非常高。Spring 也为我们提供了默认的定时器。只需要加上 <code>@Scheduled</code> 和 <code>@EnableScheduling</code> 两个注
</summary>
</entry>
<entry>
<title>Java 正则表达式的命名捕获组</title>
<link href="http://www.frankfeekr.cn/2021/09/25/java-regular-group/"/>
<id>http://www.frankfeekr.cn/2021/09/25/java-regular-group/</id>
<published>2021-09-25T08:45:04.000Z</published>
<updated>2021-10-23T14:22:52.649Z</updated>
<content type="html"><![CDATA[<h2 id="一-正则分组查询">一、正则分组查询</h2><h3 id="1-普通捕获组">1. 普通捕获组</h3><p>从正则表达式左侧开始,每出现一个左括号 “(” 记做一个分组,分组编号从 1 开始。0 代表整个表达式。</p><figure class="highlight java"><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 class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> String text = <span class="string">"2021-12-31"</span>;</span><br><span class="line"> Pattern pattern = Pattern.compile(<span class="string">"(\\d{4})-(\\d{2})-(\\d{2})"</span>);</span><br><span class="line"> Matcher matcher = pattern.matcher(text);</span><br><span class="line"> matcher.find(); <span class="comment">// 必须要有这句</span></span><br><span class="line"> System.out.println(matcher.group(<span class="number">1</span>));</span><br><span class="line"> System.out.println(matcher.group(<span class="number">2</span>));</span><br><span class="line"> System.out.println(matcher.group(<span class="number">3</span>));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="2-命名捕获组">2. 命名捕获组</h3><p>每个以左括号开始的捕获组,都紧跟着 ?\,而后才是正则表达式。</p><p>例如:2021-12-31 的正则表达式如下</p><figure class="highlight java"><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 class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> String text = <span class="string">"2021-12-31"</span>;</span><br><span class="line"> Pattern pattern = Pattern.compile(<span class="string">"(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})"</span>);</span><br><span class="line"> Matcher matcher = pattern.matcher(text);</span><br><span class="line"> matcher.find(); <span class="comment">// 必须要有这句</span></span><br><span class="line"> System.out.println(matcher.group(<span class="string">"year"</span>));</span><br><span class="line"> System.out.println(matcher.group(<span class="string">"month"</span>));</span><br><span class="line"> System.out.println(matcher.group(<span class="string">"day"</span>));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="二-regularutils-工具类">二、RegularUtils 工具类</h2><figure class="highlight java"><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><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> io.github.talelin.latticy.utils;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.alibaba.fastjson.JSON;</span><br><span class="line"><span class="keyword">import</span> com.alibaba.fastjson.JSONObject;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.Collections;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"><span class="keyword">import</span> java.util.Set;</span><br><span class="line"><span class="keyword">import</span> java.util.TreeSet;</span><br><span class="line"><span class="keyword">import</span> java.util.regex.Matcher;</span><br><span class="line"><span class="keyword">import</span> java.util.regex.Pattern;</span><br><span class="line"><span class="keyword">import</span> java.util.stream.Collectors;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RegularUtils</span> </span>{</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Pattern namedGroupCompile = Pattern.compile(<span class="string">"\\(\\?<([a-zA-Z][a-zA-Z0-9]*)>"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> JSONObject <span class="title">matchByGroupName</span><span class="params">(String text, String regex)</span> </span>{</span><br><span class="line"> Pattern pattern = Pattern.compile(regex);</span><br><span class="line"> Matcher matcher = pattern.matcher(text);</span><br><span class="line"> JSONObject jsonObject = <span class="keyword">new</span> JSONObject(<span class="keyword">true</span>);</span><br><span class="line"> Set<String> namedGroupCandidates = getNamedGroupCandidates(regex);</span><br><span class="line"> List<String> collect = namedGroupCandidates.stream().sorted(Collections.reverseOrder()).collect(Collectors.toList());</span><br><span class="line"> <span class="keyword">if</span> (matcher.find()) {</span><br><span class="line"> <span class="keyword">for</span> (String groupName : collect) {</span><br><span class="line"> jsonObject.put(groupName, matcher.group(groupName));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> jsonObject;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> jsonObject;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 命名捕获组</span></span><br><span class="line"><span class="comment"> * 正则编写好,可以直接匹配到需要的内容,不用多处理</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> regex</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> content</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> group</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">matchStr</span><span class="params">(String content, String regex, String group)</span> </span>{</span><br><span class="line"> Pattern pattern = Pattern.compile(regex);</span><br><span class="line"> Matcher matcher = pattern.matcher(content);</span><br><span class="line"> <span class="keyword">if</span> (matcher.find()) {</span><br><span class="line"> <span class="keyword">return</span> matcher.group(group);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取正则表达式对应的命名捕获组(name capture)</span></span><br><span class="line"><span class="comment"> * 输入:"(?<year>\\\\d{4})-(?<month>\\\\d{2})-(?<day>\\\\d{2}))"</span></span><br><span class="line"><span class="comment"> * 输出:[year,month,day]</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> regex</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Set<String> <span class="title">getNamedGroupCandidates</span><span class="params">(String regex)</span> </span>{</span><br><span class="line"> Set<String> namedGroups = <span class="keyword">new</span> TreeSet<String>();</span><br><span class="line"> Matcher m = namedGroupCompile.matcher(regex);</span><br><span class="line"> <span class="keyword">while</span> (m.find()) {</span><br><span class="line"> namedGroups.add(m.group(<span class="number">1</span>));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> namedGroups;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> String email = <span class="string">"2021-12-31"</span>;</span><br><span class="line"> String regex = <span class="string">"(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})"</span>;</span><br><span class="line"> JSONObject jsonObject = matchByGroupName(email, regex);</span><br><span class="line"> System.out.println(JSON.toJSONString(jsonObject, <span class="keyword">true</span>));</span><br><span class="line"> System.out.println(jsonObject.get(<span class="string">"year"</span>));</span><br><span class="line"> System.out.println(jsonObject.get(<span class="string">"month"</span>));</span><br><span class="line"> System.out.println(jsonObject.get(<span class="string">"day"</span>));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="三-参考资料">三、参考资料</h2><ul><li>Get group names in java regex<br><a href="https://newbedev.com/get-group-names-in-java-regex" target="_blank" rel="noopener">https://newbedev.com/get-group-names-in-java-regex</a></li><li>Java 正则表达式的捕获组(capture group) | 菜鸟教程<br><a href="https://www.runoob.com/w3cnote/java-capture-group.html" target="_blank" rel="noopener">https://www.runoob.com/w3cnote/java-capture-group.html</a></li></ul>]]></content>
<summary type="html">
<h2 id="一-正则分组查询">一、正则分组查询</h2>
<h3 id="1-普通捕获组">1. 普通捕获组</h3>
<p>从正则表达式左侧开始,每出现一个左括号 “(” 记做一个分组,分组编号从 1 开始。0 代表整个表达式。</p>
<figure class="hi
</summary>
</entry>
<entry>
<title>开发者如何编写优雅的技术文档 —— 手把手教你编写极致的文档</title>
<link href="http://www.frankfeekr.cn/2021/05/16/docs-tutorial/"/>
<id>http://www.frankfeekr.cn/2021/05/16/docs-tutorial/</id>
<published>2021-05-16T14:05:37.000Z</published>
<updated>2021-10-23T15:38:16.124Z</updated>
<content type="html"><</code> 标签进行引用。在使用效率上劝退了不少新手。</p><p>但是,在 Typora 编辑器中,图片的添加变得特别简单,将截图的图片直接复制即可,或是直接将图片拖动到编辑器中也可以。图片复制效率提升,方便对图片进行统一化管理。</p><blockquote><p><code>文件</code> - <code>偏好设置</code>中请将图片的配置改成如下,<code>./assets</code>。配置好后,如果通过截图复制、拖拽图片至 md 文件中,会自动在当前的目录(./assets)下复制进你的图片,可以很轻松的管理你的图片,不必再手写 md 图片标签。</p></blockquote><h4 id="表格编辑">表格编辑</h4><p>md 源码方式创建表格特别复杂,但是通过 Typora 软件可以极大提升表格编辑效率</p><ul><li>添加表格,输入行列即可成功添加表格</li><li>支持表格行、列的删除、移动</li><li>直接复制 Excel 上的表格到 Typora 自动生成表格</li></ul><h4 id="快捷键提效">快捷键提效</h4><ul><li>表格快捷键(这个推荐学习)<ul><li>表格:ctrl + t</li><li>移动表格的两行:alt + ↑ ↓ 方向键</li><li>移动表格的两列:alt + ← → 方向键</li><li>删除其中一行:ctrl + shift + delete</li><li>添加一行:ctrl + enter</li></ul></li><li>编辑快捷键(直接使用导航栏,逐步学习)<ul><li>无序列表:输入 - 之后输入空格</li><li>有序列表:输入数字 + “.” 之后输入空格</li><li>标题:ctrl + 数字</li><li>引用:输入 > 之后输入空格</li><li>代码块:输入三个反引号 ` + java + 回车</li><li>加粗:ctrl + b</li><li>倾斜:ctrl + i</li><li>下划线:ctrl + u</li><li>删除线:alt + shift + 5</li><li>插入图片:直接拖动到指定位置即可</li><li>插入链接:ctrl + k</li></ul></li><li>基础快捷键(直接使用导航栏,逐步学习)<ul><li>生成目录:输入 [TOC] 按回车</li><li>选中一整行:ctrl + l</li><li>选中单词:ctrl + d</li><li>选中相同格式的文字:ctrl + e</li><li>跳转到文章开头:ctrl + home</li><li>跳转到文章结尾:ctrl + end</li><li>搜索:ctrl + f</li><li>替换:ctrl + h</li><li>放大:ctrl + shift + =</li><li>缩小:ctrl + shift + -</li></ul></li></ul><h3 id="3-提效辅助插件">3. 提效辅助插件</h3><h4 id="tabcopy-chrome-插件">TabCopy - Chrome 插件</h4><blockquote><p>Quickly copy tabs to the clipboard!</p></blockquote><ul><li>插件下载地址:<a href="https://chrome.google.com/webstore/detail/tabcopy/micdllihgoppmejpecmkilggmaagfdmb?utm_source=chrome-ntp-icon" target="_blank" rel="noopener">https://chrome.google.com/webstore/detail/tabcopy/micdllihgoppmejpecmkilggmaagfdmb?utm_source=chrome-ntp-icon</a></li></ul><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20210314012827724.png" alt="image-20210314012827724"></p><p><strong>推荐语</strong>:</p><p>你是否还在为 md 中引用添加链接烦恼,使用 TabCopy 即可轻松实现「<strong>文字 + 链接</strong>」的复制。</p><figure class="highlight markdown"><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 class="section"># Expanded</span></span><br><span class="line">frank-lam/docs-tutorial: 开发者如何编写优雅的技术文档</span><br><span class="line">https://github.com/frank-lam/docs-tutorial</span><br><span class="line"></span><br><span class="line"><span class="section"># Compact</span></span><br><span class="line">frank-lam/docs-tutorial: 开发者如何编写优雅的技术文档: https://github.com/frank-lam/docs-tutorial</span><br><span class="line"></span><br><span class="line"><span class="section"># Link</span></span><br><span class="line">[<span class="string">frank-lam/docs-tutorial: 开发者如何编写优雅的技术文档</span>](<span class="link">https://github.com/frank-lam/docs-tutorial</span>)</span><br></pre></td></tr></table></figure><h4 id="拷贝猫-chrome-插件">拷贝猫 - Chrome 插件</h4><p>该扩展创建了一个右键菜单来提供复制功能,通过在支持的页面内容上单击右键来使用它!</p><p>插件下载地址:<a href="https://chrome.google.com/webstore/detail/copycat/jdjbiojkklnaeoanimopafmnmhldejbg?hl=zh-CN" target="_blank" rel="noopener">https://chrome.google.com/webstore/detail/copycat/jdjbiojkklnaeoanimopafmnmhldejbg?hl=zh-CN</a></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20210515102236248.png" alt="image-20210515102236248"></p><h4 id="vscode-toc-目录生成插件">VSCode TOC 目录生成插件</h4><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20210314002508169.png" alt="image-20210314002508169"></p><p>插件地址:<a href="https://marketplace.visualstudio.com/items?itemName=AlanWalk.markdown-toc" target="_blank" rel="noopener">https://marketplace.visualstudio.com/items?itemName=AlanWalk.markdown-toc</a></p><p>在你的 md 文档顶部,右键 【Markdown TOC: Insert/Update】即可自动生成文档的目录,效果类似于 <code>[TOC]</code>,但是在 Github 中并不支持 <code>[TOC]</code> 命令,故可以使用此插件生成目录,在长文阅读的时候提升体验。</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20210314002330558.png" alt="image-20210314002330558"></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20210314002358591.png" alt="image-20210314002358591"></p><h3 id="3-绘图与标注">3. 绘图与标注</h3><h4 id="draw-io"><a href="http://draw.io" target="_blank" rel="noopener">draw.io</a></h4><blockquote><p><a href="http://draw.io" target="_blank" rel="noopener">draw.io</a> 当前已经改名成 <a href="http://diagrams.net" target="_blank" rel="noopener">diagrams.net</a>,是一款免费的在线图表编辑工具,可以用来编辑 BPM, org charts, UML, ER图, 网络拓朴图等各种覆盖的图。</p><p>类似于 ProcessOn 的在线画图平台,但是 <a href="http://draw.io" target="_blank" rel="noopener">draw.io</a> 完全免费。支持在线直接画图,chrome 插件客户端,桌面客户端。</p></blockquote><ul><li>在线画图:<a href="https://app.diagrams.net" target="_blank" rel="noopener">https://app.diagrams.net</a></li><li>Chrome 插件下载地址:<a href="https://chrome.google.com/webstore/detail/diagramsnet-and-drawio-im/cnoplimhpndhhhnmoigbanpjeghjpohi?utm_source=chrome-ntp-icon" target="_blank" rel="noopener">https://chrome.google.com/webstore/detail/diagramsnet-and-drawio-im/cnoplimhpndhhhnmoigbanpjeghjpohi?utm_source=chrome-ntp-icon</a></li></ul><ul><li>官网地址:<a href="https://www.diagrams.net" target="_blank" rel="noopener">https://www.diagrams.net</a></li></ul><p>推荐使用 <strong><a href="http://draw.io" target="_blank" rel="noopener">draw.io</a></strong> 绘图,导出为 svg 图片,效果体验更好,不失真。该软件提供了 chrome 插件,可直接一键快速安装。</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20210314003521112.png" alt="image-20210314003521112"></p><h4 id="截图软件-snipaste">截图软件 Snipaste</h4><blockquote><p>推荐使用 <strong>Snipaste</strong> 作为你的截图工具。(下载解压无需安装,按下<code>F1</code>来开始截图)</p><p>官网:<a href="https://zh.snipaste.com" target="_blank" rel="noopener">https://zh.snipaste.com</a></p></blockquote><p><strong>亮点功能:</strong></p><ul><li>自动检测界面元素区域</li><li>截图清晰不失真</li><li>支持贴图功能,这个太实用了</li><li>支持截图的图片回放功能</li><li>编辑过的截图支持回放再次编辑(在写文档的时候会在截图上有大量的批注工作,这个是非常实用的!目前其他还没有一款截图工具支持这种能力)</li><li>前端 / 设计 / 原型图设计中,<strong>可作为图片拾色器</strong>(F1 启动截图,确定颜色后按住 C),<strong>参考线</strong>(F1 启动截图,按住 ALT)</li></ul><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/N3QEb3VA.png" alt="img"></p><h3 id="4-版本控制">4. 版本控制</h3><p>作为开发者,版本控制肯定会想到 Git,当然你可以使用原生的 Git 命令进行版本控制,但是这里更加推荐可视化工具 SourceTree,让新手无需学习太多命令,轻松上手。特别是在一些图片的版本控制中,方便预览图片。</p><p>官网地址:<a href="https://www.sourcetreeapp.com" target="_blank" rel="noopener">https://www.sourcetreeapp.com</a></p><h4 id="sourcetree">SourceTree</h4><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20210314003028491.png" alt="image-20210314003028491"></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20210314003049439.png" alt="image-20210314003049439"></p><h3 id="5-排版规范">5. 排版规范</h3><h4 id="中英文标点">中英文标点</h4><p>说明:在文档编写中,必须注意中英文标点符号的使用,在中文的文档中不要出现中英文符号混用的情况。常见符号,如下</p><table><thead><tr><th>说明</th><th>中文</th><th>英文</th></tr></thead><tbody><tr><td>逗号</td><td>,</td><td>,</td></tr><tr><td>句号</td><td>。</td><td>.</td></tr><tr><td>分号</td><td>;</td><td>;</td></tr><tr><td>问号</td><td>?</td><td>?</td></tr><tr><td>感叹号</td><td>!</td><td>!</td></tr></tbody></table><p>示例:</p><figure class="highlight markdown"><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></pre></td><td class="code"><pre><span class="line"><span class="section">## 错误写法 ❌</span></span><br><span class="line">作为一名程序员,日常大部分的时间主要在埋头编码、调试.</span><br><span class="line"></span><br><span class="line"><span class="section">## 推荐写法 ✅</span></span><br><span class="line">作为一名程序员,日常大部分的时间主要在埋头编码、调试。</span><br></pre></td></tr></table></figure><h4 id="专用名词拼写">专用名词拼写</h4><p>示例:</p><figure class="highlight markdown"><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></pre></td><td class="code"><pre><span class="line"><span class="section">## 错误写法 ❌</span></span><br><span class="line">我热爱 java 编程语言!</span><br><span class="line"></span><br><span class="line"><span class="section">## 推荐写法 ✅</span></span><br><span class="line">我热爱 Java 编程语言!</span><br></pre></td></tr></table></figure><h4 id="英文左右空格">英文左右空格</h4><p>说明:遇到英文字符或单词,请在左右都加上空格,让文本阅读更加舒适。无论在文档还是平面设计中这也是规范之一,特别是苹果的设计语言中我们都可以看到这一点。</p><p>示例:</p><figure class="highlight markdown"><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></pre></td><td class="code"><pre><span class="line"><span class="section">## 错误写法 ❌</span></span><br><span class="line">窄带LED背光组件使 MacBook Pro支持P3广色域显示,为照片和视频带来绚丽逼真的色彩。</span><br><span class="line"></span><br><span class="line"><span class="section">## 推荐写法 ✅</span></span><br><span class="line">窄带 LED 背光组件使 MacBook Pro 支持 P3 广色域显示,为照片和视频带来绚丽逼真的色彩。</span><br></pre></td></tr></table></figure><h4 id="使用代码块和代码高亮">使用代码块和代码高亮</h4> <figure class="highlight markdown"><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></pre></td><td class="code"><pre><span class="line"><span class="section">## 代码高亮</span></span><br><span class="line">先定位到你的目录,然后输入命令 <span class="code">`ls -al`</span> 即可列出该目录下的所有文件。</span><br><span class="line"></span><br><span class="line"><span class="section">## 代码块 </span></span><br><span class="line"><span class="code">```java</span></span><br><span class="line"><span class="code">public class HelloWorld {</span></span><br><span class="line"><span class="code"> public static void main(String[] args) {</span></span><br><span class="line"><span class="code"> System.out.println("我的第一个Java程序:Hello World。");</span></span><br><span class="line"><span class="code"> }</span></span><br><span class="line"><span class="code">}</span></span><br></pre></td></tr></table></figure><h2 id="二-文档行文">二、文档行文</h2><p>文档在行文编写部分,个人认为《给程序员的中文写作指北》一文中说明的特别详尽,本节使用原文引用。</p><h3 id="1-句子长度">1. 句子长度</h3><p>技术文章、博客文章忌用复杂的长句和重句。</p><p>主要原因是,当句子一长我们的大脑需要花太多的时间来理解这个句子的字面意义而不能专注于理解这个句子要传达的内容因而这与我们的目的完全南辕北辙。</p><p>上面这个句子读起来就非常痛苦了,换位思考一下,如果你的文章这么写读者该多痛苦。我建议,句子的长度应该控制在15个词以内,如果有长句的话,应该用标点切分为较短的句子。同时在重复率不高的情况下,尽量用简单的词汇。上面的句子可以改写为:</p><p>主要原因是,当句子一长,我们的大脑需要花太多时间,来理解这个句子的字面意义。而此时我们的大脑无法专注句子本身要传达的内容,这与我们的目的完全相悖。</p><h3 id="2-图文并茂">2. 图文并茂</h3><p>如果有条件,尽量在文章中插入一些与逻辑和表达的内容相关的图片。</p><p>图文并茂不光可以让你的读者读起来更轻松,更让你有机会用图表、gif 或其它丰富的媒体形式来表达你的观点,一举几得。同时,如果你的网站需要流量,搜索引擎对相关的图片和视频会增加权重,有利于你的搜索流量增长。</p><h3 id="3-起承转合">3. 起承转合</h3><p>即使技术文章的主要写作文体更偏向议论文,但在写作时也需要注意起承转合。</p><p>如果你需要像本文一样,列出一系列观点,将这些观点以合理的逻辑串起来也是非常重要的。</p><p>在文章结构上,我通常会采用总、分、总的结构。</p><p>总 - 即文首先告诉读者会读到什么,如果读者不感兴趣的话可以省下来读一篇文章的时间,而如果感兴趣的话正好是个好引子。</p><p>分 - 即分散表达需要表达的观点,比如在本文中,即三个部分:关于内容、关于标点和关于行文的三块建议。</p><p>总 - 最后总结文章,你要假设读者并没有时间详读全文,但这里的总结可以让读者能在 10 秒内仍然知道你在这篇文章里表达了什么</p><h3 id="4-引用链接">4. 引用链接</h3><p>把你引用的内容放在合适的位置,不仅是对被引用文章作者的尊重,也方便你的读者继续阅读下去。</p><p>千万不要觉得引用文章会让人觉得你只是在观点抄袭,事实上只要你有提出新鲜的观点、论据甚至只是更规范地总结了一篇文章,你的读者也会非常感激你的。</p><p>同时引用丰富的链接还可以帮助搜索引擎确定你的权威性——你不光文章写出来了,还指向了靠谱的参考文献,因此搜索引擎也会觉得你这篇文章的靠谱程度很高。</p><h2 id="附录:主流文档协作平台">附录:主流文档协作平台</h2><ul><li>Teambition Thoughts - 面向中小企业的知识管理工具<ul><li><strong>官网</strong>:<a href="https://thoughts.teambition.com" target="_blank" rel="noopener">https://thoughts.teambition.com</a></li><li><strong>推荐</strong>:特别适合一些产品的对外官方指导或是说明文档</li></ul></li><li>石墨文档 - 企业在线协同办公系统平台,支持云端多人在线协作编辑文档和表格<ul><li><strong>官网</strong>:<a href="https://shimo.im" target="_blank" rel="noopener">https://shimo.im</a></li><li><strong>推荐</strong>:将文档的编写真正做到了极致,使用体验和设计风格有点像水墨屏。支持文本、表格、思维导图等等。</li></ul></li><li>飞书文档 - 可多人实时编辑的在线文档。<ul><li><strong>官网</strong>:<a href="https://www.feishu.cn" target="_blank" rel="noopener">https://www.feishu.cn</a></li><li><strong>推荐</strong>:飞书系列产品真是把办公体验做到了极致,特别推荐飞书文档中的思维导图,这是我目前用到过的最好用的在线思维导图,好看又好用。</li></ul></li><li>语雀 - 专业的云端知识库<ul><li><strong>官网</strong>:<a href="https://www.yuque.com" target="_blank" rel="noopener">https://www.yuque.com</a></li><li><strong>推荐</strong>:类似于 Thoughts ,团队文档协作</li></ul></li></ul><h2 id="参考引用">参考引用</h2><ul><li>给程序员的中文写作指北 | 卡拉搜索<br><a href="https://kalasearch.cn/blog/writing-guide-for-programmers" target="_blank" rel="noopener">https://kalasearch.cn/blog/writing-guide-for-programmers</a></li></ul>]]></content>
<summary type="html">
<h2 id="前言">前言</h2>
<p>作为一名程序员,日常大部分的时间主要在埋头编码、调试。但在技术的世界里,不止有代码,如果仅仅只会编写程序是不够的。当你需要展示你的想法、技术分享、架构设计,这时候好的技术文档变的至关重要。</p>
<p>文档伴随开发者的日常工作,例如
</summary>
</entry>
<entry>
<title>一行命令实现 Github 国内镜像加速</title>
<link href="http://www.frankfeekr.cn/2021/03/28/github-speed-up/"/>
<id>http://www.frankfeekr.cn/2021/03/28/github-speed-up/</id>
<published>2021-03-28T13:47:11.000Z</published>
<updated>2021-10-23T14:22:52.647Z</updated>
<content type="html"><![CDATA[<p>请注意:该方法只使用于 https 协议,不适用于 SSH 协议!</p><h2 id="一-加速镜像">一、加速镜像</h2><h3 id="克隆加速">克隆加速</h3><figure class="highlight bash"><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 class="comment"># 原地址</span></span><br><span class="line">git <span class="built_in">clone</span> https://github.com/kubernetes/kubernetes.git</span><br><span class="line"></span><br><span class="line"><span class="comment"># 改为(推荐)</span></span><br><span class="line">git <span class="built_in">clone</span> https://github.com.cnpmjs.org/kubernetes/kubernetes.git</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或者</span></span><br><span class="line">git <span class="built_in">clone</span> https://hub.fastgit.org/kubernetes/kubernetes.git</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或者</span></span><br><span class="line">git <span class="built_in">clone</span> https://gitclone.com/github.com/kubernetes/kubernetes.git</span><br></pre></td></tr></table></figure><h3 id="release下载加速">release下载加速</h3><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 原地址</span></span><br><span class="line">wget https://github.com/goharbor/harbor/releases/download/v2.0.2/harbor-offline-installer-v2.0.2.tgz</span><br><span class="line"></span><br><span class="line"><span class="comment"># 改为</span></span><br><span class="line">wget https://hub.fastgit.org/goharbor/harbor/releases/download/v2.0.2/harbor-offline-installer-v2.0.2.tgz</span><br></pre></td></tr></table></figure><h3 id="raw文件下载加速">raw文件下载加速</h3><figure class="highlight bash"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 原地址</span></span><br><span class="line">wget https://raw.githubusercontent.com/kubernetes/kubernetes/master/README.md</span><br><span class="line"></span><br><span class="line"><span class="comment"># 替换为</span></span><br><span class="line">wget https://raw.staticdn.net/kubernetes/kubernetes/master/README.md</span><br></pre></td></tr></table></figure><h2 id="二-一键免替换镜像地址">二、一键免替换镜像地址</h2><h3 id="替换设置">替换设置</h3><p>可以直接在配置文件中动态替换 Github 的地址,这样不用每次克隆的时候都修改地址</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global url.<span class="string">"https://github.com.cnpmjs.org/"</span>.insteadOf <span class="string">"https://github.com/"</span></span><br></pre></td></tr></table></figure><p>测试</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/kubernetes/kubernetes.git</span><br></pre></td></tr></table></figure><h3 id="取消设置">取消设置</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global --<span class="built_in">unset</span> url.https://github.com/.insteadof</span><br></pre></td></tr></table></figure><h3 id="查看-git-配置信息">查看 Git 配置信息</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global --list</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<p>请注意:该方法只使用于 https 协议,不适用于 SSH 协议!</p>
<h2 id="一-加速镜像">一、加速镜像</h2>
<h3 id="克隆加速">克隆加速</h3>
<figure class="highlight bash"><table><tr><td c
</summary>
</entry>
<entry>
<title>使用 Certbot 为网站签发永久免费的 HTTPS 证书</title>
<link href="http://www.frankfeekr.cn/2021/03/28/let-is-encrypt-cerbot-for-https/"/>
<id>http://www.frankfeekr.cn/2021/03/28/let-is-encrypt-cerbot-for-https/</id>
<published>2021-03-28T11:01:10.000Z</published>
<updated>2021-10-23T14:22:52.649Z</updated>
<content type="html"><![CDATA[<p>我们常常在自己的服务器上搭建 Web 服务,但是大部分都使用的 HTTP 协议。在一些特殊的场景下,例如:微信小程序或是 APP 后台服务开发,则必须使用 HTTPS 协议。众所周知,HTTPS 可以在各个云服务厂商上购买,通过都需要高昂的费用,往往劝退很多初级的个人开发者。</p><p>但是如果你使用 Nginx、Apache 等服务器来运行服务,则可以使用 <a href="https://certbot.eff.org/" target="_blank" rel="noopener">Certbot</a> 来申请免费的 SSL 证书,轻松帮你实现 HTTPS 网站。申请 Let’s Encrypt 证书不但免费,还非常简单,虽然每次只有 90 天的有效期,但可以通过脚本定期更新,配好之后一劳永逸。</p><blockquote><p>Let’s Encrypt 是由互联网安全研究小组(ISRG,一个公益组织)提供的服务,主要赞助商包括电子前哨基金会,Mozilla基金会,Akamai以及思科。</p><p>ISRG于2015年4月9日与Linux基金会宣布合作。</p><p>Let’s Encrypt 最初是由 Mozilla 的两名员工于2012年发起,2015年12月3日开启公测,2016年4月12日,该项目正式对外使用。</p></blockquote><p>本文将记录 Centos 7.4 + Nginx + Certbot 为 <strong><a href="http://blog.frankfeekr.cn" target="_blank" rel="noopener">blog.frankfeekr.cn</a></strong> 网站安装安全的 HTTPS 证书,并且实现到期自动续期。</p><h2 id="1-安装-certbot">1. 安装 Certbot</h2><figure class="highlight bash"><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">yum install epel-release -y</span><br><span class="line">yum install certbot -y</span><br></pre></td></tr></table></figure><h2 id="2-申请证书">2. 申请证书</h2><p>这里证书可以申请单域名或是泛域名证书。这里推荐大家申请泛域名证书申请,例如:*.frankfeekr.cn 域名证书申请后,即可用于该域名下的三级域名。即:<a href="http://blog.frankfeekr.cn" target="_blank" rel="noopener">blog.frankfeekr.cn</a> / <a href="http://docs.frankfeekr.cn" target="_blank" rel="noopener">docs.frankfeekr.cn</a> 等等三级域名都可以使用。</p><ul><li>安装 python-certbot-nginx 插件</li></ul><p>在生成证书的过程需要确保 Nginx 关闭,需要使用 80 端口执行。</p><p>在申请证书前需要先安装 certbot 的 Nginx 插件,如果执行 <code>certbot --nginx</code> 出现 <code>The requested nginx plugin does not appear to be installed</code>,则需要先安装插件。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install python-certbot-nginx -y</span><br></pre></td></tr></table></figure><h3 id="1-单域名证书">(1)单域名证书</h3><p>执行命令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">certbot certonly -d blog.frankfeekr.cn --nginx</span><br></pre></td></tr></table></figure><p>执行结果</p><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line">IMPORTANT NOTES:</span><br><span class="line"> - Congratulations! Your certificate and chain have been saved at:</span><br><span class="line"> /etc/letsencrypt/live/blog.frankfeekr.cn/fullchain.pem</span><br><span class="line"> Your key file has been saved at:</span><br><span class="line"> /etc/letsencrypt/live/blog.frankfeekr.cn/privkey.pem</span><br><span class="line"> Your certificate will expire on 2021-06-26. To obtain a new or</span><br><span class="line"> tweaked version of this certificate in the future, simply run</span><br><span class="line"> certbot again. To non-interactively renew *all* of your</span><br><span class="line"> certificates, run "certbot renew"</span><br><span class="line"> - If you like Certbot, please consider supporting our work by:</span><br><span class="line"></span><br><span class="line"> Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate</span><br><span class="line"> Donating to EFF: https://eff.org/donate-le</span><br></pre></td></tr></table></figure><p>生成的证书会放在:<code>/etc/letsencrypt/live/blog.frankfeekr.cn</code> 目录下,目录证书说明如下:</p><figure class="highlight markdown"><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></pre></td><td class="code"><pre><span class="line">This directory contains your keys and certificates.</span><br><span class="line"></span><br><span class="line"><span class="code">`[cert name]/privkey.pem`</span> : the private key for your certificate.</span><br><span class="line"><span class="code">`[cert name]/fullchain.pem`</span>: the certificate file used in most server software.</span><br><span class="line"><span class="code">`[cert name]/chain.pem`</span> : used for OCSP stapling in Nginx >=1.3.7.</span><br><span class="line"><span class="code">`[cert name]/cert.pem`</span> : will break many server configurations, and should not be used</span><br><span class="line"><span class="code"> without reading further documentation (see link below).</span></span><br><span class="line"></span><br><span class="line">WARNING: DO NOT MOVE OR RENAME THESE FILES!</span><br><span class="line"><span class="code"> Certbot expects these files to remain in this location in order</span></span><br><span class="line"><span class="code"> to function properly!</span></span><br><span class="line"></span><br><span class="line">We recommend not moving these files. For more information, see the Certbot</span><br><span class="line">User Guide at https://certbot.eff.org/docs/using.html#where-are-my-certificates.</span><br></pre></td></tr></table></figure><h3 id="2-泛域名证书-推荐">(2)泛域名证书(推荐)</h3><p>上面我们学会了生成单个域名证书,但是这里更推荐大家申请泛域名证书,方便在多个网站服务托管的时候共同使用。</p><p>执行命令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">certbot certonly --preferred-challenges dns --manual -d *.frankfeekr.cn --server https://acme-v02.api.letsencrypt.org/directory</span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20210328171038158.png" alt="image-20210328171038158"></p><p>上面的命令执行到这里,会在命令行阻塞,这时候我们需要到域名解析的服务商添加一条 txt 的解析,如下图所示。</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20210328170744447.png" alt="image-20210328170744447"></p><p>添加完解析后稍等几秒钟,即可回车继续,这时候就会校验记录是否有效。</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20210328171113452.png" alt="image-20210328171113452"></p><p>至此:已经完成了泛域名的生成,生成的证书在:<code>/etc/letsencrypt/live</code> 目录下。相比于单域名的证书,泛域名步骤会稍微复杂一些,大家根据自己应用场景申请即可。</p><h2 id="3-安装-nginx">3. 安装 Nginx</h2><p>如果已经安装了 Nginx 请直接跳过这一步</p><figure class="highlight shell"><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">yum install epel-release -y</span><br><span class="line">yum install nginx -y</span><br></pre></td></tr></table></figure><h2 id="4-nginx-证书配置">4. Nginx 证书配置</h2><p>在 <code>/etc/nginx/conf.d</code> 目录下创建 <strong>blog.frankfeekr.cn.conf</strong>(文件名以 <code>.conf</code> 后缀,推荐使用域名命名方便管理)</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">server {</span><br><span class="line"> listen 443 ssl;</span><br><span class="line"> server_name blog.frankfeekr.cn;</span><br><span class="line"> ssl_certificate /etc/letsencrypt/live/frankfeekr.cn/fullchain.pem;</span><br><span class="line"> ssl_certificate_key /etc/letsencrypt/live/frankfeekr.cn/privkey.pem;</span><br><span class="line"> ssl_protocols TLSv1 TLSv1.1 TLSv1.2;</span><br><span class="line"> ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;</span><br><span class="line"> ssl_prefer_server_ciphers on;</span><br><span class="line"> root /usr/share/nginx/html;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>配置完证书后重启服务</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">service nginx restart</span><br></pre></td></tr></table></figure><h2 id="5-自动续期">5. 自动续期</h2><p>首先检查 crontab 服务是否运行</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl status crond</span><br></pre></td></tr></table></figure><p>如果没有启动,先 enable 它再 start 它</p><figure class="highlight bash"><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">systemctl <span class="built_in">enable</span> crond</span><br><span class="line">systemctl start crond</span><br></pre></td></tr></table></figure><p>编辑计划任务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">crontab -e</span><br></pre></td></tr></table></figure><p>输入下面的表达式(每天 00:00:00),让他每天都尝试一次关闭 Nginx->更新->启动 Nginx,到了最后 30 天的时候就会成功。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0 0 * * * <span class="string">"service nginx stop ; /bin/certbot renew --renew-by-default; service nginx start"</span></span><br></pre></td></tr></table></figure><h2 id="6-访问-https-网站">6. 访问 https 网站</h2><p>点击访问:<a href="https://blog.frankfeekr.cn" target="_blank" rel="noopener">https://blog.frankfeekr.cn</a></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20210328224333690.png" alt="image-20210328224333690"></p><p>证书查看:有效期是 90 天,待快到期则可以通过上述配置的定时任务来更新。</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20210328173120842.png" alt="image-20210328173120842"></p><h2 id="faq">FAQ</h2><ol><li><p>证书重置请求超过次数,一般 3 次,子域名除外。就会出现如下错误,5 天以后才可以再次重置。</p><figure class="highlight groovy"><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">An unexpected error <span class="string">occurred:</span></span><br><span class="line">There were too many requests of a given <span class="string">type :</span>: Error creating <span class="keyword">new</span> <span class="string">order :</span>: too many certificates already issu <span class="keyword">for</span> exact set of <span class="string">domains:</span> blog.frankfeekr.<span class="string">cn:</span> see <span class="string">https:</span><span class="comment">//letsencrypt.org/docs/rate-limits/</span></span><br></pre></td></tr></table></figure><p><a href="https://letsencrypt.org/zh-cn/docs/rate-limits/" target="_blank" rel="noopener">速率限制 - Let’s Encrypt - 免费的SSL/TLS证书</a></p></li></ol><h2 id="参考链接">参考链接</h2><ul><li><p>Let’s Encrypt - Free SSL/TLS Certificates<br><a href="https://letsencrypt.org/" target="_blank" rel="noopener">https://letsencrypt.org/</a></p></li><li><p>centos7+nginx+certbot+自动续期 - mntm博客<br><a href="http://www.mntm520.com/post/48/" target="_blank" rel="noopener">http://www.mntm520.com/post/48/</a></p></li><li><p>Web开发必知必会,如何使用 Letsencrypt 为网站签发 HTTPS 证书提供安全支持_如小非的博客-CSDN博客<br><a href="https://blog.csdn.net/mrmengj/article/details/112983348" target="_blank" rel="noopener">https://blog.csdn.net/mrmengj/article/details/112983348</a></p></li><li><p>轻松搞定 Let’s Encrypt 免费SSL证书 | 张衡Henry<br><a href="https://www.izhangheng.com/https-ssl-lets-encrypt-certbot" target="_blank" rel="noopener">https://www.izhangheng.com/https-ssl-lets-encrypt-certbot</a></p></li><li><p>Certbot - Centosrhel7 Nginx<br><a href="https://certbot.eff.org/lets-encrypt/centosrhel7-nginx" target="_blank" rel="noopener">https://certbot.eff.org/lets-encrypt/centosrhel7-nginx</a></p></li></ul>]]></content>
<summary type="html">
<p>我们常常在自己的服务器上搭建 Web 服务,但是大部分都使用的 HTTP 协议。在一些特殊的场景下,例如:微信小程序或是 APP 后台服务开发,则必须使用 HTTPS 协议。众所周知,HTTPS 可以在各个云服务厂商上购买,通过都需要高昂的费用,往往劝退很多初级的个人开发者
</summary>
</entry>
<entry>
<title>关于校招,你必须了解的那些事</title>
<link href="http://www.frankfeekr.cn/2020/05/31/about-campus-apply/"/>
<id>http://www.frankfeekr.cn/2020/05/31/about-campus-apply/</id>
<published>2020-05-31T08:19:49.000Z</published>
<updated>2021-10-23T14:22:52.647Z</updated>
<content type="html"><![CDATA[<h2 id="分享大纲">分享大纲</h2><p>(1)什么是校招? 什么时候开始准备校招?<br>(2)还很迷茫吗? 带你一起了解都有哪些就业岗位? 手把手教你选择适合自己的技术栈?<br>(3)如何准备校招面试?<br>(4)答疑讨论环节</p><h2 id="1-校招初体验">1. 校招初体验</h2><h3 id="1-1-什么是校招">1.1 什么是校招</h3><p>校招(即,校园招聘),应届生通过企业的校园招聘途经进行面试选拔。</p><p>应届生由于没有工作经验,如果直接走普通的社会招聘很难进入大型企业,并且拿到期待的薪资。大型企业都有完善的校园招聘通道,把握好校招的时间点才能进入自己梦寐以求的企业和岗位。</p><h3 id="1-2-校招时间">1.2 校招时间</h3><ul><li>春招<ul><li>每年 3-4 月(<strong>金三银四</strong>),主要以实习生招聘为主,和少量的正式校招岗位</li></ul></li><li>秋招<ul><li>每年 9-11 月(<strong>金九银十</strong>),主要以正式校园招聘岗位为主</li></ul></li></ul><p>例如:小明 2021 年 6 月毕业,则 2020 年春招(3-4月)着手准备<strong>实习生招聘</strong>,秋招(9-11 月)则要进行<strong>正式校招面试</strong>,面试通过发放 offer 直接签订三方协议。如果在秋招中没有拿到心仪的 offer,则只能到 2021 年春招再次进行面试,但是岗位和数量也都比较少。</p><h3 id="1-3-招聘流程">1.3 招聘流程</h3><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/trip_bg.f6847dd9.png" alt="img"></p><ul><li>关于内推:<ul><li>可以找往届学长学姐、就业群、牛客网找到一些企业内部的员工进行内推。(内推成功都会有提成的,也有很大一部分内推也作为员工 KPI 的一部分)</li></ul></li></ul><h2 id="2-还很迷茫吗?带你一起了解都有哪些就业岗位?-手把手教你选择适合自己的技术栈?">2. 还很迷茫吗?带你一起了解都有哪些就业岗位? 手把手教你选择适合自己的技术栈?</h2><p>学前端开发?学 APP 开发?学大数据开发?还是学后台开发呢?</p><p>对于 Java、C++、C#、Python、PHP 这些编程语言,又要如何选择呢?</p><p>现如今人工智能如此火热,是不是机器学习、深度学习更高级一些呢?</p><p>到底如何选择适合自己的技术岗位</p><h3 id="2-1-有哪些技术栈">2.1 有哪些技术栈</h3><p>在进行技术栈选择之前,需要对当下互联网技术进行调研。先要知道都有哪些技术栈,了解技术的基本,方能找到适合自己的技术栈。</p><p>在当前互联网技术中,笔者将核心技术分为以下四个方向,它们都是互联网主要的技术方向。</p><h4 id="1-大前端开发">1. 大前端开发</h4><p>之所以称之为“大前端”,它不仅仅是网站的界面开发,从广义上讲,用户终端视觉和交互相关的部分,都属于“大前端”的范畴。在当今互联网发展的今天,多场景融合展现技术,日益复杂的界面交互变化,赋予了前端更广阔的能力。</p><p>前端技术的核心是 HTML、CSS、JavaScript,它是整个前端的灵魂。同时在前端的技术栈中 Vue、React、Angular 是最值得关注的三个开发框架。在移动端,目前有:HTML5+原生、Javascript 开发 + 原生渲染(React Native、Weex)、自绘 UI + 原生(Flutter)、增强版 Web App(PWA)是当前主流的移动跨平台解决方案。在移动端各种软件平台都相继推出小程序,构建在自身的软件上,即扫即用、用完即走,这些小程序也同样是前端的研究领域。</p><p>前端开发是用户看得见摸得着的,它注重用户的体验,是最接进产品和设计的。相比于后台服务端,端测开发可以让你构建一个丰富多彩、所见即所得的软件交互体验。</p><p>如果你对构建有趣的界面和处理各种交互逻辑感兴趣,那么前端开发或许是你的兴趣所在。</p><h4 id="2-后台开发">2. 后台开发</h4><p>春运 12306 铁路抢票、全球双 11 购物狂欢节,之所以能够抗住巨大的流量洪峰,离不开最核心的后台系统服务。后台服务对用户不同于前端,它对用户来说是无法直接感知的,它不同于前端可见的界面。抢票时用户身份的认证、车次的查询、车票订单的查询、订单支付等等,用户数据的存储、查询、验证等等算法与逻辑,一套在系统服务端运行的程序,这就是后台服务,它是用户无法直接感受到,但是确实随时随地都在使用的。</p><p>后台开发,有时也称做后端开发。其中包含:后台编程语言(Java、Python、C++等)、数据库存储(MySQL、Postgre、Redis)、后台服务框架(如最为火热的 SpringBoot 框架)、消息中间件(Kafka)、容器化引擎(Docker、Kubernates)、服务器编程(Linux)等。总而言之,后台开发就是围绕分布式、高可用、高性能、高可靠展开的一些技术工作。</p><p>虽然后台开发不像前端一样,所见即所得。但后台开发是一个推动系统具有生命力的根,它包含了很强的架构设计与实现。一个大型的后台服务可以支撑了亿万流量的平台,是一个产品能够运营的基本。</p><p>如果你对系统设计感兴趣,热爱 Java、Python、C++、Go 等语言,想要成为一个系统架构师,那么后台开发是一个不错的选择。</p><h4 id="3-大数据开发">3. 大数据开发</h4><p>随着系统运营的时间发展,一个系统将积累了越来越多的数据,当数据达到 PB、EB 级别时,后台服务将面临了更大的挑战。这些挑战,不是后台技术栈就能够解决的,传统的关系型数据库已经无法存储这么大体量的数据。那么,这就是大数据需要解决的问题。</p><p>大数据有三个重要的特征(简称 3V 特征):大量 (Volume),高速 (Velocity),多样化 (Variety),那么面对海量数据,如何接入、如何存储与查询、如何分析?这便是大数据开发领域,需要实现的工程能力。</p><p>大数据开发,需要掌握大数据通用处理平台,如:Spark、Flink、Hadoop 等的使用;大数据编程语言,包括 Scala、Python、Java 等;分布式文件存储 HDFS;数据仓库,如:Hive、ElasticSearch;海量数据实时查询,如 Apache Druid、InfluxDB 等时序数据库;大数据计算资源调度,如 Yarn、Mesos 等;在数据接入需要了解消息队列 Kafka、RocketMQ 等;日志收集框架,如:Flume、ELK 等;此外海量数据要进行分析,也需要掌握常见的机器学习工具包,挖掘海量数据中的有用价值。</p><p>大数据开发不同于后台开发,也有别于算法。大数据开发工程师需要掌握良好的工程能力,实现从数据的接入,数据存储,数据查询,数据分析。</p><p>如果你对海量数据处理、存储、分析感兴趣,并且有一定的后台开发基础,那么大数据开发是一个适合你的选择。</p><h4 id="4-算法应用">4. 算法应用</h4><p>你一定使用过高铁站的人脸识别,也一定听说过可爱调皮的小爱同学;你可能对自动驾驶充满向往,也可能对智能家居满怀憧憬;你可能遐想过人工智能技术在医疗领域大放异彩,也可能想象过生活在智慧城市是一种怎样便捷的感觉,但这些仅仅是人工智能领域的冰山一角。总之,人工智能技术已经在我们生活中像医疗、交通、教育、金融、生活、零售、安防、园区、环保、政务等各个方面发挥着举足轻重的作用。目前世界上主要国家都在人工智能领域进行战略布局。在就业方面,国际、国内各大厂也早已打响了人工智能人才争夺战,作为热爱算法的你是否早已跃跃欲试了呢?</p><p>算法应用领域包括的方面很多,传统的数据结构、高级算法设计等是基础,在这个人工智能技术风靡全球、飞速发展的时代,人工智能技术早已在算法应用领域占据很大的比重。机器学习是人工智能的基础,机器学习在大的层面上分为传统机器学习和深度学习,从另一个角度又可以分为非监督学习、弱监督学习和强监督学习;从应用场景角度还可以分为自然语言处理(NLP)、计算机视觉(CV),以及二者相互结合。对于机器学习从业者而言,理论层面上需要掌握像逻辑回归(LR)、感知机、最近邻算法(KNN)、决策树(DT)、支持向量机(DT)等传统算法,以及随机森林(RF)、Adaboost、GBDT、Xgboost、LightGBM 等集成学习算法,以及与特征工程相关的相关知识。在深度学习层面主要包含梯度下降反向传播算法(BP)、多层感知机(MLP)、卷机神经网络(CNN),循环神经网络(RNN),强化学习,以及包含 Dropout、Batch Normalization、正则化等优化方法。实践层面主要掌握 Scikit-Learn:,Tensorflow,Keras,Pytorch 等框架。</p><p>人工智能技术特别是深度学习技术发展之快可谓日新月异,最新技术早已远超如上所述,正等待着目前作为准技术达人的你们去挖掘探索。假如你也想在阿里云天池、Kaggle 等比赛平台上去刷榜而一展才智,或者你也想去像 CVPR、ICLR 等会议上留下浓墨重彩的一笔,或者你也想去互联网大厂与各路大神做同台竞技,那么还在等什么,算法方向就是你最佳的选择。</p><p>更多请转向,各大企业的招聘网站:</p><blockquote><ul><li>微众招聘-微众银行招聘<br><a href="https://webank.cheng95.com/" target="_blank" rel="noopener">https://webank.cheng95.com/</a></li><li>应届校招| 美团点评招聘官网<br><a href="https://campus.meituan.com/campus-recruit" target="_blank" rel="noopener">https://campus.meituan.com/campus-recruit</a></li><li>加入字节跳动-招聘<br><a href="https://job.bytedance.com/campus" target="_blank" rel="noopener">https://job.bytedance.com/campus</a></li><li>阿里巴巴集团招聘官网<br><a href="https://talent.alibaba.com/campus/" target="_blank" rel="noopener">https://talent.alibaba.com/campus/</a></li></ul></blockquote><p><a href="https://www.frankfeekr.cn/2019/09/01/how-to-learn-my-major/#%E6%8A%80%E8%83%BD%E6%A0%91">【更详细请阅读】大学四年,如何选择自己的技术栈 | Frank’s 技术世界</a></p><h3 id="2-2-选择适合自己的技术">2.2 选择适合自己的技术</h3><p>上面列举了互联网技术栈中核心的几个方向,那么如果去选择一个适合自己的技术栈呢?根据笔者多年互联网技术栈学习经验,主要归纳为下面的几点。</p><p><strong>第一,找到自己的兴趣</strong>。在发现自己的兴趣之前,往往我们都是一张白纸,需要我们进行广泛的学习,通过一段时间的启蒙学习才会发现自己更适合做什么,从中发觉自己的兴趣。回到技术本身,作为一个技术“小白”,我们则可以从自己基本的切入点入手,选择自己的技术栈。如果对视觉、交互体验感兴趣,可以从前端开始,开发设计一个网站、一个 APP 开始。如果对后台架构设计、后台编程语言感兴趣则可以从后台开发开始,如:Python、Java、PHP 等语言开始。如果你有很好的数学功底,喜欢数据分析、概率统计,想要运用数学知识挖掘更大的价值,则可以上手机器学习、深度学习等算法的入门。如果你已经接触过了诸多技术栈,我想你应该已经知道自己的兴趣。通过启蒙学习发觉自己的兴趣,这是我们选择自己技术栈的第一步。</p><p><strong>第二,关注一线就业方向</strong>。互联网技术日新月异,要结合自己的兴趣和当下的就业市场,不要迷失在技术的海洋中。当下火热的机器学习、Java 后台开发、C++ 开发、前端开发、APP 开发,最热门的岗位,意味着未来的前景。试想一下,一个想要入门互联网技术栈的爱好者,如果还在学习十年前的技术,老旧的技术框架,那么又怎么能迎合互联网发展的趋势呢,势必被时代所淘汰。在中国互联网技术浪潮中,技术爱好者可以关注国内一线互联网“大厂”的技术岗位,关注开发社区,瞄准就业市场、技术方向、自己的兴趣,聚力突破。</p><p><strong>第三、洞察技术趋势与发展</strong>。技术迭代日益快速的时代下,技术人如何自处?作为一个有追求的技术的爱好者,必须紧跟技术前沿,基于自己的技术能力,关注开源技术,洞察未来的技术趋势。如当下火热的:“5G”、“人工智能”、“云计算”,5G 可以更快地传输数据,人工智能可以智能的学习分析,而云计算是为了更好地进行计算,它们必将成为未来数字经济的基础设施。例如当下的我们可以选择一些优势方向,例如:机器学习、云计算、5G 开发等等方向。洞察技术趋势与发展,才能紧跟潮流脚步。<strong><u>再如当下疫情的发展,对于远程办公和视频会议的需求明显激增,音视频开发的市场需求也水涨船高</u></strong>。</p><p>当然不仅仅只是学会一门技能,也要有自己的辅助第二技术或是第三技术。<strong>比如后台思维写前端,算法思维写后台,产品思维写算法</strong>。这些都是我们的核心竞争力,懂得更多可以让彼此之间的协作更顺畅。笔者根据自己多年的技术学习经验,从“找到自己的兴趣”、“关注一线就业方向”、“洞察技术趋势与发展”三个方面入手,一定能找到适合自己的技术栈。</p><h2 id="3-如何准备校招面试?">3. 如何准备校招面试?</h2><h3 id="3-1-夯实基础">3.1 夯实基础</h3><p>技术岗位的专业基础知识,必须扎实。</p><p>以【 Java 后台开发】岗位开发为例,需要掌握的知识:</p><ul><li><p>在线笔试基础</p><ul><li>Leetcode(必刷)</li><li>剑指 Offer(必刷)</li></ul></li><li><p>内功修炼</p><ul><li>数据结构与算法</li><li>计算机网络(应用层,传输层,网络层等相关协议)</li><li>操作系统原理</li></ul></li><li><p>语言核心基础</p><ul><li>语法与基础概念</li><li>面向对象与 23 种设计模式</li><li>Java 容器源码(数据结构 & 源码分析:ArrayList、Vector、LinkedList、HashMap、ConcurrentHashMap、HashSet、LinkedHashSet and LinkedHashMap)</li><li>Java 并发编程(线程状态、线程机制、线程通信、J.U.C 组件、JMM、线程安全、锁优化)</li><li>Java IO(磁盘操作、字节操作、字符操作、对象操作、网络操作、NIO)</li><li>Java 虚拟机(运行时数据区域、垃圾收集、内存分配机制、类加载机制、性能调优监控工具)</li><li>Java Web(学习 Spring + SpringMVC + MyBatis 框架和设计模式思想,学习 Servlet 和 JSP)</li></ul></li><li><p>后台技术栈</p><ul><li>Linux 基础</li><li>数据库(MySQL,Redis)</li><li>Git 版本管理工具使用</li><li>正则表达式</li></ul></li><li><p>高级加分项</p><ul><li>Zookeeper(分布式协调服务)</li><li>Dubbo(分布式服务治理)</li><li>分布式事务解决方案</li><li>Kafka(分布式消息通信)</li><li>Nginx(反向代理)</li><li>Docker(容器技术)</li></ul></li></ul><h3 id="3-2-面试技巧">3.2 面试技巧</h3><ul><li><p>简历几要素</p><ul><li>基本信息<ul><li>姓名 / 年龄 / 联系方式 / 教育经历 / 应聘岗位</li><li>推荐大家联系方式上使用校园 <code>edu.cn</code> 的邮箱</li></ul></li><li>校园经历 ☆☆<ul><li>参加过社团、学生会活动等;参加过校园某某平台系统开发;xxx</li></ul></li><li>获奖荣誉 ☆☆</li><li>实习经验 ☆☆</li><li>项目经验 ☆☆☆</li></ul></li><li><p>面试技巧</p><ul><li>作为技术性面试,一定要包装好一份自己的项目经历,有条件的甚至可以提早准备好自己的项目架构图。只有准备好自己的项目经验,才能在现场面试的环节中让面试官感兴趣,告诉面试官自己的亮点,从而把面试话题牵引到自己最擅长的领域。</li><li>切记不要一句话也不说,项目也不说清楚。这种面试,只会让面试官上来就考察基础,面试印象也大大折扣。</li></ul></li></ul><h3 id="3-3-经验分享:我的校招之路">3.3 经验分享:我的校招之路</h3><p>我的校招之路 | Frank’s 技术世界<br><a href="https://www.frankfeekr.cn/2018/10/19/%E6%88%91%E7%9A%84%E6%A0%A1%E6%8B%9B%E4%B9%8B%E8%B7%AF/">https://www.frankfeekr.cn/2018/10/19/我的校招之路/</a></p><p>广撒简历,重点培养。明确自己的岗位,工作地进行海投、精准面试。基本上面到 HR 面的企业都拿到了口头 Offer 或是意向书。</p><p>在面试过程中,选择往往大于努力,一定要认准自己的方向。</p><p>Offer = 40%运气 + 40%技术 + 20%表达能力</p><h2 id="4-答疑讨论环节">4. 答疑讨论环节</h2><p>希望大家今天可以在我的分享中有一点点收货,生活也需要平衡。</p><h2 id="☆-推荐一些关于校招的平台">☆ 推荐一些关于校招的平台</h2><ul><li>关注公众号【全栈开发社区】,回复 “校招” 获取资料</li></ul><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/qrcode.png" alt="img" style="zoom:33%;"><ul><li>力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台<br><a href="https://leetcode-cn.com/" target="_blank" rel="noopener">https://leetcode-cn.com/</a></li><li>牛客网-找工作神器|笔试题库|面试经验|实习招聘内推,求职就业一站解决_牛客网<br><a href="https://www.nowcoder.com/" target="_blank" rel="noopener">https://www.nowcoder.com/</a></li><li>OfferShow</li></ul><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20200531163550422.png" alt="image-20200531163550422" style="zoom:30%;"><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20200531163601726.png" alt="image-20200531163601726" style="zoom:30%;"><p></p>]]></content>
<summary type="html">
<h2 id="分享大纲">分享大纲</h2>
<p>(1)什么是校招? 什么时候开始准备校招?<br>
(2)还很迷茫吗? 带你一起了解都有哪些就业岗位? 手把手教你选择适合自己的技术栈?<br>
(3)如何准备校招面试?<br>
(4)答疑讨论环节</p>
<h2 id="1
</summary>
</entry>
<entry>
<title>进击大数据,Scala 语言火速入门</title>
<link href="http://www.frankfeekr.cn/2020/05/20/scala-quick-start/"/>
<id>http://www.frankfeekr.cn/2020/05/20/scala-quick-start/</id>
<published>2020-05-19T17:32:33.000Z</published>
<updated>2021-10-23T14:22:52.649Z</updated>
<content type="html"><![CDATA[<h2 id="第1章:初识-scala">第1章:初识 Scala</h2><h3 id="1-scala概述">1. Scala概述</h3><ul><li>Scala是一门多范式的编程语言,设计初衷集成面向对象编程和函数式编程的各种特性</li><li>Scala运行于Java平台(Java虚拟机),并兼容现有的Java程序</li><li>Scala combines object-oriented and functional programming in one concise, high-level language. Scala’s static types help avoid bugs in complex applications, and its JVM and JavaScript runtimes let you build high-performance systems with easy access to huge ecosystems of libraries.</li></ul><h3 id="2-scala安装">2. Scala安装</h3><ul><li>Java8</li></ul><figure class="highlight shell"><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">Thpffcj:software thpffcj$ echo $JAVA_HOME</span><br><span class="line">/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home</span><br><span class="line">Thpffcj:software thpffcj$ java -version</span><br><span class="line">java version "1.8.0_191"</span><br><span class="line">Java(TM) SE Runtime Environment (build 1.8.0_191-b12)</span><br><span class="line">Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)</span><br></pre></td></tr></table></figure><ul><li>下载并解压Scala</li><li>配置到系统环境变量</li></ul><figure class="highlight shell"><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">Thpffcj:software thpffcj$ vi ~/.bash_profile</span><br><span class="line"></span><br><span class="line">export SCALA_HOME=/Users/thpffcj/Public/software/scala-2.12.8</span><br><span class="line">export PATH=$PATH:$SCALA_HOME/bin</span><br><span class="line"></span><br><span class="line">Thpffcj:software thpffcj$ source ~/.bash_profile</span><br></pre></td></tr></table></figure><h3 id="3-scala使用入门及对比java">3. Scala使用入门及对比Java</h3><figure class="highlight scala"><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"><span class="type">Thpffcj</span>:bin thpffcj$ ./scala</span><br><span class="line"><span class="type">Welcome</span> to <span class="type">Scala</span> <span class="number">2.12</span><span class="number">.8</span> (<span class="type">Java</span> <span class="type">HotSpot</span>(<span class="type">TM</span>) <span class="number">64</span>-<span class="type">Bit</span> <span class="type">Server</span> <span class="type">VM</span>, <span class="type">Java</span> <span class="number">1.8</span><span class="number">.0</span>_191).</span><br><span class="line"><span class="type">Type</span> in expressions <span class="keyword">for</span> evaluation. <span class="type">Or</span> <span class="keyword">try</span> :help.</span><br><span class="line"></span><br><span class="line">scala> <span class="number">1</span> + <span class="number">3</span></span><br><span class="line">res0: <span class="type">Int</span> = <span class="number">4</span></span><br><span class="line"></span><br><span class="line">scala> res0 * <span class="number">3</span></span><br><span class="line">res1: <span class="type">Int</span> = <span class="number">12</span></span><br><span class="line"></span><br><span class="line">scala> res0 * res1</span><br><span class="line">res2: <span class="type">Int</span> = <span class="number">48</span></span><br><span class="line"></span><br><span class="line">scala> println(<span class="string">"Hello World"</span>)</span><br><span class="line"><span class="type">Hello</span> <span class="type">World</span></span><br></pre></td></tr></table></figure><p><strong>HelloWorld</strong></p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloWorld</span></span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> System.out.println(<span class="string">"Hello World"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>Scala每行代码并不强求使用;结束,但是Java是必须的</li></ul><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line">object HelloWorld {</span><br><span class="line"> def main(args : Array[String]) {</span><br><span class="line"> println("Hello World")</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>将上面代码保存为HelloWorld.scala</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="type">Thpffcj</span>:data thpffcj$ scalac <span class="type">HelloWorld</span>.scala </span><br><span class="line"><span class="type">Thpffcj</span>:data thpffcj$ ls</span><br><span class="line"><span class="type">HelloWorld</span>$.<span class="keyword">class</span> <span class="type">HelloWorld</span>.<span class="keyword">class</span> <span class="type">HelloWorld</span>.scala hello.txt</span><br><span class="line"><span class="type">Thpffcj</span>:data thpffcj$ scala <span class="type">HelloWorld</span></span><br><span class="line"><span class="type">Hello</span> <span class="type">World</span></span><br></pre></td></tr></table></figure><h2 id="第2章:scala-入门">第2章:Scala 入门</h2><h3 id="1-val-vs-var">1. val vs var</h3><ul><li>val:值<ul><li>val 值名称:类型 = xxx</li></ul></li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line">scala> <span class="keyword">val</span> money = <span class="number">100</span></span><br><span class="line">money: <span class="type">Int</span> = <span class="number">100</span></span><br><span class="line"></span><br><span class="line">scala> money =<span class="number">200</span></span><br><span class="line"><console>:<span class="number">12</span>: error: reassignment to <span class="keyword">val</span></span><br><span class="line"> money =<span class="number">200</span></span><br><span class="line"> ^</span><br><span class="line"></span><br><span class="line">scala> <span class="keyword">val</span> age:<span class="type">Int</span> = <span class="number">20</span></span><br><span class="line">age: <span class="type">Int</span> = <span class="number">20</span></span><br></pre></td></tr></table></figure><ul><li>var:变量<ul><li>var 值名称:类型 = xxx</li></ul></li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line">scala> <span class="keyword">var</span> name:<span class="type">String</span> = <span class="string">"zhangsan"</span></span><br><span class="line">name: <span class="type">String</span> = zhangsan</span><br><span class="line"></span><br><span class="line">scala> name = <span class="string">"zhangsi"</span></span><br><span class="line">name: <span class="type">String</span> = zhangsi</span><br></pre></td></tr></table></figure><h3 id="2-基本数据类型">2. 基本数据类型</h3><ul><li>Byte/Char</li><li>Short/Int/Long/Float/Double</li><li>Boolean</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line">scala> <span class="keyword">val</span> a:<span class="type">Int</span> = <span class="number">10</span></span><br><span class="line">a: <span class="type">Int</span> = <span class="number">10</span></span><br><span class="line"></span><br><span class="line">scala> <span class="keyword">val</span> b:<span class="type">Boolean</span> = <span class="literal">true</span></span><br><span class="line">b: <span class="type">Boolean</span> = <span class="literal">true</span></span><br><span class="line"></span><br><span class="line">scala> <span class="keyword">val</span> c = <span class="literal">false</span></span><br><span class="line">c: <span class="type">Boolean</span> = <span class="literal">false</span></span><br><span class="line"></span><br><span class="line">scala> <span class="keyword">val</span> d = <span class="number">1.1</span></span><br><span class="line">d: <span class="type">Double</span> = <span class="number">1.1</span></span><br><span class="line"></span><br><span class="line">scala> <span class="keyword">val</span> e:<span class="type">Float</span> = <span class="number">1.1</span></span><br><span class="line"><console>:<span class="number">11</span>: error: <span class="class"><span class="keyword">type</span> <span class="title">mismatch</span></span>;</span><br><span class="line"> found : <span class="type">Double</span>(<span class="number">1.1</span>)</span><br><span class="line"> required: <span class="type">Float</span></span><br><span class="line"> <span class="keyword">val</span> e:<span class="type">Float</span> = <span class="number">1.1</span></span><br><span class="line"> ^</span><br><span class="line"></span><br><span class="line">scala> <span class="keyword">val</span> e:<span class="type">Float</span> = <span class="number">1.1</span>f</span><br><span class="line">e: <span class="type">Float</span> = <span class="number">1.1</span></span><br></pre></td></tr></table></figure><ul><li>类型转换</li></ul><figure class="highlight scala"><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">scala> <span class="keyword">val</span> f = <span class="number">10</span></span><br><span class="line">f: <span class="type">Int</span> = <span class="number">10</span></span><br><span class="line"></span><br><span class="line">scala> <span class="keyword">val</span> g = <span class="number">10.</span>asInstanceOf[<span class="type">Double</span>]</span><br><span class="line">g: <span class="type">Double</span> = <span class="number">10.0</span></span><br><span class="line"></span><br><span class="line">scala> <span class="keyword">val</span> h = <span class="number">10.</span>isInstanceOf[<span class="type">Int</span>]</span><br><span class="line">h: <span class="type">Boolean</span> = <span class="literal">true</span></span><br></pre></td></tr></table></figure><h3 id="3-lazy在scala中的应用">3. lazy在Scala中的应用</h3><p>耗费计算网路的时候可以使用 lazy</p><figure class="highlight scala"><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">scala> <span class="keyword">val</span> i = <span class="number">1</span></span><br><span class="line">i: <span class="type">Int</span> = <span class="number">1</span></span><br><span class="line"></span><br><span class="line">scala> <span class="keyword">lazy</span> <span class="keyword">val</span> a = <span class="number">1</span></span><br><span class="line">a: <span class="type">Int</span> = <<span class="keyword">lazy</span>></span><br><span class="line"></span><br><span class="line">scala> a</span><br><span class="line">res0: <span class="type">Int</span> = <span class="number">1</span></span><br></pre></td></tr></table></figure><ul><li>我们现在想读取文件</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line">scala> <span class="keyword">import</span> scala.io.<span class="type">Source</span>._</span><br><span class="line"><span class="keyword">import</span> scala.io.<span class="type">Source</span>._</span><br><span class="line"></span><br><span class="line">scala> <span class="keyword">lazy</span> <span class="keyword">val</span> info = fromFile(<span class="string">"/Users/thpffcj/Public/data/HelloWorld.scala"</span>).mkString</span><br><span class="line">info: <span class="type">String</span> = <<span class="keyword">lazy</span>></span><br><span class="line"></span><br><span class="line">scala> info</span><br><span class="line">res3: <span class="type">String</span> =</span><br><span class="line"><span class="string">"object HelloWorld {</span></span><br><span class="line"><span class="string"> def main(args : Array[String]) {</span></span><br><span class="line"><span class="string"> println("</span><span class="type">Hello</span> <span class="type">World</span><span class="string">")</span></span><br><span class="line"><span class="string"> }</span></span><br><span class="line"><span class="string">}</span></span><br><span class="line"><span class="string">"</span></span><br></pre></td></tr></table></figure><h3 id="4-scala-常用-ide">4. Scala 常用 IDE</h3><ul><li>IDEA</li><li>Eclipse</li><li>NetBeans</li></ul><h3 id="5-使用idea整合maven构建应用程序">5. 使用IDEA整合Maven构建应用程序</h3><ol><li>新建项目勾选 Create from archetype 并选择 scala-archetype-simple</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/scala-maven-01.png" alt="scala-maven-01"></p><ol start="2"><li>起项目名一路 Next,选择自己安装的 Maven 地址,然后继续 Next</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/scala-maven-02.png" alt="scala-maven-01"></p><ol start="3"><li><p>第一次创建项目需要下载依赖包可能会比较慢</p></li><li><p>去Plugin里下载Scala的Plugin,下载完成后重启</p></li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/scala-maven-03.png" alt="scala-maven-01"></p><ol start="5"><li>添加Scala-SDK</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/scala-maven-04.png" alt="scala-maven-01"></p><ol start="6"><li>编写Hello World程序</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/scala-helloworld.png" alt="scala-helloworld"></p><h2 id="第3章:scala-函数">第3章:Scala 函数</h2><h3 id="1-方法的定义和使用">1. 方法的定义和使用</h3><ul><li>函数/方法的定义</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">方法名</span></span>(参数名:参数类型): 返回值 类型 = {</span><br><span class="line"> <span class="comment">// 括号内的叫做方法体</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 方法体内的最后一行为返回值,不需要使用return</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="assets/xxx81879ap.png" alt="img"></p><ul><li>我们去IDEA中试验一下</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">FunctionApp</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]): <span class="type">Unit</span> = {</span><br><span class="line"> println(add(<span class="number">2</span>, <span class="number">3</span>))</span><br><span class="line"> println(three())</span><br><span class="line"> println(three) <span class="comment">// 没有入参的函数,调用时括号是可以省略的</span></span><br><span class="line"> sayHello(<span class="string">"Frank"</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">add</span></span>(x:<span class="type">Int</span>, y:<span class="type">Int</span>):<span class="type">Int</span> = {</span><br><span class="line"> x+ y <span class="comment">// 最后一行就是返回值,不需要return</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">three</span></span>() = <span class="number">1</span> + <span class="number">2</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">sayHello</span></span>(name:<span class="type">String</span>): <span class="type">Unit</span> = { <span class="comment">// 不需要返回值</span></span><br><span class="line"> println(<span class="string">"Say hello "</span> + name)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="2-默认参数的使用">2. 默认参数的使用</h3><ul><li>在函数定义时,允许指定参数的默认值</li></ul><figure class="highlight scala"><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 class="function"><span class="keyword">def</span> <span class="title">sayName</span></span>(name:<span class="type">String</span> = <span class="string">"Frank"</span>): <span class="type">Unit</span> = {</span><br><span class="line"> println(name)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>我们来看一下默认参数在Spark中的使用</li></ul><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> ./spark-shell --<span class="built_in">help</span></span></span><br><span class="line"></span><br><span class="line"> --properties-file FILE Path to a file from which to load extra properties. If not</span><br><span class="line"> specified, this will look for conf/spark-defaults.conf.</span><br></pre></td></tr></table></figure><ul><li>如果你没有设置配置文件,它将会去寻找conf下的spark-defaults.conf</li></ul><h3 id="3-命名函数的使用">3. 命名函数的使用</h3><figure class="highlight scala"><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 class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]): <span class="type">Unit</span> = {</span><br><span class="line"> println(speed(time = <span class="number">10</span>, distance = <span class="number">100</span>))</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">speed</span></span>(distance:<span class="type">Float</span>, time:<span class="type">Float</span>):<span class="type">Float</span> = {</span><br><span class="line"> distance/time</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="4-可变参数的使用">4. 可变参数的使用</h3><ul><li>JDK5+:可变参数</li></ul><figure class="highlight scala"><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 class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]): <span class="type">Unit</span> = {</span><br><span class="line"> println(sum(<span class="number">1</span>, <span class="number">2</span>))</span><br><span class="line"> println(sum(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>))</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sum</span></span>(numbers:<span class="type">Int</span>*) = {</span><br><span class="line"> <span class="keyword">var</span> result = <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> (number <- numbers) {</span><br><span class="line"> result += number</span><br><span class="line"> }</span><br><span class="line"> result</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="5-条件表达式">5. 条件表达式</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(a><span class="number">0</span>) <span class="literal">true</span> <span class="keyword">else</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure><h3 id="6-循环表达式">6. 循环表达式</h3><ul><li>to:左闭右闭</li><li>Range:左闭右开</li><li>until:左闭右开</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line">scala> <span class="number">1</span> to <span class="number">10</span> <span class="comment">// 左闭右闭</span></span><br><span class="line">res11: scala.collection.immutable.<span class="type">Range</span>.<span class="type">Inclusive</span> = <span class="type">Range</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>)</span><br><span class="line"></span><br><span class="line">scala> <span class="type">Range</span>(<span class="number">1</span>,<span class="number">10</span>) <span class="comment">// 左闭右开</span></span><br><span class="line">res12: scala.collection.immutable.<span class="type">Range</span> = <span class="type">Range</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>)</span><br><span class="line"></span><br><span class="line">scala> <span class="number">1.</span>to(<span class="number">10</span>) <span class="comment">// 等同于 1 to 10</span></span><br><span class="line">res13: scala.collection.immutable.<span class="type">Range</span>.<span class="type">Inclusive</span> = <span class="type">Range</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>)</span><br><span class="line"></span><br><span class="line">scala> <span class="type">Range</span>(<span class="number">1</span>,<span class="number">10</span>,<span class="number">2</span>) <span class="comment">// 可以选择步长</span></span><br><span class="line">res14: scala.collection.immutable.<span class="type">Range</span> = <span class="type">Range</span>(<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">9</span>)</span><br><span class="line"></span><br><span class="line">scala> <span class="type">Range</span>(<span class="number">1</span>,<span class="number">10</span>,<span class="number">0</span>) <span class="comment">// 步长不可以为0,死循环</span></span><br><span class="line">java.lang.<span class="type">IllegalArgumentException</span>: step cannot be <span class="number">0.</span></span><br><span class="line"> at scala.collection.immutable.<span class="type">Range</span>.<init>(<span class="type">Range</span>.scala:<span class="number">86</span>)</span><br><span class="line"> at scala.collection.immutable.<span class="type">Range</span>$.apply(<span class="type">Range</span>.scala:<span class="number">439</span>)</span><br><span class="line"> ... <span class="number">32</span> elided</span><br><span class="line"></span><br><span class="line">scala> <span class="type">Range</span>(<span class="number">10</span>,<span class="number">0</span>,<span class="number">-1</span>) <span class="comment">// 可以从大到小来</span></span><br><span class="line">res16: scala.collection.immutable.<span class="type">Range</span> = <span class="type">Range</span>(<span class="number">10</span>, <span class="number">9</span>, <span class="number">8</span>, <span class="number">7</span>, <span class="number">6</span>, <span class="number">5</span>, <span class="number">4</span>, <span class="number">3</span>, <span class="number">2</span>, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">scala> <span class="number">1</span> until <span class="number">10</span> <span class="comment">// 左闭右开 底层调用的Range</span></span><br><span class="line">res17: scala.collection.immutable.<span class="type">Range</span> = <span class="type">Range</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>)</span><br></pre></td></tr></table></figure><ul><li>for循环</li></ul><figure class="highlight scala"><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"><span class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]): <span class="type">Unit</span> = {</span><br><span class="line"> <span class="keyword">for</span> (i <- <span class="number">1</span> to <span class="number">10</span> <span class="keyword">if</span> i % <span class="number">2</span> == <span class="number">0</span>) {</span><br><span class="line"> println(i)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">val</span> courses = <span class="type">Array</span>(<span class="string">"Hadoop"</span>, <span class="string">"Spark"</span>, <span class="string">"Storm"</span>)</span><br><span class="line"> <span class="keyword">for</span> (course <- courses) {</span><br><span class="line"> println(course)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// course 其实就是 courses 里面的每个元素</span></span><br><span class="line"> <span class="comment">// => 就是将左边的 course 作用上一个函数,变成另外一个结果</span></span><br><span class="line"> <span class="comment">// println 就是作用到 course 上的一个函数</span></span><br><span class="line"> courses.foreach(course => println(course))</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>while循环</li></ul><figure class="highlight scala"><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 class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]): <span class="type">Unit</span> = {</span><br><span class="line"> <span class="keyword">var</span> (num, sum) = (<span class="number">100</span>, <span class="number">0</span>)</span><br><span class="line"> <span class="keyword">while</span> (num > <span class="number">0</span>) {</span><br><span class="line"> sum = sum + num</span><br><span class="line"> num = num - <span class="number">1</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="第4章:scala-面向对象">第4章:Scala 面向对象</h2><h3 id="1-面向对象概述">1. 面向对象概述</h3><ul><li><strong>封装</strong>:属性,方法封装到类中<ul><li>Person<ul><li>private int id, String name, Date birthday… getter/setter</li><li>eat sleep …</li></ul></li></ul></li><li><strong>继承</strong>:父类和子类之间的关系<ul><li>User extends Person</li></ul></li><li><strong>多态</strong>:父类引用指向子类对象(精髓所在 / 开发框架的基石)<ul><li>Person person = new Person();</li><li>User user = new User();</li><li>Person person = new User();</li></ul></li></ul><h3 id="2-类的定义与使用">2. 类的定义与使用</h3><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">SimpleObjectApp</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]): <span class="type">Unit</span> = {</span><br><span class="line"> <span class="keyword">val</span> person = <span class="keyword">new</span> <span class="type">People</span>()</span><br><span class="line"> person.name = <span class="string">"Messi"</span></span><br><span class="line"> println(person.name + <span class="string">" .. "</span> + person.age)</span><br><span class="line"></span><br><span class="line"> println(<span class="string">"invoke eat method: "</span> + person.eat())</span><br><span class="line"></span><br><span class="line"> person.watchFootball(<span class="string">"Barcelona"</span>)</span><br><span class="line"></span><br><span class="line"> person.printInfo()</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">People</span> </span>{</span><br><span class="line"> <span class="comment">// 定义属性</span></span><br><span class="line"> <span class="keyword">var</span> name:<span class="type">String</span> = _ <span class="comment">// 占位符</span></span><br><span class="line"> <span class="keyword">val</span> age:<span class="type">Int</span> = <span class="number">10</span></span><br><span class="line"> <span class="keyword">private</span> [<span class="keyword">this</span>] <span class="keyword">val</span> gender = <span class="string">"male"</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 定义方法</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">eat</span></span>():<span class="type">String</span> = {</span><br><span class="line"> name + <span class="string">"eat..."</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">watchFootball</span></span>(teamName:<span class="type">String</span>): <span class="type">Unit</span> = {</span><br><span class="line"> println(name + <span class="string">" is watching match of "</span> + teamName)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">printInfo</span></span>(): <span class="type">Unit</span> = {</span><br><span class="line"> println(<span class="string">"gender: "</span> + gender)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="3-构造器">3. 构造器</h3><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">ConstructorApp</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]): <span class="type">Unit</span> = {</span><br><span class="line"> <span class="keyword">val</span> person = <span class="keyword">new</span> <span class="type">Person</span>(<span class="string">"zhangsan"</span>, <span class="number">30</span>)</span><br><span class="line"> println(person.name + <span class="string">":"</span> + person.age + <span class="string">":"</span> + person.school)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">val</span> person2 = <span class="keyword">new</span> <span class="type">Person</span>(<span class="string">"lisi"</span>, <span class="number">18</span>, <span class="string">"M"</span>)</span><br><span class="line"> println(person2.name + <span class="string">":"</span> + person2.age + <span class="string">":"</span> + person2.school + <span class="string">":"</span> + person2.gender)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 主构造器</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span>(<span class="params">val name:<span class="type">String</span>, val age:<span class="type">Int</span></span>) </span>{</span><br><span class="line"></span><br><span class="line"> println(<span class="string">"Person Constructor enter..."</span> )</span><br><span class="line"></span><br><span class="line"> <span class="keyword">val</span> school = <span class="string">"ustc"</span></span><br><span class="line"> <span class="keyword">var</span> gender:<span class="type">String</span> = _</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 附属构造器</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">this</span></span>(name: <span class="type">String</span>, age:<span class="type">Int</span>, gender:<span class="type">String</span>) {</span><br><span class="line"> <span class="keyword">this</span>(name, age) <span class="comment">// 附属构造器的第一行的代码必须要调用主构造器或者其他附属构造器</span></span><br><span class="line"> <span class="keyword">this</span>.gender = gender</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> println(<span class="string">"Person Constructor leave..."</span> )</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="4-继承与重写">4. 继承与重写</h3><ul><li>继承</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">ConstructorApp</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]): <span class="type">Unit</span> = {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">val</span> student = <span class="keyword">new</span> <span class="type">Student</span>(<span class="string">"zhangsan"</span>, <span class="number">18</span>, <span class="string">"Math"</span>)</span><br><span class="line"> println(student.name + <span class="string">":"</span> + student.major)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// major 属性需要使用var修饰</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> (<span class="params">name:<span class="type">String</span>, age:<span class="type">Int</span>, var major:<span class="type">String</span></span>) <span class="keyword">extends</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>{</span><br><span class="line"></span><br><span class="line"> println(<span class="string">"Student Constructor enter..."</span> )</span><br><span class="line"></span><br><span class="line"> println(<span class="string">"Student Constructor leave..."</span> )</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>重写</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Student</span>(<span class="params">name:<span class="type">String</span>, age:<span class="type">Int</span>, var major:<span class="type">String</span></span>) <span class="keyword">extends</span> <span class="title">Person</span>(<span class="params">name, age</span>) </span>{</span><br><span class="line"></span><br><span class="line"> println(<span class="string">"Student Constructor enter..."</span> )</span><br><span class="line"></span><br><span class="line"> <span class="keyword">override</span> <span class="keyword">val</span> school = <span class="string">"peking"</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">def</span> <span class="title">toString</span></span>: <span class="type">String</span> = <span class="string">"override def toString : "</span> + school</span><br><span class="line"></span><br><span class="line"> println(<span class="string">"Student Constructor leave..."</span> )</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="5-抽象类">5. 抽象类</h3><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">AbstractApp</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]): <span class="type">Unit</span> = {</span><br><span class="line"> <span class="keyword">val</span> student = <span class="keyword">new</span> <span class="type">Student2</span>()</span><br><span class="line"> println(student.name)</span><br><span class="line"> student.speak</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 类的一个或者多个方法没有完整的实现(只有定义,没有实现)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Person2</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">speak</span></span></span><br><span class="line"><span class="function"> </span></span><br><span class="line"><span class="function"> <span class="title">val</span> <span class="title">name</span></span>:<span class="type">String</span></span><br><span class="line"> <span class="keyword">val</span> age:<span class="type">Int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Student2</span> <span class="keyword">extends</span> <span class="title">Person2</span> </span>{</span><br><span class="line"> <span class="keyword">override</span> <span class="function"><span class="keyword">def</span> <span class="title">speak</span></span>: <span class="type">Unit</span> = {</span><br><span class="line"> println(<span class="string">"speak"</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">override</span> <span class="keyword">val</span> name: <span class="type">String</span> = <span class="string">"Frank"</span></span><br><span class="line"> <span class="keyword">override</span> <span class="keyword">val</span> age: <span class="type">Int</span> = <span class="number">18</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="6-伴生类和伴生对象">6. 伴生类和伴生对象</h3><figure class="highlight scala"><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 class="comment">/**</span></span><br><span class="line"><span class="comment"> * 伴生类和伴生对象</span></span><br><span class="line"><span class="comment"> * 如果有一个class,还有一个与class同名的object</span></span><br><span class="line"><span class="comment"> * 那么就称这个object是class的伴生对象,class是object的伴生类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ApplyTest</span> </span>{</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">ApplyTest</span> </span>{</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="7-apply">7. apply</h3><figure class="highlight scala"><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><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">ApplyApp</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]): <span class="type">Unit</span> = {</span><br><span class="line"> <span class="keyword">for</span> (i <- <span class="number">1</span> to <span class="number">10</span>) {</span><br><span class="line"> <span class="type">ApplyTest</span>.incr</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> println(<span class="type">ApplyTest</span>.count) <span class="comment">// 10 说明object本身就是一个单例对象</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">val</span> b = <span class="type">ApplyTest</span>() <span class="comment">// ==> Object.apply</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">val</span> c = <span class="keyword">new</span> <span class="type">ApplyTest</span>()</span><br><span class="line"> c()</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 类名() ==> Object.apply</span></span><br><span class="line"> <span class="comment">// 对象() ==> Class.apply</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ApplyTest</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">apply</span></span>() = {</span><br><span class="line"> println(<span class="string">"Class ApplyTest apply..."</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">ApplyTest</span> </span>{</span><br><span class="line"></span><br><span class="line"> println(<span class="string">"Object ApplyTest enter..."</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> count = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">incr</span> </span>= {</span><br><span class="line"> count = count + <span class="number">1</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 最佳实践:在Object的apply方法中去new Class</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">apply</span></span>() = {</span><br><span class="line"> println(<span class="string">"Object ApplyTest apply..."</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 在object中的apply中new class</span></span><br><span class="line"> <span class="keyword">new</span> <span class="type">ApplyTest</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> println(<span class="string">"Object ApplyTest leave..."</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="8-case-class">8. case class</h3><figure class="highlight scala"><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 class="comment">// 通常用在模式匹配</span></span><br><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">CaseClassApp</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]): <span class="type">Unit</span> = {</span><br><span class="line"> println(<span class="type">Dog</span>(<span class="string">"wangcai"</span>).name)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// case class 不用new</span></span><br><span class="line"><span class="keyword">case</span> <span class="class"><span class="keyword">class</span> <span class="title">Dog</span>(<span class="params">name:<span class="type">String</span></span>)</span></span><br></pre></td></tr></table></figure><h3 id="9-trait">9. trait</h3><ul><li>Scala中的Triat是一种特殊的概念</li><li>首先我们可以将Trait作为接口来使用,此时的Triat就与Java中的接口非常类似</li><li>在triat中可以定义抽象方法,就与抽象类中的抽象方法一样,只要不给出方法的具体实现即可</li><li>类可以使用extends关键字继承trait,注意,这里不是implement,而是extends,在scala中没有implement的概念,无论继承类还是trait,统一都是extends</li><li>类继承trait后,必须实现其中的抽象方法,实现时不需要使用override关键字</li><li>scala 不支持对类进行多继承,但是支持多重继承trait,使用with关键字即可</li><li>xxx extends ATrait with BTrait</li></ul><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line">class SparkConf(loadDefaults: Boolean)</span><br><span class="line"> extends Cloneable</span><br><span class="line"> with Logging</span><br><span class="line"> with Serializable</span><br></pre></td></tr></table></figure><h2 id="第5章:scala-集合">第5章:Scala 集合</h2><h3 id="1-数组">1. 数组</h3><p><strong>定长数组</strong></p><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line">scala> <span class="keyword">val</span> a = <span class="keyword">new</span> <span class="type">Array</span>[<span class="type">String</span>](<span class="number">5</span>)</span><br><span class="line">a: <span class="type">Array</span>[<span class="type">String</span>] = <span class="type">Array</span>(<span class="literal">null</span>, <span class="literal">null</span>, <span class="literal">null</span>, <span class="literal">null</span>, <span class="literal">null</span>)</span><br><span class="line"></span><br><span class="line">scala> a.length</span><br><span class="line">res0: <span class="type">Int</span> = <span class="number">5</span></span><br><span class="line"></span><br><span class="line">scala> a(<span class="number">1</span>) = <span class="string">"hello"</span></span><br><span class="line"></span><br><span class="line">scala> a</span><br><span class="line">res2: <span class="type">Array</span>[<span class="type">String</span>] = <span class="type">Array</span>(<span class="literal">null</span>, hello, <span class="literal">null</span>, <span class="literal">null</span>, <span class="literal">null</span>)</span><br></pre></td></tr></table></figure><ul><li>我们再来看一种其他创建数组的方法,其实它就是用了上面我们提到的apply</li></ul><figure class="highlight scala"><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">scala> <span class="keyword">val</span> b = <span class="type">Array</span>(<span class="string">"hadoop"</span>, <span class="string">"spark"</span>, <span class="string">"storm"</span>)</span><br><span class="line">b: <span class="type">Array</span>[<span class="type">String</span>] = <span class="type">Array</span>(hadoop, spark, storm)</span><br><span class="line"></span><br><span class="line">scala> b(<span class="number">1</span>) = <span class="string">"flink"</span></span><br><span class="line"></span><br><span class="line">scala> b</span><br><span class="line">res4: <span class="type">Array</span>[<span class="type">String</span>] = <span class="type">Array</span>(hadoop, flink, storm)</span><br></pre></td></tr></table></figure><ul><li>我们看看数组的其他方法</li></ul><figure class="highlight scala"><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">scala> <span class="keyword">val</span> c = <span class="type">Array</span>(<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>)</span><br><span class="line">c: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>)</span><br><span class="line"></span><br><span class="line">scala> c.sum</span><br><span class="line">res5: <span class="type">Int</span> = <span class="number">44</span></span><br><span class="line"></span><br><span class="line">scala> c.min</span><br><span class="line">res7: <span class="type">Int</span> = <span class="number">2</span></span><br><span class="line"></span><br><span class="line">scala> c.mkString(<span class="string">","</span>)</span><br><span class="line">res8: <span class="type">String</span> = <span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>,<span class="number">8</span>,<span class="number">9</span></span><br></pre></td></tr></table></figure><p><strong>变长数组</strong></p><figure class="highlight scala"><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">scala> <span class="keyword">val</span> d = scala.collection.mutable.<span class="type">ArrayBuffer</span>[<span class="type">Int</span>]()</span><br><span class="line">d: scala.collection.mutable.<span class="type">ArrayBuffer</span>[<span class="type">Int</span>] = <span class="type">ArrayBuffer</span>()</span><br></pre></td></tr></table></figure><ul><li>对可变数组进行操作</li></ul><figure class="highlight scala"><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">d += <span class="number">1</span></span><br><span class="line">d += <span class="number">2</span></span><br><span class="line">d += (<span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>)</span><br><span class="line">d ++= <span class="type">Array</span>(<span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>)</span><br><span class="line">d.insert(<span class="number">0</span>, <span class="number">0</span>)</span><br><span class="line">d.remove(<span class="number">1</span>)</span><br><span class="line">d.trimEnd(<span class="number">2</span>)</span><br><span class="line">println(d)</span><br></pre></td></tr></table></figure><ul><li>我们看到最后的结果是ArrayBuffer(0, 2, 3, 4, 5, 6)</li><li>我们如何对变长数组迭代呢</li></ul><figure class="highlight scala"><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 class="keyword">for</span> (i <- (<span class="number">0</span> until d.length).reverse) {</span><br><span class="line"> println(d(i))</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (ele <- d) {</span><br><span class="line"> println(ele)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>可变数组可以通过toArray方法变为定长数组</li></ul><figure class="highlight scala"><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">scala> d.toArray</span><br><span class="line">res10: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>()</span><br></pre></td></tr></table></figure><h3 id="2-list">2. List</h3><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line">scala> <span class="keyword">val</span> l = <span class="type">List</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>)</span><br><span class="line">l: <span class="type">List</span>[<span class="type">Int</span>] = <span class="type">List</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>)</span><br><span class="line"></span><br><span class="line">scala> l.head</span><br><span class="line">res1: <span class="type">Int</span> = <span class="number">1</span></span><br><span class="line"></span><br><span class="line">scala> l.tail</span><br><span class="line">res2: <span class="type">List</span>[<span class="type">Int</span>] = <span class="type">List</span>(<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>)</span><br><span class="line"></span><br><span class="line">scala> <span class="type">Nil</span></span><br><span class="line">res0: scala.collection.immutable.<span class="type">Nil</span>.<span class="keyword">type</span> = <span class="type">List</span>()</span><br><span class="line"></span><br><span class="line">scala> <span class="keyword">val</span> l2 = <span class="number">1</span> :: <span class="type">Nil</span></span><br><span class="line">l2: <span class="type">List</span>[<span class="type">Int</span>] = <span class="type">List</span>(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">scala> <span class="keyword">val</span> l3 = <span class="number">2</span> :: l2</span><br><span class="line">l3: <span class="type">List</span>[<span class="type">Int</span>] = <span class="type">List</span>(<span class="number">2</span>, <span class="number">1</span>)</span><br></pre></td></tr></table></figure><ul><li>我们也可以创建变长list</li></ul><figure class="highlight scala"><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">scala> <span class="keyword">val</span> l5 = scala.collection.mutable.<span class="type">ListBuffer</span>[<span class="type">Int</span>]()</span><br><span class="line">l5: scala.collection.mutable.<span class="type">ListBuffer</span>[<span class="type">Int</span>] = <span class="type">ListBuffer</span>()</span><br><span class="line"></span><br><span class="line">scala> l5 += (<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>)</span><br><span class="line">res3: l5.<span class="keyword">type</span> = <span class="type">ListBuffer</span>(<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>)</span><br><span class="line"></span><br><span class="line">scala> l5.toList</span><br><span class="line">res4: <span class="type">List</span>[<span class="type">Int</span>] = <span class="type">List</span>(<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>)</span><br><span class="line"></span><br><span class="line">scala> l5.toArray</span><br><span class="line">res5: <span class="type">Array</span>[<span class="type">Int</span>] = <span class="type">Array</span>(<span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>)</span><br></pre></td></tr></table></figure><ul><li>接下来我们看个方法,这个方法可以完成求和操作</li></ul><figure class="highlight scala"><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 class="function"><span class="keyword">def</span> <span class="title">sum</span></span>(nums:<span class="type">Int</span>*):<span class="type">Int</span> = {</span><br><span class="line"> <span class="keyword">if</span> (nums.length == <span class="number">0</span>) {</span><br><span class="line"> <span class="number">0</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> nums.head + sum(nums.tail:_*)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="3-set">3. Set</h3><figure class="highlight scala"><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">scala> <span class="keyword">val</span> set = <span class="type">Set</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">2</span>, <span class="number">1</span>, <span class="number">4</span>, <span class="number">3</span>)</span><br><span class="line">set: scala.collection.immutable.<span class="type">Set</span>[<span class="type">Int</span>] = <span class="type">Set</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">4</span>, <span class="number">3</span>)</span><br></pre></td></tr></table></figure><h3 id="4-map">4. Map</h3><ul><li>Map(映射)是一种可迭代的键值对(key/value)结构</li><li>所有的值都可以通过键来获取</li><li>Map 中的键都是唯一的</li><li>Map 也叫哈希表(Hash tables)</li><li>Map 有两种类型,可变与不可变,区别在于可变对象可以修改它,而不可变对象不可以</li><li>默认情况下 Scala 使用不可变 Map。如果你需要使用可变集合,你需要显式的引入 import scala.collection.mutable.Map 类</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 空哈希表,键为字符串,值为整型</span></span><br><span class="line"><span class="keyword">var</span> <span class="type">A</span>:<span class="type">Map</span>[<span class="type">Char</span>,<span class="type">Int</span>] = <span class="type">Map</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment">// Map 键值对演示</span></span><br><span class="line"><span class="keyword">val</span> colors = <span class="type">Map</span>(<span class="string">"red"</span> -> <span class="string">"#FF0000"</span>, <span class="string">"azure"</span> -> <span class="string">"#F0FFFF"</span>)</span><br></pre></td></tr></table></figure><ul><li>定义 Map 时,需要为键值对定义类型。如果需要添加 key-value 对,可以使用 + 号</li></ul><figure class="highlight scala"><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"><span class="type">A</span> += ('<span class="type">I</span>' -> <span class="number">1</span>)</span><br><span class="line"><span class="type">A</span> += ('<span class="type">J</span>' -> <span class="number">5</span>)</span><br></pre></td></tr></table></figure><p><strong>Map 基本操作</strong></p><ul><li>keys:返回 Map 所有的键(key)</li><li>values:返回 Map 所有的值(value)</li><li>isEmpty:在 Map 为空时返回true</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]) {</span><br><span class="line"> <span class="keyword">val</span> colors = <span class="type">Map</span>(<span class="string">"red"</span> -> <span class="string">"#FF0000"</span>,</span><br><span class="line"> <span class="string">"azure"</span> -> <span class="string">"#F0FFFF"</span>,</span><br><span class="line"> <span class="string">"peru"</span> -> <span class="string">"#CD853F"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">val</span> nums: <span class="type">Map</span>[<span class="type">Int</span>, <span class="type">Int</span>] = <span class="type">Map</span>()</span><br><span class="line"></span><br><span class="line"> println( <span class="string">"colors 中的键为 : "</span> + colors.keys )</span><br><span class="line"> println( <span class="string">"colors 中的值为 : "</span> + colors.values )</span><br><span class="line"> println( <span class="string">"检测 colors 是否为空 : "</span> + colors.isEmpty )</span><br><span class="line"> println( <span class="string">"检测 nums 是否为空 : "</span> + nums.isEmpty )</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>输出结果为:</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line">colors 中的键为 : <span class="type">Set</span>(red, azure, peru)</span><br><span class="line">colors 中的值为 : <span class="type">MapLike</span>(#<span class="type">FF0000</span>, #<span class="type">F0FFFF</span>, #<span class="type">CD853F</span>)</span><br><span class="line">检测 colors 是否为空 : <span class="literal">false</span></span><br><span class="line">检测 nums 是否为空 : <span class="literal">true</span></span><br></pre></td></tr></table></figure><ul><li>你可以使用 ++ 运算符或 Map.++() 方法来连接两个 Map,Map 合并时会移除重复的 key</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]) {</span><br><span class="line"> <span class="keyword">val</span> colors1 = <span class="type">Map</span>(<span class="string">"red"</span> -> <span class="string">"#FF0000"</span>,</span><br><span class="line"> <span class="string">"azure"</span> -> <span class="string">"#F0FFFF"</span>,</span><br><span class="line"> <span class="string">"peru"</span> -> <span class="string">"#CD853F"</span>)</span><br><span class="line"> <span class="keyword">val</span> colors2 = <span class="type">Map</span>(<span class="string">"blue"</span> -> <span class="string">"#0033FF"</span>,</span><br><span class="line"> <span class="string">"yellow"</span> -> <span class="string">"#FFFF00"</span>,</span><br><span class="line"> <span class="string">"red"</span> -> <span class="string">"#FF0000"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// ++ 作为运算符</span></span><br><span class="line"> <span class="keyword">var</span> colors = colors1 ++ colors2</span><br><span class="line"> println( <span class="string">"colors1 ++ colors2 : "</span> + colors )</span><br><span class="line"></span><br><span class="line"> <span class="comment">// ++ 作为方法</span></span><br><span class="line"> colors = colors1.++(colors2)</span><br><span class="line"> println( <span class="string">"colors1.++(colors2)) : "</span> + colors )</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>以下通过 foreach 循环输出 Map 中的 keys 和 values</li></ul><figure class="highlight scala"><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 class="class"><span class="keyword">object</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]) {</span><br><span class="line"> <span class="keyword">val</span> sites = <span class="type">Map</span>(<span class="string">"runoob"</span> -> <span class="string">"http://www.runoob.com"</span>,</span><br><span class="line"> <span class="string">"baidu"</span> -> <span class="string">"http://www.baidu.com"</span>,</span><br><span class="line"> <span class="string">"taobao"</span> -> <span class="string">"http://www.taobao.com"</span>)</span><br><span class="line"></span><br><span class="line"> sites.keys.foreach{ i => </span><br><span class="line"> print( <span class="string">"Key = "</span> + i )</span><br><span class="line"> println(<span class="string">" Value = "</span> + sites(i) )}</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="5-option-some-none">5. Option & Some & None</h3><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">OptionApp</span> <span class="keyword">extends</span> <span class="title">App</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">val</span> m = <span class="type">Map</span>(<span class="number">1</span> -> <span class="number">2</span>)</span><br><span class="line"> println(m.get(<span class="number">1</span>))</span><br><span class="line"> println(m.getOrElse(<span class="number">2</span>, <span class="string">"None"</span>))</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * final case class Some[+A](@deprecatedName('x, "2.12.0") value: A) extends Option[A] {</span></span><br><span class="line"><span class="comment"> * def isEmpty = false</span></span><br><span class="line"><span class="comment"> * def get = value</span></span><br><span class="line"><span class="comment"> * }</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * case object None extends Option[Nothing] {</span></span><br><span class="line"><span class="comment"> * def isEmpty = true</span></span><br><span class="line"><span class="comment"> * def get = throw new NoSuchElementException("None.get")</span></span><br><span class="line"><span class="comment"> * }</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure><h3 id="6-tuple">6. Tuple</h3><ul><li>与列表一样,元组也是不可变的,但与列表不同的是元组可以包含不同类型的元素</li><li>元组的值是通过将单个的值包含在圆括号中构成的</li></ul><figure class="highlight scala"><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">scala> <span class="keyword">val</span> t = (<span class="number">1</span>, <span class="number">3.14</span>, <span class="string">"Fred"</span>) </span><br><span class="line">t: (<span class="type">Int</span>, <span class="type">Double</span>, <span class="type">String</span>) = (<span class="number">1</span>,<span class="number">3.14</span>,<span class="type">Fred</span>)</span><br></pre></td></tr></table></figure><ul><li>访问元组的元素可以通过数字索引,我们可以使用 t._1 访问第一个元素, t._2 访问第二个元素</li></ul><figure class="highlight scala"><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 class="class"><span class="keyword">object</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]) {</span><br><span class="line"> <span class="keyword">val</span> t = (<span class="number">4</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">val</span> sum = t._1 + t._2 + t._3 + t._4</span><br><span class="line"></span><br><span class="line"> println( <span class="string">"元素之和为: "</span> + sum )</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>你可以使用 Tuple.productIterator() 方法来迭代输出元组的所有元素</li></ul><figure class="highlight scala"><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 class="class"><span class="keyword">object</span> <span class="title">Test</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">main</span></span>(args: <span class="type">Array</span>[<span class="type">String</span>]) {</span><br><span class="line"> <span class="keyword">val</span> t = (<span class="number">4</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"> t.productIterator.foreach{ i =>println(<span class="string">"Value = "</span> + i )}</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="第6章:scala-模式匹配">第6章:Scala 模式匹配</h2><h3 id="1-基本数据类型模式匹配">1. 基本数据类型模式匹配</h3><ul><li>Java:对一个值进行条件判断,返回针对不同的条件进行不同的处理</li></ul><figure class="highlight scala"><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">变量 <span class="keyword">match</span> {</span><br><span class="line"> <span class="keyword">case</span> value1 => 代码<span class="number">1</span></span><br><span class="line"> <span class="keyword">case</span> value2 => 代码<span class="number">2</span></span><br><span class="line"> ......</span><br><span class="line"> <span class="keyword">case</span> _ => 代码<span class="type">N</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>具体代码实现</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">MatchApp</span> <span class="keyword">extends</span> <span class="title">App</span> </span>{</span><br><span class="line"> <span class="keyword">val</span> names = <span class="type">Array</span>(<span class="string">"Akiho Yoshizawa"</span>, <span class="string">"YuiHatano"</span>, <span class="string">"Aoi Sola"</span>)</span><br><span class="line"> <span class="keyword">val</span> name = names(<span class="type">Random</span>.nextInt(names.length))</span><br><span class="line"></span><br><span class="line"> name <span class="keyword">match</span> {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"Akiho Yoshizawa"</span> => println(<span class="string">"吉老师"</span>)</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"YuiHatano"</span> => println(<span class="string">"波老师"</span>)</span><br><span class="line"> <span class="keyword">case</span> _ => println(<span class="string">"真不知道你们在说什么"</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">judgeGrade</span></span>(grade:<span class="type">String</span>) = {</span><br><span class="line"> grade <span class="keyword">match</span> {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"A"</span> => println(<span class="string">"Excellent"</span>)</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"B"</span> => println(<span class="string">"Good"</span>)</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"C"</span> => println(<span class="string">"Just so so"</span>)</span><br><span class="line"> <span class="keyword">case</span> _ => println(<span class="string">"You need work harder"</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> judgeGrade(<span class="string">"A"</span>)</span><br><span class="line"> judgeGrade(<span class="string">"C"</span>)</span><br><span class="line"> judgeGrade(<span class="string">"D"</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>加条件的模式匹配</strong></p><figure class="highlight scala"><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 class="function"><span class="keyword">def</span> <span class="title">judgeGrade</span></span>(name:<span class="type">String</span>, grade:<span class="type">String</span>) = {</span><br><span class="line"> grade <span class="keyword">match</span> {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"A"</span> => println(<span class="string">"Excellent"</span>)</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"B"</span> => println(<span class="string">"Good"</span>)</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"C"</span> => println(<span class="string">"Just so so"</span>)</span><br><span class="line"> <span class="keyword">case</span> _ <span class="keyword">if</span> (name == <span class="string">"lisi"</span>) => println(name + <span class="string">", you are a good boy, but ..."</span>)</span><br><span class="line"> <span class="keyword">case</span> _ => println(<span class="string">"You need work harder"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">judgeGrade(<span class="string">"zhangsan"</span>, <span class="string">"D"</span>)</span><br><span class="line">judgeGrade(<span class="string">"lisi"</span>, <span class="string">"D"</span>)</span><br></pre></td></tr></table></figure><h3 id="2-array模式匹配">2. Array模式匹配</h3><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">greeting</span></span>(array:<span class="type">Array</span>[<span class="type">String</span>]) = {</span><br><span class="line"> array <span class="keyword">match</span> {</span><br><span class="line"> <span class="keyword">case</span> <span class="type">Array</span>(<span class="string">"zhangsan"</span>) => println(<span class="string">"Hi:zhangsan"</span>)</span><br><span class="line"> <span class="keyword">case</span> <span class="type">Array</span>(x, y) => println(<span class="string">"Hi:"</span> + x + <span class="string">","</span> + y)</span><br><span class="line"> <span class="keyword">case</span> <span class="type">Array</span>(<span class="string">"zhangsan"</span>, _*) => println(<span class="string">"Hi:zhangsan and other friend"</span>)</span><br><span class="line"> <span class="keyword">case</span> _ => println(<span class="string">"Hi:everybody"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">greeting(<span class="type">Array</span>(<span class="string">"zhangsan"</span>))</span><br><span class="line">greeting(<span class="type">Array</span>(<span class="string">"lisi"</span>, <span class="string">"wangwu"</span>))</span><br><span class="line">greeting(<span class="type">Array</span>(<span class="string">"zhangsan"</span>, <span class="string">"lisi"</span>, <span class="string">"wangwu"</span>))</span><br><span class="line">greeting(<span class="type">Array</span>(<span class="string">"lisi"</span>))</span><br></pre></td></tr></table></figure><h3 id="3-list模式匹配">3. List模式匹配</h3><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">greeting</span></span>(list:<span class="type">List</span>[<span class="type">String</span>]) = {</span><br><span class="line"> list <span class="keyword">match</span> {</span><br><span class="line"> <span class="keyword">case</span> “zhangsan”::<span class="type">Nil</span> => println(“<span class="type">Hi</span>:zhangsan”)</span><br><span class="line"> <span class="keyword">case</span> x::y::<span class="type">Nil</span> => println(“<span class="type">Hi</span>:” + x + “,” + y)</span><br><span class="line"> <span class="keyword">case</span> “zhangsan”::tail => println(“<span class="type">Hi</span>:zhangsan and other friend”)</span><br><span class="line"> <span class="keyword">case</span> _ => println(“<span class="type">Hi</span>:everybody”)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">greeting(<span class="type">List</span>(“zhangsan”))</span><br><span class="line">greeting(<span class="type">List</span>(“lisi”, “wangwu”))</span><br><span class="line">greeting(<span class="type">List</span>(“zhangsan”, “lisi”, “wangwu”))</span><br><span class="line">greeting(<span class="type">List</span>(“lisi”))</span><br></pre></td></tr></table></figure><h3 id="4-类型匹配">4. 类型匹配</h3><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">matchType</span></span>(obj:<span class="type">Any</span>): <span class="type">Unit</span> = {</span><br><span class="line">obj <span class="keyword">match</span> {</span><br><span class="line"><span class="keyword">case</span> x:<span class="type">Int</span> => println(“<span class="type">Int</span>”)</span><br><span class="line"><span class="keyword">case</span> s:<span class="type">String</span> => println(“<span class="type">String</span>”)</span><br><span class="line"><span class="keyword">case</span> m:<span class="type">Map</span>[_,_] => m.foreach(println)</span><br><span class="line"><span class="keyword">case</span> _ => println(“other <span class="class"><span class="keyword">type</span><span class="title">”</span>)</span></span><br><span class="line"><span class="class">}</span></span><br><span class="line"><span class="class">}</span></span><br><span class="line"><span class="class"></span></span><br><span class="line"><span class="class"><span class="title">matchType</span>(<span class="params">1</span>)</span></span><br><span class="line"><span class="class"><span class="title">matchType</span>(<span class="params">“1”</span>)</span></span><br><span class="line"><span class="class"><span class="title">matchType</span>(<span class="params">1f</span>)</span></span><br><span class="line"><span class="class"><span class="title">matchType</span>(<span class="params"><span class="type">Map</span>(“name” -> “thpffcj”</span>))</span></span><br></pre></td></tr></table></figure><h3 id="5-scala异常处理">5. Scala异常处理</h3><figure class="highlight scala"><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 class="class"><span class="keyword">object</span> <span class="title">ExceptionApp</span> <span class="keyword">extends</span> <span class="title">App</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">val</span> i = <span class="number">10</span> / <span class="number">0</span></span><br><span class="line"> println(i)</span><br><span class="line"> } <span class="keyword">catch</span> {</span><br><span class="line"> <span class="keyword">case</span> e:<span class="type">ArithmeticException</span> => println(<span class="string">"除数不能为0"</span>)</span><br><span class="line"> <span class="keyword">case</span> e:<span class="type">Exception</span> => println(e.getMessage)</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> <span class="comment">// 释放资源,一定能执行</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="6-case-class模式匹配">6. case class模式匹配</h3><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span></span></span><br><span class="line"><span class="class"><span class="title">case</span> <span class="title">class</span> <span class="title">CTO</span>(<span class="params">name:<span class="type">String</span>, floor:<span class="type">String</span></span>) <span class="keyword">extends</span> <span class="title">Person</span></span></span><br><span class="line"><span class="class"><span class="title">case</span> <span class="title">class</span> <span class="title">Employee</span>(<span class="params">name:<span class="type">String</span>, floor:<span class="type">String</span></span>) <span class="keyword">extends</span> <span class="title">Person</span></span></span><br><span class="line"><span class="class"><span class="title">case</span> <span class="title">class</span> <span class="title">Other</span>(<span class="params">name:<span class="type">String</span></span>) <span class="keyword">extends</span> <span class="title">Person</span></span></span><br><span class="line"><span class="class"></span></span><br><span class="line"><span class="class"><span class="title">def</span> <span class="title">caseclassMatch</span>(<span class="params">person: <span class="type">Person</span></span>) </span>= {</span><br><span class="line"> person <span class="keyword">match</span> {</span><br><span class="line"> <span class="keyword">case</span> <span class="type">CTO</span>(name, floor) => println(<span class="string">"CTO name is: "</span> + name + <span class="string">", floor is: "</span> + floor)</span><br><span class="line"> <span class="keyword">case</span> <span class="type">Employee</span>(name, floor) => println(<span class="string">"Employee name is: "</span> + name + <span class="string">", floor is: "</span> + floor)</span><br><span class="line"> <span class="keyword">case</span> _ => println(<span class="string">"other"</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">caseclassMatch(<span class="type">CTO</span>(<span class="string">"Thpffcj"</span>, <span class="string">"22"</span>))</span><br><span class="line">caseclassMatch(<span class="type">Employee</span>(<span class="string">"zhagnsan"</span>, <span class="string">"2"</span>))</span><br><span class="line">caseclassMatch(<span class="type">Other</span>(<span class="string">"other"</span>))</span><br></pre></td></tr></table></figure><h3 id="7-some-none模式匹配-略">7. Some & None模式匹配(略)</h3><h2 id="第7章:scala-函数高级操作">第7章:Scala 函数高级操作</h2><h3 id="1-字符串高级操作">1. 字符串高级操作</h3><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">StringApp</span> <span class="keyword">extends</span> <span class="title">App</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">val</span> name = <span class="string">"Thpffcj"</span></span><br><span class="line"> <span class="keyword">val</span> team = <span class="string">"AC Milan"</span></span><br><span class="line"> <span class="comment">// 插值</span></span><br><span class="line"> println(<span class="string">s"Hello:<span class="subst">$name</span>, Welcome to <span class="subst">$team</span>"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">val</span> b =</span><br><span class="line"> <span class="string">""</span><span class="string">"</span></span><br><span class="line"><span class="string"> |这是一个多行字符串</span></span><br><span class="line"><span class="string"> |hello</span></span><br><span class="line"><span class="string"> |world</span></span><br><span class="line"><span class="string"> |Thpffcj</span></span><br><span class="line"><span class="string"> "</span><span class="string">""</span>.stripMargin</span><br><span class="line"> println(b)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="2-匿名函数">2. 匿名函数</h3><ul><li>匿名函数:函数是可以命名的,也可以不命名</li><li>(参数名:参数类型) => 函数体</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line">scala> <span class="keyword">val</span> m1 = (x:<span class="type">Int</span>) => x+<span class="number">1</span></span><br><span class="line"></span><br><span class="line">scala> m1(<span class="number">10</span>)</span><br><span class="line">res2: <span class="type">Int</span> = </span><br><span class="line"></span><br><span class="line">scala> <span class="function"><span class="keyword">def</span> <span class="title">add</span> </span>= (x:<span class="type">Int</span>, y:<span class="type">Int</span>) => {x+y}</span><br><span class="line">add: (<span class="type">Int</span>, <span class="type">Int</span>) => <span class="type">Int</span></span><br><span class="line"></span><br><span class="line">scala> add(<span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line">res3: <span class="type">Int</span> = <span class="number">5</span></span><br></pre></td></tr></table></figure><h3 id="3-curry函数">3. curry函数</h3><figure class="highlight scala"><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"><span class="comment">// 将原来接收两个参数的一个函数,转换成2个</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sum</span></span>(a:<span class="type">Int</span>, b:<span class="type">Int</span>) = {a+b}</span><br><span class="line">println(sum(<span class="number">2</span>,<span class="number">3</span>))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sum2</span></span>(a:<span class="type">Int</span>)(b:<span class="type">Int</span>) = a + b</span><br><span class="line">println(sum2(<span class="number">2</span>)(<span class="number">3</span>))</span><br></pre></td></tr></table></figure><h3 id="4-高阶函数">4. 高阶函数</h3><h4 id="map">map</h4><ul><li>map:逐个去操作集合中的每个元素</li></ul><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line">scala> <span class="keyword">val</span> l = <span class="type">List</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>)</span><br><span class="line">l: <span class="type">List</span>[<span class="type">Int</span>] = <span class="type">List</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>)</span><br><span class="line"></span><br><span class="line">scala> l.map((x: <span class="type">Int</span>) => x + <span class="number">1</span>)</span><br><span class="line">res0: <span class="type">List</span>[<span class="type">Int</span>] = <span class="type">List</span>(<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>)</span><br><span class="line"></span><br><span class="line">scala> l.map((x) => x * <span class="number">2</span>)</span><br><span class="line">res1: <span class="type">List</span>[<span class="type">Int</span>] = <span class="type">List</span>(<span class="number">2</span>, <span class="number">4</span>, <span class="number">6</span>, <span class="number">8</span>, <span class="number">10</span>, <span class="number">12</span>, <span class="number">14</span>, <span class="number">16</span>)</span><br><span class="line"></span><br><span class="line">scala> l.map(x => x * <span class="number">2</span>)</span><br><span class="line">res2: <span class="type">List</span>[<span class="type">Int</span>] = <span class="type">List</span>(<span class="number">2</span>, <span class="number">4</span>, <span class="number">6</span>, <span class="number">8</span>, <span class="number">10</span>, <span class="number">12</span>, <span class="number">14</span>, <span class="number">16</span>)</span><br><span class="line"></span><br><span class="line">scala> l.map(_ * <span class="number">2</span>)</span><br><span class="line">res3: <span class="type">List</span>[<span class="type">Int</span>] = <span class="type">List</span>(<span class="number">2</span>, <span class="number">4</span>, <span class="number">6</span>, <span class="number">8</span>, <span class="number">10</span>, <span class="number">12</span>, <span class="number">14</span>, <span class="number">16</span>)</span><br></pre></td></tr></table></figure><h4 id="filter">filter</h4><figure class="highlight scala"><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">scala> l.map(_ * <span class="number">2</span>).filter(_ > <span class="number">8</span>)</span><br><span class="line">res4: <span class="type">List</span>[<span class="type">Int</span>] = <span class="type">List</span>(<span class="number">10</span>, <span class="number">12</span>, <span class="number">14</span>, <span class="number">16</span>)</span><br></pre></td></tr></table></figure><h4 id="take">take</h4><figure class="highlight scala"><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">scala> l.take(<span class="number">4</span>)</span><br><span class="line">res5: <span class="type">List</span>[<span class="type">Int</span>] = <span class="type">List</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>)</span><br></pre></td></tr></table></figure><h4 id="reduce">reduce</h4><p>scala当中的reduce可以对集合当中的元素进行归约操作。两两相加</p><p>reduce包含reduceLeft和reduceRight。reduceLeft就是从左向右归约,reduceRight就是从右向左归约。</p><figure class="highlight scala"><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">scala> l.reduce(_ + _)</span><br><span class="line">res6: <span class="type">Int</span> = <span class="number">36</span></span><br><span class="line"></span><br><span class="line">scala> l.reduceLeft(_ - _)</span><br><span class="line">res7: <span class="type">Int</span> = <span class="number">-34</span></span><br><span class="line"></span><br><span class="line">scala> l.reduceRight(_ - _)</span><br><span class="line">res8: <span class="type">Int</span> = <span class="number">-4</span></span><br></pre></td></tr></table></figure><h4 id="flatten">flatten</h4><p>所有元素都压平</p><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line">scala> <span class="keyword">var</span> f = <span class="type">List</span>(<span class="type">List</span>(<span class="number">1</span>,<span class="number">2</span>),<span class="type">List</span>(<span class="number">3</span>,<span class="number">4</span>),<span class="type">List</span>(<span class="number">5</span>,<span class="number">6</span>))</span><br><span class="line">f: <span class="type">List</span>[<span class="type">List</span>[<span class="type">Int</span>]] = <span class="type">List</span>(<span class="type">List</span>(<span class="number">1</span>, <span class="number">2</span>), <span class="type">List</span>(<span class="number">3</span>, <span class="number">4</span>), <span class="type">List</span>(<span class="number">5</span>, <span class="number">6</span>))</span><br><span class="line"></span><br><span class="line">scala> f.flatten</span><br><span class="line">res0: <span class="type">List</span>[<span class="type">Int</span>] = <span class="type">List</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>)</span><br></pre></td></tr></table></figure><h4 id="flatmap">flatMap</h4><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line">scala> f.map(_.map((_ * <span class="number">2</span>)))</span><br><span class="line">res10: <span class="type">List</span>[<span class="type">List</span>[<span class="type">Int</span>]] = <span class="type">List</span>(<span class="type">List</span>(<span class="number">2</span>, <span class="number">4</span>), <span class="type">List</span>(<span class="number">6</span>, <span class="number">8</span>), <span class="type">List</span>(<span class="number">10</span>, <span class="number">12</span>))</span><br><span class="line"></span><br><span class="line">scala> f.flatMap(_.map(_ * <span class="number">2</span>))</span><br><span class="line">res11: <span class="type">List</span>[<span class="type">Int</span>] = <span class="type">List</span>(<span class="number">2</span>, <span class="number">4</span>, <span class="number">6</span>, <span class="number">8</span>, <span class="number">10</span>, <span class="number">12</span>)</span><br></pre></td></tr></table></figure><h3 id="5-偏函数-略">5. 偏函数(略)</h3><h2 id="第8章:scala-隐式转换">第8章:Scala 隐式转换</h2><p>需求:为一个已存在的类添加一个新的方法<br>Java:动态代理<br>Scala:隐式转换<br>双刃剑<br>Spark/Hive/MR… 调优</p><figure class="highlight scala"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.io.<span class="type">File</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//import ImplicitAspect._</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">ImplicitApp</span> </span>{</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 定义隐式转换函数即可</span></span><br><span class="line"> <span class="keyword">implicit</span> <span class="function"><span class="keyword">def</span> <span class="title">man2superman</span></span>(man:<span class="type">Man</span>):<span class="type">Superman</span> = <span class="keyword">new</span> <span class="type">Superman</span>(man.name)</span><br><span class="line"> <span class="keyword">val</span> man = <span class="keyword">new</span> <span class="type">Man</span>(<span class="string">"PK"</span>)</span><br><span class="line"> man.fly()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Man</span>(<span class="params">val name: <span class="type">String</span></span>) </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">eat</span></span>(): <span class="type">Unit</span> = {</span><br><span class="line"> println(<span class="string">s"man[ <span class="subst">$name</span> ] eat ..... "</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Superman</span>(<span class="params">val name: <span class="type">String</span></span>) </span>{</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">fly</span></span>(): <span class="type">Unit</span> = {</span><br><span class="line"> println(<span class="string">s"superman[ <span class="subst">$name</span> ] fly ..... "</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="第9章:scala-操作外部数据-略">第9章:Scala 操作外部数据(略)</h2><h2 id="第10章:综合案例-略">第10章:综合案例(略)</h2><h3 id="1-项目概述">1. 项目概述</h3><ul><li>Spring Boot + Spring Data + Scala + Java 混编</li></ul><h3 id="2-项目需求">2. 项目需求</h3><ul><li>论统一元数据管理在大数据平台中的重要性(SparkSQL/Hive)</li><li>构建大数据统一元数据管理</li><li>元数据管理:MetaStore<ul><li>采集</li><li>维护</li><li>稽查</li><li>分析</li></ul></li></ul><h3 id="3-项目需求分析">3. 项目需求分析</h3><ul><li>本个项目实战:<ul><li>数据库管理<ul><li>id:数据库编号</li><li>name:数据库名称</li><li>location:数据库存放在HDFS/S3/OSS等文件系统上的目录</li></ul></li><li>表管理:表是要属于某一个数据库<ul><li>id:表编号</li><li>name:表名称</li><li>tableType:表类型</li><li>dbId:该表所属的数据库id</li><li>默认存储路径:db对应的location/tableName</li></ul></li></ul></li></ul><h3 id="4-功能开发">4. 功能开发</h3><h3 id="5-使用postman进行交互测试">5. 使用postman进行交互测试</h3><h3 id="6-扩展">6. 扩展</h3><h2 id="附录一:scala-图解">附录一:Scala 图解</h2><p>在线运行地址:<a href="https://scastie.scala-lang.org/joymufeng/EctSt3TMSUm4gxDPj11JIw" target="_blank" rel="noopener">图解 Scala 基本语法代码片段</a></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/5c17408eeeab5676bdb7dfa8.png" alt="img"></p><p>(来源:<a href="https://www.playscala.cn/article/view?_id=10-5c166352eeab5676bdb7c68c" target="_blank" rel="noopener">图解 Scala 基本语法 V2018.12.17 - PlayScala中文社区 | Scala中文技术交流社区</a>)</p><h2 id="引用">引用</h2><ul><li>参考链接:<a href="http://www.thpffcj.com/2019/05/10/Scala-Learning-1/" target="_blank" rel="noopener">Scala 学习 1 | Thpffcj的树洞</a></li><li>代码工程:<a href="https://github.com/frank-lam/scalaNocturne" target="_blank" rel="noopener">frank-lam/scalaNocturne: scala小夜曲</a></li></ul>]]></content>
<summary type="html">
<h2 id="第1章:初识-scala">第1章:初识 Scala</h2>
<h3 id="1-scala概述">1. Scala概述</h3>
<ul>
<li>Scala是一门多范式的编程语言,设计初衷集成面向对象编程和函数式编程的各种特性</li>
<li>Scala运
</summary>
</entry>
<entry>
<title>SpringBoot 打包成 war,并部署到 Tomcat</title>
<link href="http://www.frankfeekr.cn/2020/01/28/springboot-package-war/"/>
<id>http://www.frankfeekr.cn/2020/01/28/springboot-package-war/</id>
<published>2020-01-28T15:26:57.000Z</published>
<updated>2021-10-23T14:22:52.650Z</updated>
<content type="html"><![CDATA[<h2 id="一-jar-与-war-包的区别">一、jar 与 war 包的区别</h2><p>参考来源:<a href="https://blog.csdn.net/ljj_9/article/details/79423148" target="_blank" rel="noopener">spring boot打jar包和打war包的区别作用 - 赖进杰的专栏 - CSDN博客</a></p><ul><li><p>jar 包:直接通过内置 tomcat 运行,不需要额外安装 tomcat。如需修改内置 tomcat 的配置,只需要在SpringBoot 的配置文件中配置。内置 tomcat 没有自己的日志输出,全靠 jar 包应用输出日志。但是比较方便,快速,比较简单。</p></li><li><p>war 包:传统的应用交付方式,需要安装 tomcat,然后放到 webapps 目录下运行 war 包,可以灵活选择tomcat 版本,可以直接修改 tomcat 的配置,有自己的 tomcat 日志输出,可以灵活配置安全策略。相对打成 jar 包来说没那么快速方便。</p></li></ul><h2 id="二-把-springboot-打成-war-包部署到-tomcat-中">二、把 SpringBoot 打成 war 包部署到 Tomcat 中</h2><p>参考来源:<a href="https://juejin.im/post/5cd15ed2e51d453b5854b881" target="_blank" rel="noopener">三分钟把spring boot打成war包部署到tomcat中 - 掘金</a></p><h3 id="1-将-springboot-项目打包成-war">1. 将 SpringBoot 项目打包成 war</h3><p>修改pom.xml指定打包方式为 war 包: <code><packaging>war</packaging></code> 修改 pom.xml 修改 SpringBoot 内置的 tomcat 依赖,指定 scope 为 provided</p><figure class="highlight xml"><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></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-tomcat<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">scope</span>></span>provided<span class="tag"></<span class="name">scope</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure><p>修改成果如下:</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/0370086000102300003.20200115145244.95604277177340573599350435562405:20200128232755:2800:98F94DB7383592A4CB1FD3AF5127D23D48B11C6549DD29F3310092D07A208CA9.png" alt="img"></p><p>创建一个初始化文件初始化项目比如:MySpringBootServletInitializer.java 核心代码如下</p><figure class="highlight java"><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"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MySpringBootServletInitializer</span> <span class="keyword">extends</span> <span class="title">SpringBootServletInitializer</span> </span>{</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> SpringApplicationBuilder <span class="title">configure</span><span class="params">(SpringApplicationBuilder builder)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> builder.sources(MywarApplication.class);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>执行命令 <code>mvn clean install -Dmaven.test.skip=true</code> 将项目打成war包,执行后到target目录可以看到成功的把项目打成了war包</p><ul><li><p><strong>一些注意的点</strong></p><ul><li>可以修改最终的war包的名称,比如去掉文件名里的版本号:mywar.war,在pom.xml使用finalName指令可以实现</li></ul></li></ul><blockquote><p>上述配置如果觉得麻烦,可以直接下载这位作者的代码包,直接下载打包体验(GitHub:<a href="https://github.com/neatlife/mywar" target="_blank" rel="noopener">neatlife/mywar</a>)</p></blockquote><h3 id="2-把项目的-war-放到-tomcat-里运行">2. 把项目的 war 放到 Tomcat 里运行</h3><blockquote><p>这里我们以 CentOS 7 为例,进行 Tomcat 配置和部署</p></blockquote><ol><li>下载 Tomcat9,并解压</li></ol><figure class="highlight shell"><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">wget http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-9/v9.0.22/bin/apache-tomcat-9.0.22.tar.gz</span><br><span class="line"></span><br><span class="line">tar -zxvf apache-tomcat-9.0.22.tar.gz</span><br></pre></td></tr></table></figure><ol start="2"><li><p>将项目打成 WAR 包放在 Tomcat 的 webapps 目录下</p></li><li><p>在 <code>/conf</code> 目录下找到 <code>server.xml</code> 的文件</p></li><li><p>在Host标签里边添加 <code><Context path="" docBase="myproject" reloadable="true" /></code></p></li></ol><figure class="highlight xml"><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"><span class="tag"><<span class="name">Host</span> <span class="attr">name</span>=<span class="string">"localhost"</span> <span class="attr">appBase</span>=<span class="string">"webapps"</span> <span class="attr">unpackWARs</span>=<span class="string">"true"</span> <span class="attr">autoDeploy</span>=<span class="string">"true"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">Valve</span> <span class="attr">className</span>=<span class="string">"org.apache.catalina.valves.AccessLogValve"</span> <span class="attr">directory</span>=<span class="string">"logs"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">prefix</span>=<span class="string">"localhost_access_log"</span> <span class="attr">suffix</span>=<span class="string">".txt"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">pattern</span>=<span class="string">"%h %l %u %t "</span>%<span class="attr">r</span>" %<span class="attr">s</span> %<span class="attr">b</span>" /></span></span><br><span class="line"> <span class="tag"><<span class="name">Context</span> <span class="attr">path</span>=<span class="string">""</span> <span class="attr">docBase</span>=<span class="string">"myproject"</span> <span class="attr">reloadable</span>=<span class="string">"true"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">Host</span>></span></span><br></pre></td></tr></table></figure><p><code>Context</code> 标签内容,注意 <code>path</code> 填空,<code>docBase</code> 为项目名称</p><ol start="5"><li><p>启动 Tomcat,<code>sh bin/startup.sh</code></p></li><li><p>再次访问即可携带项目名称或不带都可以访问到项目。如 localhost:8080/ 或 localhost:8080/myproject</p></li></ol><p>参考来源:<a href="https://blog.csdn.net/h363659487/article/details/80796946" target="_blank" rel="noopener">Tomcat9部署WAR包访问不带项目名的方式 - h363659487的博客 - CSDN博客</a></p>]]></content>
<summary type="html">
<h2 id="一-jar-与-war-包的区别">一、jar 与 war 包的区别</h2>
<p>参考来源:<a href="https://blog.csdn.net/ljj_9/article/details/79423148" target="_blank" rel="
</summary>
</entry>
<entry>
<title>一文学会 SpringBoot gzip 压缩传输(请求/响应)</title>
<link href="http://www.frankfeekr.cn/2020/01/13/springboot-http-gzip-compress/"/>
<id>http://www.frankfeekr.cn/2020/01/13/springboot-http-gzip-compress/</id>
<published>2020-01-13T15:36:06.000Z</published>
<updated>2021-10-23T14:22:52.649Z</updated>
<content type="html"><![CDATA[<h2 id="前言">前言</h2><p>经常我们都会与服务端进行大数据量的文本传输,例如 JSON 就是常见的一种格式。通过 REST API 接口进行 GET 和 POST 请求,可能会有大量的文本格式数据提交、返回。然后对于文本,它有很高的压缩率,如果在 GET/POST 请求时候对文本进行压缩会节省大量的网络带宽,减少网络时延。</p><p>HTTP 协议在相应部分支持 <code>Content-Encoding: gzip</code> ,浏览器请求时带上 <code>Accept-Encoding: gzip</code> 即可,服务端对返回的 response body 进行压缩,并在 response 头带上 <code>Content-Encoding: gzip</code>,浏览器会自动解析。</p><p>然而 HTTP 没有压缩 request body 的设计,因为在客户端发起请求时并不知道服务器是否支持压缩。因此没法通过 HTTP 协议来解决,只能在服务端做一些过滤器进行判断,人为约束。<strong>压缩和解压在提升网络带宽的同时,会带来 CPU 资源的损耗。</strong></p><p>本文将手把手带你实现 SpringBoot 项目中,<strong>请求时</strong>和<strong>响应时</strong>对 body 文本进行 gzip 压缩,减小网络时延,提升传输效率。</p><h2 id="一-请求压缩-request-compress">一、请求压缩(request compress)</h2><h3 id="1-springboot-整合-gzip">1. SpringBoot 整合 gzip</h3><p>考虑到通用性,仿效 response 的 header <code>Content-Encoding: gzip</code> 方式。</p><p>客户端把压缩过的 json 作为 post-body 传输,然后增加一个 request header: <code>Content-Encoding: gzip</code> 来告诉服务器端是压缩的格式。</p><p>服务端增加一个 Filter,对 request 头进行检查,如果有 Content-Encoding 则解压缩后继续。这样不影响现有程序。</p><h4 id="1-springboot-添加请求过滤器">(1)Springboot 添加请求过滤器</h4><p><strong>增加 2 个类:</strong></p><ul><li><strong>ContentEncodingFilter.java</strong></li></ul><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.frankfeekr.sample.controller;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.slf4j.Logger;</span><br><span class="line"><span class="keyword">import</span> org.slf4j.LoggerFactory;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Service;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.filter.OncePerRequestFilter;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.servlet.FilterChain;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.ServletException;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServletRequest;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServletResponse;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ContentEncodingFilter</span> <span class="keyword">extends</span> <span class="title">OncePerRequestFilter</span> </span>{</span><br><span class="line"> Logger logger = LoggerFactory.getLogger(ContentEncodingFilter.class);</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">doFilterInternal</span><span class="params">(HttpServletRequest request, HttpServletResponse response,</span></span></span><br><span class="line"><span class="function"><span class="params"> FilterChain chain)</span> <span class="keyword">throws</span> ServletException, IOException </span>{</span><br><span class="line"></span><br><span class="line"> String conentEncoding = request.getHeader(<span class="string">"Content-Encoding"</span>);</span><br><span class="line"> <span class="keyword">if</span> (conentEncoding != <span class="keyword">null</span> && (<span class="string">"gzip"</span>.equalsIgnoreCase(conentEncoding) || <span class="string">"deflate"</span>.equalsIgnoreCase(conentEncoding))) {</span><br><span class="line"> logger.trace(<span class="string">"Content-Encoding: {}"</span>, conentEncoding);</span><br><span class="line"> chain.doFilter(<span class="keyword">new</span> GZIPRequestWrapper(request), response);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> chain.doFilter(request, response);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li><strong>GZIPRequestWrapper.java</strong></li></ul><figure class="highlight java"><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><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.frankfeekr.sample.controller;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.slf4j.Logger;</span><br><span class="line"><span class="keyword">import</span> org.slf4j.LoggerFactory;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.servlet.ReadListener;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.ServletInputStream;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServletRequest;</span><br><span class="line"><span class="keyword">import</span> javax.servlet.http.HttpServletRequestWrapper;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.io.InputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.UnsupportedEncodingException;</span><br><span class="line"><span class="keyword">import</span> java.util.zip.DeflaterInputStream;</span><br><span class="line"><span class="keyword">import</span> java.util.zip.GZIPInputStream;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">GZIPRequestWrapper</span> <span class="keyword">extends</span> <span class="title">HttpServletRequestWrapper</span> </span>{</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> Logger logger = LoggerFactory.getLogger(GZIPRequestWrapper.class);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">protected</span> HttpServletRequest request;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">GZIPRequestWrapper</span><span class="params">(HttpServletRequest request)</span> </span>{</span><br><span class="line"> <span class="keyword">super</span>(request);</span><br><span class="line"> <span class="keyword">this</span>.request = request;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> ServletInputStream <span class="title">getInputStream</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> ServletInputStream sis = request.getInputStream();</span><br><span class="line"> InputStream is = <span class="keyword">null</span>;</span><br><span class="line"> String conentEncoding = request.getHeader(<span class="string">"Content-Encoding"</span>);</span><br><span class="line"> <span class="keyword">if</span> (<span class="string">"gzip"</span>.equalsIgnoreCase(conentEncoding)) {</span><br><span class="line"> is = <span class="keyword">new</span> GZIPInputStream(sis);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">"deflate"</span>.equalsIgnoreCase(conentEncoding)) {</span><br><span class="line"> is = <span class="keyword">new</span> DeflaterInputStream(sis);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedEncodingException(conentEncoding + <span class="string">" is not supported."</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">final</span> InputStream compressInputStream = is;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> ServletInputStream() {</span><br><span class="line"> ReadListener readListener;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">read</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> <span class="keyword">int</span> b = compressInputStream.read();</span><br><span class="line"> <span class="keyword">if</span> (b == -<span class="number">1</span> && readListener != <span class="keyword">null</span>) {</span><br><span class="line"> readListener.onAllDataRead();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> b;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isFinished</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">return</span> compressInputStream.available() == <span class="number">0</span>;</span><br><span class="line"> } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> logger.error(<span class="string">"error"</span>, e);</span><br><span class="line"> <span class="keyword">if</span> (readListener != <span class="keyword">null</span>) {</span><br><span class="line"> readListener.onError(e);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isReady</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">return</span> compressInputStream.available() > <span class="number">0</span>;</span><br><span class="line"> } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> logger.error(<span class="string">"error"</span>, e);</span><br><span class="line"> <span class="keyword">if</span> (readListener != <span class="keyword">null</span>) {</span><br><span class="line"> readListener.onError(e);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setReadListener</span><span class="params">(<span class="keyword">final</span> ReadListener readListener)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.readListener = readListener;</span><br><span class="line"> sis.setReadListener(<span class="keyword">new</span> ReadListener() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onDataAvailable</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> logger.trace(<span class="string">"onDataAvailable"</span>);</span><br><span class="line"> <span class="keyword">if</span> (readListener != <span class="keyword">null</span>) {</span><br><span class="line"> readListener.onDataAvailable();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onAllDataRead</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> logger.trace(<span class="string">"onAllDataRead"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onError</span><span class="params">(Throwable throwable)</span> </span>{</span><br><span class="line"> logger.error(<span class="string">"onError"</span>, throwable);</span><br><span class="line"> <span class="keyword">if</span> (readListener != <span class="keyword">null</span>) {</span><br><span class="line"> readListener.onError(throwable);</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="2-springboot-controller-demo">(2)SpringBoot Controller Demo</h4><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.frankfeekr.sample.controller;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.alibaba.fastjson.JSON;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.*;</span><br><span class="line"></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping</span>(produces = <span class="string">"application/json;charset=UTF-8"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloController</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="meta">@RequestMapping</span>(value = <span class="string">"/gzip"</span>, method = RequestMethod.POST, consumes = <span class="string">"application/json"</span>)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">post</span><span class="params">(@RequestBody Map<String, String> request)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> request.toString();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="2-客户端测试">2. 客户端测试</h3><h4 id="1-gzip-body-方式请求">(1)gzip body 方式请求</h4><p>只要在 Headers 头域带上 gzip,即可告知服务器为通过 gzip 方式来提交</p><figure class="highlight shell"><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"><span class="meta">#</span><span class="bash"> 生成一个 gzip 压缩的包</span></span><br><span class="line">echo '{"type": "json", "length": 2020, "name": "Frank"}' | gzip > body.gz</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> curl 命令模拟 POST gzip 压缩请求</span></span><br><span class="line">curl --location --request POST 'http://127.0.0.1:9090/gzip' \</span><br><span class="line">--header 'Content-Type: application/json' \</span><br><span class="line">--header 'Content-Encoding: gzip' \</span><br><span class="line">--data-binary '@body.gz'</span><br></pre></td></tr></table></figure><p>断点调试结果如下:</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/0370086000102300003.20200113163301.69890847611891790047508850940355:20200113233941:2800:A90525B2D241FDD9E98C57EFD895CDE2B93E999C4FA9EC8FE12575C2E2D7822A.png" alt="img"></p><h4 id="2-json-body-方式请求">(2)json body 方式请求</h4><p>如果不需要进行压缩,则不带上 <code>Content-Encoding: gzip</code> 头域配置项即可。</p><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> curl 命令模拟 gzip</span></span><br><span class="line">curl --location --request POST 'http://127.0.0.1:9090/gzip' \</span><br><span class="line">--header 'Content-Type: application/json' \</span><br><span class="line">--header 'Content-Encoding: gzip' \</span><br><span class="line">--data-raw '{"type": "json", "length": 2020, "name": "Frank"}'</span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/0370086000102300003.20200113163312.30834098482141976236368552551055:20200113233941:2800:62E24B316E4C398722B7FB2B8DE7FCDF4D7A7C02347576395CFFAD166A1DB017.png" alt="img"></p><p>上述可以发现如果通过 data-raw 方式请求,则必须要去掉 <code>--header 'Content-Encoding: gzip'</code>,否则会出现 400 Bad Request 错误。现正确请求如下:</p><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> curl 命令模拟 gzip</span></span><br><span class="line">curl --location --request POST 'http://127.0.0.1:9090/gzip' \</span><br><span class="line">--header 'Content-Type: application/json' \</span><br><span class="line">--data-raw '{"type": "json", "length": 2020, "name": "Frank"}'</span><br></pre></td></tr></table></figure><h2 id="二-响应压缩-response-compress">二、响应压缩(response compress)</h2><h3 id="1-springboot-配置-response-策略">1. SpringBoot 配置 response 策略</h3><p>SpringBoot 默认是不开启 gzip 压缩的,需要我们手动开启,在配置文件中添加两行</p><figure class="highlight yml"><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></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line"><span class="attr"> compression:</span></span><br><span class="line"><span class="attr"> enabled:</span> <span class="literal">true</span></span><br><span class="line"><span class="attr"> mime-types:</span> <span class="string">application/json,application/xml,text/html,text/plain,text/css,application/x-javascript</span></span><br></pre></td></tr></table></figure><p>注意下上面配置中的 <code>mime-types</code>,在 SpringBoot 2.0+ 的版本中,默认值如下,所以一般我们不需要特意添加这个配置</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* Comma-separated list of MIME types that should be compressed.</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">private</span> String[] mimeTypes = <span class="keyword">new</span> String[]{<span class="string">"text/html"</span>, <span class="string">"text/xml"</span>, <span class="string">"text/plain"</span>, <span class="string">"text/css"</span>, <span class="string">"text/javascript"</span>, <span class="string">"application/javascript"</span>, <span class="string">"application/json"</span>, <span class="string">"application/xml"</span>};</span><br></pre></td></tr></table></figure><h3 id="2-测试">2. 测试</h3><p>写一个测试的demo</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.frankfeekr.sample.controller;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.alibaba.fastjson.JSON;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.*;</span><br><span class="line"></span><br><span class="line"><span class="meta">@ResponseBody</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping</span>(produces = <span class="string">"application/json;charset=UTF-8"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloController</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="meta">@GetMapping</span>(<span class="string">"bigReq"</span>)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">bigReqList</span><span class="params">()</span> </span>{</span><br><span class="line"> List<String> result = <span class="keyword">new</span> ArrayList<>(<span class="number">2048</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">2048</span>; i++) {</span><br><span class="line"> result.add(UUID.randomUUID().toString());</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> JSON.toJSONString(result);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>测试效果,可以明显发现请求 body 被压缩,时延也变小。</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/0370086000102300003.20200113163251.83993183554300941025109943358891:20200113233941:2800:5A39D17A736F37BF50B339A89B1018FD414BFC500C6C65C4F683A6B7EDA5B9BA.png" alt="img"></p><h2 id="参考资料">参考资料</h2><ul><li><a href="https://github.com/gexiangdong/tutorial/wiki/%E4%BC%A0%E8%BE%93%E5%8E%8B%E7%BC%A9%E7%9A%84JSON" target="_blank" rel="noopener">传输压缩的JSON · gexiangdong/tutorial Wiki</a></li><li><a href="http://spring.hhui.top/spring-blog/2019/11/20/191120-SpringBoot%E7%B3%BB%E5%88%97%E6%95%99%E7%A8%8BWeb%E7%AF%87%E4%B9%8B%E5%BC%80%E5%90%AFGZIP%E6%95%B0%E6%8D%AE%E5%8E%8B%E7%BC%A9/" target="_blank" rel="noopener">191120-SpringBoot系列教程Web篇之开启GZIP数据压缩 | 一灰灰Blog</a></li></ul>]]></content>
<summary type="html">
<h2 id="前言">前言</h2>
<p>经常我们都会与服务端进行大数据量的文本传输,例如 JSON 就是常见的一种格式。通过 REST API 接口进行 GET 和 POST 请求,可能会有大量的文本格式数据提交、返回。然后对于文本,它有很高的压缩率,如果在 GET/POS
</summary>
</entry>
<entry>
<title>IDEA 中配置 PMD 静态代码检查插件,提升你的代码质量</title>
<link href="http://www.frankfeekr.cn/2019/12/16/idea-pmd-plugin/"/>
<id>http://www.frankfeekr.cn/2019/12/16/idea-pmd-plugin/</id>
<published>2019-12-16T12:48:15.000Z</published>
<updated>2021-10-23T14:22:52.648Z</updated>
<content type="html"><![CDATA[<h2 id="什么是-pmd">什么是 PMD</h2><blockquote><p>PMD 官网: PMD - An extensible cross-language static code analyzer. (<a href="https://pmd.github.io/%EF%BC%89" target="_blank" rel="noopener">https://pmd.github.io/)</a></p></blockquote><p>PMD是一种开源分析 Java 代码错误的工具,它是一个静态代码检测工具(所谓的静态,就是在 Java 代码不运行时就可以进行检查)。通过 PMD 的一些规则,可以检查出代码中存在的问题,例如:</p><ul><li>可能存在的 BUG<ul><li>空的 <code>try/catch/finally/switch</code> 语句</li></ul></li><li>无效代码<ul><li>未使用的局部变量、参数、私有方法等</li></ul></li><li>可选的代码<ul><li><code>String / StringBuffer</code> 的滥用</li></ul></li><li>复杂的表达式<ul><li>不必须的 <code>if</code> 语句</li><li>可以使用 <code>while</code> 循环完成的 <code>for</code> 循环</li></ul></li><li>重复的代码<ul><li>复制/粘贴代码意味着复制/粘贴 BUG</li></ul></li></ul><p>总而言之,降低潜在的代码风险,减少人工审核成本,提高编码质量。此外,你也可以自己自定义规则。</p><h2 id="pmd-插件配置">PMD 插件配置</h2><h3 id="1-插件下载">1. 插件下载</h3><p>IDEA 插件下载地址:PMDPlugin - Plugins | JetBrains(或是直接在插件市场搜索安装)</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/idea-pmd-plugin-1.png" alt="img"></p><h3 id="2-选择从磁盘安装">2. 选择从磁盘安装</h3><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/idea-pmd-plugin-2.png" alt="img"></p><h3 id="3-重启生效">3. 重启生效</h3><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/idea-pmd-plugin-3.png" alt="img"></p><h3 id="4-添加配置模板">4. 添加配置模板</h3><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/idea-pmd-plugin-4.png" alt="img"></p><p>示例模板:</p><figure class="highlight xml"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?xml version="1.0" encoding="UTF-8"?></span></span><br><span class="line"><span class="tag"><<span class="name">ruleset</span> <span class="attr">xmlns</span>=<span class="string">"http://pmd.sourceforge.net/ruleset/2.0.0"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">name</span>=<span class="string">""</span></span></span><br><span class="line"><span class="tag"> <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">description</span>></span></span><br><span class="line"> This is the PMD additional ruleset for TEAMMATES production code.</span><br><span class="line"> <span class="tag"></<span class="name">description</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">exclude-pattern</span>></span>.*/test/java/.*<span class="tag"></<span class="name">exclude-pattern</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">exclude-pattern</span>></span>.*/client/java/.*<span class="tag"></<span class="name">exclude-pattern</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">exclude-pattern</span>></span>.*.html<span class="tag"></<span class="name">exclude-pattern</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">exclude-pattern</span>></span>.*.jsp<span class="tag"></<span class="name">exclude-pattern</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">exclude-pattern</span>></span>.*.js<span class="tag"></<span class="name">exclude-pattern</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">exclude-pattern</span>></span>.*.xml<span class="tag"></<span class="name">exclude-pattern</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">rule</span> <span class="attr">ref</span>=<span class="string">"category/java/bestpractices.xml/AvoidPrintStackTrace"</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">rule</span> <span class="attr">ref</span>=<span class="string">"category/java/bestpractices.xml/SystemPrintln"</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">rule</span> <span class="attr">ref</span>=<span class="string">"category/java/design.xml/AvoidThrowingRawExceptionTypes"</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">rule</span> <span class="attr">ref</span>=<span class="string">"category/java/design.xml/SignatureDeclareThrowsException"</span>/></span></span><br><span class="line"><span class="tag"></<span class="name">ruleset</span>></span></span><br></pre></td></tr></table></figure><h3 id="5-pmd本地检查扫描">5. PMD本地检查扫描</h3><p>每次在代码上库,或者合并分之前就可以先进行一遍静态检查,降低提交到 GitLab 后代码检查异常的可能性,从而提升开发效率。</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/idea-pmd-plugin-5.png" alt="img"></p><p>😎 自此,配置结束,可以开始你的 PMD 使用之旅了。</p>]]></content>
<summary type="html">
<h2 id="什么是-pmd">什么是 PMD</h2>
<blockquote>
<p>PMD 官网: PMD - An extensible cross-language static code analyzer. (<a href="https://pmd.github.
</summary>
</entry>
<entry>
<title>Java 字符串分割 split 函数使用知多少</title>
<link href="http://www.frankfeekr.cn/2019/12/11/in-depth-java-split-function/"/>
<id>http://www.frankfeekr.cn/2019/12/11/in-depth-java-split-function/</id>
<published>2019-12-11T15:17:52.000Z</published>
<updated>2021-10-23T14:22:52.648Z</updated>
<content type="html"><![CDATA[<h2 id="最近使用-split-遇到的问题">最近使用 split 遇到的问题</h2><p>你是否常常在使用 Java 的字符串 <code>split()</code> 函数,例如字符串简单的分割。</p><p>我想大部分人都跟我一样进行简单的分割,也没有发现什么大问题,示例代码如下:</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> String str = <span class="string">"apiVersion|uuid|timestamp|userInfo|arg1|arg2|arg3|args"</span>;</span><br><span class="line"> String[] arrs = str.split(<span class="string">"\\|"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20191211232109238.png" alt="image-20191211232109238"></p><p>但是最近在进行字符串分割的时候,发现想要对下面的字符串进行分割的时候就出现问题了</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> String str = <span class="string">"6.0|ff129239||frank2019|1||3|"</span>;</span><br><span class="line"> String[] arrs = str.split(<span class="string">"\\|"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>得到的 arrs 数组如下:</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20191211233831354.png" alt="image-20191211233831354"></p><p>我们发现,字符串最后的一个 <code>|</code> 之后没有元素,结尾的空字符串被丢弃了。但实际上在我的业务场景空字符也代表一个占位符,需要分割成 8 个元素的数组。</p><p>查看了 split 的源码,发现其实它还有一个重载方法</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> String[] split(String regex, <span class="keyword">int</span> limit) {</span><br><span class="line"> <span class="comment">// ... ...</span></span><br><span class="line"> <span class="keyword">return</span> Pattern.compile(regex).split(<span class="keyword">this</span>, limit);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果不想让结尾的空字符串被丢弃,可以这么实现:</p><figure class="highlight java"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> String str = <span class="string">"6.0|ff129239||frank2019|1||3|"</span>;</span><br><span class="line"> String[] arrs = str.split(<span class="string">"\\|"</span>,-<span class="number">1</span>);</span><br><span class="line"> System.out.println(args);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>执行效果如下</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20191211234818473.png" alt="image-20191211234818473"></p><h2 id="split-regex-和-split-regex-limit-使用知多少">split(regex) 和 split(regex, limit) 使用知多少</h2><h3 id="split-regex-方法">split(regex)方法</h3><ul><li>regex:分割的字符串或者正则表达式,根据字符串中的分割符,进行拆分成字符串数组</li></ul><h3 id="split-regex-limit-方法">split(regex, limit)方法</h3><ul><li>regex:分割的字符串或者正则表达式</li><li>limit:作用是控制模式应用的次数 (这里的<strong>模式</strong>理解成:从左往右分割次数即可)<ul><li>Limit<0:模式被应用尽可能多的次数</li><li>limit =0:表示模式应用尽可能多的次数,数组可以是任意长度,并且结尾空字符串将被丢弃。</li><li>limit>0:模式将会应用 limit-1 次数组长度不会超过 limit</li></ul></li></ul><p><strong>举例说明</strong>:</p><ol><li><p><code>split("6.0|ff129239||frank2019|1||3|",0)</code> 是切割默认模式等同于 <code>split("6.0|ff129239||frank2019|1||3|")</code> 结尾分割字符为空不进行进行分割</p></li><li><p><code>split("6.0|ff129239||frank2019|1||3|",-1)</code>,limit 参数小于 0 结尾分割字符为空也进行分割</p></li><li><p><code>split("6.0|ff129239||frank2019|1||3|",3)</code>,不管字符串有多少个符合分割的分隔符,只会从左到右分成长度为 3 的数组</p></li></ol>]]></content>
<summary type="html">
<h2 id="最近使用-split-遇到的问题">最近使用 split 遇到的问题</h2>
<p>你是否常常在使用 Java 的字符串 <code>split()</code> 函数,例如字符串简单的分割。</p>
<p>我想大部分人都跟我一样进行简单的分割,也没有发现什么大
</summary>
</entry>
<entry>
<title>大学四年,如何选择自己的技术栈</title>
<link href="http://www.frankfeekr.cn/2019/09/01/how-to-learn-my-major/"/>
<id>http://www.frankfeekr.cn/2019/09/01/how-to-learn-my-major/</id>
<published>2019-09-01T13:37:23.000Z</published>
<updated>2021-10-23T14:22:52.648Z</updated>
<content type="html"><![CDATA[<h2 id="引言">引言</h2><p>你们知道程序员最熟悉,最熟练,最常用的两个快捷键是哪两个吗?</p><div align="center"><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/ctrl-c-ctrl-v.png" width="300px" style="margin-bottom:20px;"></div><p>没错,就是你现在心中所想的:ctrl+c 和 ctrl+v ,俗名为:复制和粘贴。对于大部分程序员来说:复制和粘贴就是他创造伟大产品的左膀和右臂。</p><p>不知道大家有没有看过这个梗,这个梗其实相当的现实,但是你又不得不说它是说到点子上了。我前段时间听到过这样一句话:天下代码不过一个抄字。</p><ul><li>(基础)从网上抄写程序=¥1</li><li>(入门)知道哪部分程序能抄=¥100</li><li>(高手)知道抄前后需要怎样调整=¥1000</li><li>(精英)知道怎么才能让别人看不出来你抄过=¥10000</li><li>(创业)知道怎么在抄的情况下依然让所有人认可你独特的价值=¥100000</li></ul><h2 id="一-技术学习方法论">一、技术学习方法论</h2><h3 id="为什么要讲学习方法">为什么要讲学习方法</h3><p>在学习技术这条路上并不是一帆风顺,也一直在探索一条适合自己的学习方法。从一开始的技术小白,到现在还比较上道的老鸟,在这个过程中走了太多的弯路,想在这里和大家分享一些我的经历和学习方法。</p><p>对于立志成为一个技术达人,或者对于技术有执着追求的,学习一定是伴随着终生。</p><p><strong>技术是科学、是工具、更是理论应用于实践的途径</strong>。技术学习是无止境的,人的精力也是有限的,我们不应该一味的追求新技术。<strong>基础很重要,特别是数据结构与算法、操作系统原理、计算机网络、Linux 基础,这些在如今应用层编码的过程中或许不会被过多的关注,但是计算机的基础决定了你能走多远</strong>。从某一个技术点的深度着手,同时不断扩展自己知识的广度。项目需要什么就用什么学什么,业余时间当然可以扩展一下自己感兴趣的技术栈。</p><h3 id="我和技术有关的日子">我和技术有关的日子</h3><h4 id="1-从懵懂到想做技术">1. 从懵懂到想做技术</h4><p>从我的个人学习经历说起吧,学习的过程挺曲折的,但是回首过去发现这些经历的都将成为你未来的谈资。从进入大学开始,个人的对于摄像与平面有浓厚的兴趣,也花了整整三年的时间投身于学生工作(主要是平面设计、视频后期、微信公众号运营相关的),一度的规划是希望成为一个设计师或是视频后期工作者。也跟随很多摄制组拍摄过广告、做过平面设计物料、微信公众号媒体运管,我认为一个好的设计一定是给人带来美的享受,对于追求完美主义的我,深深的爱上了这个角色。</p><p>与此同时,本科作为软件工程专业出身的我,同时被各种计算机的学科熏陶着,自认为技术掌握的还不错。那个时候对于 Web 特别感兴趣,特别现在的人们打开计算机,大部分时间都是和你的浏览器相处。那个时候开始学习一些 HTML/CSS/JavaScript 相关的一些东西,可以自己做一些简单的静态网页,很快不久就写了一个非常简陋的个人主页。后来,接触了动态网页写过 JSP、<span>ASP.NET</span>、PHP 网页,但是那时候大部分还是前后台混合渲染的方式,开发效率倒是很高但是一旦想要从新设计前端界面,就会陷入重构的泥塘。后来,慢慢接触到了前后台分离的开发方式,知道了 Vue、React 相关的一些 MVVM 框架。</p><p>在 2015 年的学生工作中,马上迎来了学代会的换届选举,在过程中发现纸质选票效率低,通常中场投票和检票也耗费大量的时间。我算是一个善于发现问题的人,两天时间泡在图书馆中,搭建了一个学代会投票系统。在后来的投票过程中,我将原来的 30 分钟,缩短到了 5 分钟,并在大屏实时滚动显示投票柱状图。这个投票系统也得到了团委老师的一致认可,在后来的 4 年中一直使用了我的投票系统,并且保持了很好的稳定性和效果。在每一年的系统中,我也不断的升级投票系统,在这几年的升级过程中也记录了自己的成长轨迹。虽然毕业了,但口碑的也在各个学院中流传开了,也正在计划将它升级为一个平台,可以让所有注册到平台上的人都可以使用我的投票平台。</p><p>在设计师和技术研发人员的角色选择中,纠结过很长一段时间。会发现,设计师可以设计出很简洁、美观、善心悦目的作品,但是只有技术研发人员才能实实在在的为它赋能,真正的实现一个具有功能性、体验性的东西。他们都是艺术家,都在各自的领域增添异彩。我从来不认为开发的是功能,每一行代码都有它的灵魂,都是我的产品,更是我的尊严。在后来的日子里,我发现这两个角色对于逐渐开始并存,为什么不能够同时应用和学习呢?做设计中最懂技术的,做技术中最懂设计的。</p><p>同时在过去三年的学生工作中,也很好的锻炼了我的团队领导能力和技术分享能力。这些软实力,也许和很多的代码能力都无关,但是在后来的项目团队协作和管理中有着潜移默化的帮助。</p><blockquote><p>★★★ 大一大二的时间还很充裕,推荐大家多参加一些社团、学生会来提升自己。</p></blockquote><h4 id="2-我的成长与技术路线">2. 我的成长与技术路线</h4><p>从大三的暑假到目前的研究生阶段,开始学习前端开发技、跨平台应用、后台开发技术、了解系统架构的一些东西、机器学习算法应用、产品设计思维等等相关的东西。我开始了技术广度的挖掘,很多东西不敢说是精通,但至少一定是入门到进阶的水平,当然目前还在技术深度不断学习的路上。</p><p>我很认可一句话,“人人都是产品经理”,“人人都是架构师”。我们不应该为自己打上太多的标签,“我是一个算法工程师”、“我是一个 XX 语言开发工程师”、“我是一个产品经理”。在学习这条路上,我们应该首先抓好自己的本职工作,也就是技术深度的学习(例如:开始学会重构自己的项目代码、开始看项目源码),从而在工作之余不断的拓展自己技术的广度和思维的广度。这里说一个题外话,谷歌宣扬的 80% 的时间用来工作,20% 的时间用来学习,大名鼎鼎的 Go 语言就是三位科学家利用 20% 时间里创造的。</p><p>思考了很久,我对自己未来的规划是希望成为架构师方向,在技术达到一定高度,并有很好的想法的时候会有创业做产品的打算。于是我还是回到了自己的老本行,后台开发的方向,当然不仅仅只会关注后台相关的技术点,也会在业余时间学习一些算法和前端技术栈。逐渐的,不断在不同维度中学习技术,不断成长。</p><p>总之在技术快跑的过程中,我们不仅仅要学会一门精通的手艺,同时也要有自己的辅助第二技术或是第三技术。要学会用后台思维写前端,算法思维写后台,产品思维写算法。我们做的不仅仅是功能,要让每一行代码都具备灵魂。</p><p>个人对于产品是非常热爱的,这几年互联网的高速发展,整个中国发生了翻天覆地的变化。国民的衣食住行都发生了巨变,吃饭——美团、点评、饿了么,买衣服——淘宝、唯品会、京东,出行——哈啰单车、各种共享电动车、滴滴出行、曹操专车,住房——自如、贝壳找房、58 同城,还有改变生活方式的微信和支付宝。他们都将逐步成为我们生活中的水电煤,逐步融入人们的血液。在技术水平和思维格局到达已经层次,十分希望在未来的日子,在某个对的时刻能够创造一个影响一个时代的产品。</p><p><em>(图:技术路线与管理路线)</em></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/image-20190514134343973.png" alt="技术路线与管理路线"></p><h3 id="如何学习与技术快跑">如何学习与技术快跑</h3><p>上文通过自身经验举例,说了一些自己的技术成长路线。下面来谈谈技术学习的一些方法论,主要围绕以下四个部分进行讨论:</p><ul><li>技术大厦</li><li>极限编程</li><li>学习总结</li><li>技术分享</li></ul><h4 id="1-技术大厦">1. 技术大厦</h4><p>在学习技术前,我不知道大家是怎样的一种心态。我喜欢把技术比作一栋大厦,大部分人都会从大门进入,一步一步往上走,技术也在一点点的提高,但很多人大部分时候都会质疑自己学来到底有什么用,或是会遇到一些技术瓶颈,或是很多时候迷茫了也不知道该学习哪些东西。</p><p>技术的成长我认为分成三个层次:<strong>我不知道我不知道</strong>,<strong>我知道我不知道</strong>,<strong>我知道我知道</strong>。从技术的小白,到技术小达人,再到技术牛人。</p><p>在这栋技术大厦中,在技术小白阶段,就是按部就班的往上爬,但是当你技术学习到一定程度的时候,你必须开始了解整栋大厦的框架结构。所以在这里,必须要开始转向系统架构。<strong><em>这里摘抄一段《许式伟:架构设计的宏观视角》中的话,也正是我们想要表达的思想。</em></strong></p><blockquote><p>如同造房子有建筑工人(负责搬砖)和建筑师(负责架构设计)一样,软件系统的开发过程同样需要有程序员(负责搬“砖”)和架构师(负责架构设计)。作为架构师,我们需要的第一个能力是宏观的全局掌控能力。</p><p>如果把应用程序比作一座大厦,那么我们作为大厦的架构师,需要把大厦的结构搭建好,让程序员可以把砖填充进去,我们都知道,一个大厦的结构建得是否稳固,与地基密不可分。</p><p>所以,我们首先就需要从大厦的地基开始,熟悉这座大厦。毕竟,你对所依赖的基础架构了解得越全面,做业务架构设计就会越发从容。</p></blockquote><p>技术想要成长,必须首先在思想上成长,提升自己的技术思想高度。一个不想成为建筑师的包工头不是一个好的架构师,在学习这条往上的路上,必须要学会用宏观视角来解决问题。在技术成长的过程中,有时候不需要太多的注意某个细节是怎么实现的,而是要有技术判断力,技术是否可行,技术如何选型。</p><hr><p><strong>这里列一些大家在大学中会学习到的课程和技能</strong>:</p><ul><li>C / C++ 第一门语言</li><li>Web 网页编程 HTML/CSS/JavaScript</li><li>多媒体技术</li><li>Java</li><li>计算机基础知识<ul><li>数据结构与算法</li><li>计算机网络</li><li>操作系统</li><li>计算机组成原理</li></ul></li><li>软件工程</li><li>信息安全与密码学</li><li>…</li></ul><hr><p>最后推荐一些阅读脚本</p><ul><li>一张<a href="https://www.frankfeekr.cn/2019/04/17/%E5%90%8E%E5%8F%B0%E5%BC%80%E5%8F%91%E6%8A%80%E8%83%BD%E5%9B%BE%E8%B0%B1/">后台开发技能图谱</a>分享给大家</li><li>整理了一些<a href="https://www.frankfeekr.cn/2019/04/19/cs-learning-list/">阅读清单与学习课程⎡计算机学习的“武林秘籍”⎦</a>,欢迎大家学习</li></ul><h4 id="2-极限编程">2. 极限编程</h4><p>接着,来聊聊极限编程。极限,就一个字"快"!</p><p>身边也经常有很多人,常常在每次项目中,都没来得及系统学习技术栈就要开始项目开发了;大部分的时间都在复制粘贴,面向搜索引擎编程,找不到自己的技术核心点。</p><p>包括自己在内,很多时候也都希望当自己掌握了完整的一套技术栈以后再开始你的项目编码。经历过很多项目之后,我会发现其实这并不是正确的做法。同样的,这也遵循二八定律,在技术中我们只要花少量的时间学会 20% 的技术,就可以开始项目开发,剩下的 80% 大部分都应该在项目实践中去学习的。</p><p>在很多的项目中,根本来不及有太多的时间让我们学习。很可能一个新的技术,通过短短 5-7 天的学习,就必须要应用到项目中去,很难有太多的时间让你系统学习。特别在互联网的应用场景中,时间就是金钱,一个好的想法,必须在最短的时间内上线。极限编程正是这样一种敏捷快速的开发方式,要求团队成员拥有很高的技术素养,在很短时间内就可以学会并且应用。</p><p>在这里,我们从个人的经验,分享我在极限编程学习过程中的路线:</p><ul><li><p><strong>先看技术效果</strong></p><ul><li>学会检索一些牛逼的项目,看看通过这个技术可以达到什么样的效果,判断是否满足自己的需求。</li><li>结果导向的学习,更加能提升我们的学习兴趣。</li></ul></li><li><p><strong>快速视频学习入门(10小时以内)</strong></p><ul><li>特别像我一样学习不太喜欢看书,感觉到很枯燥的同学,我推荐直接找视频快速入门,视频不宜太长,10小时左右(2.0x 速度看)。快速的了解技术的状况,掌握基本的核心技术,会的部分直接跳过。</li><li>这里推荐一些好的学习网站:慕课网、极客时间、哔哩哔哩、极客学院、实验楼、学堂在线。</li></ul></li><li><p><strong>开源小项目</strong></p><ul><li>在视频学习入门之后我们可能还不具备一个项目的编码能力,这时候我们可以找一些开源项目来跑一跑(学会使用 GitHub),项目不宜太复杂,一个简单的小项目即可。</li></ul></li><li><p><strong>配合文档同步学习</strong></p><ul><li>技术文档不像小说,一定更不要从头到尾的读,选择有必要的部分尝试通读,但是大部分的文档应该作为字典一样的查询工具书。</li></ul></li><li><p><strong>开始投入项目生产</strong></p><ul><li>终于可以正式开始项目实践了,选择好自己的项目框架,配合文档、视频,不会的部分当然还得靠搜索引擎。</li></ul></li><li><p><strong>项目复盘与重构</strong></p><ul><li>重新审视自己的代码,有条件的应该让大神给你看看,指点一下代码,以便在未来升级改进。</li></ul></li><li><p><strong>技术进阶,重新回到书本(或是项目文档)</strong></p><ul><li>无论你在搜索引擎找到的答案,都是比较碎片化的知识。当你想要深入到底层或是原理相关的部分,你应该选择一本大众认可的书籍进行深入阅读,大部分的书才是一个最系统和深入的学习。当然要看技术点的感兴趣和必要成分,不是所有的技术都需要深入了解,但是在自己的技术领域应该还是需要不断精通和升华。</li></ul></li></ul><h4 id="3-学习总结">3. 学习总结</h4><p>在技术学习的过程中,我们可能会遇到很多的技术难点,还会遇到很多碎片化的检索工作,很多技术点我们肯定是不可能都记住甚至背下来。经常,我们会遇到一些常见遇到一些常见的东西,每次见到我们都要重新谷歌检索一遍,浪费了大量的时间。所以,学习总结是非常有必要。</p><p>都说好记性不如烂笔头,定期的学习和整理必然对学习巩固有所帮助,这里通过索引的方式对技术做一个系统分类,方便随时巩固和学习,当然还有面试。在学习这条路上难免会有很多盲点和学不完的知识。有道无术,术尚可求,掌握好思维能力才能应对千变万化的技术。<strong>不要把大脑当成硬盘,也不要做高速运转的 CPU,而修行自己的大脑成为一个搜索引擎,学会分析解决问题。</strong></p><p>这里盘点一些学习总结的方式:</p><ul><li><strong>技术博客 & 微信订阅号</strong><ul><li>首先技术博客是一个非常好的途径,可以通过掘金、CSDN、知乎专栏、知识星球等等途径编写自己的技术博客。</li><li>稍微极客一点的同学可以自己搭建一个自己的博客,例如:WordPress、Hexo、Hugo 这样的博客。(推荐使用 Hexo + GitHub + Typora + MD 来写自己的技术博客)。欢迎来我的博客逛逛:<a href="https://www.frankfeekr.cn/">https://www.frankfeekr.cn/</a></li><li>在技术博客的编写过程中,首先对你知识的回顾是很有帮助的,其次也将成为你的个人检索宝库,把自己常见的一些问题都记录在你的博客中,再下一次遇到的时候就可以信手捏来。</li><li>很多著名的技术书籍早期都是通过博客来积累自己的素材,当你的技术和文字达到一定功底,我想出书也是非常自然而然的事情。</li><li>此外还可以申请个人的微信订阅号来编写自己的技术博客,这也是一个很好的传播途径,慢慢的积累自己的粉丝哈哈。欢迎关注我的微信公众号,<a href="https://raw.githubusercontent.com/frank-lam/fullstack-tutorial/master/assets/wechat-fullstack3.png" target="_blank" rel="noopener">“全栈开发社区”</a>。</li></ul></li><li><strong>GitHub 开源项目</strong><ul><li>GitHub 不仅仅可以分享代码性质的项目,当然在上面你还可以开源自己博客项目,你可以通过 <code>.md</code> 来记录自己的技术博客。</li><li>很多开源的书籍、文档翻译项目都可以通过 GitHub 来托管</li><li>通过 GitHub 你还获取到了版本控制的文本和图床,这相比自己搭建服务器来构建博客更加的方便和安全,并且永不丢失。</li><li>欢迎看看我在 GitHub 上的技术项目:<a href="https://github.com/frank-lam/fullstack-tutorial" target="_blank" rel="noopener">https://github.com/frank-lam/fullstack-tutorial</a></li></ul></li><li><strong>其他</strong><ul><li>此外你还可以通过诸如:为知笔记、有道云笔记等方式进行学习总结</li><li>最后建议大家不要再用你的 txt 或是 word 来记录你的笔记</li></ul></li></ul><h4 id="4-分享技术">4. 分享技术</h4><p>学习金字塔是美国缅因州的国家训练实验室研究成果,它用数字形式形象显示了:采用不同的学习方式,学习者在两周以后还能记住内容(平均学习保持率)的多少。</p><p>在金字塔基座位置的学习方式,是“教别人”或者“马上应用”,可以记住 90% 的学习内容。通过学习金字塔,我们会发现学习最好的方式应该是主动学习并且学会分享教授别人如何学习。在教授别人的过程中,我们在输出知识,也在不断的输入别人的疑惑,看似单向学习的过程中实际上也是双向的学习,不断的巩固自己的知识。</p><p>那么在技术学习中呢?没错我们不仅要学会倾听别人的技术,很多时候我们要学会分享技术。要知道一个好的技术管理者,必须要学会根据团队成员的不同情况,制定不一样的培养方案,这也是很重要的一个能力。通过分享你的技术,同时你也会不断的巩固自己的逻辑能力和技术表达能力。</p><p><em>(图:学习金字塔,图片来源网络)</em></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/153614595514-23194.jpg" alt="学习金字塔"></p><h3 id="最后几句话">最后几句话</h3><p>技术学习的路是很长的,需要我们掌握好高效的学习方式,终身学习。</p><p>你要相信自己,可以通过 20 小时学会任何技能,也能通过 10000 小时成为某个领域的资深专家。在立足于本身技术深度学习的同时,也记得拓展自己思维的广度。</p><p>只有一个好的技术学习方法 ,才能让你在如今不断动荡的互联网江湖中,屹立不倒、处变不惊。</p><h2 id="二-计算机学科技术栈">二、计算机学科技术栈</h2><h3 id="技能云">技能云</h3><p>为了更好的学习技术,找到自己的方向和目标。我们应该先了解,计算机学科都有哪些技术栈。</p><div align="center"><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/fullstack-cloud.svg" width="85%"></div><h3 id="技能树">技能树</h3><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/skill-tree-8928737.svg" alt="skill-tree"></p>]]></content>
<summary type="html">
<h2 id="引言">引言</h2>
<p>你们知道程序员最熟悉,最熟练,最常用的两个快捷键是哪两个吗?</p>
<div align="center"><img src="/assets/ctrl-c-ctrl-v.png" width="300px" style="marg
</summary>
</entry>
<entry>
<title>IntelliJ IDEA 2019 开发工具和谐</title>
<link href="http://www.frankfeekr.cn/2019/08/31/intellij-idea-2019-crack/"/>
<id>http://www.frankfeekr.cn/2019/08/31/intellij-idea-2019-crack/</id>
<published>2019-08-31T07:48:05.000Z</published>
<updated>2021-10-23T14:22:52.649Z</updated>
<content type="html"><![CDATA[<p>在一个下着雨,美妙的下午,撸着代码。突然,IDEA 提示到期,瞬间让人泪奔。</p><p>查找了很多资料,终于完美的和谐了 IDEA。</p><p><strong>所谓永久,就是让你撸码到头秃,撸码到虚脱送 ICU。</strong></p><h2 id="环境">环境</h2><ul><li>win10</li><li>IntelliJ IDEA 2019.1.3 x64</li><li>测试时间:2019/08/31</li></ul><h2 id="和谐步骤">和谐步骤</h2><h3 id="1-修改-hosts-目录">1. 修改 Hosts 目录</h3><p>在 windows 的 hosts 目录:<code>C:\Windows\System32\drivers\etc</code> 追加如下记录</p><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span> <span class="string">https:</span><span class="comment">//account.jetbrains.com:443</span></span><br></pre></td></tr></table></figure><ul><li>注意:不需要 <code>0.0.0.0 www.jetbrains.com</code> 整体写入文件, 不然会影响某些功能</li></ul><h3 id="2-刷新-dsn-缓存">2. 刷新 DSN 缓存</h3><p>打开 cmd 终端,输入 <code>ipconfig/flushdns</code>,然后回车刷新 DNS 缓存</p><h3 id="3-获取激活码">3. 获取激活码</h3><p>打开 <a href="http://idea.lanyus.com" target="_blank" rel="noopener">http://idea.lanyus.com</a> 点击“获得激活码”,并复制</p><h3 id="4-马上激活">4. 马上激活</h3><p>打开激活界面,选择 <code>Activation code</code>,复制上面的激活码,点击 <code>active</code>,即可激活</p><p>至此,已经完成了全部步骤,可以开始撸代码了。</p>]]></content>
<summary type="html">
<p>在一个下着雨,美妙的下午,撸着代码。突然,IDEA 提示到期,瞬间让人泪奔。</p>
<p>查找了很多资料,终于完美的和谐了 IDEA。</p>
<p><strong>所谓永久,就是让你撸码到头秃,撸码到虚脱送 ICU。</strong></p>
<h2 id="环境">环
</summary>
<category term="IDEA" scheme="http://www.frankfeekr.cn/tags/IDEA/"/>
</entry>
<entry>
<title>CentOS 环境下 Open JDK 替换为 Oracle JDK</title>
<link href="http://www.frankfeekr.cn/2019/08/29/centos-openjdk-2-oraclejdk/"/>
<id>http://www.frankfeekr.cn/2019/08/29/centos-openjdk-2-oraclejdk/</id>
<published>2019-08-29T03:34:00.000Z</published>
<updated>2021-10-23T14:22:52.647Z</updated>
<content type="html"><![CDATA[<h2 id="环境">环境</h2><ul><li>CentOS 7.4</li></ul><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# java -version</span><br><span class="line">openjdk version "1.8.0_212"</span><br><span class="line">OpenJDK Runtime Environment (build 1.8.0_212-b04)</span><br><span class="line">OpenJDK 64-Bit Server VM (build 25.212-b04, mixed mode</span><br></pre></td></tr></table></figure><h2 id="下载-oracle-jdk">下载 Oracle JDK</h2><p>首先从 <a href="https://www.oracle.com/technetwork/java/javase/downloads/java-archive-javase8-2177648.html" target="_blank" rel="noopener">Oracle 网站</a> 下载所需的JDK。</p><h2 id="替换为-oracle-jdk">替换为 Oracle JDK</h2><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# rpm -ivh jdk-8u144-linux-x64.rpm</span><br><span class="line">准备中... ################################# [100%]</span><br><span class="line">正在升级/安装...</span><br><span class="line"> 1:jdk1.8.0_144-2000:1.8.0_144-fcs ################################# [100%]</span><br><span class="line">Unpacking JAR files...</span><br><span class="line"> tools.jar...</span><br><span class="line"> plugin.jar...</span><br><span class="line"> javaws.jar...</span><br><span class="line"> deploy.jar...</span><br><span class="line"> rt.jar...</span><br><span class="line"> jsse.jar...</span><br><span class="line"> charsets.jar...</span><br><span class="line"> localedata.jar...</span><br><span class="line">[root@localhost ~]# update-alternatives --config java</span><br><span class="line"></span><br><span class="line">共有 2 个提供“java”的程序。</span><br><span class="line"></span><br><span class="line"> 选项 命令</span><br><span class="line">-----------------------------------------------</span><br><span class="line">*+ 1 java-1.8.0-openjdk.x86_64 (/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.212.b04-0.el7_6.x86_64/jre/bin/java)</span><br><span class="line"> 2 /usr/java/jdk1.8.0_144/jre/bin/java</span><br><span class="line"></span><br><span class="line">按 Enter 保留当前选项[+],或者键入选项编号:2</span><br></pre></td></tr></table></figure><h2 id="验证">验证</h2><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line">[root@localhost ~]# java -version</span><br><span class="line">java version "1.8.0_144"</span><br><span class="line">Java(TM) SE Runtime Environment (build 1.8.0_144-b01)</span><br><span class="line">Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)</span><br></pre></td></tr></table></figure><h2 id="扩展">扩展</h2><ul><li>rpm:<a href="https://man.linuxde.net/rpm" target="_blank" rel="noopener">rpm命令_Linux rpm 命令用法详解:RPM软件包的管理工具</a></li><li>update-alternatives:<a href="https://blog.csdn.net/JasonDing1354/article/details/50470109" target="_blank" rel="noopener">【Linux】使用update-alternatives命令进行版本的切换</a></li></ul>]]></content>
<summary type="html">
<h2 id="环境">环境</h2>
<ul>
<li>CentOS 7.4</li>
</ul>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</
</summary>
<category term="Linux" scheme="http://www.frankfeekr.cn/tags/Linux/"/>
</entry>
<entry>
<title>玩转时序数据库 InfluxDB(一)初体验</title>
<link href="http://www.frankfeekr.cn/2019/07/24/influxdb-tutorial-start/"/>
<id>http://www.frankfeekr.cn/2019/07/24/influxdb-tutorial-start/</id>
<published>2019-07-24T02:31:37.000Z</published>
<updated>2021-10-23T14:22:52.648Z</updated>
<content type="html"><![CDATA[<blockquote><p>申明:本文为学习过程中的笔记,在现有的资料基础之上做了学习和整理,非完全原创的。引用资料见文末,参考资料。</p></blockquote><p>官方文档:<a href="https://docs.influxdata.com/influxdb/v1.7/" target="_blank" rel="noopener">InfluxDB 1.7 documentation | InfluxData Documentation</a></p><h2 id="一-启程">一、启程</h2><h3 id="1-介绍">1. 介绍</h3><p>InfluxDB 是使用 GO 编写的基于时间序列的数据库,用于存储大量带有时间戳的数据,报错 DevOps 监控,日志数据,应用程序的指标、数据分析数据等等。通过 InfluxDB 自动保存数据,你不需要删除和清理,只需要定义一段时间 DB 会帮你自动清理。</p><p>InfluxDB 提供三种操作方式:</p><ul><li><p>客户端命令行方式</p></li><li><p>HTTP API 接口</p></li><li><p>各语言 API 库</p></li></ul><h3 id="2-关键概念">2. 关键概念</h3><h4 id="基本概念">基本概念</h4><p>InfluxDB 和传统数据库(如:MySQL)的一些区别</p><table><thead><tr><th style="text-align:center">InfluxDB</th><th style="text-align:center">传统数据库中的概念</th></tr></thead><tbody><tr><td style="text-align:center">database</td><td style="text-align:center">数据库</td></tr><tr><td style="text-align:center">measurement</td><td style="text-align:center">数据库中的表</td></tr><tr><td style="text-align:center">points</td><td style="text-align:center">表里面的一行数据</td></tr></tbody></table><h4 id="特有概念">特有概念</h4><ol><li><p>tag–标签,在 InfluxDB 中,tag 是一个非常重要的部分,表名+tag 一起作为数据库的索引,是“key-value”的形式</p></li><li><p>field–数据,field 主要是用来存放数据的部分,也是“key-value”的形式</p></li><li><p>timestamp–时间戳,作为时序型数据库,时间戳是 InfluxDB 中最重要的部分,在插入数据时可以自己指定也可留空让系统指定</p><p><strong>说明</strong>:<em>在插入新数据时,tag、field 和 timestamp 之间用空格分隔</em></p></li><li><p>series–序列,所有在数据库中的数据,都需要通过图表来展示,而这个 series 表示这个表里面的数据,可以在图表上画成几条线。具体可以通过 <code>SHOW SERIES FROM "表名"</code> 进行查询</p></li><li><p>Retention policy–数据保留策略,可以定义数据保留的时长,每个数据库可以有多个数据保留策略,但只能有一个默认策略</p></li><li><p>Point–点,表示每个表里某个时刻的某个条件下的一个 field 的数据,因为体现在图表上就是一个点,于是将其称为 point。Point 由时间戳(time)、数据(field)、标签(tags)组成</p></li></ol><table><thead><tr><th style="text-align:center">Point 属性</th><th style="text-align:center">传统数据库中的概念</th></tr></thead><tbody><tr><td style="text-align:center">time</td><td style="text-align:center">每个数据记录时间,是数据库中的主索引 (会自动生成)</td></tr><tr><td style="text-align:center">fields</td><td style="text-align:center">表中的列(没有索引的属性)也就是记录的值:温度, 湿度</td></tr><tr><td style="text-align:center">tags</td><td style="text-align:center">表中的索引:地区,海拔</td></tr></tbody></table><h3 id="3-端口服务">3. 端口服务</h3><ul><li>8083:Web admin 管理服务的端口, <a href="http://localhost:8083" target="_blank" rel="noopener">http://localhost:8083</a></li><li>8086:HTTP API 的端口</li><li>8088:集群端口 (目前还不是很清楚, 配置在全局的 bind-address,默认不配置就是开启的)</li></ul><h2 id="二-安装">二、安装</h2><h3 id="1-基于-linux-centos-安装">1. 基于 Linux CentOS 安装</h3><figure class="highlight shell"><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">wget https://dl.influxdata.com/influxdb/releases/influxdb-0.13.0.x86_64.rpm</span><br><span class="line">sudo yum localinstall influxdb-0.13.0.x86_64.rpm</span><br></pre></td></tr></table></figure><h3 id="2-基于-docker-安装">2. 基于 Docker 安装</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -d -p 8083:8083 -p8086:8086 --expose 8090 --expose 8099 --name influxDbService influxdb</span><br></pre></td></tr></table></figure><ul><li><p>-d:容器在后台运行</p></li><li><p>-p:将容器内端口映射到宿主机端口,格式为 宿主机端口:容器内端口;8083 是 influxdb 的 web 管理工具端口,8086 是 influxdb 的 HTTP API 端口</p></li><li><p>–expose:可以让容器接受外部传入的数据</p></li><li><p>–name:容器名称 此处 influxDbService 则是启动后的容器名</p></li><li><p>最后是镜像名称 influxdb,镜像名可以通过 docker images 查看; 通过 tag 区分启镜像版本</p></li><li><p>若不加 tag 则启动的是最新版本 latest</p></li></ul><h2 id="三-可视化客户端安装">三、可视化客户端安装</h2><p>客户端为绿色版,下载解压打开即可。下载地址:<a href="https://github.com/CymaticLabs/InfluxDBStudio/releases" target="_blank" rel="noopener">Releases · CymaticLabs/InfluxDBStudio</a></p><ul><li>这里也提供 InfluxDB Studio 的使用说明,供大家参考:<a href="https://cloud.tencent.com/developer/article/1444098" target="_blank" rel="noopener">windows 下 influxDB 操作工具 InfluxDBStudio </a></li></ul><h2 id="四-客户端命令操作-基本语法">四、客户端命令操作(基本语法)</h2><h3 id="1-数据库操作">1. 数据库操作</h3><h4 id="创建">创建</h4><p>CREATE DATABASE {NAME};</p><figure class="highlight sql"><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">> create database frank</span><br><span class="line">> show databases</span><br><span class="line">name: databases</span><br><span class="line">name</span><br><span class="line"><span class="comment">----</span></span><br><span class="line">_internal</span><br><span class="line">telegraf</span><br><span class="line">frank</span><br></pre></td></tr></table></figure><ul><li>这时候我们发现数据库有一个表“_internal”,其实这个表是 influxdb 数据库的一些指标存储库。有点类似 mysql 数据库的 mysql 库。</li></ul><h4 id="删除">删除</h4><p>DROP DATABASE {NAME};</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">> drop database frank</span><br></pre></td></tr></table></figure><h4 id="使用">使用</h4><p>DROP {DB};</p><figure class="highlight sql"><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">> use frank</span><br><span class="line">Using database frank</span><br></pre></td></tr></table></figure><h3 id="2-数据表和数据操作">2. 数据表和数据操作</h3><p>建库的操作可以发现非常类似于 MySQL 下的操作。而在 InfluxDB 下没有细分的表的概念,InfluxDB 下的表在插入数据库的时候自动会创建。可以通过 <code>show measurements</code> 命令查看所有的表,这个类似于 MySQL 下的<code>show tables</code></p><h4 id="显示所有表">显示所有表</h4><figure class="highlight sql"><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">> show measurements</span><br><span class="line">name: measurements</span><br><span class="line">name</span><br><span class="line"><span class="comment">----</span></span><br><span class="line">cpu</span><br><span class="line">temperature</span><br></pre></td></tr></table></figure><h4 id="新建表-写数据">新建表(写数据)</h4><p>标准格式,注意在写数据的时候如果不添加时间戳,系统会默认添加一个时间。InfluxDB 中没有显式的新建表的语句,只能通过 insert 数据的方式来建立新表。</p><ul><li>语法格式</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">insert</span> <measurement>[,<tag-<span class="keyword">key</span>>=<tag-<span class="keyword">value</span>>...] <<span class="keyword">field</span>-<span class="keyword">key</span>>=<<span class="keyword">field</span>-<span class="keyword">value</span>>[,<field2-<span class="keyword">key</span>>=<field2-<span class="keyword">value</span>>...] [unix-nano-<span class="built_in">timestamp</span>]</span><br></pre></td></tr></table></figure><ul><li>示例</li></ul><figure class="highlight"><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">> INSERT cpu,host=serverA,region=us_west value=0.64</span><br><span class="line"></span><br><span class="line">> INSERT temperature,machine=unit42,type=assembly external=25,internal=37 1434067467000000000</span><br></pre></td></tr></table></figure><h4 id="删除表">删除表</h4><figure class="highlight sql"><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">> drop measurement disk_free</span><br><span class="line">> show measurements</span><br><span class="line">name: measurements</span><br><span class="line"><span class="comment">------------------</span></span><br><span class="line">name</span><br><span class="line">weather</span><br></pre></td></tr></table></figure><h4 id="读数据">读数据</h4><p>查询语句与 SQL 一样,不用过多的学习</p><ul><li>查询数据</li></ul><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT "host", "region", "value" FROM "cpu"</span><br><span class="line">name: cpu</span><br><span class="line">time host region value</span><br><span class="line"><span class="comment">---- ---- ------ -----</span></span><br><span class="line">1563895618490964877 serverA us_west 0.64</span><br></pre></td></tr></table></figure><ul><li>每个表输出一行(支持 Go 语言的正则表达式、支持类似于 MySQL 中的 limit 语句)</li></ul><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT * FROM /.*/ LIMIT 1</span><br><span class="line">name: cpu</span><br><span class="line">time external host internal machine region type value</span><br><span class="line"><span class="comment">---- -------- ---- -------- ------- ------ ---- -----</span></span><br><span class="line">1563895618490964877 serverA us_west 0.64</span><br><span class="line"></span><br><span class="line">name: temperature</span><br><span class="line">time external host internal machine region type value</span><br><span class="line"><span class="comment">---- -------- ---- -------- ------- ------ ---- -----</span></span><br><span class="line">1434067467000000000 25 37 unit42 assembly</span><br></pre></td></tr></table></figure><h4 id="修改和删除数据">修改和删除数据</h4><p>InfluxDB 属于时序数据库,没有提供修改和删除数据的方法。</p><p>但是删除可以通过 InfluxDB 的数据保存策略(Retention Policies)来实现</p><p>update 更新语句没有,不过有 alter 命令,在 influxdb 中,删除操作用和更新基本不用到 。在针对数据保存策略方面,有一个特殊的删除方式,这个后面再提。</p><h3 id="3-series-操作">3. series 操作</h3><p>series 表示这个表里面的数据,可以在图表上画成几条线,series 主要通过 tags 排列组合算出来。</p><p>我们可以查询表的 series,如下所示:</p><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">></span><span class="bash"> show series from mem</span></span><br><span class="line">key</span><br><span class="line">mem,host=ResourcePool-0246-billing07</span><br><span class="line">mem,host=billing07</span><br></pre></td></tr></table></figure><h3 id="4-用户操作">4. 用户操作</h3><figure class="highlight shell"><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"><span class="meta">#</span><span class="bash"> 显示用户</span></span><br><span class="line">SHOW USERS</span><br><span class="line"><span class="meta">#</span><span class="bash"> 创建用户</span></span><br><span class="line">CREATE USER "username" WITH PASSWORD 'password'</span><br><span class="line"><span class="meta">#</span><span class="bash"> 创建管理员权限的用户</span></span><br><span class="line">CREATE USER "username" WITH PASSWORD 'password' WITH ALL PRIVILEGES</span><br><span class="line"><span class="meta">#</span><span class="bash"> 删除用户</span></span><br><span class="line">DROP USER "username"</span><br></pre></td></tr></table></figure><p>influxdb 的权限设置比较简单,只有读、写、ALL 几种。</p><h2 id="五-http-api-操作">五、HTTP API 操作</h2><h3 id="接口地址">接口地址</h3><table><thead><tr><th>接口路径</th><th>描述</th></tr></thead><tbody><tr><td>/debug/pprof</td><td>debug 排查问题使用</td></tr><tr><td>/debug/requests</td><td>使用这个请求监听最近是否有请求</td></tr><tr><td>/debug/vars</td><td>查询 influxdb 收集到静态信息</td></tr><tr><td>/ping</td><td>检测 influxdb 状态</td></tr><tr><td>/query</td><td>查询数据接口(同时可以创建 ku)</td></tr><tr><td>/write</td><td>写入数据接口(一个已存在数据库)</td></tr></tbody></table><h3 id="状态码">状态码</h3><ul><li>2xx:服务请求正常</li><li>4xx:代表请求语法有问题</li><li>5xx:服务端出问题,导致超时等故障</li></ul><h3 id="1-创建数据库">1. 创建数据库</h3><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line">curl -i -XPOST http://localhost:8086/query --data-urlencode "q=CREATE DATABASE mydb"</span><br><span class="line">HTTP/1.1 200 OK</span><br><span class="line">Content-Type: application/json</span><br><span class="line">Request-Id: 5edd88a8-ef90-11e8-83cd-a0999b0f94e3</span><br><span class="line">X-Influxdb-Build: OSS</span><br><span class="line">X-Influxdb-Version: 1.7.0~n201811230800</span><br><span class="line">X-Request-Id: 5edd88a8-ef90-11e8-83cd-a0999b0f94e3</span><br><span class="line">Date: Sat, 24 Nov 2018 02:26:38 GMT</span><br><span class="line">Transfer-Encoding: chunked</span><br><span class="line">{"results":[{"statement_id":0}]}</span><br></pre></td></tr></table></figure><h3 id="2-写入数据">2. 写入数据</h3><figure class="highlight shell"><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">curl -i -XPOST 'http://localhost:8086/write?db=mydb' --data-binary 'cpu_load_short,host=server01,region=us-west value=0.65 1434055564000000000'</span><br><span class="line">HTTP/1.1 204 No Content</span><br><span class="line">Content-Type: application/json</span><br><span class="line">Request-Id: 1ae386c4-ef91-11e8-83d8-a0999b0f94e3</span><br><span class="line">X-Influxdb-Build: OSS</span><br><span class="line">X-Influxdb-Version: 1.7.0~n201811230800</span><br><span class="line">X-Request-Id: 1ae386c4-ef91-11e8-83d8-a0999b0f94e3</span><br><span class="line">Date: Sat, 24 Nov 2018 02:31:53 GMT</span><br></pre></td></tr></table></figure><h3 id="3-写入多个数据点">3. 写入多个数据点</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -i -XPOST 'http://localhost:8086/write?db=mydb' --data-binary 'cpu_load_short,host=server02 value=0.67</span><br></pre></td></tr></table></figure><blockquote><p>cpu_load_short,host=server02,region=us-west value=0.55 1422568543702900257<br>cpu_load_short,direction=in,host=server01,region=us-west value=2.0 1422568543702900257’<br>HTTP/1.1 204 No Content<br>Content-Type: application/json<br>Request-Id: 574f52a0-ef91-11e8-83d9-a0999b0f94e3<br>X-Influxdb-Build: OSS<br>X-Influxdb-Version: 1.7.0~n201811230800<br>X-Request-Id: 574f52a0-ef91-11e8-83d9-a0999b0f94e3<br>Date: Sat, 24 Nov 2018 02:33:34 GMT</p></blockquote><h3 id="4-从文件导入数据库">4. 从文件导入数据库</h3><p>从文件导入时候建议不要超过 5000 条,如果超过请对文件进行切割,因为 http api 的接口 5s 会超时,请求数据过多会导致数据无法确认是否成功。<br>文件 cpu_data.txt 内容如下:</p><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line">cpu_load_short,host=server02 value=111</span><br><span class="line">cpu_load_short,host=server02,region=us-west value=0.222 1543027130702900257</span><br><span class="line">cpu_load_short,direction=in,host=server01,region=us-west value=111.222 1543027129702900257</span><br><span class="line">curl -i -XPOST 'http://localhost:8086/write?db=mydb' --data-binary @cpu_data.txt</span><br></pre></td></tr></table></figure><blockquote><p>HTTP/1.1 204 No Content<br>Content-Type: application/json<br>Request-Id: 4b2ed710-ef92-11e8-83e3-a0999b0f94e3<br>X-Influxdb-Build: OSS<br>X-Influxdb-Version: 1.7.0~n201811230800<br>X-Request-Id: 4b2ed710-ef92-11e8-83e3-a0999b0f94e3<br>Date: Sat, 24 Nov 2018 02:40:24 GMT</p></blockquote><h2 id="六-数据保存策略-retention-policies">六、数据保存策略(Retention Policies)</h2><p>InfluxDB 每秒可以处理成千上万条数据,要将这些数据全部保存下来会占用大量的存储空间,有时我们可能并不需要将所有历史数据进行存储。InfluxDB 没有提供直接删除 Points 的方法,但是它提供了 Retention Policies,用来让我们自定义数据的保留时间。</p><h3 id="1-查看">1. 查看</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SHOW RETENTION POLICIES ON <span class="string">"testDB"</span></span><br></pre></td></tr></table></figure><h3 id="2-创建">2. 创建</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CREATE RETENTION POLICY <span class="string">"rp_name"</span> ON <span class="string">"db_name"</span> DURATION 30d REPLICATION 1 DEFAULT</span><br></pre></td></tr></table></figure><p>其中:</p><ol><li>rp_name:策略名</li><li>db_name:具体的数据库名</li><li>30d:保存 30 天,30 天之前的数据将被删除<br>它具有各种时间参数,比如:h(小时),w(星期)</li><li>REPLICATION 1:副本个数,这里填 1 就可以了</li><li>DEFAULT 设为默认的策略</li></ol><h3 id="3-修改">3. 修改</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ALTER RETENTION POLICY <span class="string">"rp_name"</span> ON db_name<span class="string">" DURATION 3w DEFAULT</span></span><br></pre></td></tr></table></figure><h3 id="4-删除">4. 删除</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DROP RETENTION POLICY <span class="string">"rp_name"</span> ON <span class="string">"db_name"</span></span><br></pre></td></tr></table></figure><h2 id="七-常用函数">七、常用函数</h2><p>InfluxDB 提供了很多的有用的函数,这里列举了常用的三个维度函数,Use InfluxQL functions to aggregate, select, and transform data.</p><table><thead><tr><th>Aggregations</th><th>Selectors</th><th>Transformations</th></tr></thead><tbody><tr><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#count" target="_blank" rel="noopener">COUNT()</a></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#bottom" target="_blank" rel="noopener">BOTTOM()</a></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#ceiling" target="_blank" rel="noopener">CEILING()</a></td></tr><tr><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#distinct" target="_blank" rel="noopener">DISTINCT()</a></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#first" target="_blank" rel="noopener">FIRST()</a></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#derivative" target="_blank" rel="noopener">DERIVATIVE()</a></td></tr><tr><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#integral" target="_blank" rel="noopener">INTEGRAL()</a></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#last" target="_blank" rel="noopener">LAST()</a></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#difference" target="_blank" rel="noopener">DIFFERENCE()</a></td></tr><tr><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#mean" target="_blank" rel="noopener">MEAN()</a></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#max" target="_blank" rel="noopener">MAX()</a></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#elapsed" target="_blank" rel="noopener">ELAPSED()</a></td></tr><tr><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#median" target="_blank" rel="noopener">MEDIAN()</a></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#min" target="_blank" rel="noopener">MIN()</a></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#floor" target="_blank" rel="noopener">FLOOR()</a></td></tr><tr><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#spread" target="_blank" rel="noopener">SPREAD()</a></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#percentile" target="_blank" rel="noopener">PERCENTILE()</a></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#histogram" target="_blank" rel="noopener">HISTOGRAM()</a></td></tr><tr><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#sum" target="_blank" rel="noopener">SUM()</a></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#top" target="_blank" rel="noopener">TOP()</a></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#moving-average" target="_blank" rel="noopener">MOVING_AVERAGE()</a></td></tr><tr><td></td><td></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#non-negative-derivative" target="_blank" rel="noopener">NON_NEGATIVE_DERIVATIVE()</a></td></tr><tr><td></td><td></td><td><a href="https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#stddev" target="_blank" rel="noopener">STDDEV()</a></td></tr></tbody></table><h3 id="1-聚合类函数">1. 聚合类函数</h3><h4 id="1-count-函数">1)COUNT() 函数</h4><p>返回一个(field)字段中的非空值的数量。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">COUNT</span>(<field_key>) <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] [<span class="keyword">GROUP</span> <span class="keyword">BY</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>示例:</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT COUNT(water_level) FROM h2o_feet</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time count</span><br><span class="line">1970-01-01T00:00:00Z 15258</span><br></pre></td></tr></table></figure><p>说明 water_level 这个字段在 h2o_feet 表中共有 15258 条数据。</p><p>注意:InfluxDB 中的函数如果没有指定时间的话,会默认以 epoch 0 (<code>1970-01-01T00:00:00Z</code>) 作为时间。</p><p>可以在 where 中加入时间条件,如下:</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT COUNT(water_level) FROM h2o_feet WHERE time >= '2015-08-18T00:00:00Z' AND time < '2015-09-18T17:00:00Z' GROUP BY time(4d)</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time count</span><br><span class="line">2015-08-17T00:00:00Z 1440</span><br><span class="line">2015-08-21T00:00:00Z 1920</span><br><span class="line">2015-08-25T00:00:00Z 1920</span><br><span class="line">2015-08-29T00:00:00Z 1920</span><br><span class="line">2015-09-02T00:00:00Z 1915</span><br><span class="line">2015-09-06T00:00:00Z 1920</span><br><span class="line">2015-09-10T00:00:00Z 1920</span><br><span class="line">2015-09-14T00:00:00Z 1920</span><br><span class="line">2015-09-18T00:00:00Z 335</span><br></pre></td></tr></table></figure><h4 id="2-distinct-函数">2)DISTINCT() 函数</h4><p>返回一个字段(field)的唯一值。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">DISTINCT</span>(<field_key>) <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] [<span class="keyword">GROUP</span> <span class="keyword">BY</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>使用示例</p><figure class="highlight sql"><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">> SELECT DISTINCT("level description") FROM h2o_feet</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time distinct</span><br><span class="line">1970-01-01T00:00:00Z between 6 and 9 feet</span><br><span class="line">1970-01-01T00:00:00Z below 3 feet</span><br><span class="line">1970-01-01T00:00:00Z between 3 and 6 feet</span><br><span class="line">1970-01-01T00:00:00Z at or greater than 9 feet</span><br></pre></td></tr></table></figure><p>这个例子显示 level description 这个字段共有四个值,然后将其显示了出来,时间为默认时间。</p><h4 id="3-mean-函数">3)MEAN() 函数</h4><p>返回一个字段(field)中的值的算术平均值(平均值)。字段类型必须是长整型或 float64。</p><p>语法格式:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> MEAN(<field_key>) <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] [<span class="keyword">GROUP</span> <span class="keyword">BY</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>使用示例</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT MEAN(water_level) FROM h2o_feet</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time mean</span><br><span class="line">1970-01-01T00:00:00Z 4.286791371454075</span><br></pre></td></tr></table></figure><p>说明 water_level 字段的平均值为<code>4.286791371454075</code>,时间为默认时间,当然,你也可以加入 where 条件。</p><h4 id="4-median-函数">4)MEDIAN() 函数</h4><p>从单个字段(field)中的排序值返回中间值(中位数)。字段值的类型必须是长整型或 float64 格式。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">MEDIAN</span>(<field_key>) <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] [<span class="keyword">GROUP</span> <span class="keyword">BY</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>使用示例</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT MEDIAN(water_level) from h2o_feet</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time median</span><br><span class="line">1970-01-01T00:00:00Z 4.124</span><br></pre></td></tr></table></figure><p>说明表中 water_level 字段的中位数是 4.124</p><h4 id="5-spread-函数">5)SPREAD() 函数</h4><p>返回字段的最小值和最大值之间的差值。数据的类型必须是长整型或 float64。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> SPREAD(<field_key>) <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] [<span class="keyword">GROUP</span> <span class="keyword">BY</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>使用示例</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT SPREAD(water_level) FROM h2o_feet</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time spread</span><br><span class="line">1970-01-01T00:00:00Z 10.574</span><br></pre></td></tr></table></figure><h4 id="6-sum-函数">6)SUM() 函数</h4><p>返回一个字段中的所有值的和。字段的类型必须是长整型或 float64。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">SUM</span>(<field_key>) <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] [<span class="keyword">GROUP</span> <span class="keyword">BY</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>使用示例:</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT SUM(water_level) FROM h2o_feet</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time sum</span><br><span class="line">1970-01-01T00:00:00Z 67777.66900000002</span><br></pre></td></tr></table></figure><p>此语句计算出了 h2o_feet 表中 所有 water_level 字段的和。</p><h3 id="2-选择类函数">2. 选择类函数</h3><h4 id="1-top-函数">1)TOP() 函数</h4><p>作用:返回一个字段中最大的 N 个值,字段类型必须是长整型或 float64 类型。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> TOP( <field_key>[,<tag_key(s)>],<N> )[,<tag_key(s)>|<field_key(s)>] [INTO_clause] FROM_clause [WHERE_clause] [GROUP_BY_clause] [ORDER_BY_clause] [LIMIT_clause] [OFFSET_clause] [SLIMIT_clause] [SOFFSET_clause]</span><br></pre></td></tr></table></figure><p>使用示例</p><figure class="highlight sql"><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">> SELECT TOP("water_level",3) FROM "h2o_feet"</span><br><span class="line"></span><br><span class="line">name: h2o_feet</span><br><span class="line">time top</span><br><span class="line"><span class="comment">---- ---</span></span><br><span class="line">2015-08-29T07:18:00Z 9.957</span><br><span class="line">2015-08-29T07:24:00Z 9.964</span><br><span class="line">2015-08-29T07:30:00Z 9.954</span><br></pre></td></tr></table></figure><p>这个例子返回表中 water_level 字段中最大的三个值。</p><h4 id="2-bottom-函数">2)BOTTOM() 函数</h4><p>作用:返回一个字段中最小的 N 个值。字段类型必须是长整型或 float64 类型。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> BOTTOM(<field_key>[,<tag_keys>],<N>)[,<tag_keys>] <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] [<span class="keyword">GROUP</span> <span class="keyword">BY</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>使用示例</p><figure class="highlight sql"><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">> SELECT BOTTOM(water_level,3) FROM h2o_feet</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time bottom</span><br><span class="line">2015-08-29T14:30:00Z -0.61</span><br><span class="line">2015-08-29T14:36:00Z -0.591</span><br><span class="line">2015-08-30T15:18:00Z -0.594</span><br></pre></td></tr></table></figure><p>这个例子返回表中 water_level 字段中最小的三个值。</p><p>也可将关联 tag 放在一起查询,但如果 tag 值少于 N 的值,则返回的值的个数只会取 tag 中字段值少的那个。</p><p>如下所示:</p><figure class="highlight sql"><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">> SELECT BOTTOM(water_level,location,3) FROM h2o_feet</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time bottom location</span><br><span class="line">2015-08-29T10:36:00Z -0.243 santa_monica</span><br><span class="line">2015-08-29T14:30:00Z -0.61 coyote_creek</span><br></pre></td></tr></table></figure><p>语句取最小的三个值,然而结果只返回了 2 个值,因为 location 这个 tag 只有 两个取值。</p><h4 id="3-first-函数">3)FIRST() 函数</h4><p>作用:返回一个字段中最老的取值。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">FIRST</span>(<field_key>)[,<tag_key(s)>] <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] [<span class="keyword">GROUP</span> <span class="keyword">BY</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>示例:</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT FIRST(water_level) FROM h2o_feet WHERE location = 'santa_monica'</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time first</span><br><span class="line">2015-08-18T00:00:00Z 2.064</span><br></pre></td></tr></table></figure><p>这个语句返回了 在 location 为 santa_monica 条件下,最旧的那个 water_level 字段的取值和时间。</p><h4 id="4-last-函数">4)LAST() 函数</h4><p>作用:返回一个字段中最新的取值。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">LAST</span>(<field_key>)[,<tag_key(s)>] <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] [<span class="keyword">GROUP</span> <span class="keyword">BY</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>示例:</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT LAST(water_level),location FROM h2o_feet WHERE time >= '2015-08-18T00:42:00Z' and time <= '2015-08-18T00:54:00Z'</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time last location</span><br><span class="line">2015-08-18T00:54:00Z 6.982 coyote_creek</span><br></pre></td></tr></table></figure><h4 id="5-max-函数">5)MAX() 函数</h4><p>作用:返回一个字段中的最大值。该字段类型必须是长整型,float64,或布尔类型。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">MAX</span>(<field_key>)[,<tag_key(s)>] <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] [<span class="keyword">GROUP</span> <span class="keyword">BY</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>示例:</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT MAX(water_level),location FROM h2o_feet</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time max location</span><br><span class="line">2015-08-29T07:24:00Z 9.964 coyote_creek</span><br></pre></td></tr></table></figure><h4 id="6-min-函数">6)MIN() 函数</h4><p>作用:返回一个字段中的最小值。该字段类型必须是长整型,float64,或布尔类型。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">MIN</span>(<field_key>)[,<tag_key(s)>] <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] [<span class="keyword">GROUP</span> <span class="keyword">BY</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>示例:</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT MIN(water_level),location FROM h2o_feet</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time min location</span><br><span class="line">2015-08-29T14:30:00Z -0.61 coyote_creek</span><br></pre></td></tr></table></figure><h4 id="7-percentile-函数">7)PERCENTILE() 函数</h4><p>作用:返回排序值排位为 N 的百分值。字段的类型必须是长整型或 float64。</p><p>百分值是介于 100 到 0 之间的整数或浮点数,包括 100。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> PERCENTILE(<field_key>, <N>)[,<tag_key(s)>] <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] [<span class="keyword">GROUP</span> <span class="keyword">BY</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>示例:</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT PERCENTILE(water_level,5),location FROM h2o_feet</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time percentile location</span><br><span class="line">2015-08-28T12:06:00Z 1.122 santa_monica</span><br></pre></td></tr></table></figure><p>就是将 water_level 字段按照不同的 location 求百分比,然后取第五位数据。</p><h3 id="3-变换类函数">3. 变换类函数</h3><h4 id="1-derivative-函数">1)DERIVATIVE() 函数</h4><p>作用:返回一个字段在一个 series 中的变化率。</p><p>InfluxDB 会计算按照时间进行排序的字段值之间的差异,并将这些结果转化为单位变化率。其中,单位可以指定,默认为 1s。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> DERIVATIVE(<field_key>, [<unit>]) <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>其中,<code>unit</code>取值可以为以下几种:</p><figure class="highlight sql"><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">u <span class="comment">--microseconds</span></span><br><span class="line">s <span class="comment">--seconds</span></span><br><span class="line">m <span class="comment">--minutes</span></span><br><span class="line">h <span class="comment">--hours</span></span><br><span class="line">d <span class="comment">--days</span></span><br><span class="line">w <span class="comment">--weeks</span></span><br></pre></td></tr></table></figure><p>DERIVATIVE() 函数还可以在 GROUP BY time() 的条件下与聚合函数嵌套使用,格式如下:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> DERIVATIVE(AGGREGATION_FUNCTION(<field_key>),[<unit>]) <span class="keyword">FROM</span> <measurement_name> <span class="keyword">WHERE</span> <<span class="keyword">stuff</span>> <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="built_in">time</span>(<aggregation_interval>)</span><br></pre></td></tr></table></figure><p>示例:</p><p>假设 location = santa_monica 条件下数据有以下几条:</p><figure class="highlight sql"><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">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time water_level</span><br><span class="line">2015-08-18T00:00:00Z 2.064</span><br><span class="line">2015-08-18T00:06:00Z 2.116</span><br><span class="line">2015-08-18T00:12:00Z 2.028</span><br><span class="line">2015-08-18T00:18:00Z 2.126</span><br><span class="line">2015-08-18T00:24:00Z 2.041</span><br><span class="line">2015-08-18T00:30:00Z 2.051</span><br></pre></td></tr></table></figure><p>计算每一秒的变化率:</p><figure class="highlight sql"><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">> SELECT DERIVATIVE(water_level) FROM h2o_feet WHERE location = 'santa_monica' LIMIT 5</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time derivative</span><br><span class="line">2015-08-18T00:06:00Z 0.00014444444444444457</span><br><span class="line">2015-08-18T00:12:00Z -0.00024444444444444465</span><br><span class="line">2015-08-18T00:18:00Z 0.0002722222222222218</span><br><span class="line">2015-08-18T00:24:00Z -0.000236111111111111</span><br><span class="line">2015-08-18T00:30:00Z 2.777777777777842e-05</span><br></pre></td></tr></table></figure><p>第一行数据的计算公式为<code>(2.116 - 2.064) / (360s / 1s)</code></p><p>计算每六分钟的变化率</p><figure class="highlight sql"><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">> SELECT DERIVATIVE(water_level,6m) FROM h2o_feet WHERE location = 'santa_monica' LIMIT 5</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time derivative</span><br><span class="line">2015-08-18T00:06:00Z 0.052000000000000046</span><br><span class="line">2015-08-18T00:12:00Z -0.08800000000000008</span><br><span class="line">2015-08-18T00:18:00Z 0.09799999999999986</span><br><span class="line">2015-08-18T00:24:00Z -0.08499999999999996</span><br><span class="line">2015-08-18T00:30:00Z 0.010000000000000231</span><br></pre></td></tr></table></figure><p>第一行数据的计算过程如下:<code>(2.116 - 2.064) / (6m / 6m)</code></p><p>计算每 12 分钟的变化率:</p><figure class="highlight sql"><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">> SELECT DERIVATIVE(water_level,12m) FROM h2o_feet WHERE location = 'santa_monica' LIMIT 5</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time derivative</span><br><span class="line">2015-08-18T00:06:00Z 0.10400000000000009</span><br><span class="line">2015-08-18T00:12:00Z -0.17600000000000016</span><br><span class="line">2015-08-18T00:18:00Z 0.19599999999999973</span><br><span class="line">2015-08-18T00:24:00Z -0.16999999999999993</span><br><span class="line">2015-08-18T00:30:00Z 0.020000000000000462</span><br></pre></td></tr></table></figure><p>第一行数据计算过程为:<code>(2.116 - 2.064 / (6m / 12m)</code></p><p>计算每 12 分钟最大值的变化率</p><figure class="highlight sql"><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">> SELECT DERIVATIVE(MAX(water_level)) FROM h2o_feet WHERE location = 'santa_monica' AND time >= '2015-08-18T00:00:00Z' AND time < '2015-08-18T00:36:00Z' GROUP BY time(12m)</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time derivative</span><br><span class="line">2015-08-18T00:12:00Z 0.009999999999999787</span><br><span class="line">2015-08-18T00:24:00Z -0.07499999999999973</span><br></pre></td></tr></table></figure><p>这个函数功能非常多,也非常复杂,更多对于此功能的详细解释请看官网:<a href="https://www.linuxdaxue.com/wp-content/themes/template/inc/go.php?url=https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#derivative" target="_blank" rel="noopener">https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#derivative</a></p><h4 id="2-difference-函数">2)DIFFERENCE() 函数</h4><p>作用:返回一个字段中连续的时间值之间的差异。字段类型必须是长整型或 float64。</p><p>最基本的语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">DIFFERENCE</span>(<field_key>) <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>与 GROUP BY time() 以及其他嵌套函数一起使用的语法格式:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">DIFFERENCE</span>(<<span class="keyword">function</span>>(<field_key>)) <span class="keyword">FROM</span> <measurement_name> <span class="keyword">WHERE</span> <<span class="keyword">stuff</span>> <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="built_in">time</span>(<time_interval>)</span><br></pre></td></tr></table></figure><p>其中,函数可以包含以下几个:</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">COUNT</span>(), <span class="selector-tag">MEAN</span>(), <span class="selector-tag">MEDIAN</span>(), <span class="selector-tag">SUM</span>(), <span class="selector-tag">FIRST</span>(), <span class="selector-tag">LAST</span>(), <span class="selector-tag">MIN</span>(), <span class="selector-tag">MAX</span>(), 和 <span class="selector-tag">PERCENTILE</span>()</span><br></pre></td></tr></table></figure><p>使用示例</p><p>例子中使用的源数据如下所示:</p><figure class="highlight sql"><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">> SELECT water_level FROM h2o_feet WHERE location='santa_monica' AND time >= '2015-08-18T00:00:00Z' and time <= '2015-08-18T00:36:00Z'</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time water_level</span><br><span class="line">2015-08-18T00:00:00Z 2.064</span><br><span class="line">2015-08-18T00:06:00Z 2.116</span><br><span class="line">2015-08-18T00:12:00Z 2.028</span><br><span class="line">2015-08-18T00:18:00Z 2.126</span><br><span class="line">2015-08-18T00:24:00Z 2.041</span><br><span class="line">2015-08-18T00:30:00Z 2.051</span><br><span class="line">2015-08-18T00:36:00Z 2.067</span><br></pre></td></tr></table></figure><p>计算<code>water_level</code>间的差异:</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT DIFFERENCE(water_level) FROM h2o_feet WHERE location='santa_monica' AND time >= '2015-08-18T00:00:00Z' and time <= '2015-08-18T00:36:00Z'</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time difference</span><br><span class="line">2015-08-18T00:06:00Z 0.052000000000000046</span><br><span class="line">2015-08-18T00:12:00Z -0.08800000000000008</span><br><span class="line">2015-08-18T00:18:00Z 0.09799999999999986</span><br><span class="line">2015-08-18T00:24:00Z -0.08499999999999996</span><br><span class="line">2015-08-18T00:30:00Z 0.010000000000000231</span><br><span class="line">2015-08-18T00:36:00Z 0.016000000000000014</span><br></pre></td></tr></table></figure><p>数据类型都为 float 类型。</p><h4 id="3-elapsed-函数">3)ELAPSED() 函数</h4><p>作用:返回一个字段在连续的时间间隔间的差异,间隔单位可选,默认为 1 纳秒。</p><p>单位可选项如下:</p><table><thead><tr><th>Units</th><th>Meaning</th></tr></thead><tbody><tr><td>ns</td><td>nanoseconds (1 billionth of a second)</td></tr><tr><td>u or µ</td><td>microseconds (1 millionth of a second)</td></tr><tr><td>ms</td><td>milliseconds (1 thousandth of a second)</td></tr><tr><td>s</td><td>second</td></tr><tr><td>m</td><td>minute</td></tr><tr><td>h</td><td>hour</td></tr><tr><td>d</td><td>day</td></tr><tr><td>w</td><td>week</td></tr></tbody></table><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> ELAPSED(<field_key>, <unit>) <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>示例:</p><p>计算 h2o_feet 字段在纳秒间隔下的差异。</p><figure class="highlight sql"><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">> SELECT ELAPSED(water_level) FROM h2o_feet WHERE location = 'santa_monica' AND time >= '2015-08-18T00:00:00Z' and time <= '2015-08-18T00:24:00Z'</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time elapsed</span><br><span class="line">2015-08-18T00:06:00Z 360000000000</span><br><span class="line">2015-08-18T00:12:00Z 360000000000</span><br><span class="line">2015-08-18T00:18:00Z 360000000000</span><br><span class="line">2015-08-18T00:24:00Z 360000000000</span><br></pre></td></tr></table></figure><p>在一分钟间隔下的差异率:</p><figure class="highlight sql"><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">> SELECT ELAPSED(water_level,1m) FROM h2o_feet WHERE location = 'santa_monica' AND time >= '2015-08-18T00:00:00Z' and time <= '2015-08-18T00:24:00Z'</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time elapsed</span><br><span class="line">2015-08-18T00:06:00Z 6</span><br><span class="line">2015-08-18T00:12:00Z 6</span><br><span class="line">2015-08-18T00:18:00Z 6</span><br><span class="line">2015-08-18T00:24:00Z 6</span><br></pre></td></tr></table></figure><p>注意:如果设置的时间间隔比字段数据间的时间间隔更大时,则函数会返回 0,如下所示:</p><figure class="highlight sql"><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">> SELECT ELAPSED(water_level,1h) FROM h2o_feet WHERE location = 'santa_monica' AND time >= '2015-08-18T00:00:00Z' and time <= '2015-08-18T00:24:00Z'</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time elapsed</span><br><span class="line">2015-08-18T00:06:00Z 0</span><br><span class="line">2015-08-18T00:12:00Z 0</span><br><span class="line">2015-08-18T00:18:00Z 0</span><br><span class="line">2015-08-18T00:24:00Z 0</span><br></pre></td></tr></table></figure><h4 id="4-moving-average-函数">4)MOVING_AVERAGE() 函数</h4><p>作用:返回一个连续字段值的移动平均值,字段类型必须是长整形或者 float64 类型。</p><p>语法:</p><p>基本语法</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> MOVING_AVERAGE(<field_key>,<<span class="keyword">window</span>>) <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>与其他函数和 GROUP BY time() 语句一起使用时的语法</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> MOVING_AVERAGE(<<span class="keyword">function</span>>(<field_key>),<<span class="keyword">window</span>>) <span class="keyword">FROM</span> <measurement_name> <span class="keyword">WHERE</span> <<span class="keyword">stuff</span>> <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="built_in">time</span>(<time_interval>)</span><br></pre></td></tr></table></figure><p>此函数可以和以下函数一起使用:</p><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">COUNT</span>(), <span class="selector-tag">MEAN</span>(),<span class="selector-tag">MEDIAN</span>(), <span class="selector-tag">SUM</span>(), <span class="selector-tag">FIRST</span>(), <span class="selector-tag">LAST</span>(), <span class="selector-tag">MIN</span>(), <span class="selector-tag">MAX</span>(), <span class="selector-tag">and</span> <span class="selector-tag">PERCENTILE</span>().</span><br></pre></td></tr></table></figure><p>示例:</p><figure class="highlight sql"><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">> SELECT water_level FROM h2o_feet WHERE location = 'santa_monica' AND time >= '2015-08-18T00:00:00Z' and time <= '2015-08-18T00:36:00Z'</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time water_level</span><br><span class="line">2015-08-18T00:00:00Z 2.064</span><br><span class="line">2015-08-18T00:06:00Z 2.116</span><br><span class="line">2015-08-18T00:12:00Z 2.028</span><br><span class="line">2015-08-18T00:18:00Z 2.126</span><br><span class="line">2015-08-18T00:24:00Z 2.041</span><br><span class="line">2015-08-18T00:30:00Z 2.051</span><br><span class="line">2015-08-18T00:36:00Z 2.067</span><br></pre></td></tr></table></figure><h4 id="5-non-negative-derivative-函数">5)NON_NEGATIVE_DERIVATIVE() 函数</h4><p>作用:返回在一个 series 中的一个字段中值的变化的非负速率。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> NON_NEGATIVE_DERIVATIVE(<field_key>, [<unit>]) <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>其中 unit 取值可以为以下几个:</p><p>Valid time specifications for <code>unit</code> are:</p><ul><li><code>u</code> microseconds</li><li><code>s</code> seconds</li><li><code>m</code> minutes</li><li><code>h</code> hours</li><li><code>d</code> days</li><li><code>w</code> weeks</li></ul><p>与聚合类函数放在一起使用时的语法如下所示:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> NON_NEGATIVE_DERIVATIVE(AGGREGATION_FUNCTION(<field_key>),[<unit>]) <span class="keyword">FROM</span> <measurement_name> <span class="keyword">WHERE</span> <<span class="keyword">stuff</span>> <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="built_in">time</span>(<aggregation_interval>)</span><br></pre></td></tr></table></figure><p>此函数示例请参阅:<a href="https://www.linuxdaxue.com/influxdb-study-influxdb-transformations-funcitons.html#title-0" target="_blank" rel="noopener">DERIVATIVE()</a>``函数</p><h4 id="6-stddev-函数">6)STDDEV() 函数</h4><p>作用:返回一个字段中的值的标准偏差。值的类型必须是长整型或 float64 类型。</p><p>语法:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">STDDEV</span>(<field_key>) <span class="keyword">FROM</span> <measurement_name> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] [<span class="keyword">GROUP</span> <span class="keyword">BY</span> <<span class="keyword">stuff</span>>]</span><br></pre></td></tr></table></figure><p>示例:</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT STDDEV(water_level) FROM h2o_feet</span><br><span class="line">name: h2o_feet</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">time stddev</span><br><span class="line">1970-01-01T00:00:00Z 2.279144584196145</span><br></pre></td></tr></table></figure><p>示例 2:</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> SELECT STDDEV(water_level) FROM h2o_feet WHERE time >= '2015-08-18T00:00:00Z' and time < '2015-09-18T12:06:00Z' GROUP BY time(1w), location</span><br><span class="line">name: h2o_feet</span><br><span class="line">tags: location = coyote_creek</span><br><span class="line">time stddev</span><br><span class="line"><span class="comment">---- ------</span></span><br><span class="line">2015-08-13T00:00:00Z 2.2437263080193985</span><br><span class="line">2015-08-20T00:00:00Z 2.121276150144719</span><br><span class="line">2015-08-27T00:00:00Z 3.0416122170786215</span><br><span class="line">2015-09-03T00:00:00Z 2.5348065025435207</span><br><span class="line">2015-09-10T00:00:00Z 2.584003954882673</span><br><span class="line">2015-09-17T00:00:00Z 2.2587514836274414</span><br><span class="line"></span><br><span class="line">name: h2o_feet</span><br><span class="line">tags: location = santa_monica</span><br><span class="line">time stddev</span><br><span class="line"><span class="comment">---- ------</span></span><br><span class="line">2015-08-13T00:00:00Z 1.11156344587553</span><br><span class="line">2015-08-20T00:00:00Z 1.0909849279082366</span><br><span class="line">2015-08-27T00:00:00Z 1.9870116180096962</span><br><span class="line">2015-09-03T00:00:00Z 1.3516778450902067</span><br><span class="line">2015-09-10T00:00:00Z 1.4960573811500588</span><br><span class="line">2015-09-17T00:00:00Z 1.075701669442093</span><br></pre></td></tr></table></figure><h2 id="八-连续查询">八、连续查询</h2><h3 id="定义">定义</h3><p>InfluxDB 的连续查询是在数据库中自动定时启动的一组语句,语句中必须包含 <code>SELECT</code>关键词和<code>GROUP BY time()</code>关键词。</p><p>InfluxDB 会将查询结果放在指定的数据表中。</p><h3 id="目的">目的</h3><p>使用连续查询是最优的降低采样率的方式,连续查询和存储策略搭配使用将会大大降低 InfluxDB 的系统占用量。</p><p>而且使用连续查询后,数据会存放到指定的数据表中,这样就为以后统计不同精度的数据提供了方便。</p><h3 id="操作">操作</h3><p>只有管理员用户可以操作连续查询。</p><p>1)新建连续查询</p><p>新建连续查询的语法如下所示:</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> CONTINUOUS <span class="keyword">QUERY</span> <cq_name> <span class="keyword">ON</span> <database_name> </span><br><span class="line">[RESAMPLE [EVERY <<span class="built_in">interval</span>>] [<span class="keyword">FOR</span> <<span class="built_in">interval</span>>]] </span><br><span class="line"><span class="keyword">BEGIN</span> <span class="keyword">SELECT</span> <<span class="keyword">function</span>>(<<span class="keyword">stuff</span>>)[,<<span class="keyword">function</span>>(<<span class="keyword">stuff</span>>)] <span class="keyword">INTO</span> <different_measurement> </span><br><span class="line"><span class="keyword">FROM</span> <current_measurement> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="built_in">time</span>(<<span class="built_in">interval</span>>)[,<<span class="keyword">stuff</span>>] </span><br><span class="line"><span class="keyword">END</span></span><br></pre></td></tr></table></figure><p>查询部分被 CREATE CONTINUOUS QUERY […] BEGIN 和 END 所包含,主要的逻辑代码也是在这一部分。</p><p>使用示例:</p><figure class="highlight sql"><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></pre></td><td class="code"><pre><span class="line">> CREATE CONTINUOUS QUERY cq_30m ON telegraf BEGIN SELECT mean(used) INTO mem_used_30m FROM mem GROUP BY time(30m) END</span><br><span class="line">> SHOW CONTINUOUS QUERIES</span><br><span class="line">name: telegraf</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">name query</span><br><span class="line">cq_30m <span class="keyword">CREATE</span> CONTINUOUS <span class="keyword">QUERY</span> cq_30m <span class="keyword">ON</span> telegraf <span class="keyword">BEGIN</span> </span><br><span class="line"><span class="keyword">SELECT</span> mean(used) <span class="keyword">INTO</span> telegraf.<span class="string">"default"</span>.mem_used_30m <span class="keyword">FROM</span> telegraf.<span class="string">"default"</span>.mem </span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="built_in">time</span>(<span class="number">30</span>m) <span class="keyword">END</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">name</span>: _internal</span><br><span class="line"><span class="comment">---------------</span></span><br><span class="line"><span class="keyword">name</span> <span class="keyword">query</span></span><br></pre></td></tr></table></figure><p>示例在 telegraf 库中新建了一个名为 cq_30m 的连续查询,每三十分钟取一个 used 字段的平均值,加入 mem_used_30m 表中。使用的数据保留策略都是 default。</p><p>2)显示所有已存在的连续查询</p><p>查询所有连续查询可以使用如下语句:</p><figure class="highlight sql"><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">> SHOW CONTINUOUS QUERIES</span><br><span class="line">name: telegraf</span><br><span class="line"><span class="comment">--------------</span></span><br><span class="line">name query</span><br><span class="line">cq_30m <span class="keyword">CREATE</span> CONTINUOUS <span class="keyword">QUERY</span> cq_30m <span class="keyword">ON</span> telegraf </span><br><span class="line"><span class="keyword">BEGIN</span> <span class="keyword">SELECT</span> mean(used) <span class="keyword">INTO</span> telegraf.<span class="string">"default"</span>.mem_used_30m <span class="keyword">FROM</span> telegraf.<span class="string">"default"</span>.mem </span><br><span class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="built_in">time</span>(<span class="number">30</span>m) <span class="keyword">END</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">name</span>: _internal</span><br><span class="line"><span class="comment">---------------</span></span><br><span class="line"><span class="keyword">name</span> <span class="keyword">query</span></span><br></pre></td></tr></table></figure><p>可以看到其连续查询的名称以及 语句等信息。</p><p>3)删除 Continuous Queries</p><p>删除连续查询的语句如下:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> CONTINUOUS <span class="keyword">QUERY</span> <cq_name> <span class="keyword">ON</span> <database_name></span><br></pre></td></tr></table></figure><h3 id="其他说明">其他说明</h3><p>在 InfluxDB 中,将连续查询与数据存储策略一起使用会达到最好的效果。</p><p>比如,将精度高的表的存储策略定为一个周,然后将精度底的表存储策略定的时间久一点,这要就可以实现高低搭配,以满足不同的工作需要。</p><h2 id="九-再谈连续查询">九、再谈连续查询</h2><h3 id="连续查询语法">连续查询语法</h3><p>连续查询的语法如下:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> CONTINUOUS <span class="keyword">QUERY</span> <cq_name> <span class="keyword">ON</span> <database_name> [RESAMPLE [EVERY <<span class="built_in">interval</span>>] [<span class="keyword">FOR</span> <<span class="built_in">interval</span>>]] <span class="keyword">BEGIN</span> <span class="keyword">SELECT</span> <<span class="keyword">function</span>>(<<span class="keyword">stuff</span>>)[,<<span class="keyword">function</span>>(<<span class="keyword">stuff</span>>)] <span class="keyword">INTO</span> <different_measurement> <span class="keyword">FROM</span> <current_measurement> [<span class="keyword">WHERE</span> <<span class="keyword">stuff</span>>] <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="built_in">time</span>(<<span class="built_in">interval</span>>)[,<<span class="keyword">stuff</span>>] <span class="keyword">END</span></span><br></pre></td></tr></table></figure><h3 id="指定连续查询的时间范围">指定连续查询的时间范围</h3><p>可以使用 <code>RESAMPLE FOR</code> 关键词来指定连续查询的时间范围,比如,每次执行都对 1 小时内的数据进行连续查询:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> CONTINUOUS <span class="keyword">QUERY</span> vampires_1 <span class="keyword">ON</span> transylvania RESAMPLE <span class="keyword">FOR</span> <span class="number">60</span>m <span class="keyword">BEGIN</span> <span class="keyword">SELECT</span> <span class="keyword">count</span>(dracula) <span class="keyword">INTO</span> vampire_populations_1 <span class="keyword">FROM</span> raw_vampires <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="built_in">time</span>(<span class="number">30</span>m) <span class="keyword">END</span></span><br></pre></td></tr></table></figure><p>这个语句每次会将 1 小时的数据执行连续查询,也就是说,每次执行时,会将 now() 到 now()-30m 和 now()-30m 到 now()-60m 分别做连续查询,这样我们就可以手动指定连续查询的时间范围了。</p><h3 id="指定连续查询的执行频次">指定连续查询的执行频次</h3><p>可以使用 <code>RESAMPLE EVERY</code> 关键词来指定连续查询的执行频次,比如,指定连续查询的执行频次为每 15m 执行一次:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> CONTINUOUS <span class="keyword">QUERY</span> vampires <span class="keyword">ON</span> transylvania RESAMPLE EVERY <span class="number">15</span>m <span class="keyword">BEGIN</span> <span class="keyword">SELECT</span> <span class="keyword">count</span>(dracula) <span class="keyword">INTO</span> vampire_populations <span class="keyword">FROM</span> raw_vampires <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="built_in">time</span>(<span class="number">30</span>m) <span class="keyword">END</span></span><br></pre></td></tr></table></figure><p>这样,连续查询会每隔 15m 执行一次。</p><h3 id="同时指定连续查询的范围和频次">同时指定连续查询的范围和频次</h3><p>将 RESAMPLE FOR 和 EVERY 关键词同时使用,可以同时指定连续查询的范围和频次,如下:</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> CONTINUOUS <span class="keyword">QUERY</span> vampires_2 <span class="keyword">ON</span> transylvania RESAMPLE EVERY <span class="number">15</span>m <span class="keyword">FOR</span> <span class="number">60</span>m <span class="keyword">BEGIN</span> <span class="keyword">SELECT</span> <span class="keyword">count</span>(dracula) <span class="keyword">INTO</span> vampire_populations_2 <span class="keyword">FROM</span> raw_vampires <span class="keyword">GROUP</span> <span class="keyword">BY</span> <span class="built_in">time</span>(<span class="number">30</span>m) <span class="keyword">END</span></span><br></pre></td></tr></table></figure><p>这个语句指定连续查询每 15m 执行一次,每次执行的范围为 60m。</p><h2 id="参考资料">参考资料</h2><ul><li><p>基础教程文档:<a href="https://www.linuxdaxue.com/series/influxdb-series/" target="_blank" rel="noopener">InfluxDB 系列教程 | Linux 大学 </a></p></li><li><p><a href="https://blog.51cto.com/nginxs/2321857" target="_blank" rel="noopener">00-InfluxDB 入门介绍-我的运维历程-51CTO 博客 </a></p></li><li><p><a href="http://www.361way.com/influxdb-user/5291.html" target="_blank" rel="noopener">influxdb 的简单使用 - 运维之路 </a></p></li><li><p><a href="https://segmentfault.com/a/1190000012385313" target="_blank" rel="noopener">influxdb 语法 - 个人文章 - SegmentFault 思否 </a></p></li></ul>]]></content>
<summary type="html">
<blockquote>
<p>申明:本文为学习过程中的笔记,在现有的资料基础之上做了学习和整理,非完全原创的。引用资料见文末,参考资料。</p>
</blockquote>
<p>官方文档:<a href="https://docs.influxdata.com/influxd
</summary>
<category term="influxdb" scheme="http://www.frankfeekr.cn/tags/influxdb/"/>
</entry>
<entry>
<title>基于 SpringBoot & IDEA & JRebel 玩转远程热部署与远程调试</title>
<link href="http://www.frankfeekr.cn/2019/07/17/springboot-idea-jrebel-hotswap/"/>
<id>http://www.frankfeekr.cn/2019/07/17/springboot-idea-jrebel-hotswap/</id>
<published>2019-07-17T12:37:24.000Z</published>
<updated>2021-10-23T14:22:52.650Z</updated>
<content type="html"><![CDATA[<h2 id="前言">前言</h2><p>在 SpringBoot 开发过程中,当我们在 Debug 我们的工程时,随便修改一段代码逻辑、修改接口路由、新增一个工具类等等情况下,都需要我们重新启动工程。反复的修改逻辑,反复的重启,这是相当痛苦的过程。当工程越来越大的时候,效率将变得特别低下,大部分的时间就是在不断的重启项目、编译包、部署包。</p><p>近期在研究基于 SpringBoot + IDEA 远程调试功能,可以通过 remote-debug 方式对测试环境中的 JVM 中的代码进行调试。所谓的远程调试就是,在本地代码可以调试服务器上的代码,模拟真实环境的请求(前提是本地的代码必须和远程服务器运行的代码一致)。在这种调试环境下,只能修改少量基本的业务代码,例如新增一个函数则都无法生效。</p><p>这里强力推荐 JRebel 插件,JRebel 是一款热部署插件。当项目在 Debug 的时候,修改某一个 Java文件时,JRebel 就可以解决在项目运行状态 debug 状态下任意修改 Java文件并动态反馈到运行的项目中。JRebel 有两个非常酷的特性,<strong>(一)支持热部署</strong>,<strong>(二)支持远程热部署</strong>。</p><p>查阅大量资料,总算对配置非常清晰,决定详细整理以供备忘,也系统可以帮助小白更快的实现远程部署与远程调试!</p><h2 id="配置环境说明">配置环境说明</h2><blockquote><p>这里以我的配置环境为例,进行</p></blockquote><ul><li><p>框架:SpringBoot 2.1.6</p></li><li><p>工具:IntelliJ IDEA 2019.1.3 x64</p></li><li><p>服务器:CentOS Linux release 7.6.1810</p></li><li><p>JDK:Java 1.8</p></li><li><p>JRebel</p><ul><li>官网:<a href="https://jrebel.com/" target="_blank" rel="noopener">JRebel, XRebel & QRebel By ZeroTurnaround | JRebel.com</a></li><li>文档:<a href="http://manuals.zeroturnaround.com/jrebel/index.html" target="_blank" rel="noopener">JRebel — JRebel 2019.x documentation</a></li></ul></li></ul><h2 id="一-远程调试配置">一、远程调试配置</h2><h3 id="1-右上角点击-edit-configurations-点击-号-创建-remote-应用">1. 右上角点击,Edit Configurations,点击 + 号,创建 Remote 应用</h3><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563194200137.png" alt="1563194200137"></p><h3 id="2-填写远程服务器信息">2. 填写远程服务器信息</h3><ul><li>Name:应用名</li><li>Host:服务器地址</li><li>Port:5005</li></ul><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563194304996.png" alt="1563194304996"></p><h3 id="3-复制-command-line-arguments-for-remote-jvm-下的命令">3. 复制 Command line arguments for remote JVM 下的命令</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005</span><br></pre></td></tr></table></figure><p>说明:自此,已经获得了<strong>启动远程服务的参数</strong>,命令将在启动的时候来使用</p><h3 id="4-启动服务">4. 启动服务</h3><p>我们知道 SpringBoot 通过 Maven 打包后会生成 jar 包,服务端我们通过 jar -jar 的方式启动。通过上一步我们获得了服务端启动的配置参数命令,我们可以执行一下命令启动我的服务。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 webapp.jar</span><br></pre></td></tr></table></figure><p>我们可以用如下命令进行验证是否监听成功</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">netstat -anp | grep 5005</span><br></pre></td></tr></table></figure><h3 id="5-远程调试">5. 远程调试</h3><p>选择我们配置的远程调试项,点击 Debug 即可进行远程调试。</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563194804383.png" alt="1563194804383"></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563194936470.png" alt="1563194936470"></p><p>启动完成,对需要 Debug 的代码打上断点,剩下的操作步骤就是访问远程服务器对应的业务请求,本地就会同步Debug。</p><p>😎 自此,可以尽情的开始远程调试工作了!</p><blockquote><p>但是在使用远程调试的过程中,如果进行了断点,会出现服务阻塞的情况。如何解决这样的问题,将在下面进行描述。</p></blockquote><h2 id="二-热部署">二、热部署</h2><p>这里将引入 JRebel 插件,可以实现对本地的服务和远程的服务进行热部署。本节将主要针对本地热部署进行配置。</p><h3 id="1-idea-中安装-jrebel-插件">1. IDEA 中安装 Jrebel 插件</h3><p>File -> Settings -> Plugins -> Search plugins in marketplace</p><p>搜索 <code>JRebel for IntelliJ</code> 安装即可,离线安装包下载地址:<a href="https://plugins.jetbrains.com/plugin/4441-jrebel-for-intellij/" target="_blank" rel="noopener">JRebel for IntelliJ - Plugins | JetBrains</a></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563195324070.png" alt="1563195324070"></p><p>安装完成后,重启 IDEA 生效。此时在 Settings 面板中会出现 JRebel 的选项卡</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563195374877.png" alt="1563195374877"></p><h3 id="2-激活-jrebel-插件">2. 激活 JRebel 插件</h3><h4 id="一-官方-active-code-方式激活-只可使用10天">(一)官方 Active Code 方式激活(只可使用10天)</h4><ol><li>进入官网 <a href="https://jrebel.com/software/jrebel/trial/" target="_blank" rel="noopener">Evaluate JRebel</a>,填写完整信息</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563195929214.png" alt="1563195929214"></p><ol start="2"><li>点击 Register 获取激活码</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563195961612.png" alt="1563195961612"></p><p>此时即可获取你的激活码,复制保存到本地,后续将会用到。</p><ol start="3"><li>JRebel 插件激活</li></ol><p>点击 help -> JRebel -> Activaction -> Activation code,将我们网页上获取到的 <code>Activation code</code> 粘贴后,点击 <code>Activate JRebel</code> 即可激活。</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563196093481.png" alt="1563196093481"></p><p>激活成功后在 JRebel 配置面板中,会显示 VALID 标志,即说明激活成功了。</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563196236960.png" alt="1563196236960"></p><p>此时,你已经获取到了试用版的 JRebel 的使用权限,即可实现本地热部署的功能,可以进行第 3 步配置。(其他激活方式请参考:<a href="http://manuals.zeroturnaround.com/jrebel/standalone/activate.html#command-line-utility" target="_blank" rel="noopener">Activation — JRebel 2019.x documentation</a>,若想要永久破解,请往下阅读。)</p><h4 id="二-永久和谐方式">(二)永久和谐方式</h4><blockquote><p>本人用户正版软件,推荐尽可能使用正版,如果想要 cracked(和谐)可以参考以下配置说明</p></blockquote><p>在这里我们将通过 JRebel 的 Team URL(connect to online licensing service) 方式进行和谐(cracked)。</p><ol><li>由于网络稳定性考虑,这里我们将搭建自己的 license server for JRebel</li></ol><p>具体的搭建过程可以参考 Gitee 上的这位作者的仓库配置说明:<a href="https://gitee.com/gsls200808/JrebelLicenseServerforJava/" target="_blank" rel="noopener">JrebelLicenseServerforJava: A license server for Jrebel</a>。当然同时,这里我也将提供一个打包完成的 jar 包,直接上传至服务器运行即可。</p><p>JrebelBrainsLicenseServerforJava-1.0-SNAPSHOT-jar-with-dependencies</p><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 服务启动(阻塞)</span></span><br><span class="line">java -jar JrebelBrainsLicenseServerforJava-1.0-SNAPSHOT-jar-with-dependencies.jar -port 8081</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 服务启动(后台),推荐使用这种方式</span></span><br><span class="line">nohup java -jar JrebelBrainsLicenseServerforJava-1.0-SNAPSHOT-jar-with-dependencies.jar -port 8081 &</span><br></pre></td></tr></table></figure><p>浏览器中打开链接 <a href="http://192.168.72.131:8081" target="_blank" rel="noopener">http://192.168.72.131:8081</a> ,出现如下界面则服务端配置成功。</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563284703729.png" alt="1563284703729"></p><blockquote><p>如果是团队协作推荐搭建在一台内部都能够访问的服务器。</p></blockquote><p>这里我使用的 JRebel 是 <code>JRebel for IntelliJ v2019.1.4</code> 版本的,我们可以通过如下 URL 进行注册</p><ul><li><code>http://host:8081/{guid}</code>,其中 GUID 可以通过在线工具自己生成一个,用于区分不同的用户,而 GUID 基本上是不会出现重复的情况。<a href="http://www.ofmonkey.com/transfer/guid" target="_blank" rel="noopener">生成GUID - 程序员在线工具</a></li><li>在此处,我可以通过 <code>http://192.168.72.131:8081/bc1fdd38-9be0-4251-a619-e14a4a6c21b9</code> 和 <code>任意一个邮箱</code> 进行和谐(这里我们先准备好这两个参数即可)</li></ul><ol start="2"><li>在 Team URL 中填入以上两个参数,即可实现激活</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563285310324.png" alt="1563285310324"></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563285346373.png" alt="1563285346373"></p><p>在 file->settings->plugins->JRebel 中查看是否已显示激活,激活后显示 valid,图标为绿色。此时可以点击 Work offline 实现离线的验证,离线下有效期为半年时间,半年后可以重新认证即可。若是 online 则实时会往服务端进行匹配验证。推荐使用 <strong>Work offline</strong> 方式!</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563285389975.png" alt="1563285389975"></p><ol start="3"><li>补充:这里我们主要使用自己搭建 Server 的方式进行验证,这里也提供一些网上第三方的服务平台。在网络可达的情况下,免去搭建的烦恼。</li></ol><ul><li><a href="https://tools.hexianwei.com/#/tools/jrebel" target="_blank" rel="noopener">beer_tools for jrebel</a></li></ul><p>在和谐的过程中,参考的一些资料:</p><ul><li><a href="https://blog.csdn.net/songfei_dream/article/details/90921505" target="_blank" rel="noopener">JRebel永久破解激活方法 - songfei_dream的专栏</a></li><li><a href="https://blog.csdn.net/lzt099/article/details/77455345" target="_blank" rel="noopener">idea离线使用jrebel(亲测可用) - lzt099的博客 - CSDN博客</a></li><li><a href="http://3ms.huawei.com/km/groups/961/blogs/details/6201649" target="_blank" rel="noopener">Jrebel在Idea中使用 - 鹰眼eagle-故障主动识别 - 3MS知识管理社区</a></li><li><a href="https://gitee.com/gsls200808/JrebelLicenseServerforJava" target="_blank" rel="noopener">JrebelLicenseServerforJava: A license server for Jrebel & JetBrains products, it also support JRebel for Android and XRebel.</a></li><li><a href="http://3ms.huawei.com/hi/group/3690817/wiki_5444639.html" target="_blank" rel="noopener">IDEA+Jrebel实现热部署和远程调试 - ManageOne云服务保障 - 3ms知识管理社区</a></li></ul><h3 id="3-设置-idea-为自动编译">3. 设置 IDEA 为自动编译</h3><ol><li>由于 JRebel 是实时监控 class 文件的变化来实现热部署的,所以在 IDEA 环境下需要打开自动编译功能才能实现随时修改,随时生效。</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563196516607.png" alt="1563196516607"></p><ol start="2"><li>按住 Ctrl + Alt + Shift + / 弹出,选择 Registry 后勾选</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563196625339.png" alt="1563196625339"></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563196598708.png" alt="1563196598708"></p><h3 id="4-debug-启动服务">4. Debug 启动服务</h3><p>选择 SpringBoot 的入口类,右键选择 Debug with JRebel,等待启动完成即可</p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563196770320.png" alt="1563196770320"></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563197215671.png" alt="1563197215671"></p><h3 id="5-本地热部署">5. 本地热部署</h3><p>此时可以修改或者增加代码,通过 Ctrl + Shift + F9 即可实现重新编译热部署。此时再也不需要每增加一个函数体、类都需要进行重启的操作,大大提升了 SpringBoot 开发调试的效率。</p><p>🤩 自此,我们已经学会了本地热部署的方式,需要远程调试与热部署方式进行配合调试请往下阅读。</p><h2 id="三-远程热部署">三、远程热部署</h2><p>在本节,将针对远程热部署与调试进行说明,搭建一个高效的开发测试环境。</p><p>配置远程热部署服务,主要步骤如下:</p><ol><li>在服务器安装 JRebel</li><li>配置本地 JRebel</li><li>在服务器用 JRebel 启动专案</li><li>本地新增远端服务器进行热部署</li></ol><h3 id="1-配置服务器-jrebel">1. 配置服务器 JRebel</h3><blockquote><p>官方配置文档:<a href="http://manuals.zeroturnaround.com/jrebel/remoteserver/intellij.html#intellijremoteserver" target="_blank" rel="noopener">JRebel remote server support in IntelliJ IDEA — JRebel 2019.x documentation</a></p></blockquote><ol><li>下载安装包,确保容器启动用户有权限访问该路径</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -O http://dl.zeroturnaround.com/jrebel-stable-nosetup.zip</span><br></pre></td></tr></table></figure><ul><li>由于,使用 SpringBoot 2.x,必须要下载安装最新版的 JRebel</li><li>也可以通过官网地址:<a href="https://jrebel.com/software/jrebel/download/prev-releases/" target="_blank" rel="noopener">JRebel Release Archive | JRebel.com</a>,进行下载</li></ul><ol start="2"><li>下载好后可以通过 FTP/SFTP 工具上传至服务器,在 CentOS 下使用 unzip 进行解压,得到 <code>jrebel</code> 文件夹</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">unzip jrebel-stable-nosetup.zip</span><br></pre></td></tr></table></figure><ol start="3"><li>设置密码(进入到 <code>jrebel</code> 目录)</li></ol><figure class="highlight shell"><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></pre></td><td class="code"><pre><span class="line">java -jar jrebel.jar -set-remote-password <password></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 例如,设置密码为 12341234</span></span><br><span class="line">java -jar jrebel.jar -set-remote-password 12341234</span><br></pre></td></tr></table></figure><ol start="4"><li>激活服务端(进入到 <code>jrebel/bin</code> 目录,执行脚本)</li></ol><p>在激活服务端中,JRebel 也提供了 3 种激活方式,详细地址:<a href="http://manuals.zeroturnaround.com/jrebel/standalone/activate.html#command-line-activation-examples" target="_blank" rel="noopener">Activation — JRebel 2019.x documentation</a></p><p>在这里我们通过 With <strong>license server URL</strong> as an argument 方式,具体脚本如下</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./activate.sh http://192.168.72.131:8081/bc1fdd38-9be0-4251-a619-e14a4a6c21b9 [email protected]</span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563286046514.png" alt="1563286046514"></p><p>With evaluation license <strong>activation code</strong> as an argument</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./activate.sh <activation-code></span><br></pre></td></tr></table></figure><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563239243081.png" alt="1563239243081"></p><h3 id="2-配置本地-jrebel">2. 配置本地 JRebel</h3><ol><li>Startup 初始化配置,点击确认</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563239986427.png" alt="1563239986427"></p><ol start="2"><li>JRebel Panel 窗口,将需要热部署的项目打包,生成 <code>rebel.xml</code> 和 <code>rebel-remote.xml</code>,如下图:</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563240819085.png" alt="1563240819085"></p><ol start="3"><li>通过 maven package 重新打包生成 jar 包,上传至服务器</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563240728219.png" alt="1563240728219"></p><ol start="4"><li>服务器端启动服务</li></ol><ul><li>启动参数</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-agentpath:[/path/to/JRebel Agent] -Drebel.remoting_plugin=true</span><br></pre></td></tr></table></figure><ul><li>热部署启动</li></ul><figure class="highlight shell"><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"><span class="meta">#</span><span class="bash"> 远程热部署 启动</span></span><br><span class="line">java -agentpath:/root/commom/jrebel/lib/libjrebel64.so -Drebel.remoting_plugin=true -jar webapp.jar</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 远程热部署+远程调试 启动</span></span><br><span class="line">java -agentpath:/root/commom/jrebel/lib/libjrebel64.so -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Drebel.remoting_plugin=true -jar webapp.jar</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">java -agentpath:/var/www/jrebel/lib/libjrebel64.so -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Drebel.remoting_plugin=true -jar webapp.jar -port 8083</span><br></pre></td></tr></table></figure><p>详细官网配置说明:<a href="http://manuals.zeroturnaround.com/jrebel/remoteserver/serverconfiguration.html" target="_blank" rel="noopener">Server configuration — JRebel 2019.x documentation</a></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563239798406.png" alt="1563239798406"></p><p>启动日志如下,即说明启动成功</p><ul><li><code>Listening for transport dt_socket at address: 5005</code> 说明启动了远程调试</li><li><code>JRebel started in remote server mode.</code> 说明启动了远程热部署</li></ul><figure class="highlight shell"><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">Listening for transport dt_socket at address: 5005</span><br><span class="line">2019-07-16 09:15:20 JRebel: Starting logging to file: /root/.jrebel/jrebel.log</span><br><span class="line">2019-07-16 09:15:20 JRebel:</span><br><span class="line">2019-07-16 09:15:20 JRebel: #############################################################</span><br><span class="line">2019-07-16 09:15:20 JRebel:</span><br><span class="line">2019-07-16 09:15:20 JRebel: JRebel Agent 2019.1.4 (201907051008)</span><br><span class="line">2019-07-16 09:15:20 JRebel: (c) Copyright 2007-2019 Rogue Wave Software, Inc.</span><br><span class="line">2019-07-16 09:15:20 JRebel:</span><br><span class="line">2019-07-16 09:15:20 JRebel: Over the last 1 days JRebel prevented</span><br><span class="line">2019-07-16 09:15:20 JRebel: at least 0 redeploys/restarts saving you about 0 hours.</span><br><span class="line">2019-07-16 09:15:20 JRebel:</span><br><span class="line">2019-07-16 09:15:20 JRebel: JRebel started in remote server mode.</span><br><span class="line">2019-07-16 09:15:20 JRebel:</span><br><span class="line">2019-07-16 09:15:20 JRebel:</span><br><span class="line">2019-07-16 09:15:20 JRebel: #############################################################</span><br></pre></td></tr></table></figure><ol start="5"><li>本地 JRebel -> Remote Servers 添加远程服务器</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563240185570.png" alt="1563240185570"></p><ol start="6"><li>点击 Test Connection,点击 OK 确认</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563240386725.png" alt="1563240386725"></p><ol start="7"><li>此时即可点击右上角 “远程热部署” 按钮后,通过 Ctrl + Shift + F9 即可实现重新编译热远程部署</li></ol><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/1563241396567.png" alt="1563241396567"></p><p>可以先远程热部署后,再点击远程调试 Debug,即可实现先热部署再调试,大大加快开发效率。</p><p>😄 自此,所有的配置过程都结束了!即可开始尽情享受,JRebel 远程热部署和远程调试~ 实现真正的热部署,无论是改了代码片段还是配置文件,都可以做到不用重新启动就生效!</p><h2 id="todo">TODO</h2><p>文档还有很多不足的地方,这是接下来会更新的部分</p><ul><li>[ ] 基于 Docker 远程调试与部署</li><li>[ ] 远程调试,服务阻塞怎么办</li></ul><h2 id="参考资料">参考资料</h2><ul><li><a href="https://blog.csdn.net/wo541075754/article/details/75008617" target="_blank" rel="noopener">Intellij IDEA基于Springboot的远程调试 - 程序新视界 - CSDN博客</a></li><li><a href="https://blog.csdn.net/duanluan/article/details/79080370" target="_blank" rel="noopener">IDEA + Spring Boot + JRebel 热部署不能自动编译的解决方案和启动的几种方式 - github.com/duanluan - CSDN博客</a></li><li><a href="https://www.jiweichengzhu.com/article/33c0330308f5429faf7a1e74127c9708" target="_blank" rel="noopener">IntelliJ IDEA热部署插件JRebel免费激活图文教程(持续更新)_心得技巧_积微成著</a></li><li><a href="https://www.itread01.com/content/1546463193.html" target="_blank" rel="noopener">JRebel遠端熱部署springboot教程 - IT閱讀</a></li><li><a href="https://juejin.im/post/5c0fc3736fb9a049d51957a1" target="_blank" rel="noopener">微服务开发神器–JRebel 插件破解和实现本地/远程热部署教程(IntelliJ IDEA版) - 掘金</a></li><li><a href="https://blog.csdn.net/qq_18748427/article/details/79853101" target="_blank" rel="noopener">JRebel远程热部署springboot教程 - whz的博客 - CSDN博客</a></li></ul>]]></content>
<summary type="html">
<h2 id="前言">前言</h2>
<p>在 SpringBoot 开发过程中,当我们在 Debug 我们的工程时,随便修改一段代码逻辑、修改接口路由、新增一个工具类等等情况下,都需要我们重新启动工程。反复的修改逻辑,反复的重启,这是相当痛苦的过程。当工程越来越大的时候,效率
</summary>
</entry>
<entry>
<title>Linux 磁盘分区管理</title>
<link href="http://www.frankfeekr.cn/2019/06/12/Linux-%E7%A3%81%E7%9B%98%E5%88%86%E5%8C%BA%E7%AE%A1%E7%90%86/"/>
<id>http://www.frankfeekr.cn/2019/06/12/Linux-磁盘分区管理/</id>
<published>2019-06-12T09:21:24.000Z</published>
<updated>2021-10-23T14:22:52.646Z</updated>
<content type="html"><![CDATA[<h2 id="查看磁盘信息">查看磁盘信息</h2><h3 id="df">df</h3><blockquote><p>列出文件系统的整体磁盘使用量</p></blockquote><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">df -h</span><br></pre></td></tr></table></figure><h3 id="du">du</h3><blockquote><p>评估文件系统的磁盘使用量(常用在估计目录所占磁盘容量)</p></blockquote><figure class="highlight shell"><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">du -sh</span><br><span class="line">du -sh *</span><br></pre></td></tr></table></figure><h3 id="fdisk">fdisk</h3><blockquote><p>输出设备的所有分区内容和操作分区(删除,新增)</p></blockquote><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fdisk -l</span><br></pre></td></tr></table></figure><h2 id="gpt-分区">GPT 分区</h2><ol><li>进入 parted 分区</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">parted</span><br></pre></td></tr></table></figure><ol start="2"><li>选择你需要操作的分区</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select /dev/sda</span><br></pre></td></tr></table></figure><ol start="3"><li>设定使用的分区类型</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mklabel gpt</span><br></pre></td></tr></table></figure><ul><li>说明:如果要用 MBR 分区,输入 msdos 即可</li></ul><ol start="4"><li>添加一个分区</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkpart</span><br></pre></td></tr></table></figure><p>(1) 输入分区格式,默认 ext2,回车。(此处可以随意选,之后重新格式化写入文件系统)</p><p>(2) 输入分区的开始位置,输入 1,从第 1Mb 开始(最好不要从 0 开始,这里有一个所谓的 4k 对齐得问题,有兴趣的可以了解:xxx)</p><p>(3) 输入分区从第几Mb结束,输入2000,第 2000Mb 结束(如果想要把剩余的全部挂载,请输入 -1)</p><p>(4) 此时已经分区成功,输入print查看目前分区情况</p><ul><li>或者可以通过一句命令直接创建</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkpart webapp 4000 10000</span><br></pre></td></tr></table></figure><ul><li>可以设置单位</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">unit GB</span><br></pre></td></tr></table></figure><ol start="5"><li>删除分区</li></ol><figure class="highlight accesslog"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rm <span class="string">[Number]</span></span><br></pre></td></tr></table></figure><p>例子</p><figure class="highlight shell"><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">mkpart docker 1 400000</span><br><span class="line">mkpart webapp 400000 1000000</span><br><span class="line">mkpart home 1000000 -1</span><br></pre></td></tr></table></figure><h2 id="格式化">格式化</h2><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkfs<span class="selector-class">.ext4</span> /dev/sda1</span><br></pre></td></tr></table></figure><h2 id="磁盘挂载">磁盘挂载</h2><h3 id="mount">mount</h3><p>将 /dev/hda1 挂在 /mnt 之下</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mount /dev/hda1 /mnt</span><br></pre></td></tr></table></figure><h3 id="umount">umount</h3><p>下面两条命令分别通过设备名和挂载点卸载文件系统,同时输出详细信息:</p><figure class="highlight shell"><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 class="meta">#</span><span class="bash"> 通过设备名卸载</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> umount -v /dev/sda1</span></span><br><span class="line">/dev/sda1 umounted</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 通过挂载点卸载</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> umount -v /mnt/mymount/</span></span><br><span class="line">/tmp/diskboot.img umounted</span><br></pre></td></tr></table></figure><h2 id="分区永久挂载">分区永久挂载</h2><p>修改分区文件 <code>/etc/fstab</code>,加一行</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/dev/sdb1 /var/www ext4 defaults 0 1</span><br></pre></td></tr></table></figure><h2 id="docker-修改默认存储位置">Docker 修改默认存储位置</h2><ol><li>关闭 Docker</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">service docker stop</span><br></pre></td></tr></table></figure><ol start="2"><li>先创建挂载点,然后将 docker 得文件移动到目标路径</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mv /var/lib/docker /var/lib/docker_backup</span><br></pre></td></tr></table></figure><ol start="3"><li>拷贝 docker 文件到 dockerdata</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp -rf /var/lib/docker_backup/* /var/lib/dockerdata</span><br></pre></td></tr></table></figure><ol start="4"><li>创建软连接,此时确保 <code>/var/lib/docker</code> 文件夹不存在</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ln -s /var/lib/dockerdata /var/lib/docker</span><br></pre></td></tr></table></figure><ol start="5"><li>Docker 重启</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">service docker restart</span><br></pre></td></tr></table></figure><h2 id="删除软链接注意事项">删除软链接注意事项</h2><p>软链接创建好了,我们来看看怎么删除它</p><p>正确的删除方式(删除软链接,但不删除实际数据)</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rm -rf ./dockerdata</span><br></pre></td></tr></table></figure><p>错误的删除方式</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rm -rf ./dockerdata/</span><br></pre></td></tr></table></figure><p>(这样就会把原来test_chk下的内容删除)</p><p><strong>现在看到区别了吧,所以说,以后大家在用rm -rf 删除软链接时,注意了!!!!</strong></p><h2 id="参考资料">参考资料</h2><ul><li><p><a href="https://blog.51cto.com/forangela/1949947" target="_blank" rel="noopener">四个修改Docker默认存储位置的方法-一生志在千里也知似水流年-51CTO博客</a></p></li><li><p><a href="http://www.mamicode.com/info-detail-1977427.html" target="_blank" rel="noopener">Ubuntu 16.04开机自动挂载硬盘分区(转)</a></p></li><li><p><a href="https://www.runoob.com/linux/linux-comm-umount.html" target="_blank" rel="noopener">Linux umount命令 | 菜鸟教程</a></p></li><li><p><a href="https://www.cnblogs.com/DaDaOnline/p/5527833.html" target="_blank" rel="noopener">使用userdel命令删除Linux用户 - Zidane_Zhang - 博客园</a></p></li></ul>]]></content>
<summary type="html">
<h2 id="查看磁盘信息">查看磁盘信息</h2>
<h3 id="df">df</h3>
<blockquote>
<p>列出文件系统的整体磁盘使用量</p>
</blockquote>
<figure class="highlight shell"><table><tr>
</summary>
</entry>
<entry>
<title>搜索引擎使用的小贴士</title>
<link href="http://www.frankfeekr.cn/2019/06/01/search-engine-tips/"/>
<id>http://www.frankfeekr.cn/2019/06/01/search-engine-tips/</id>
<published>2019-05-31T17:01:34.000Z</published>
<updated>2021-10-23T14:22:52.649Z</updated>
<content type="html"><![CDATA[<h2 id="必须掌握的十个搜索技巧">必须掌握的十个搜索技巧</h2><h3 id="1-准确搜索">1、准确搜索</h3><form> <span style="display: block;"></span> <input type="text" value="" Frank Blog "" style="width:50%;"> <span><input value="百度/谷歌" class="search-btn"></span> </form><br><h3 id="2-排除关键词">2、排除关键词</h3><form> <span style="display: block;"></span> <input type="text" value="武汉大学 -浙江大学" style="width:50%;"> <span><input value="百度/谷歌" class="search-btn"></span> </form><br><h3 id="3-用-either-or-或-逻辑进行搜索">3、用「Either OR」(或)逻辑进行搜索</h3><form> <span style="display: block;"></span> <input type="text" value="武汉大学 OR 浙江大学" style="width:50%;"> <span><input value="百度/谷歌" class="search-btn"></span> </form><br><h3 id="4-同义词搜索">4、同义词搜索</h3><form> <span style="display: block;"></span> <input type="text" value="plumbing ~university" style="width:50%;"> <span><input value="百度/谷歌" class="search-btn"></span> </form><br><ul><li><p>有时候对不太确切的关键词进行搜索反而会显得更加合适。在未能准确判断关键词的情况下,你可以通过同义词进行搜索。</p></li><li><p>如果你在搜索引擎输入「plumbing ~university」,你所得到的反馈结果会包含「plumbing universities」和「plumbing colleges」等相似条目。</p></li></ul><br><h3 id="5-在站内进行搜索">5、在站内进行搜索</h3><form> <span style="display: block;"></span> <input type="text" value="site:www.frankfeekr.cn 如何选择自己的技术栈" style="width:50%;"> <span><input value="百度/谷歌" class="search-btn"></span> </form><br><h3 id="6-善用星号">6、善用星号</h3><form> <span style="display: block;"></span> <input type="text" value="architect*" style="width:50%;"> <span><input value="百度/谷歌" class="search-btn"></span> </form><br><ul><li>例如,如果你在搜索引擎中输入「architect*」,你所得到的反馈结果将会是所有包含 architect、architectural、architecture、architected、architecting 以及其他所有以「architect」作为开头的词汇的条目。</li></ul><br><h3 id="7-在两个数值之间进行搜索">7、在两个数值之间进行搜索</h3><form> <span style="display: block;"></span> <input type="text" value="英国首相 1920.. 1950" style="width:50%;"> <span><input value="百度/谷歌" class="search-btn"></span> </form><br><ul><li><p>在寻找问题的答案时,一个很好的方法是在一定范围内寻找和关键词相关的资讯。例如想要找出 1920 至 1950 年间的英国首相,直接在搜索引擎中输入「英国首相 1920… 1950」即可得出想要的结果。</p></li><li><p>记住,数值之间的符号是两个英文句号加一个空格键。</p></li></ul><br><h3 id="8-在网页标题-链接和主体中搜索关键词">8、在网页标题、链接和主体中搜索关键词</h3><form> <span style="display: block;"></span> <input type="text" value="intitle: 后台登录" style="width:50%;"> <span><input value="百度/谷歌" class="search-btn"></span> </form><br><ul><li><p>有时你或许会遇上找出所有和关键词相关的所有网页标题、链接和网页主体的需求,在这个时候你需要使用的是限定词「inurl:」(供在 url 链接中搜索使用)、「intext:」(供在网页主体中搜索使用)以及「intitle:」(供在网页标题中搜索使用)。</p></li><li><p>例如,在搜索引擎中输入「intitle: 后台登录」会得到所有和关键词「后台登录」相关的网页标题。</p></li></ul><br><h3 id="9-搜索相关网站">9、搜索相关网站</h3><form> <span style="display: block;"></span> <input type="text" value="related:whu.edu.cn" style="width:50%;"> <span><input value="百度/谷歌" class="search-btn"></span> </form><br><ul><li>相关的限定词可用于搜索相关网站时使用。例如,你仅需在搜索引擎中输入「related:whu.edu.cn」即可得到所有和「<a href="http://whu.edu.cn" target="_blank" rel="noopener">whu.edu.cn</a>」相关的网站反馈结果。</li></ul><br><h3 id="10-搜索技能的组合使用">10、搜索技能的组合使用</h3><form> <span style="display: block;"></span> <input type="text" value="武汉大学 OR 浙江大学 site:www.whu.edu.cn" style="width:50%;"> <span><input value="百度/谷歌" class="search-btn"></span> </form><br><ul><li>你可以对上述所有搜索技能进行组合运用,以便按照自己的意愿缩小或者扩展搜索范围。尽管有些技能或许并不常用,但准确搜索和站内搜索这些技能的使用范围还是相当广泛的。</li></ul><br><h2 id="参考资料">参考资料</h2><ul><li><a href="https://www.imooc.com/article/4071" target="_blank" rel="noopener">程序员应该掌握的10个搜索技巧_慕课手记</a></li><li><a href="https://www.theguardian.com/technology/2016/jan/15/how-to-use-search-like-a-pro-10-tips-and-tricks-for-google-and-beyond" target="_blank" rel="noopener">How to use search like a pro: 10 tips and tricks for Google and beyond | Technology | The Guardian</a></li></ul><style type="text/css">.search-btn { cursor: pointer; width: 102px; height: 38px; line-height: 38px; padding: 0; border: 0; background: none; background-color: #38f; font-size: 16px; color: white; box-shadow: none; font-weight: normal; text-align:center; height:31px;}</style>]]></content>
<summary type="html">
<h2 id="必须掌握的十个搜索技巧">必须掌握的十个搜索技巧</h2>
<h3 id="1-准确搜索">1、准确搜索</h3>
<form>
<span style="display: block;"></span>
<input type="text" valu
</summary>
</entry>
<entry>
<title>策划了11期的技术沙龙,顺利结束啦!纪念一下</title>
<link href="http://www.frankfeekr.cn/2019/05/30/luojia-talk-show-s1/"/>
<id>http://www.frankfeekr.cn/2019/05/30/luojia-talk-show-s1/</id>
<published>2019-05-30T15:59:59.000Z</published>
<updated>2021-10-23T14:22:52.649Z</updated>
<content type="html"><![CDATA[<h2 id="一些感悟和心得">一些感悟和心得</h2><p>在过去策划了 11 期的技术沙龙活动中,确实没想到坚持下来了!我想,三人行必有我师,在和大家交流的过程中也是对自己技术升华的一个过程。</p><p>某种程度上来说,策划这样的技术沙龙,主要是想要提升大家的技术视野,另一方面想要调解每周紧张的工作学习氛围。</p><p>坚持是一件太难得事情了,感谢自己的坚持,还有大家的支持。希望第二季更加精彩~</p><div align="center"><p style="text-align:right;">EDN 2019年05月30日</p></div><h2 id="珞珈小讲坛-技术沙龙-第一季回顾">珞珈小讲坛 - 技术沙龙 第一季回顾</h2><h3 id="第1期">第1期</h3><ul><li>《customvision》柳家胜</li><li>《linux命令行小工具z jump》廖浩男</li><li>《如何写一首歌》张健</li><li>《博士生活是怎样的体验》吴光生</li><li>《python代码优化案例》谢老师</li></ul><h3 id="第2期">第2期</h3><ul><li>《关于 MySQL 你不知道的》张健</li><li>《游戏外挂怪圈》陈子豪</li><li>《打开 Latex 的大门》陈哲</li></ul><h3 id="第3期">第3期</h3><ul><li>《你还在关linux防火墙吗?》张健</li><li>《竞赛分享》李宇翔</li><li>《我曾经的英语学习之道》花春兵</li><li>《线性回归与正则化》李光耀</li></ul><h3 id="第4期">第4期</h3><ul><li>《五笔输入法入门》张健</li><li>《实验室深度学习工作站管理》柳家胜</li><li>《手把手教你,搭建 VPS 翻墙服务》林立城</li><li>《带你打开 5G 大门》陈子豪</li><li>《从<东邪西毒>初窥王家卫的光影世界》胡鹏</li></ul><h3 id="第5期">第5期</h3><p>《绿皮书》电影沙龙</p><h3 id="第6期">第6期</h3><ul><li>《CrossEntropy你了解多少?》柳家胜</li><li>《手把手教你,搭建 VPS 翻墙服务》林立城</li><li>《普通的密码学》李卓彧</li><li>《医学小常识(上篇)》张健</li></ul><h3 id="第7期">第7期</h3><p>《模仿游戏》电影沙龙</p><h3 id="第8期">第8期</h3><ul><li>《免费获取 VPS 10个月主机,搭建 SS 翻墙服务》廖浩南</li><li>《适老化设计+DigitalOcean云服务》廖浩南</li><li>《Git Ftp》胡鹏</li><li>《一句代码也不写,教你优雅搭建免费的博客》林立城</li></ul><h3 id="第9期">第9期</h3><ul><li>《心电图识别的简易医学知识》张健</li><li>《贸易战》陈子豪</li><li>《常见深度学习微信公众号你知道多少(#^.^#)》柳家胜</li><li>《前端知识体系,和一些你不知道的奇技淫巧》林立城</li><li>《博弈论知多少》花春兵</li></ul><h3 id="第10期">第10期</h3><ul><li>《浅谈深度学习多GPU模型训练》柳家胜</li><li>《谈谈技术学习的一些方法论》林立城</li></ul><h3 id="第11期-完结篇">第11期 - 完结篇</h3><ul><li>《Search Engine Tips》胡鹏</li><li>《表白神器:荧光反应》张健</li><li>《Git 版本控制工具实战》林立城</li><li>《zsh,让你的爱上终端》马浩原</li></ul><table><thead><tr><th>Talker</th><th>分享主题</th></tr></thead><tbody><tr><td>张健(7)</td><td>《如何写一首歌》</td></tr><tr><td></td><td>《关于 MySQL 你不知道的》</td></tr><tr><td></td><td>《你还在关linux防火墙吗?》</td></tr><tr><td></td><td>《五笔输入法入门》</td></tr><tr><td></td><td>《心电图识别的简易医学知识》</td></tr><tr><td></td><td>《医学小常识(上篇)》</td></tr><tr><td></td><td>《表白神器:荧光反应》</td></tr><tr><td></td><td></td></tr><tr><td>柳家胜(6)</td><td>《customvision》</td></tr><tr><td></td><td>《实验室深度学习工作站管理》</td></tr><tr><td></td><td>《CrossEntropy你了解多少?》</td></tr><tr><td></td><td>《实验室深度学习工作站管理》</td></tr><tr><td></td><td>《常见深度学习微信公众号你知道多少(#^.^#)》</td></tr><tr><td></td><td>《浅谈深度学习多GPU模型训练》</td></tr><tr><td></td><td></td></tr><tr><td>林立城(4)</td><td>《手把手教你,搭建 VPS 翻墙服务》</td></tr><tr><td></td><td>《一句代码也不写,教你优雅搭建免费的博客》</td></tr><tr><td></td><td>《前端知识体系,和一些你不知道的奇技淫巧》</td></tr><tr><td></td><td>《谈谈技术学习的一些方法论》</td></tr><tr><td></td><td>《Git 版本控制工具实战》</td></tr><tr><td></td><td></td></tr><tr><td>廖浩男(3)</td><td>《linux命令行小工具z jump》</td></tr><tr><td></td><td>《免费获取 VPS 10个月主机,搭建 SS 翻墙服务》</td></tr><tr><td></td><td>《适老化设计+DigitalOcean云服务》</td></tr><tr><td></td><td></td></tr><tr><td>陈子豪(3)</td><td>《游戏外挂怪圈》</td></tr><tr><td></td><td>《带你打开 5G 大门》</td></tr><tr><td></td><td>《中美贸易战》</td></tr><tr><td></td><td></td></tr><tr><td>花春兵(2)</td><td>《我曾经的英语学习之道》</td></tr><tr><td></td><td>《博弈论知多少》</td></tr><tr><td></td><td></td></tr><tr><td>胡鹏(3)</td><td>《从<东邪西毒>初窥王家卫的光影世界》</td></tr><tr><td></td><td>《Git Ftp》</td></tr><tr><td></td><td>《Search Engine Tips》</td></tr><tr><td></td><td></td></tr><tr><td>陈哲(1)</td><td>《打开 Latex 的大门》</td></tr><tr><td></td><td></td></tr><tr><td>吴光生(1)</td><td>《博士生活是怎样的体验》</td></tr><tr><td></td><td></td></tr><tr><td>李光耀(1)</td><td>《线性回归与正则化》</td></tr><tr><td></td><td></td></tr><tr><td>李卓彧(1)</td><td>《普通的密码学》</td></tr><tr><td></td><td></td></tr><tr><td>李宇翔(1)</td><td>《竞赛分享》</td></tr><tr><td></td><td></td></tr><tr><td>马浩原(1)</td><td>《zsh,让你的爱上终端》</td></tr><tr><td></td><td></td></tr><tr><td>谢老师(1)</td><td>《python代码优化案例》</td></tr></tbody></table><h2 id="照片合辑">照片合辑</h2><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/talk-show-20190530-1.jpg" alt="talk-show-20190530-1"></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/talk-show-20190530-2.jpg" alt="talk-show-20190530-2"></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/talk-show-20190530-3.jpg" alt="talk-show-20190530-3"></p><p><img src="https://cdn.jsdelivr.net/gh/frank-lam/hexo-blog-site/assets/talk-show-20190530-4.jpg" alt="talk-show-20190530-4"></p>]]></content>
<summary type="html">
<h2 id="一些感悟和心得">一些感悟和心得</h2>
<p>在过去策划了 11 期的技术沙龙活动中,确实没想到坚持下来了!我想,三人行必有我师,在和大家交流的过程中也是对自己技术升华的一个过程。</p>
<p>某种程度上来说,策划这样的技术沙龙,主要是想要提升大家的技术视野
</summary>
</entry>
</feed>