diff --git a/_config.yml b/_config.yml index 718a70cff5..cf8ecda5d0 100644 --- a/_config.yml +++ b/_config.yml @@ -15,10 +15,10 @@ keywords: ityouknow,纯洁的微笑,Spring,Spring Boot,Spring Cloud,MongoDB,Jvm, encoding: "utf-8" favicon: favicon.ico -repository: ityouknow/ityouknow.github.io -github_url: https://github.com/ityouknow -url: http://www.ityouknow.com -enforce_ssl: ityouknow.com +repository: luhuahan/luhuahan.github.io +github_url: https://github.com/luhuahan +url: http://www.luhuahan.com +enforce_ssl: luhuahan.com ## Gem plugins: @@ -259,4 +259,4 @@ gitalk: clientID: 61bfc53d957e74e78f8f clientSecret: 31c61e66cdcc9ada8db2267ee779d0bdafac434c # 在使用其它评论组件时可点击显示 Disqus -lazy_load_disqus : true \ No newline at end of file +lazy_load_disqus : true diff --git a/_posts/2015/2015-01-01-about-blog.md b/_posts/2015/2015-01-01-about-blog.md deleted file mode 100644 index 0e4b7a0610..0000000000 --- a/_posts/2015/2015-01-01-about-blog.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -layout: post -title: 关于此博客 -category: life -tags: [life] -no-post-nav: true ---- - -我是一名来自一线的coder,目前就职于某互联网金融公司,负责公司的整体技术架构,热爱开源。 - -刚刚入行的几年很少写博客,早期经常混迹于CSDN,后来ITEYE,再然后博客园。慢慢的在博客园写了一些文章用于记录自己对技术的理解,在后来就发现了github,程序员的一片乐土,沉迷于此! - -> CSDN和ITEYE因为过度的商业化,已经远离很久,现在经常使用博客园和github这两个网站。 - -在博客园写文章因为排版的问题,耗费了一些精力,虽然我使用官方推荐的工具 windows live,但依然很麻烦。这时候markdown走到了我面前,可以安心的用程序员的视角去写点东西,配合着sublime3来使用味道更佳! - - -搭建这个博客也是无意中看到了Yummy-Jekyll这个博客的主题,比较适合程序员风格!作者也是一个coder,我在gitbook看她翻译的gradle中文手册而关注到。 - -[Yummy-Jekyll的主题地址](https://github.com/DONGChuan/Yummy-Jekyll)
-[我的博客园地址](http://www.cnblogs.com/ityouknow/)
- - -{% highlight java %} -此博客将记录我对技术的理解和部分生活的感悟,另外总结收集一些感兴趣的开源项目。 -{% endhighlight %} \ No newline at end of file diff --git a/_posts/2015/2015-01-22-git-summary.md b/_posts/2015/2015-01-22-git-summary.md deleted file mode 100644 index bced7dfcd1..0000000000 --- a/_posts/2015/2015-01-22-git-summary.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -layout: post -title: git 使用总结 -category: other -tags: [other] ---- - -git 使用总结 - - -## git 常常用命令 - -1、查看文件状态 -> git status - -2、查看历史提交记录 -> git log - -退出输入q - - - - - -thymeleaf - - - - - - - -## 问题处理 - diff --git a/_posts/2015/2015-03-03-redis-summary.md b/_posts/2015/2015-03-03-redis-summary.md deleted file mode 100644 index 5dc88073ae..0000000000 --- a/_posts/2015/2015-03-03-redis-summary.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -layout: post -title: redis使用总结 -category: redis -tags: [redis] ---- - -Redis是目前业界使用最广泛的内存数据存储。相比memcached,Redis支持更丰富的数据结构,例如hashes, lists, sets等,同时支持数据持久化。除此之外,Redis还提供一些类数据库的特性,比如事务,HA,主从库。可以说Redis兼具了缓存系统和数据库的一些特性,因此有着丰富的应用场景。本文介绍Redis在Spring Boot中两个典型的应用场景。 - - -## 运营命令 - -1、启动redis -> src/redis-server & - -2、关闭redis -> src/redis-cli shutdown - -3、连接 -> telnet localhost 6379 - - -## 诊断命令 - -1、查看基本信息 -> INFO - -2、查看缓存键 -> keys '*' - -3、查看key的类型 -> type yourkey - -## 操作命令 -1、插入命令 -> set key yy - -2、获取命令 -> get key - -2、清空所有数据 -> FLUSHALL - -## 配置命令 - -1、添加日志 -> vim redis.conf -> logfile /data/redis_cache/logs/redis.log #日志路径 diff --git a/_posts/2015/2015-06-06-idea-use-sum.md b/_posts/2015/2015-06-06-idea-use-sum.md deleted file mode 100644 index 9e7e861295..0000000000 --- a/_posts/2015/2015-06-06-idea-use-sum.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -layout: post -title: idea使用总结 -category: other -tags: [other] ---- - -记录idea的一些常用快捷键 - -## 快捷键 - -1、进入实现方法的快捷键 - -> Ctrl + Alt + B -> > Ctrl + Alt + 鼠标点击 - -2、自动给方法添加注释的快捷键 - -> 输入/**,然后回车 - -3、删除一行 - -> CTRL + Y / CTRL + X - -4、复制一行 - -> CTRL + D - -5、自动导入包 - -> ALT + Enter - -6、生成getter、setter方式 - -> ALT + insert - -7、实现接口方法 - -> CTRL + O - -8、移动整个代码块 - -> Ctrl + SHIfT + 上下箭头 - -9、返回至上次浏览的位置 - -> Ctrl + ALT + 左/右 - -10、查找和替换 - -> Ctrl + SHIfT + R,替换文本 -> Ctrl + SHIfT + F,查找文本 - -11、 debug - -> Ctrl+F1 ,显示结果 -> F9 ,下一个断点 -> F8 ,下一步 - -12、 移动代码 - -> Alt+Shift+Up/Down,上/下移一行 - -13、显示类图 - -> Ctrl+Shift+Alt+U - - -## 错误 - -1、maven导入项目报错 - -> Failed to read artifact descriptor for org.springframework.cloud:spring-cloud-starter-eureka-server:jar:1.3.0.RELE -> intellij inspects a maven model for resolution problems - - -不同project下面存在相同的module,导致此问题,删除其它project中项目,再次导入OK。 - - -2、gradle no cached version available for offline mode - -> Error:No cached version of org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE available for offline mode. - -抱这个错误直接把gradle中的offline work关掉就可以解决 -File > Settings > Gradle 中的offline work 的勾去掉。 - - -## 其它 - -1、添加插件mac - -Intellij IDEA 菜单 -》Preferences-》Plugins 选项卡 - - - -2、开启自动 import 包的功能 - -Java就是这种包组合在一个的一个东西, 我们在写代码时常常需要引入一些类, 一些第三方的包. 在eclipse时我们使用快捷键引入, IDEA也可以使用Alt + Enter进行导入包. - -如果我们在写代码时IDE自动帮我们引入相关的包, 是不是很酷的意见事情. IDEA提供了这个功能, 不过默认是关闭的. 打开自动导入包设置如下: - - -![](http://favorites.ren/assets/images/2015/idea-auto-import.jpg) - diff --git a/_posts/2015/2015-06-10-exception-record.md b/_posts/2015/2015-06-10-exception-record.md deleted file mode 100644 index b5fa94fd18..0000000000 --- a/_posts/2015/2015-06-10-exception-record.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -layout: post -title: 异常处理记录 -category: other -tags: [other] ---- - -记录工程中的各种异常 - -# How to Fix 504 Gateway Timeout using Nginx - -生产销售系统出现 504 Gateway Timeout 异常,其实就是服务器响应太慢导致nginx带来超时,先不说服务端慢的优化问题;只是单纯的解决504。到网上发现了一篇文章fix it - -Add these variables to nginx.conf file: - -``` xml - proxy_connect_timeout 600; - proxy_send_timeout 600; - proxy_read_timeout 600; - send_timeout 600; -``` - -Then restart nginx: - -``` xml -service nginx reload -``` - -[原文链接](https://www.scalescale.com/tips/nginx/504-gateway-time-out-using-nginx) - - -## Nginx returns empty response on long URL - (failed) net::ERR_EMPTY_RESPONSE - -nginx 返回的数据量大+反应时间过程会导致这个问题。 - - -## 查询特别耗时,高达6分钟 - -最后查明,开发人员在for循环中查询sql导致数据库压力巨大,查询速度非常慢,将sql提取出统一查询,问题解决! - - - - diff --git a/_posts/2015/2015-06-20-about-stock.md b/_posts/2015/2015-06-20-about-stock.md deleted file mode 100644 index f54a57d5d9..0000000000 --- a/_posts/2015/2015-06-20-about-stock.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -layout: post -title: 股票分析相关网站 -category: other -tags: [other] ---- - -收集股票相关的一些网站 - -## 股票分析的网站 - -### 股票个维度的分析 -[stockflare.com](https://stockflare.com) - -### 股票新闻 -[finance.yahoo.com](https://finance.yahoo.com/quote/bidu?ltr=1) - -### 股票交流 -[雪球](https://xueqiu.com/) - -### 工具资源 -[股海网](http://www.guhai.com.cn/) - - -### 量化分析 -[股票分析(一):常见的技术指标雪球](http://blog.callmewhy.com/2016/02/27/stock-analyse-1/)
-[股票分析(二):金叉到底灵不灵](http://blog.callmewhy.com/2016/03/06/stock-analyse-2/)
-[股票分析(三):谁与我生死与共](http://blog.callmewhy.com/2016/03/24/stock-analyse-3/) - - diff --git a/_posts/2015/2015-08-12-github-open-source.md b/_posts/2015/2015-08-12-github-open-source.md deleted file mode 100644 index f88df2cf43..0000000000 --- a/_posts/2015/2015-08-12-github-open-source.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -layout: post -title: github 好玩的开源项目 -category: other -tags: [other] ---- - -收集 github 开源的好玩好用的项目 - - -## X-Alarm - -非常有意思的app,如果你是一个起床困难者,OK,就是它了。闹钟响了之后需要做一些小游戏或者小动作可以熄灭闹钟知道你彻底清醒过来。 - -> X-ALARM is a cool application to help lazy boys/girls to get up. -> Yes, we are all lazy in the morning :D. -> -> It offers a variety of interesting ways to unlock alarm. -> It provides a very soothing ringtone which wakes you up smoothly. -> Welcome to have a try! - -### 效果图: -![](http://loop-x.github.io/X-Alarm/assets/css/images/banner.png)
- -项目地址: -[X-Alarm](http://loop-x.github.io/X-Alarm/) - - -## text-diagram -可以通过输入特定的格式来输出文字对话,挺好玩的: - - -### 效果图: - -``` - +-------+ +-------+ +-------+ - | April | | Todd | | Monad | - +-------+ +-------+ +-------+ - | | | - | Prepare food for lunch. | | - |------------------------ | | - | | | | - |<----------------------- | | -------------------\ | | | -| Lunch is ready. |-| | | -|-----------------| | | | - | Todd, what are you doing? | | - |------------------------------>| | - | | ------\ | - | |-| @_@ | | - | | |-----| | - | Well... | | - | I'm programming. | | - |<------------------------------| | - | | | - | How about you? | | - |------------------------------------------>| - | | | - | I'm reading book. | - |<------------------------------------------| - | | | - | Good boy! | | - |------------------------------------------>| - | | | ------\ - | | |-| ^_^ | - | | | |-----| - | | | -``` - - -项目地址: -[text-diagram](https://github.com/weidagang/text-diagram) - - diff --git a/_posts/2015/2015-09-09-win10-skills.md b/_posts/2015/2015-09-09-win10-skills.md deleted file mode 100644 index 972d0f9eb9..0000000000 --- a/_posts/2015/2015-09-09-win10-skills.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -layout: post -title: win10使用技巧 -category: other -tags: [other] ---- - -换了win10感觉还挺好玩的,记录一下使用方式 - -## 1、切换桌面快捷键 -使用win10的最大动力之一就是可以方便的时候多桌面来办公,因为我经常打开太多的应用导致下面的工具栏占满,使用多桌面的话,就可以根据不同的使用需求来划分,比如查询类的应用一个桌面,开发类的应用放到一个桌面。切换桌面的方式也很方便。 - -> - win + tab 键 可以方便的浏览整个桌面的应用,利用鼠标切换桌面 -> - win + ctrl + ->/<-(左右键) 可以非常直接方便的切换桌面 - - -## 2、分桌面操作 -因为经常需要看这这个页面的数据录入另一个页面的数据,自己有时候拖来拖去很麻烦而且还经常托不到位,使用win10的时候拖住应用往左侧或则右侧中间位置一碰有弹框的效果,这个时候放开就会自动的占据半个桌面,其它应用会自动缩小的另一侧选择需要对比的界面就可以 - - -## 3、如何把一个应用从A桌面切换到B桌面 -首先使用tab键进入缩略图,选择应用直接拖到另一个做梦的缩率图既可以。 diff --git a/_posts/2015/2015-10-10-gradle-summary.md b/_posts/2015/2015-10-10-gradle-summary.md deleted file mode 100644 index 4c325b5ab9..0000000000 --- a/_posts/2015/2015-10-10-gradle-summary.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -layout: post -title: gradle-使用总结 -category: other -tags: [other] ---- - -spring 项目建议使用Gradle进行构建项目,相比maven来讲Gradle更简洁,而且gradle更时候大型复杂项目的构建。gradle吸收了maven和ant的特点而来,不过目前maven仍然是Java界的主流 - - -### 命令 -1、gradle build 编译 - - -#### Execution failed for task ':compileJava'. -> 无效的源发行版: 1.8 - -报错不匹配的Java编译版本 - -使用```gradle -version``` 查看gradle支持的jdk版本号,如果不是1.8,修改本地的Java_home环境变量为1.8,在次执行OK。 - - -### * What went wrong: -Execution failed for task ':findMainClass'. -> Unable to find a single main class from the following candidates [**] - -报错没有找到主入口 - -build.gradle 添加 - -``` properties -springBoot { - mainClass = "com.favorites.Application" -} -``` - diff --git a/_posts/2015/2015-10-18-markdown-summary.md b/_posts/2015/2015-10-18-markdown-summary.md deleted file mode 100644 index f094caee91..0000000000 --- a/_posts/2015/2015-10-18-markdown-summary.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -layout: post -title: markdown 使用总结 -category: other -tags: [other] ---- - -markdown来写文章,有一些命令或者是快捷键不是特别熟悉在此记录下来,方便日后查看 - -## Markdown介绍 -Markdown 是一种轻量级标记语言,创始人为约翰·格鲁伯(John Gruber)。它允许人们“使用易读易写的纯文本格式编写文档,然后转换成有效的XHTML(或者HTML)文档”。这种语言吸收了很多在电子邮件中已有的纯文本标记的特性。 - -## 常用语法 -### 标题 -``` xml -# 一级标题 -## 二级标题 -### 三级标题 -#### 四级标题 -##### 五级标题 -###### 六级标题 -``` -注:# 和「一级标题」之间建议保留一个字符的空格,这是最标准的 Markdown 写法。 - -### 列表 - -列表格式也很常用,在 Markdown 中,你只需要在文字前面加上 - 就可以了,例如: -``` xml -- 文本1 -- 文本2 -- 文本3 -``` -如果你希望有序列表,也可以在文字前面加上 1. 2. 3. 就可以了,例如: -``` xml -1. 文本1 -2. 文本2 -3. 文本3 -``` -注:-、1.和文本之间要保留一个字符的空格。 - -### 链接和图片 -在 Markdown 中,插入链接不需要其他按钮,你只需要使用 ``` xml[显示文本](链接地址) ``` 这样的语法即可,例如: -``` xml -[Deadpool](http://img31.mtime.cn/mg/2016/02/05/145836.38850143_210X210X4.jpg) -``` -在 Markdown 中,插入图片不需要其他按钮,你只需要使用 ``` ![](图片链接地址) ```这样的语法即可,例如: -``` xml -![](http://img31.mtime.cn/mg/2016/02/05/145836.38850143_210X210X4.jpg) -``` -注:插入图片的语法和链接的语法很像,只是前面多了一个 !。 - -效果如下:
-[Deadpool](http://img31.mtime.cn/mg/2016/02/05/145836.38850143_210X210X4.jpg)
-![](http://img31.mtime.cn/mg/2016/02/05/145836.38850143_210X210X4.jpg) - -### 引用 -在我们写作的时候经常需要引用他人的文字,这个时候引用这个格式就很有必要了,在 Markdown 中,你只需要在你希望引用的文字前面加上``` > ``` 就好了,例如: - -> markdown 引用 - -注:> 和文本之间要保留一个字符的空格。 - -### 粗体和斜体 -Markdown 的粗体和斜体也非常简单,用两个``` *``` 包含一段文本就是粗体的语法,用一个``` * ``` 包含一段文本就是斜体的语法。例如: -``` xml - *一盏灯*, 一片昏黄;**一简书**, 一杯淡茶。 守着那一份淡定, 品读属于自己的寂寞。 保持淡定, 才能欣赏到最美丽 -``` -最终显示的就是下文,其中「一盏灯」是斜体,「一简书」是粗体: - -### 表格 -相关代码: -``` xml -| Tables | Are | Cool | -| ------------- |:-------------:| -----:| -| col 3 is | right-aligned | $1600 | -| col 2 is | centered | $12 | -| zebra stripes | are neat | $1 | -``` -显示效果: - -| Tables | Are | Cool | -| ------------- |:-------------:| -----:| -| col 3 is | right-aligned | $1600 | -| col 2 is | centered | $12 | -| zebra stripes | are neat | $1 | - - -### 代码高亮 - -``` xml - 使用``` xxx 开头 ,```结尾的代码块; xxx 代表了语言,比如:Java、xml、js等; - 示例: - *``` java - this is java code space - *``` -``` -效果如下: -``` java - this is java code space -``` \ No newline at end of file diff --git a/_posts/2015/2015-11-12-sublime3-summary.md b/_posts/2015/2015-11-12-sublime3-summary.md deleted file mode 100644 index 4e658a2f4a..0000000000 --- a/_posts/2015/2015-11-12-sublime3-summary.md +++ /dev/null @@ -1,136 +0,0 @@ ---- -layout: post -title: sublime3 使用总结 -category: other -tags: [other] ---- - -Sublime 刚刚使用有一段时间了,感觉可以用惊艳来形容这款工具。对于使用了6年的eclipse的我,sublime的启动、反应速度就是火箭,当然eclipse也是一款优秀的编程工具,可以调优,但从最根本角度来讲:不在同一个视角。因为刚刚使用也不是很熟悉,因此在这里总结一些基础的用法和快捷键。 - -## Sublime Text介绍 -一款具有代码高亮、语法提示、自动完成且反应快速的编辑器软件,不仅具有华丽的界面,还支持插件扩展机制,用她来写代码,绝对是一种享受。相比于难于上手的Vim,浮肿沉重的Eclipse,VS,即便体积轻巧迅速启动的Editplus、Notepad++,在SublimeText面前大略显失色,无疑这款性感无比的编辑器是Coding和Writing最佳的选择,没有之一。 - -我使用sublime主要有 2 个作用: - -* 编程 -* 写作 - -## 快捷键 - -### 新建文件 - > 1. Ctrl+N:新建窗口 - > 2. 输入文件名 - > 3. Ctrl+S:保存 - -### 列编辑 - > 1. Ctrl+A 全选 - > 2. Ctrl+Shift+L 进入列选模式 - > 3. 使用方向键左右移动所有列的光标,并配合使用Shift键来多选每行的字符 - -### 快速查找 -> 1. Goto Anything功能 — 快速查找(ctrl + P) -> 2. 输入@+函数名可以快速找到函数。 -> 3. 输入#+文本可以快速进行文件内文本匹配。 - -### 快速跳转到某一行 -> 按下Ctrl + G,输入行号,可以快速跳转到该行。 - -### 注释 -> 1. Ctrl+/ 注释单行。 -> 2. Ctrl+Shift+/ 注释多行。 - -### 删除 -> 1. Ctrl+KK 从光标处删除至行尾 -> 2. Ctrl+Shift+K 删除整行 - -### 替换 -> Ctrl+Shift+F:查找并替换 -> Ctrl+H:替换 - - -## 插件介绍 - -### HTML-CSS-JS Prettify -一款集成了格式化(美化)html、css、js三种文件类型的插件,即便html,js写在PHP文件之内。插件依赖于nodejs,因此需要事先安装nodejs,然后才可以正常运行。插件安装完成后,快捷键ctrl+shift+H完成当前文件的美化操作。 - -### SublimeTmpl 快速生成文件模板 -一直都很奇怪为什么sublime text 3没有新建文件模板的功能,像html头部的DTD声明每次都要复制粘贴。用SublimeTmpl这款插件终于可以解脱了,SublimeTmpl能新建html、css、javascript、php、python、ruby六种类型的文件模板,所有的文件模板都在插件目录的templates文件夹里,可以自定义编辑文件模板。 - -> - SublimeTmpl默认的快捷键: -> - ctrl+alt+h html -> - ctrl+alt+j javascript -> - ctrl+alt+c css -> - ctrl+alt+p php -> - ctrl+alt+r ruby -> - ctrl+alt+shift+p python - -如果想要新建其他类型的文件模板的话,先自定义文件模板方在templates文件夹里,再分别打开Default (Windows).sublime-keymap、Default.sublime-commands、Main.sublime-menu、SublimeTmpl.sublime-settings这四个文件照着里面的格式自定义想要新建的类型 - -### sublime git -git 的各种操作 - -### Markdown Extended and Monokai Extended - -markdown 代码高亮插件,两者配合使用效果最佳。 - -### MarkDown Editing -在 Sublime 中编写 Markdown 还有一个直观的不适就是缺少辅助提示,比如输入 *,编辑器应当自动补上一个 *,并使光标保持在两 * 之间,又比如应当支持选中一段文字快捷键添加链接。 - -> - ctrl + win + R - 插入链接; -> - ctrl + win + V - 粘贴为链接格式; -> - Shift + win + K - 插入图片。 - - -### OmniMarkupPreviewer -作为 Sublime Text 的一款强大插件,支持将标记语言渲染为 HTML 并在浏览器上实时预览,同时支持导出 HTML 源码文件。插件安装成功后我们就可以使用快捷键对编辑的markdown源文件进行预览了。 -下面是几个常用快捷键. - -> - Command +Option +O: 在浏览器中预览 -> - Command+Option+X: 导出HTML -> - Ctrl+Alt+C: HTML标记拷贝至剪贴板 - -### WordCount -字数统计的一款小插件 - -### IMESupport - -sublime text 有个BUG,那就是不支持中文的鼠标跟随,这个插件就是干这个事情的 - -## 如何安装插件? - -安装插件之前,我们需要首先安装一个Sublime 中最不可缺少的插件 Package Control, 以后我们安装和管理插件都需要这个插件的帮助。 - -安装"Package Control" -使用快捷键 " ctrl + `" 打开Sublime的控制台 ,或者选择 View > Show Console 。 - -在控制台的命令行输入框,把下面一段代码粘贴进去,回车 就可以完成Pacakge Control 的安装了。 - -``` xml -import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf), 'wb').write(urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ','%20')).read()) -``` - -### 插件的安装 -Package Control 安装成功后我们就可以使用它方便的管理插件了,首先使用快捷键 'command + shift + p ' 进入到Sublime 命令面板,输入 "package install" 从列表中选择 "install Package" 然后回车。在弹出的框中输入我们想要安装的插件名称,回车就自动安装了 - - -### 输入法光标不跟随解决方案 - -1.手动安装:在GitHub页面下载该项目的ZIP包,解压出来将文件夹名称改为IMESupport,放到Sublime Text的插件目录重启软件即可。插件目录可通过菜单->preferences->packages来打开。 - -2.通过Package Control在线安装:安装Package Control插件(安装方法请自行搜索),通过Install Package选项列出插件列表,搜索IMESupport安装即可。 - -插件名称:IMESupport -GitHub页面:https://github.com/chikatoike/IMESupport - -## mac下的快捷键 - -1、替换 - -> Alt+Command+F - - - - - - - diff --git a/_posts/2015/2015-12-01-mac-operate.md b/_posts/2015/2015-12-01-mac-operate.md deleted file mode 100644 index 5523104499..0000000000 --- a/_posts/2015/2015-12-01-mac-operate.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -layout: post -title: mac 常用操作 -category: other -tags: [other] ---- - -## 1、shell命令下载 - -linux终端上面 执行sz操作后,文件目录 - -``` shell -/Users/neo/Documents/ -``` - -## 2、如何强制退出Mac 应用 - -1)从苹果() 菜单中选取“强制退出”,或按下Command-Option-Esc。 这类似于在PC 上按下Control-Alt-Delete。 - -2)在“强制退出”窗口中,选择该应用,然后点按“强制退出”。 - -## 3、如何强制删除Mac应用 - -前往/go 前往“应用程序”,选择需要删除的应用 快捷键command+delete或者拖到垃圾箱 - - -## 4、mac安装不同版本jdk,已经命令切换到不同版本 - -[jdk下载地址](http://www.oracle.com/technetwork/java/javase/downloads/index.html) - -安装完之后,jkd 在 /Library/Java/JavaVirtualMachines 目录下 - -打开~/.bash_profile,没有的话创建 - -``` shell -vim ~/.bash_profile -``` - -``` shell -export JAVA_7_HOME=/Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home -export JAVA_8_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home - -alias jdk7="export JAVA_HOME=$JAVA_7_HOME" #编辑一个命令jdk8,输入则转至jdk1.7 -alias jdk8="export JAVA_HOME=$JAVA_8_HOME" #编辑一个命令jdk8,输入则转至jdk1.8 - -export JAVA_HOME=`/usr/libexec/java_home` #最后安装的版本,这样当自动更新时,始终指向最新版本 -``` - -验证 - -``` shell -#使配置文件生效 -. .bash_profile -jdk8 -java -version -jdk7 -java -version -``` - -执行jdk7就会切换到jdk7的环境,输入jdk8就会切入到jdk8的环境。 - - - - - diff --git a/_posts/2015/2015-12-30-springboot-collect.md b/_posts/2015/2015-12-30-springboot-collect.md deleted file mode 100644 index 0d3c06ba74..0000000000 --- a/_posts/2015/2015-12-30-springboot-collect.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -layout: post -title: Spring Boot 学习资料汇总 -category: springboot -tags: [springboot] ---- - -收集 Spring Boot 相关的学习资料,[Spring Cloud点这里](http://www.ityouknow.com/springcloud/2016/12/30/springcloud-collect.html) - - -**重点推荐:[Spring Boot 中文索引](http://springboot.fun/)** - -## 推荐博客 - -- [纯洁的微笑-Spring Boot系列文章](http://www.ityouknow.com/spring-boot.html) -- [林祥纤-从零开始学Spring Boot](http://412887952-qq-com.iteye.com/category/356333) -- [Mkyong-Spring Boot教程(国外)](http://www.mkyong.com/tutorials/spring-boot-tutorials/) -- [baeldung-Spring Boot教程(国外)](https://www.baeldung.com/spring-boot) -- [liaokailin的专栏-Spring Boot实战](http://blog.csdn.net/liaokailin/article/category/5765237) -- [catoop的专栏-Spring Boot 学习](http://blog.csdn.net/column/details/spring-boot.html) -- [方志朋-SpringBoot 非官方教程](http://blog.csdn.net/column/details/15397.html) -- [嘟嘟-Spring-Boot干货系列](http://tengj.top/categories/Spring-Boot%E5%B9%B2%E8%B4%A7%E7%B3%BB%E5%88%97/) -- [小柒-SpringBoot开发案例](https://blog.52itstyle.com/category/springBoot/) -- [江南一点雨-关于Spring Boot](http://blog.csdn.net/column/details/13987.html) -- [天码营-Spring Boot](https://www.tianmaying.com/tutorials/tag/Springboot) -- [猿天地-Spring Boot](http://cxytiandi.com/blog/detail/17437) -- [刘冬的博客-Spring Boot](http://www.cnblogs.com/GoodHelper/tag/spring%20boot/default.html) -- [唐亚峰 Battcn-Spring Boot](https://blog.battcn.com/categories/SpringBoot/) -- [sylvanassun-Spring Boot](https://sylvanassun.github.io/categories/%E5%90%8E%E7%AB%AF/Java/Spring-Boot/) -- [dalaoyang-Spring Boot](https://www.dalaoyang.cn/tag/springboot/) - -## 开源 - -- [纯洁的微笑 Spring Boot 示例](https://github.com/ityouknow/spring-boot-examples) -- [Spring Boot 官方示例](https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples) -- [Spring Boot开源软件 云收藏](https://github.com/cloudfavorites/favorites-web) -- [Docker+SpringBoot+Mybatis+thymeleaf等技术实现的Java博客系统](https://github.com/ZHENFENG13/My-Blog) -- [Spring boot & Shiro 权限管理系统](https://github.com/wuyouzhuguli/FEBS) -- [Spring Boot实现支付服务:支付宝,微信...](https://gitee.com/52itstyle/spring-boot-pay) -- [Spring Boot后台商城 h5 小程序](https://gitee.com/JiaGou-XiaoGe/webappchat) -- [基于Spring Boot响应式文件浏览管理器](https://gitee.com/alexyang/spring-boot-filemanager) -- [Spring Boot开源博客](https://github.com/Raysmond/SpringBlog) -- [邮件发送服务多种实现,队列,线程定时任务](https://gitee.com/52itstyle/spring-boot-mail) -- [Spring Boot视频展示项目](https://github.com/ChinaSilence/any-video) -- [Spring Boot项目实践总结](https://github.com/timebusker/spring-boot) -- [Vue+SpringBoot实现的多用户博客管理平台](https://github.com/lenve/VBlog) -- [Vue+SpringBoot实现的人力资源管理系统](https://github.com/lenve/vhr) -- [hsweb企业后台管理系统基础框架](https://github.com/hs-web/hsweb-framework) -- [一个基于spring boot 实现的股票指数💹爬虫](https://github.com/kingschan1204/istock) -- [KKFileView-SpringBoot实现在线预览](https://gitee.com/kekingcn/file-online-preview) -- [boot-websocket-log-SpringBoot实现日志WEB输出](https://gitee.com/kailing/boot-websocket-log) -- [SpringBoot+MyBatis+A pacheShiro+Ehcahe基础平台](https://gitee.com/lcg0124/bootdo) -- [leelance Spring Boot各种示例](https://github.com/leelance/spring-boot-all) -- [一个基于Spring Boot & MyBatis的种子项目,用于快速构建中小型API、RESTful API项目](https://github.com/lihengming/spring-boot-api-project-seed) -- [JWT (Json Web Token) with Spring Security and Spring Boot 2](https://github.com/szerhusenBC/jwt-spring-security-demo) -- [基于Spring-boot和bootstrap搭建的商城系统](https://github.com/vito16/shop) -- [Deployment scripts & config for Sock Shop](https://github.com/microservices-demo/microservices-demo) -- [Spring Boot 开源博客-DBlog](https://gitee.com/yadong.zhang/DBlog) -- [Spring Boot 实现的简易社区](https://github.com/ChinaLHR/JavaQuarkBBS) -- [springboot+shiro+jwt 开源项目](https://gitee.com/tomsun28/bootshiro) -- [Guns-基于SpringBoot的后台管理系统](https://github.com/stylefeng/Guns) -- [halo-基于SpringBoot的博客系统](https://github.com/ruibaby/halo) -- [zhudyos/duic Distributed configuration center(分布式配置中心):new:](https://github.com/zhudyos/duic) -- [Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端](https://github.com/linlinjava/litemall) -- [mall-SpringBoot+MyBatis 电商系统](https://github.com/macrozheng/mall) -- [基于Spring Boot2.0微服务脚手架](https://github.com/Senssic/sc-whorl) - - -## 网站 - -- [云收藏](http://favorites.ren/lookAround) -- [Spring boot 官网](http://projects.spring.io/spring-boot/) -- [Spring Boot 参考指南- 英文版](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/) -- [网易云课堂Spring Boot视频](http://study.163.com/courses-search?keyword=Spring%20Boot) -- [慕课网Spring Boot视频](https://www.imooc.com/search/?words=spring%20boot) -- [开源书籍-微服务:从设计到部署](https://github.com/oopsguy/microservices-from-design-to-deployment-chinese) - - -**欢迎大家推荐更多的资料** \ No newline at end of file diff --git a/_posts/2016/2016-01-06-spring-boot-quick-start.md b/_posts/2016/2016-01-06-spring-boot-quick-start.md deleted file mode 100644 index c2ae2cd77a..0000000000 --- a/_posts/2016/2016-01-06-spring-boot-quick-start.md +++ /dev/null @@ -1,196 +0,0 @@ ---- -layout: post -title: Spring Boot(一):入门篇 -copyright: java -category: springboot -tags: [springboot] ---- - -## 什么是 Spring Boot - -Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。用我的话来理解,就是 Spring Boot 其实不是什么新的框架,它默认配置了很多框架的使用方式,就像 Maven 整合了所有的 Jar 包,Spring Boot 整合了所有的框架。 - - -## 使用 Spring Boot 有什么好处 - -其实就是简单、快速、方便!平时如果我们需要搭建一个 Spring Web 项目的时候需要怎么做呢? - -- 1)配置 web.xml,加载 Spring 和 Spring mvc -- 2)配置数据库连接、配置 Spring 事务 -- 3)配置加载配置文件的读取,开启注解 -- 4)配置日志文件 -- ... -- 配置完成之后部署 Tomcat 调试 -- ... - -现在非常流行微服务,如果我这个项目仅仅只是需要发送一个邮件,如果我的项目仅仅是生产一个积分;我都需要这样折腾一遍! - - -但是如果使用 Spring Boot 呢? -很简单,我仅仅只需要非常少的几个配置就可以迅速方便的搭建起来一套 Web 项目或者是构建一个微服务! - -使用 Spring Boot 到底有多爽,用下面这幅图来表达 - -![](http://favorites.ren/assets/images/2016/dog.jpg) - -## 快速入门 - -说了那么多,手痒痒的很,马上来一发试试! - -**Maven 构建项目** - -- 1、访问 http://start.spring.io/ -- 2、选择构建工具 Maven Project、Java、Spring Boot 版本 2.1.3 以及一些工程基本信息,可参考下图所示: - -![](http://favorites.ren/assets/images/2019/springboot/spring-boot-start.png) - -- 3、点击 Generate Project 下载项目压缩包 -- 4、解压后,使用 Idea 导入项目,File -> New -> Model from Existing Source.. -> 选择解压后的文件夹 -> OK,选择 Maven 一路 Next,OK done! -- 5、如果使用的是 Eclipse,Import -> Existing Maven Projects -> Next -> 选择解压后的文件夹 -> Finsh,OK done! - -**Idea 构建项目** - -- 1、选择 File -> New —> Project... 弹出新建项目的框 -- 2、选择 Spring Initializr,Next 也会出现上述类似的配置界面,Idea 帮我们做了集成 -- 3、填写相关内容后,点击 Next 选择依赖的包再点击 Next,最后确定信息无误点击 Finish。 - -**项目结构介绍** - -![](http://favorites.ren/assets/images/2016/springboot2.png) - - -如上图所示,Spring Boot 的基础结构共三个文件: -- `src/main/java` 程序开发以及主程序入口 -- `src/main/resources` 配置文件 -- `src/test/java` 测试程序 - -另外, Spring Boot 建议的目录结果如下: -root package 结构:`com.example.myproject` - -``` java -com - +- example - +- myproject - +- Application.java - | - +- model - | +- Customer.java - | +- CustomerRepository.java - | - +- service - | +- CustomerService.java - | - +- controller - | +- CustomerController.java - | -``` - -- 1、Application.java 建议放到根目录下面,主要用于做一些框架配置 -- 2、model 目录主要用于实体与数据访问层(Repository) -- 3、service 层主要是业务类代码 -- 4、controller 负责页面访问控制 - -采用默认配置可以省去很多配置,当然也可以根据自己的喜欢来进行更改 -最后,启动 Application main 方法,至此一个 Java 项目搭建好了! - - -**引入 web 模块** - -1、pom.xml中添加支持web的模块: - -``` xml - - org.springframework.boot - spring-boot-starter-web - -``` - -pom.xml 文件中默认有两个模块: - -- `spring-boot-starter` :核心模块,包括自动配置支持、日志和 YAML,如果引入了 `spring-boot-starter-web` web 模块可以去掉此配置,因为 `spring-boot-starter-web` 自动依赖了 `spring-boot-starter`。 -- `spring-boot-starter-test` :测试模块,包括 JUnit、Hamcrest、Mockito。 - -2、编写 Controller 内容: - -``` java -@RestController -public class HelloWorldController { - @RequestMapping("/hello") - public String index() { - return "Hello World"; - } -} -``` - -`@RestController` 的意思就是 Controller 里面的方法都以 json 格式输出,不用再写什么 jackjson 配置的了! - -3、启动主程序,打开浏览器访问 `http://localhost:8080/hello`,就可以看到效果了,有木有很简单! - - -**如何做单元测试** - -打开的`src/test/`下的测试入口,编写简单的 http 请求来测试;使用 mockmvc 进行,利用`MockMvcResultHandlers.print()`打印出执行结果。 - -``` java -@RunWith(SpringRunner.class) -@SpringBootTest -public class HelloTests { - - - private MockMvc mvc; - - @Before - public void setUp() throws Exception { - mvc = MockMvcBuilders.standaloneSetup(new HelloWorldController()).build(); - } - - @Test - public void getHello() throws Exception { - mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().string(equalTo("Hello World"))); - } - -} -``` - -**开发环境的调试** - -热启动在正常开发项目中已经很常见了吧,虽然平时开发web项目过程中,改动项目启重启总是报错;但springBoot对调试支持很好,修改之后可以实时生效,需要添加以下的配置: - -``` xml - - - org.springframework.boot - spring-boot-devtools - true - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - true - - - - -``` - -该模块在完整的打包环境下运行的时候会被禁用。如果你使用 `java -jar`启动应用或者用一个特定的 classloader 启动,它会认为这是一个“生产环境”。 - - -## 总结 - -使用 Spring Boot 可以非常方便、快速搭建项目,使我们不用关心框架之间的兼容性,适用版本等各种问题,我们想使用任何东西,仅仅添加一个配置就可以,所以使用 Spring Boot 非常适合构建微服务。 - - -> 文章内容已经升级到 Spring Boot 2.x - -**[示例代码-github](https://github.com/ityouknow/spring-boot-examples/tree/master/spring-boot-helloWorld)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-boot-examples/tree/master/spring-boot-helloWorld)** - diff --git a/_posts/2016/2016-02-03-spring-boot-web.md b/_posts/2016/2016-02-03-spring-boot-web.md deleted file mode 100644 index 5567a6ac68..0000000000 --- a/_posts/2016/2016-02-03-spring-boot-web.md +++ /dev/null @@ -1,415 +0,0 @@ ---- -layout: post -title: Spring Boot(二):Web 综合开发 -copyright: java -category: springboot -tags: [springboot] -lock: need ---- - -上篇文章介绍了 Spring Boot 初级教程:[Spring Boot(一):入门篇](http://www.ityouknow.com/springboot/2016/01/06/spring-boot-quick-start.html),方便大家快速入门、了解实践 Spring Boot 特性;本篇文章接着上篇内容继续为大家介绍 Spring Boot 的其它特性(有些未必是 Spring Boot 体系桟的功能,但是是 Spring 特别推荐的一些开源技术本文也会介绍),对了这里只是一个大概的介绍,特别详细的使用我们会在其它的文章中来展开说明。 - - -## Web 开发 - -Spring Boot Web 开发非常的简单,其中包括常用的 json 输出、filters、property、log 等 - -### json 接口开发 - -在以前使用 Spring 开发项目,需要提供 json 接口时需要做哪些配置呢 - - > 1. 添加 jackjson 等相关 jar 包 - > 2. 配置 Spring Controller 扫描 - > 3. 对接的方法添加 @ResponseBody - -就这样我们会经常由于配置错误,导致406错误等等,Spring Boot 如何做呢,只需要类添加 `@RestController` 即可,默认类中的方法都会以 json 的格式返回 - -``` java -@RestController -public class HelloController { - @RequestMapping("/getUser") - public User getUser() { - User user=new User(); - user.setUserName("小明"); - user.setPassWord("xxxx"); - return user; - } -} -``` - -如果需要使用页面开发只要使用`@Controller`注解即可,下面会结合模板来说明 - -### 自定义 Filter - -我们常常在项目中会使用 filters 用于录调用日志、排除有 XSS 威胁的字符、执行权限验证等等。Spring Boot 自动添加了 OrderedCharacterEncodingFilter 和 HiddenHttpMethodFilter,并且我们可以自定义 Filter。 - -两个步骤: - - > 1. 实现 Filter 接口,实现 Filter 方法 - > 2. 添加`@Configuration` 注解,将自定义Filter加入过滤链 - -好吧,直接上代码 - -``` java -@Configuration -public class WebConfiguration { - @Bean - public RemoteIpFilter remoteIpFilter() { - return new RemoteIpFilter(); - } - - @Bean - public FilterRegistrationBean testFilterRegistration() { - - FilterRegistrationBean registration = new FilterRegistrationBean(); - registration.setFilter(new MyFilter()); - registration.addUrlPatterns("/*"); - registration.addInitParameter("paramName", "paramValue"); - registration.setName("MyFilter"); - registration.setOrder(1); - return registration; - } - - public class MyFilter implements Filter { - @Override - public void destroy() { - // TODO Auto-generated method stub - } - - @Override - public void doFilter(ServletRequest srequest, ServletResponse sresponse, FilterChain filterChain) - throws IOException, ServletException { - // TODO Auto-generated method stub - HttpServletRequest request = (HttpServletRequest) srequest; - System.out.println("this is MyFilter,url :"+request.getRequestURI()); - filterChain.doFilter(srequest, sresponse); - } - - @Override - public void init(FilterConfig arg0) throws ServletException { - // TODO Auto-generated method stub - } - } -} -``` - -### 自定义 Property - -在 Web 开发的过程中,我经常需要自定义一些配置文件,如何使用呢 - -### 配置在 application.properties 中 - -``` xml -com.neo.title=纯洁的微笑 -com.neo.description=分享生活和技术 -``` - -自定义配置类 - -``` java -@Component -public class NeoProperties { - @Value("${com.neo.title}") - private String title; - @Value("${com.neo.description}") - private String description; - - //省略getter settet方法 - - } - -``` - -### log配置 - -配置输出的地址和输出级别 - -``` properties -logging.path=/user/local/log -logging.level.com.favorites=DEBUG -logging.level.org.springframework.web=INFO -logging.level.org.hibernate=ERROR -``` - -path 为本机的 log 地址,`logging.level` 后面可以根据包路径配置不同资源的 log 级别 - - -## 数据库操作 - -在这里我重点讲述 Mysql、spring data jpa 的使用,其中 Mysql 就不用说了大家很熟悉。Jpa 是利用 Hibernate 生成各种自动化的 sql,如果只是简单的增删改查,基本上不用手写了,Spring 内部已经帮大家封装实现了。 - -下面简单介绍一下如何在 Spring Boot 中使用 - -### 1、添加相 jar 包 - -``` xml - - org.springframework.boot - spring-boot-starter-data-jpa - - - mysql - mysql-connector-java - -``` - -### 2、添加配置文件 - -``` properties -spring.datasource.url=jdbc:mysql://localhost:3306/test -spring.datasource.username=root -spring.datasource.password=root -spring.datasource.driver-class-name=com.mysql.jdbc.Driver - -spring.jpa.properties.hibernate.hbm2ddl.auto=update -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect -spring.jpa.show-sql= true -``` - -其实这个 hibernate.hbm2ddl.auto 参数的作用主要用于:自动创建|更新|验证数据库表结构,有四个值: - -> 1. create: 每次加载 hibernate 时都会删除上一次的生成的表,然后根据你的 model 类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。 -> 2. create-drop :每次加载 hibernate 时根据 model 类生成表,但是 sessionFactory 一关闭,表就自动删除。 -> 3. update:最常用的属性,第一次加载 hibernate 时根据 model 类会自动建立起表的结构(前提是先建立好数据库),以后加载 hibernate 时根据 model 类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等 应用第一次运行起来后才会。 -> 4. validate :每次加载 hibernate 时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。 - -`dialect` 主要是指定生成表名的存储引擎为 InnoDBD -`show-sql` 是否打印出自动生成的 SQL,方便调试的时候查看 - -### 3、添加实体类和 Dao - -``` java -@Entity -public class User implements Serializable { - - private static final long serialVersionUID = 1L; - @Id - @GeneratedValue - private Long id; - @Column(nullable = false, unique = true) - private String userName; - @Column(nullable = false) - private String passWord; - @Column(nullable = false, unique = true) - private String email; - @Column(nullable = true, unique = true) - private String nickName; - @Column(nullable = false) - private String regTime; - - //省略getter settet方法、构造方法 - -} -``` -dao 只要继承 JpaRepository 类就可以,几乎可以不用写方法,还有一个特别有尿性的功能非常赞,就是可以根据方法名来自动的生成 SQL,比如`findByUserName` 会自动生成一个以 `userName` 为参数的查询方法,比如 `findAlll` 自动会查询表里面的所有数据,比如自动分页等等。。 - -**Entity 中不映射成列的字段得加 @Transient 注解,不加注解也会映射成列** - - -``` java -public interface UserRepository extends JpaRepository { - User findByUserName(String userName); - User findByUserNameOrEmail(String username, String email); -} -``` - -### 4、测试 - -``` java -@RunWith(SpringJUnit4ClassRunner.class) -@SpringApplicationConfiguration(Application.class) -public class UserRepositoryTests { - - @Autowired - private UserRepository userRepository; - - @Test - public void test() throws Exception { - Date date = new Date(); - DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG); - String formattedDate = dateFormat.format(date); - - userRepository.save(new User("aa1", "aa@126.com", "aa", "aa123456",formattedDate)); - userRepository.save(new User("bb2", "bb@126.com", "bb", "bb123456",formattedDate)); - userRepository.save(new User("cc3", "cc@126.com", "cc", "cc123456",formattedDate)); - - Assert.assertEquals(9, userRepository.findAll().size()); - Assert.assertEquals("bb", userRepository.findByUserNameOrEmail("bb", "cc@126.com").getNickName()); - userRepository.delete(userRepository.findByUserName("aa1")); - } - -} -``` - -当让 Spring Data Jpa 还有很多功能,比如封装好的分页,可以自己定义 SQL,主从分离等等,这里就不详细讲了 - -## Thymeleaf 模板 - -Spring Boot 推荐使用 Thymeleaf 来代替 Jsp,Thymeleaf 模板到底是什么来头呢,让 Spring 大哥来推荐,下面我们来聊聊 - -### Thymeleaf 介绍 - -Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。类似 JSP,Velocity,FreeMaker 等,它也可以轻易的与 Spring MVC 等 Web 框架进行集成作为 Web 应用的模板引擎。与其它模板引擎相比,Thymeleaf 最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个 Web 应用。 - -好了,你们说了我们已经习惯使用了什么 Velocity,FreMaker,beetle之类的模版,那么到底好在哪里呢? - -比一比吧 - -Thymeleaf 是与众不同的,因为它使用了自然的模板技术。这意味着 Thymeleaf 的模板语法并不会破坏文档的结构,模板依旧是有效的XML文档。模板还可以用作工作原型,Thymeleaf 会在运行期替换掉静态值。Velocity 与 FreeMarke r则是连续的文本处理器。 -下面的代码示例分别使用 Velocity、FreeMarker 与 Thymeleaf 打印出一条消息: - -``` xml -Velocity:

$message

-FreeMarker:

${message}

-Thymeleaf:

Hello World!

-``` - -**注意,由于 Thymeleaf 使用了 XML DOM 解析器,因此它并不适合于处理大规模的 XML 文件。** - -### URL - -URL 在 Web 应用模板中占据着十分重要的地位,需要特别注意的是 Thymeleaf 对于 URL 的处理是通过语法 `@{...}` 来处理的。Thymeleaf 支持绝对路径 URL: - -``` html -Thymeleaf -``` - -### 条件求值 - -``` html -Login -``` - -### for循环 - -``` html - - Onions - 2.41 - yes - -``` - -就列出这几个吧 - -### 页面即原型 - -在 Web 开发过程中一个绕不开的话题就是前端工程师与后端工程师的协作,在传统 Java Web 开发过程中,前端工程师和后端工程师一样,也需要安装一套完整的开发环境,然后各类 Java IDE 中修改模板、静态资源文件,启动/重启/重新加载应用服务器,刷新页面查看最终效果。 - -但实际上前端工程师的职责更多应该关注于页面本身而非后端,使用 JSP,Velocity 等传统的 Java 模板引擎很难做到这一点,因为它们必须在应用服务器中渲染完成后才能在浏览器中看到结果,而 Thymeleaf 从根本上颠覆了这一过程,通过属性进行模板渲染不会引入任何新的浏览器不能识别的标签,例如 JSP 中的 ,不会在 Tag 内部写表达式。整个页面直接作为 HTML 文件用浏览器打开,几乎就可以看到最终的效果,这大大解放了前端工程师的生产力,它们的最终交付物就是纯的 HTML/CSS/JavaScript 文件。 - - -## Gradle 构建工具 - -Spring 项目建议使用 Maven/Gradle 进行构建项目,相比 Maven 来讲 Gradle 更简洁,而且 Gradle 更适合大型复杂项目的构建。Gradle 吸收了 Maven 和 Ant 的特点而来,不过目前 Maven 仍然是 Java 界的主流,大家可以先了解了解。 - -一个使用 Gradle 配置的项目 - -``` -buildscript { - repositories { - maven { url "http://repo.spring.io/libs-snapshot" } - mavenLocal() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.6.RELEASE") - } -} - -apply plugin: 'java' //添加 Java 插件, 表明这是一个 Java 项目 -apply plugin: 'spring-boot' //添加 Spring-boot支持 -apply plugin: 'war' //添加 War 插件, 可以导出 War 包 -apply plugin: 'eclipse' //添加 Eclipse 插件, 添加 Eclipse IDE 支持, Intellij Idea 为 "idea" - -war { - baseName = 'favorites' - version = '0.1.0' -} - -sourceCompatibility = 1.7 //最低兼容版本 JDK1.7 -targetCompatibility = 1.7 //目标兼容版本 JDK1.7 - -repositories { // Maven 仓库 - mavenLocal() //使用本地仓库 - mavenCentral() //使用中央仓库 - maven { url "http://repo.spring.io/libs-snapshot" } //使用远程仓库 -} - -dependencies { // 各种 依赖的jar包 - compile("org.springframework.boot:spring-boot-starter-web:1.3.6.RELEASE") - compile("org.springframework.boot:spring-boot-starter-thymeleaf:1.3.6.RELEASE") - compile("org.springframework.boot:spring-boot-starter-data-jpa:1.3.6.RELEASE") - compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.6' - compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.4' - compile("org.springframework.boot:spring-boot-devtools:1.3.6.RELEASE") - compile("org.springframework.boot:spring-boot-starter-test:1.3.6.RELEASE") - compile 'org.webjars.bower:bootstrap:3.3.6' - compile 'org.webjars.bower:jquery:2.2.4' - compile("org.webjars:vue:1.0.24") - compile 'org.webjars.bower:vue-resource:0.7.0' - -} - -bootRun { - addResources = true -} -``` - -## WebJars - -WebJars 是一个很神奇的东西,可以让大家以 Jar 包的形式来使用前端的各种框架、组件。 - -### 什么是 WebJars - -WebJars 是将客户端(浏览器)资源(JavaScript,Css等)打成 Jar 包文件,以对资源进行统一依赖管理。WebJars 的 Jar 包部署在 Maven 中央仓库上。 - -### 为什么使用 - -我们在开发 Java web 项目的时候会使用像 Maven,Gradle 等构建工具以实现对 Jar 包版本依赖管理,以及项目的自动化管理,但是对于 JavaScript,Css 等前端资源包,我们只能采用拷贝到 webapp 下的方式,这样做就无法对这些资源进行依赖管理。那么 WebJars 就提供给我们这些前端资源的 Jar 包形势,我们就可以进行依赖管理。 - - -### 如何使用 - -1、 [WebJars主官网](http://www.webjars.org/bower) 查找对于的组件,比如 Vuejs - -``` xml - - org.webjars - vue - 2.5.16 - -``` - -2、页面引入 - -``` html - -``` - -就可以正常使用了! - -**[示例代码-github](https://github.com/ityouknow/spring-boot-examples/tree/master/spring-boot-web)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-boot-examples/tree/master/spring-boot-web)** - -> 文章内容已经升级到 Spring Boot 2.x - -## 参考: - -[新一代Java模板引擎Thymeleaf](http://www.tianmaying.com/tutorial/using-thymeleaf) - -[Spring Boot参考指南-中文版](https://qbgbook.gitbooks.io/spring-boot-reference-guide-zh/content/) - - - - - - - - - - - - - - diff --git a/_posts/2016/2016-03-06-spring-boot-redis.md b/_posts/2016/2016-03-06-spring-boot-redis.md deleted file mode 100644 index 75c07b6072..0000000000 --- a/_posts/2016/2016-03-06-spring-boot-redis.md +++ /dev/null @@ -1,215 +0,0 @@ ---- -layout: post -title: Spring Boot(三):Spring Boot 中 Redis 的使用 -copyright: java -category: springboot -tags: [springboot] ---- - -Spring Boot 对常用的数据库支持外,对 Nosql 数据库也进行了封装自动化。 - -## Redis 介绍 - -Redis 是目前业界使用最广泛的内存数据存储。相比 Memcached,Redis 支持更丰富的数据结构,例如 hashes, lists, sets 等,同时支持数据持久化。除此之外,Redis 还提供一些类数据库的特性,比如事务,HA,主从库。可以说 Redis 兼具了缓存系统和数据库的一些特性,因此有着丰富的应用场景。本文介绍 Redis 在 Spring Boot 中两个典型的应用场景。 - -## 如何使用 - -1、引入依赖包 - -``` xml - - org.springframework.boot - spring-boot-starter-data-redis - - - org.apache.commons - commons-pool2 - -``` - -Spring Boot 提供了对 Redis 集成的组件包:`spring-boot-starter-data-redis`,`spring-boot-starter-data-redis`依赖于`spring-data-redis` 和 `lettuce` 。Spring Boot 1.0 默认使用的是 Jedis 客户端,2.0 替换成 Lettuce,但如果你从 Spring Boot 1.5.X 切换过来,几乎感受不大差异,这是因为 `spring-boot-starter-data-redis` 为我们隔离了其中的差异性。 - -Lettuce 是一个可伸缩线程安全的 Redis 客户端,多个线程可以共享同一个 RedisConnection,它利用优秀 netty NIO 框架来高效地管理多个连接。 - -2、添加配置文件 - -``` properties -# Redis数据库索引(默认为0) -spring.redis.database=0 -# Redis服务器地址 -spring.redis.host=localhost -# Redis服务器连接端口 -spring.redis.port=6379 -# Redis服务器连接密码(默认为空) -spring.redis.password= -# 连接池最大连接数(使用负值表示没有限制) 默认 8 -spring.redis.lettuce.pool.max-active=8 -# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 -spring.redis.lettuce.pool.max-wait=-1 -# 连接池中的最大空闲连接 默认 8 -spring.redis.lettuce.pool.max-idle=8 -# 连接池中的最小空闲连接 默认 0 -spring.redis.lettuce.pool.min-idle=0 -``` - -3、添加 cache 的配置类 - -``` java -@Configuration -@EnableCaching -public class RedisConfig extends CachingConfigurerSupport{ - - @Bean - public KeyGenerator keyGenerator() { - return new KeyGenerator() { - @Override - public Object generate(Object target, Method method, Object... params) { - StringBuilder sb = new StringBuilder(); - sb.append(target.getClass().getName()); - sb.append(method.getName()); - for (Object obj : params) { - sb.append(obj.toString()); - } - return sb.toString(); - } - }; - } -} -``` - -注意我们使用了注解:`@EnableCaching`来开启缓存。 - - -3、好了,接下来就可以直接使用了 - -``` java -@RunWith(SpringRunner.class) -@SpringBootTest -public class TestRedis { - @Autowired - private StringRedisTemplate stringRedisTemplate; - @Autowired - private RedisTemplate redisTemplate; - - @Test - public void test() throws Exception { - stringRedisTemplate.opsForValue().set("aaa", "111"); - Assert.assertEquals("111", stringRedisTemplate.opsForValue().get("aaa")); - } - - @Test - public void testObj() throws Exception { - User user=new User("aa@126.com", "aa", "aa123456", "aa","123"); - ValueOperations operations=redisTemplate.opsForValue(); - operations.set("com.neox", user); - operations.set("com.neo.f", user,1, TimeUnit.SECONDS); - Thread.sleep(1000); - //redisTemplate.delete("com.neo.f"); - boolean exists=redisTemplate.hasKey("com.neo.f"); - if(exists){ - System.out.println("exists is true"); - }else{ - System.out.println("exists is false"); - } - // Assert.assertEquals("aa", operations.get("com.neo.f").getUserName()); - } -} -``` - -以上都是手动使用的方式,如何在查找数据库的时候自动使用缓存呢,看下面; - -4、自动根据方法生成缓存 - -``` java -@RestController -public class UserController { - - @RequestMapping("/getUser") - @Cacheable(value="user-key") - public User getUser() { - User user=new User("aa@126.com", "aa", "aa123456", "aa","123"); - System.out.println("若下面没出现“无缓存的时候调用”字样且能打印出数据表示测试成功"); - return user; - } -} -``` - -其中 value 的值就是缓存到 Redis 中的 key - - -## 共享 Session - -分布式系统中,Session 共享有很多的解决方案,其中托管到缓存中应该是最常用的方案之一, - -### Spring Session 官方说明 - -Spring Session provides an API and implementations for managing a user’s session information. - -Spring Session 提供了一套创建和管理 Servlet HttpSession 的方案。Spring Session 提供了集群 Session(Clustered Sessions)功能,默认采用外置的 Redis 来存储 Session 数据,以此来解决 Session 共享的问题。 - -### 如何使用 - -1、引入依赖 - -``` xml - - org.springframework.session - spring-session-data-redis - -``` - -2、Session 配置: - -``` java -@Configuration -@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30) -public class SessionConfig { -} -``` - -> maxInactiveIntervalInSeconds: 设置 Session 失效时间,使用 Redis Session 之后,原 Spring Boot 的 server.session.timeout 属性不再生效。 - -好了,这样就配置好了,我们来测试一下 - - -3、测试 - -添加测试方法获取 sessionid - -``` java -@RequestMapping("/uid") -String uid(HttpSession session) { - UUID uid = (UUID) session.getAttribute("uid"); - if (uid == null) { - uid = UUID.randomUUID(); - } - session.setAttribute("uid", uid); - return session.getId(); -} -``` - -登录 Redis 输入 `keys '*sessions*'` - -``` -t 文章内容已经升级到 Spring Boot 2.x - -## 参考 - -[Redis的两个典型应用场景](http://emacoo.cn/blog/spring-redis) -[SpringBoot应用之分布式会话](https://segmentfault.com/a/1190000004358410) \ No newline at end of file diff --git a/_posts/2016/2016-05-01-spring-boot-thymeleaf.md b/_posts/2016/2016-05-01-spring-boot-thymeleaf.md deleted file mode 100644 index 8602270f5e..0000000000 --- a/_posts/2016/2016-05-01-spring-boot-thymeleaf.md +++ /dev/null @@ -1,490 +0,0 @@ ---- -layout: post -title: Spring Boot(四):Thymeleaf 使用详解 -copyright: java -category: springboot -tags: [thymeleaf] -lock: need ---- - -在上篇文章[Spring Boot (二):Web 综合开发](http://www.ityouknow.com/springboot/2016/02/03/spring-boot-web.html)中简单介绍了一下 Thymeleaf,这篇文章将更加全面详细的介绍 Thymeleaf 的使用。Thymeleaf 是新一代的模板引擎,在 Spring4.0 中推荐使用 Thymeleaf 来做前端模版引擎。 - -## Thymeleaf 介绍 - -简单说,Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点: - -- 1.Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 Thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。 -- 2.Thymeleaf 开箱即用的特性。它提供标准和 Spring 标准两种方言,可以直接套用模板实现 JSTL、 OGNL表达式效果,避免每天套模板、改 Jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。 -- 3.Thymeleaf 提供 Spring 标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。 - - -## 标准表达式语法 - -它们分为四类: - -- 1.变量表达式 -- 2.选择或星号表达式 -- 3.文字国际化表达式 -- 4.URL 表达式 - - -### 变量表达式 - -变量表达式即 OGNL 表达式或 Spring EL 表达式(在 Spring 术语中也叫 model attributes)。如下所示: - `${session.user.name}` - -它们将以HTML标签的一个属性来表示: - -``` html - -
  • -``` - -### 选择(星号)表达式 - -选择表达式很像变量表达式,不过它们用一个预先选择的对象来代替上下文变量容器(map)来执行,如下: -`*{customer.name}` - -被指定的 object 由 th:object 属性定义: - -``` html -
    - ... - ... - ... -
    -``` - -### 文字国际化表达式 - -文字国际化表达式允许我们从一个外部文件获取区域文字信息(.properties),用 Key 索引 Value,还可以提供一组参数(可选). - -``` html -#{main.title} -#{message.entrycreated(${entryId})} -``` - -可以在模板文件中找到这样的表达式代码: - -``` html - - ... - - - ... -
    ......
    -``` - -### URL 表达式 - -URL 表达式指的是把一个有用的上下文或回话信息添加到 URL,这个过程经常被叫做 URL 重写。 -`@{/order/list}` - -URL还可以设置参数: -`@{/order/details(id=${orderId})}` - -相对路径: -`@{../documents/report}` - -让我们看这些表达式: - -``` html -
    - -``` - -### 变量表达式和星号表达有什么区别吗? - -如果不考虑上下文的情况下,两者没有区别;星号语法评估在选定对象上表达,而不是整个上下文 -什么是选定对象?就是父标签的值,如下: - -``` html -
    -

    Name: Sebastian.

    -

    Surname: Pepper.

    -

    Nationality: Saturn.

    -
    -``` - -这是完全等价于: - -``` html -
    -

    Name: Sebastian.

    -

    Surname: Pepper.

    -

    Nationality: Saturn.

    -
    -``` - -当然,美元符号和星号语法可以混合使用: - -``` html -
    -

    Name: Sebastian.

    -

    Surname: Pepper.

    -

    Nationality: Saturn.

    -
    -``` - - -### 表达式支持的语法 - -#### 字面(Literals) - -- 文本文字(Text literals): `'one text', 'Another one!',…` -- 数字文本(Number literals): `0, 34, 3.0, 12.3,…` -- 布尔文本(Boolean literals):`true, false` -- 空(Null literal):`null` -- 文字标记(Literal tokens):`one, sometext, main,…` - -#### 文本操作(Text operations) - -- 字符串连接(String concatenation):`+` -- 文本替换(Literal substitutions):`|The name is ${name}|` - -#### 算术运算(Arithmetic operations) - -- 二元运算符(Binary operators):`+, -, *, /, %` -- 减号(单目运算符)Minus sign (unary operator):`-` - -#### 布尔操作(Boolean operations) - -- 二元运算符(Binary operators):`and, or` -- 布尔否定(一元运算符)Boolean negation (unary operator):`!, not` - -#### 比较和等价(Comparisons and equality) - -- 比较(Comparators):`>, <, >=, <= (gt, lt, ge, le)` -- 等值运算符(Equality operators):`==, != (eq, ne)` - -#### 条件运算符(Conditional operators) - -- If-then:`(if) ? (then)` -- If-then-else:`(if) ? (then) : (else)` -- Default: (value) ?:`(defaultvalue)` - -所有这些特征可以被组合并嵌套: - -``` html -'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown')) -``` - -## 常用th标签都有那些? - -关键字 | 功能介绍 | 案例 ---- |--- |--- -th:id | 替换id | `` -th:text|文本替换| `

    description

    ` -th:utext|支持html的文本替换| `

    conten

    ` -th:object |替换对象|`
    ` -th:value |属性赋值|` ` -th:with |变量赋值运算|`
    ` -th:style |设置样式|`th:style="'display:' + @{(${sitrue} ? 'none' : 'inline-block')} + ''" ` -th:onclick |点击事件|`th:onclick="'getCollect()'" ` -th:each |属性赋值|`tr th:each="user,userStat:${users}"> ` -th:if |判断条件|`
    ` -th:unless |和th:if判断相反|`Login ` -th:href |链接地址|`Login /> ` -th:switch |多路选择 配合th:case 使用|`
    ` -th:case |th:switch的一个分支| `

    User is an administrator

    ` -th:fragment |布局标签,定义一个代码片段,方便其它地方引用|`
    ` -th:include |布局标签,替换内容到引入的文件 |` /> ` -th:replace |布局标签,替换整个标签到引入的文件|`
    ` -th:selected |selected选择框 选中|`th:selected="(${xxx.id} == ${configObj.dd})"` -th:src|图片类地址引入|`App Logo ` -th:inline |定义js脚本可以使用变量|` -``` - -js 附加代码: - -``` -/*[+ -var msg = 'This is a working application'; -+]*/ -``` - -js 移除代码: - -``` -/*[- */ -var msg = 'This is a non-working template'; -/* -]*/ -``` - -### 6、内嵌变量 - -为了模板更加易用,Thymeleaf 还提供了一系列 Utility 对象(内置于 Context 中),可以通过 # 直接访问: - -- dates : *java.util.Date的功能方法类。* -- calendars : *类似#dates,面向java.util.Calendar* -- numbers : *格式化数字的功能方法类* -- strings : *字符串对象的功能类,contains,startWiths,prepending/appending等等。* -- objects: *对objects的功能类操作。* -- bools: *对布尔值求值的功能方法。* -- arrays:*对数组的功能类方法。* -- lists: *对lists功能类方法* -- sets -- maps - ... - - -下面用一段代码来举例一些常用的方法: - -#### dates - -``` html -/* - * Format date with the specified pattern - * Also works with arrays, lists or sets - */ -${#dates.format(date, 'dd/MMM/yyyy HH:mm')} -${#dates.arrayFormat(datesArray, 'dd/MMM/yyyy HH:mm')} -${#dates.listFormat(datesList, 'dd/MMM/yyyy HH:mm')} -${#dates.setFormat(datesSet, 'dd/MMM/yyyy HH:mm')} - -/* - * Create a date (java.util.Date) object for the current date and time - */ -${#dates.createNow()} - -/* - * Create a date (java.util.Date) object for the current date (time set to 00:00) - */ -${#dates.createToday()} -``` - -#### strings - -``` html -/* - * Check whether a String is empty (or null). Performs a trim() operation before check - * Also works with arrays, lists or sets - */ -${#strings.isEmpty(name)} -${#strings.arrayIsEmpty(nameArr)} -${#strings.listIsEmpty(nameList)} -${#strings.setIsEmpty(nameSet)} - -/* - * Check whether a String starts or ends with a fragment - * Also works with arrays, lists or sets - */ -${#strings.startsWith(name,'Don')} // also array*, list* and set* -${#strings.endsWith(name,endingFragment)} // also array*, list* and set* - -/* - * Compute length - * Also works with arrays, lists or sets - */ -${#strings.length(str)} - -/* - * Null-safe comparison and concatenation - */ -${#strings.equals(str)} -${#strings.equalsIgnoreCase(str)} -${#strings.concat(str)} -${#strings.concatReplaceNulls(str)} - -/* - * Random - */ -${#strings.randomAlphanumeric(count)} - -``` - -## 使用 Thymeleaf 布局 - -Spring Boot 2.0 将布局单独提取了出来,需要单独引入依赖:thymeleaf-layout-dialect。 - -``` - - org.springframework.boot - spring-boot-starter-thymeleaf - - - nz.net.ultraq.thymeleaf - thymeleaf-layout-dialect - -``` - -定义代码片段 - -``` html -
    -© 2019 -
    -``` - -在页面任何地方引入: - -``` html - -
    -
    - -``` - -th:insert 和 th:replace 区别,insert 只是加载,replace 是替换。Thymeleaf 3.0 推荐使用 th:insert 替换 2.0 的 th:replace。 - -返回的 HTML 如下: - -``` html - -
    © 2019
    -
    © 2019
    - -``` - -下面是一个常用的后台页面布局,将整个页面分为头部,尾部、菜单栏、隐藏栏,点击菜单只改变 content 区域的页面 - -``` html - - - -``` - -任何页面想使用这样的布局值只需要替换中见的 content 模块即可 - -``` - - -
    - ... -``` - -也可以在引用模版的时候传参 - -``` html - -``` - -layout 是文件地址,如果有文件夹可以这样写`fileName/layout:htmlhead`,htmlhead 是指定义的代码片段 如`th:fragment="copy"` - -**文章示例项目** - -**[示例代码-github](https://github.com/ityouknow/spring-boot-examples/tree/master/spring-boot-thymeleaf)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-boot-examples/tree/master/spring-boot-thymeleaf)** - -> 文章内容已经升级到 Spring Boot 2.x - -## 参考 - -[thymeleaf官方指南](http://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#integrating-thymeleaf-with-spring) -[新一代Java模板引擎Thymeleaf](http://www.tianmaying.com/tutorial/using-thymeleaf) -[Thymeleaf基本知识](http://www.webinno.cn/blog/article/view/131) -[thymeleaf总结文章](http://v8en.com/news/list/47/0) -[Thymeleaf 模板的使用](http://www.cnblogs.com/lazio10000/p/5603955.html) -[thymeleaf 学习笔记](http://www.blogjava.net/bjwulin/archive/2013/02/07/395234.html) diff --git a/_posts/2016/2016-05-08-programmer-finance.md b/_posts/2016/2016-05-08-programmer-finance.md deleted file mode 100644 index e14b5d3d3e..0000000000 --- a/_posts/2016/2016-05-08-programmer-finance.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -layout: post -title: 程序员该用哪种姿势来理财 -copyright: money -category: money -tags: [money] ---- - -其实一直想写一篇文章名字都想好了,叫做“程序员该不该理财?”。后来想了想,该不该这个就不用想了,必须要理财!那么市面上那么多理财的方式对于我们屌丝的程序员该如何选择呢? - -其实我也是那种土的掉咋的那种类型,以前几乎没有想过神马理财的,一来呢毕业的时候工资全都不够花的还理个毛线,二来总是感觉理财好像都是有钱人搞的东西;后来偶然进入了互联网金融行业,呆了几年,慢慢也接触了很多理财方式,但也还是一个门外汉,此文就是和大家一起聊聊我们程序员该如何去理财?算是抛砖引玉,欢迎拍砖。 - - -## 我的理财经历 -先聊聊我的理财经历,我的理财大概也是从毕业三年之后才慢慢开始的,对了也就是从余额宝开始,后来在第三方支付、互联网金融公司工作,对理财的种类了解慢慢多了起来,互联网金融也就是P2P或者金融公司产品各种变种(其实也是债权类产品的进一步封装),有活期产品、封闭期的固定收益产品(日、月、年类的固定收益);买了股票(先是A股,后来美股),在了解了基金(指数基金、债券基金、股票基金等等),后来也看了黄金和期货(了解较少)、保险接触最少。 - -一路慢慢走来,理财的这种想法和认识也是在不断的发展变化,不认识、不了解、不认同的一些偏见也在慢慢的消失,可以以更理性的角度去看待投资理财的这件事情,理财真的是我们每一个人都应该好好的学习的事情,如何让你辛辛苦苦的积蓄可以跑赢通货膨胀,不随着时间推移我们的资产不断缩水;但是在工作中常常发现我们的程序员对于理财目前来讲大多都还是比较保守,常常是全部都银行或者全部宝宝类产品,更有激进者全都投入股市,有一年损失十几万的。下面分几个阶段来聊聊我对理财的几个认识: - -### 宝宝时代 -说起了理财或者第三方支付,总是不能避开一家伟大的公司-支付宝,支付宝一直在引领、教育着我们这一代人对于理财的认识;我的理财起点就是从余额宝开始的,清晰的记得13年6月余额宝出来的时候,大家都感觉到稀奇或者不了解(当时仍然有很多人认为还是把钱放到银行保险,哪怕余额宝也是!),记得当初刚开始余额宝转进去了100元,每天看着它给带来的几分钱收益都很满足,然后就一发不扣收拾,几乎是前脚收到发工资的短信,下一分钟钱就同步到了余额宝里面,曾经有一段时间中国闹钱荒,导致余额宝的最高收益高达6.76; - -不过后来由于这块的蛋糕太大了,各大银行也都着急出了很多的政策来限制:第一步,开限额,导致大额资金无法进入;第二步,自己干,很多银行自己也推出了资金的货币基金理财产品和余额宝来竞争,理财通也横空出世,理财通搞了一个同卡进出的理念比较好,很大程度上对资金安全起了很大的作用。 - -### 互联网金融 -2014年算是互联网金融的元年,其实互联网金融在很早的时候就被引入了国内,之前一直是不温不火的发展着,突然到了2014年随着资本的大量进入催化了互联网金融的大发展。现在的互联网金融主要分为大的两个部分:p2p和众筹,p2p在中国发展进行非常多的变种,现在主要的产品有活期债权、封装成月或者年的理财产品,然后后面去打包,从债权方面又分为,企业和个人。我也是在2014年由于工作原因接触到了p2p,小做尝试了之后,发现完爆余额宝,几乎又是全部积蓄投入到p2p里面,当初算了一笔账,按照年化12的标的来计算的话,投入10万,一年的收益就是1.2万,投资1000万,那么每年不用工作就是120万!,轻轻松松的年收入破百万!对了没有一点税!! - -那时候各家平台又是送苹果手机、又是送电脑的各种活动玩的不亦乐乎,后来到了15年跑路了很多平台,慢慢的才把很大的一部分撤了出来。众筹也是在中国玩出了很多的花样,主要还是分为产品众筹和股权众筹,产品众筹主要是以高科技产品为主,但是现在很多新品发布会也搞上去了;股权众筹也就是拿出自己的钱去投资一些比较看好的小公司来占一部分原始股权,一般股权众筹都有一定的门槛,没有资金百万级别以上基本上不用参与了,风险也非常大不是咱这小市民可以玩的。 - -### 股票 -我是在第一次股灾快结束的时候进入的,15年股市正火爆的时候公司有很多的同事整天都在关注着股票,什么过了3000点了,这会进去就是炮灰了,然后大盘到了4000点,有同事又说,人民日报都刊登了文章,4000点只是一个起点,好吧,他们最后的结局都是什么,我就不说了大家也猜的到,那时候我们部门几乎有一半的程序员多多少少都在在A股开了户,活动的时候也是在讨论着各种的股票。 - -到了6月份以后我突然有了点时间,也就特别好奇股市为啥有这么大的吸引力,也试探性的在网上开了户,在网上找了费率最便宜的那种,好多同事都是在模拟炒股的软件上面模拟的先玩上一段时候再进入市场,但是我觉得模拟因为没有真正的资金在上面,感觉不到任何的刺激。于是乎刚开始搞了1000元的资金去试探。清楚的记得买的第一只股票是“苏宁云商”,然后迫不及待的每天看着它涨了还是跌了乐此不疲,买了没有多久“苏宁云商”就停牌了,发公告说阿里投资,重大利好!好多人都说股市新手的运气都会比较好,先给你一点甜头,让你先陷进去,然后慢慢在和你玩,后来“苏宁云商”开盘后连续三个涨停,赚了几百块钱。 - -我立刻对股市产生了极大的兴趣,``` 大家是不是又准备看一个A股小散被收割的故事:)``` 于是乎,追涨杀跌、专门看股票技术指标(成交量、KDJ、MACD ...)、请教一些玩股票的朋友推荐股票,分析股票。专门组建了一个讨论组,把喜欢炒股的几个IT的朋友都拉进去,大家讨论每天的行情,那是一个热闹!慢慢的发现很多科技股票买不了才发现创业板是需要专门去线下开通,好吧,迫不及待就去开通了创业板。短短的6个月买了很多股票,乐视、掌趣科技、东方财富、卫宁健康、华裔兄弟、万达院线、信息发展。。。。。多到我也不清楚到底买了多少,全部短线交易!资金也从最初的1000一直增加到最高50000,经历了股灾2.0、3.0,也经历了创业板最低的1800点、10月份的超级大反弹、今年的熔断。认识的两个朋友中,有一个在牛市的时候曾经一周每天盈利1万(这样还有什么心情上班),有一个在股灾中还么赔多少但是熔断没有躲过去,搭进去近一年的工资。 - -作为一个IT男,中国最优秀的互联网几乎都在美国上市,所以就一直了解如何去买美股,那时候“宜人贷”刚上纳斯达克破发了,"lending club 简称LC" 也跌到12美元左右,觉得是个机会。刚好积木出了一款产品叫做积木股票,就迫不及待的注册、开通海外汇款。进去之后就买了LC ,结果LC没多久就跌到8美元、7、6 ...。失望之极,刚退出没多久LC又出黑天鹅时间 ,直接从7左右跌到最低3.44美元!美股由于没有涨跌幅度的限制比A股要刺激很多,经常看到某一个股票一天涨40%、50%,但跌的时候也一样经常一下子一半市值就没了,我在美股的亏损几乎都来自LC。宜人贷是让我非常震撼的一只股票,因为从开盘一直跌到3美元左右,然后开始上升到现在达30美元左右,没有抓住非常可惜。现在手里拿的都是一些中概念股,京东、58、猎豹等。 - -### 基金 -一个人的精力总是有限,整天看股票真的很累还影响工作、有时候甚至影响心情还不一定赚钱。如何想让牛逼的人来替我们理财,这时候也就慢慢的了解到了基金,特别是在关注了微信公共账号的“股社区”学了很多的知识,目前也是刚刚了解一些。基金我了解主要是这几种:债卷基金,主要是买国家的债卷或者国企类型的公司债卷为主,风险度比较低,收益率也偏低一些,我目前就是定投的这类;股票基金,也就是找一个牛逼的基金经理帮我们炒股,盈利了大家分钱这种模式,风险也很高,也比较依赖于基金经理的个人水平。大盘类的基金,比如ETF300 之类,就是买主板最牛逼的股票各买一点,只要大盘涨,我们收益就会涨,大盘跌,我们就会亏损这种,短期看收益很难确定,长期来看肯定收益为正;也有一部分混合基金就是股票和债卷都买一些。 - -### 黄金 -2016年初的时候,各种报道黄金跌破1000美元就是底部,那时候是1100美元左右吧,感觉差不多了,就买了交通银行黄金延期(T+D)买了不到一万元,没几天亏损了一点就出来了没有坚持,如果坚持到现在那也赚了一些;有时候你想到和做到完全是两种情形。现在如果想投资黄金的话,直接买蚂蚁聚宝的“存金宝”,关键方便快捷。 - - - -## 我的投资经验与教训 - -结合我这三年多的投资理财教训,谈一些对投资的看法。 - -### 银行存款 -一直流行着一句话,穷人往银行里面存钱,富人从银行中贷款,可以很大程度上反应中国情况。如果你把积蓄都放到银行里面躺着,结合通货膨胀来讲,你就是在亏钱!而且银行的服务超级烂,办业务超级慢,因此建议银行卡里面一分钱也不要放! - -### 宝宝类产品 -可以把平时需要紧急用的钱和零花的钱都可以放到这里面,既可以享受一定的收益性,也可以灵活的存取、消费,建议以大平台余额宝、理财通为主,其它类的谨慎选择。 - -### 互联网金融 -**平台真的重要!** **平台真的重要!!** **平台真的重要!!!** 重要的事情说三遍,如果选择不好平台就是0和1的事情,投资的平台跑路了就一分钱都没有了,笔者就投资了一个跑路的公司e租宝和一个在跑路之前投资过的公司。我们经常说,**你贪恋的是人家的利息 ,人家贪恋的是你的本金。** 但回过头来看其实大多数跑路的公司都是骗子公司,几乎没有什么正经的互联网金融公司,只是打着互联网金融的旗号来行骗而已。P2P真的风险很高吗,其实笔者看来只要选择好平台,应该还是非常好的一种理财方式。那怎么选择平台呢,尽量选择评级靠前、国企或者上市公司背景的企业。 - -### 股市和基金 -股市是一个学问很深的东西,有人穷尽一生也不能领悟,主要看性格、价值观、人性的把握。大家要以我为戒,禁忌追涨杀跌、频繁买卖,要坚持价值投资,持续持用,追求长远的收益。股市也是一个风险度很高的投资,大家需要根据自己的风险偏好来选择投资,也可以适当的选择合适的基金产品来投资,一般股票配置比率应该和年龄成反比,越年轻就可以多占比你投资的一部分,最重要的是不要让股票影响了我们正常的生活和工作!一直想找一个既可以买A股、港股和美股的产品,一直没有发现很可惜。对于股市,要敬畏它、尊重它、理解它。 - -比如程序员来讲最酷的方法就是用 Python 写一些程序进行量化交易或者分析股票相关内容,我这里也给大家推荐一个:[股票分析(一):常见的技术指标雪球](http://blog.callmewhy.com/2016/02/27/stock-analyse-1/) - -### 黄金和保险 -和平时期买股票,战乱期间买黄金,这个要看每个的理解了,我了解也不是很多;保险是我了解最少的一部分了,总感觉我们这么年轻考虑保险干啥呢,但是这种保险的意识也需要慢慢的养成。 - -### 买房 -好多人评论说没有提到买房,是因为对于这个买房还真不太懂,但我感觉拐点也快到了,一线城市的房价很难说,但是三四线投资房产的必要性真的不太大了,而且买房建议一定买核心地段或者位置环境偏好的小区,贵一点不要紧,方便以后转手。 - -### 资产配置 -我目前的资金配置是,宝宝类产品1/4、p2p投资1/4、a股+美股3/8、基金1/8;大家可以根据各自风险承受度来选择自己的资产配比。 - -## 最后 -理财是我们每一个人必须要关注的事情,那怕我们现在并没有多少钱,经常看到一个广告说,投资是你人生最后的一个事业,随着年龄的增长投资理财对我们越来越重要。投资也是一件谨慎、严肃的事情毕竟关系着我们辛苦赚的血汗钱。 - -### 最后的最后 -**对于我们,投资自己更重要!** -**投资有风险,理财需谨慎 !** - - -*好多人私信想一起交流,我就建了一个QQ群,有兴趣的入群交流的同学请关注公众号:纯洁的微信,回复“理财”* - - -------------- - -**作者:纯洁的微笑** -**出处:[www.ityouknow.com](http://www.ityouknow.com)** -**版权归作者所有,转载请注明出处** - - - - - - - - - - - - - - - - - - - - diff --git a/_posts/2016/2016-06-10-Blog-With-Jekyll.md b/_posts/2016/2016-06-10-Blog-With-Jekyll.md deleted file mode 100644 index 7ca9cad75d..0000000000 --- a/_posts/2016/2016-06-10-Blog-With-Jekyll.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -layout: post -title: Blog With Jekyll -category: other -tags: [other] ---- - -## Create a blog site -For blogging, we have to specify a local folder as the root of your blog site with the commands below: - -~~~ - cd [your local folder] - jekyll new . -~~~ - -As one of the results of running 'Jekyll new .', the skeleton of your blog site with a sample blog is created in the specified folder. - -## Running your blog site -Theoretically Jekyll is all set and you're supposed to host your sample site with Jekyll management process right now. -Try the command below: - -~~~ -jekyll serve -~~~ - -If you are able to see similar messages as below, you are lucky: - -> Configuration file: C:/**/Blog/_config.yml - Source: C:/**/Blog - Destination: C:/**/Blog/_site -> Incremental build: disabled. Enable with --incremental - Generating... - done in 1.413 seconds. - Please add the following to your Gemfile to avoid polling for changes: - gem 'wdm', '>= 0.1.0' if Gem.win_platform? -> Auto-regeneration: enabled for 'C:/**/Blog' -> Configuration file: C:/**/Blog/_config.yml - Server address: http://127.0.0.1:4000/ - Server running... press ctrl-c to stop. - -it says your sample site is being served at : **http://127.0.0.1:4000/** -Check it with your browser: -![Sample Blog Site in Browser](/assets/images/2016/BrowsingSampleSite.PNG) - -However, I am not a lucky guy most of time :( -The 1st time I run `jekyll serve`, I got errors below: - -> C:/Ruby23/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- bundler (LoadError) - from C:/Ruby23/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require' - from C:/Ruby23/lib/ruby/gems/2.3.0/gems/jekyll-3.2.1/lib/jekyll/plugin_manager.rb:34:in `require_from_bundler' - from C:/Ruby23/lib/ruby/gems/2.3.0/gems/jekyll-3.2.1/exe/jekyll:9:in `' - from C:/Ruby23/bin/jekyll:23:in `load' - from C:/Ruby23/bin/jekyll:23:in `
    ' - -*Bundler* is a gem dependency of Jekyll which is supposed to be installed with Jekyll. Apparently, it's not. After installing *Bundler* with the command bellow, I got rid of above errors: - -~~~ - gem install bundler -~~~ - -But another dependency that is also supposed to be installed with Jekyll came out: - -> C:/Ruby23/lib/ruby/gems/2.3.0/gems/bundler-.12.5/lib/bundler/resolver.rb:356:in block in verify_gemfile_dependencies_are_found!: -> Could not find gem minima x86-mingw32 in any of the gem sources listed in your Gemfile or available on this machine.(Bundler::GemNotFound) -> from C:/Ruby23/lib/ruby/gems/2.3.0/gems/bundler-1.12.5/lib/bundler/resolver.rb:331:in each -> from C:/Ruby23/lib/ruby/gems/2.3.0/gems/bundler-1.12.5/lib/bundler/resolver.rb:331:in verify_gemfile_dependencies_are_found! -> from C:/Ruby23/lib/ruby/gems/2.3.0/gems/bundler-1.12.5/lib/bundler/resolver.rb:200:in start -> from C:/Ruby23/lib/ruby/gems/2.3.0/gems/bundler-1.12.5/lib/bundler/resolver.rb:184:in resolve -> from C:/Ruby23/lib/ruby/gems/2.3.0/gems/bundler-1.12.5/lib/bundler/definition.rb:200:in resolve -> from C:/Ruby23/lib/ruby/gems/2.3.0/gems/bundler-1.12.5/lib/bundler/definition.rb:140:in specs -> from C:/Ruby23/lib/ruby/gems/2.3.0/gems/bundler-1.12.5/lib/bundler/definition.rb:185:in specs_for -> from C:/Ruby23/lib/ruby/gems/2.3.0/gems/bundler-1.12.5/lib/bundler/definition.rb:174:in requested_specs -> from C:/Ruby23/lib/ruby/gems/2.3.0/gems/bundler-1.12.5/lib/bundler/environment.rb:19:in requested_specs -> from C:/Ruby23/lib/ruby/gems/2.3.0/gems/bundler-1.12.5/lib/bundler/runtime.rb:14:in setup -> from C:/Ruby23/lib/ruby/gems/2.3.0/gems/bundler-1.12.5/lib/bundler.rb:95:in setup -> from C:/Ruby23/lib/ruby/gems/2.3.0/gems/jekyll-3.2.1/lib/jekyll/plugin_manager.rb:36:in require_from_bundler -> from C:/Ruby23/lib/ruby/gems/2.3.0/gems/jekyll-3.2.1/exe/jekyll:9:in -> from C:/Ruby23/bin/jekyll:23:in load -> from C:/Ruby23/bin/jekyll:23:in
    - -Finally, my blog site is able to be served successfully after I installed the dependency '**minima**'. - - -## else - -in some case ,you must change gem source - -~~~ -gem sources --add http://gems.ruby-china.org/ --remove https://rubygems.org/ -~~~ - -congig bundle miroor - -~~~ -bundle config mirror.https://rubygems.org http://gems.ruby-china.org -~~~ - -then excute bundle command - -now, find a nather error - ->jekyll service ->WARN: Unresolved specs during Gem::Specification.reset: -> rouge (~> 1.7) -> jekyll-watch (~> 1.1) -> WARN: Clearing out unresolved specs. -> Please report a bug if this causes problems. -> fatal: 'jekyll service' could not be found. You may need to install the jekyll-service gem or a related gem to be able to use this subcommand. - -then excute bundle install command - -other command : - -``` xml -netstat –ano|findstr “4000” - -bundle exec jekyll serve -``` - -done!! diff --git a/_posts/2016/2016-08-20-spring-boot-jpa.md b/_posts/2016/2016-08-20-spring-boot-jpa.md deleted file mode 100644 index b6b5bc7439..0000000000 --- a/_posts/2016/2016-08-20-spring-boot-jpa.md +++ /dev/null @@ -1,324 +0,0 @@ ---- -layout: post -title: Spring Boot(五):Spring Boot Jpa 的使用 -category: springboot -copyright: java -tags: [springboot] -lock: need ---- - -在上篇文章[Spring Boot(二):Web 综合开发](http://www.ityouknow.com/springboot/2016/02/03/spring-boot-web.html)中简单介绍了一下 Spring Boot Jpa 的基础性使用,这篇文章将更加全面的介绍 Spring Boot Jpa 常见用法以及注意事项。 - -使用 Spring Boot Jpa 开发时,发现国内对 Spring Boot Jpa 全面介绍的文章比较少案例也比较零碎,因此写文章总结一下。本人也正在翻译[Spring Data JPA 参考指南](https://ityouknow.gitbooks.io/spring-data-jpa-reference-documentation/content/),有兴趣的同学欢迎联系我,一起加入翻译中! - -## Spring Boot Jpa 介绍 - -### 首先了解 Jpa 是什么? - -Jpa (Java Persistence API) 是 Sun 官方提出的 Java 持久化规范。它为 Java 开发人员提供了一种对象/关联映射工具来管理 Java 应用中的关系数据。它的出现主要是为了简化现有的持久化开发工作和整合 ORM 技术,结束现在 Hibernate,TopLink,JDO 等 ORM 框架各自为营的局面。 - -值得注意的是,Jpa是在充分吸收了现有 Hibernate,TopLink,JDO 等 ORM 框架的基础上发展而来的,具有易于使用,伸缩性强等优点。从目前的开发社区的反应上看,Jpa 受到了极大的支持和赞扬,其中就包括了 Spring 与 EJB3. 0的开发团队。 - ->注意:Jpa 是一套规范,不是一套产品,那么像 Hibernate,TopLink,JDO 他们是一套产品,如果说这些产品实现了这个 Jpa 规范,那么我们就可以叫他们为 Jpa 的实现产品。 - -### Spring Boot Jpa - -Spring Boot Jpa 是 Spring 基于 ORM 框架、Jpa 规范的基础上封装的一套 Jpa 应用框架,可使开发者用极简的代码即可实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data Jpa 可以极大提高开发效率! - -> Spring Boot Jpa 让我们解脱了 DAO 层的操作,基本上所有 CRUD 都可以依赖于它来实现 - - -## 基本查询 - -基本查询也分为两种,一种是 Spring Data 默认已经实现,一种是根据查询的方法来自动解析成 SQL。 - -### 预先生成方法 - -Spring Boot Jpa 默认预先生成了一些基本的CURD的方法,例如:增、删、改等等 - -1 继承 JpaRepository - -``` java -public interface UserRepository extends JpaRepository { -} -``` - -2 使用默认方法 - -``` java -@Test -public void testBaseQuery() throws Exception { - User user=new User(); - userRepository.findAll(); - userRepository.findOne(1l); - userRepository.save(user); - userRepository.delete(user); - userRepository.count(); - userRepository.exists(1l); - // ... -} -``` - -就不解释了根据方法名就看出意思来 - -### 自定义简单查询 - -自定义的简单查询就是根据方法名来自动生成 SQL,主要的语法是`findXXBy`,`readAXXBy`,`queryXXBy`,`countXXBy`, `getXXBy`后面跟属性名称: - -``` java -User findByUserName(String userName); -``` - -也使用一些加一些关键字`And `、 `Or` - -``` java -User findByUserNameOrEmail(String username, String email); -``` - -修改、删除、统计也是类似语法 - -``` java -Long deleteById(Long id); -Long countByUserName(String userName) -``` - -基本上 SQL 体系中的关键词都可以使用,例如:` LIKE `、 `IgnoreCase`、 `OrderBy`。 - -``` java -List findByEmailLike(String email); -User findByUserNameIgnoreCase(String userName); -List findByUserNameOrderByEmailDesc(String email); -``` - -**具体的关键字,使用方法和生产成SQL如下表所示** - -Keyword | Sample |JPQL snippet ---- |--- |--- -And |findByLastnameAndFirstname |… where x.lastname = ?1 and x.firstname = ?2 -Or |findByLastnameOrFirstname |… where x.lastname = ?1 or x.firstname = ?2 -Is,Equals| findByFirstnameIs,findByFirstnameEquals |… where x.firstname = ?1 -Between |findByStartDateBetween |… where x.startDate between ?1 and ?2 -LessThan | findByAgeLessThan |… where x.age < ?1 -LessThanEqual| findByAgeLessThanEqual |… where x.age ⇐ ?1 -GreaterThan |findByAgeGreaterThan |… where x.age > ?1 -GreaterThanEqual| findByAgeGreaterThanEqual |… where x.age >= ?1 -After |findByStartDateAfter |… where x.startDate > ?1 -Before |findByStartDateBefore |… where x.startDate < ?1 -IsNull |findByAgeIsNull |… where x.age is null -IsNotNull,NotNull| findByAge(Is)NotNull |… where x.age not null -Like |findByFirstnameLike |… where x.firstname like ?1 -NotLike |findByFirstnameNotLike |… where x.firstname not like ?1 -StartingWith| findByFirstnameStartingWith |… where x.firstname like ?1 (parameter bound with appended %) -EndingWith |findByFirstnameEndingWith |… where x.firstname like ?1 (parameter bound with prepended %) -Containing |findByFirstnameContaining |… where x.firstname like ?1 (parameter bound wrapped in %) -OrderBy |findByAgeOrderByLastnameDesc |… where x.age = ?1 order by x.lastname desc -Not |findByLastnameNot |… where x.lastname <> ?1 -In |findByAgeIn(Collection ages) |… where x.age in ?1 -NotIn| findByAgeNotIn(Collection age) |… where x.age not in ?1 -TRUE| findByActiveTrue() |… where x.active = true -FALSE| findByActiveFalse() |… where x.active = false -IgnoreCase| findByFirstnameIgnoreCase |… where UPPER(x.firstame) = UPPER(?1) - - -## 复杂查询 - -在实际的开发中我们需要用到分页、删选、连表等查询的时候就需要特殊的方法或者自定义 SQL - -### 分页查询 - -分页查询在实际使用中非常普遍了,Spring Boot Jpa 已经帮我们实现了分页的功能,在查询的方法中,需要传入参数`Pageable` ,当查询中有多个参数的时候`Pageable`建议做为最后一个参数传入. - -``` java -Page findALL(Pageable pageable); -Page findByUserName(String userName,Pageable pageable); -``` - -`Pageable` 是 Spring 封装的分页实现类,使用的时候需要传入页数、每页条数和排序规则 - -``` java -@Test -public void testPageQuery() throws Exception { - int page=1,size=10; - Sort sort = new Sort(Direction.DESC, "id"); - Pageable pageable = new PageRequest(page, size, sort); - userRepository.findALL(pageable); - userRepository.findByUserName("testName", pageable); -} -``` - -**限制查询** - -有时候我们只需要查询前N个元素,或者支取前一个实体。 - -``` -User findFirstByOrderByLastnameAsc(); -User findTopByOrderByAgeDesc(); -Page queryFirst10ByLastname(String lastname, Pageable pageable); -List findFirst10ByLastname(String lastname, Sort sort); -List findTop10ByLastname(String lastname, Pageable pageable); -``` - -### 自定义SQL查询 - -其实 Spring Data 觉大部分的 SQL 都可以根据方法名定义的方式来实现,但是由于某些原因我们想使用自定义的 SQL 来查询,Spring Data 也是完美支持的;在 SQL 的查询方法上面使用`@Query`注解,如涉及到删除和修改在需要加上`@Modifying`.也可以根据需要添加 `@Transactional`对事物的支持,查询超时的设置等。 - -``` -@Modifying -@Query("update User u set u.userName = ?1 where u.id = ?2") -int modifyByIdAndUserId(String userName, Long id); - -@Transactional -@Modifying -@Query("delete from User where id = ?1") -void deleteByUserId(Long id); - -@Transactional(timeout = 10) -@Query("select u from User u where u.emailAddress = ?1") -User findByEmailAddress(String emailAddress); -``` - -### 多表查询 - -多表查询 Spring Boot Jpa 中有两种实现方式,第一种是利用 Hibernate 的级联查询来实现,第二种是创建一个结果集的接口来接收连表查询后的结果,这里主要第二种方式。 - -首先需要定义一个结果集的接口类。 - -``` java -public interface HotelSummary { - - City getCity(); - - String getName(); - - Double getAverageRating(); - - default Integer getAverageRatingRounded() { - return getAverageRating() == null ? null : (int) Math.round(getAverageRating()); - } - -} -``` - -查询的方法返回类型设置为新创建的接口 - -``` java -@Query("select h.city as city, h.name as name, avg(r.rating) as averageRating " - - "from Hotel h left outer join h.reviews r where h.city = ?1 group by h") -Page findByCity(City city, Pageable pageable); - -@Query("select h.name as name, avg(r.rating) as averageRating " - - "from Hotel h left outer join h.reviews r group by h") -Page findByCity(Pageable pageable); -``` - -使用 - -``` -Page hotels = this.hotelRepository.findByCity(new PageRequest(0, 10, Direction.ASC, "name")); -for(HotelSummary summay:hotels){ - System.out.println("Name" +summay.getName()); - } -``` - -> 在运行中 Spring 会给接口(HotelSummary)自动生产一个代理类来接收返回的结果,代码汇总使用 `getXX`的形式来获取 - - -## 多数据源的支持 - -### 同源数据库的多源支持 - -日常项目中因为使用的分布式开发模式,不同的服务有不同的数据源,常常需要在一个项目中使用多个数据源,因此需要配置 Spring Boot Jpa 对多数据源的使用,一般分一下为三步: - -- 1 配置多数据源 -- 2 不同源的实体类放入不同包路径 -- 3 声明不同的包路径下使用不同的数据源、事务支持 - -### 异构数据库多源支持 - -比如我们的项目中,即需要对 mysql 的支持,也需要对 Mongodb 的查询等。 - -实体类声明`@Entity` 关系型数据库支持类型、声明`@Document` 为 Mongodb 支持类型,不同的数据源使用不同的实体就可以了 - -``` java -interface PersonRepository extends Repository { - … -} - -@Entity -public class Person { - … -} - -interface UserRepository extends Repository { - … -} - -@Document -public class User { - … -} -``` - -但是,如果 User 用户既使用 Mysql 也使用 Mongodb 呢,也可以做混合使用 - -``` java -interface JpaPersonRepository extends Repository { - … -} - -interface MongoDBPersonRepository extends Repository { - … -} - -@Entity -@Document -public class Person { - … -} -``` - -也可以通过对不同的包路径进行声明,比如 A 包路径下使用 mysql,B 包路径下使用 MongoDB - -``` -@EnableJpaRepositories(basePackages = "com.neo.repositories.jpa") -@EnableMongoRepositories(basePackages = "com.neo.repositories.mongo") -interface Configuration { } -``` - - -## 其它 - -**使用枚举** - -使用枚举的时候,我们希望数据库中存储的是枚举对应的 String 类型,而不是枚举的索引值,需要在属性上面添加` @Enumerated(EnumType.STRING) ` 注解 - -``` -@Enumerated(EnumType.STRING) -@Column(nullable = true) -private UserType type; -``` - -**不需要和数据库映射的属性** - -正常情况下我们在实体类上加入注解`@Entity`,就会让实体类和表相关连如果其中某个属性我们不需要和数据库来关联只是在展示的时候做计算,只需要加上`@Transient`属性既可。 - -``` -@Transient -private String userName; -``` - - -**源码案例** - -**[示例代码-github](https://github.com/ityouknow/spring-boot-examples/tree/master/spring-boot-jpa)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-boot-examples/tree/master/spring-boot-jpa)** - - -> 文章内容已经升级到 Spring Boot 2.x - -## 参考 - -[Spring Data JPA - Reference Documentation](http://docs.spring.io/spring-data/jpa/docs/current/reference/html/) - -[Spring Data JPA——参考文档 中文版](https://www.gitbook.com/book/ityouknow/spring-data-jpa-reference-documentation/details) diff --git a/_posts/2016/2016-09-09-linux-mysql-scheduled-backup.md b/_posts/2016/2016-09-09-linux-mysql-scheduled-backup.md deleted file mode 100644 index efc3f43121..0000000000 --- a/_posts/2016/2016-09-09-linux-mysql-scheduled-backup.md +++ /dev/null @@ -1,309 +0,0 @@ ---- -layout: post -title: linux定时备份mysql并同步到其它服务器 -category: mysql -tags: [mysql] ---- - -数据在任何一家公司里面都是最核心的资产,定期备份则是为了保证数据库出现问题的时候能够及时回滚到最近的备份点,将损失缩小到最小 - -这篇文章将会两部分来说明:1、mysql的定期备份;2、同步到其它服务器 - - -## mysql 备份 - -### 备份还原某个数据库 - -备份还原 - -``` shell -# 导出数据库 -/usr/bin/mysqldump -u root -ppwd database > database20160929.sql -# 导入数据库 -mysql -u root -p database < database20160929.sql -``` - -备份到压缩文件从压缩文件导入 - -``` shell -#备份到压缩文件 -/usr/bin/mysqldump -u root -ppwd database | gzip > database20160929.sql.gz -#从压缩文件导入 -gzip < database20160929.sql.gz | mysql -u root -p database -``` - -### crontab定时备份 - -1、创建备份目录 - -``` shell -# root 用户,创建备份目录 -mkdir -p /bak/mysqlbak -cd /bak/mysqldata -``` - -2、编写运行脚本 - -``` shell -vi /usr/sbin/bakmysql.sh -``` - -脚本代码: - -``` shell -#!/bin/bash -# Name:bakmysql.sh -# This is a ShellScript For Auto DB Backup and Delete old Backup -# -backupdir=/bak/mysqlbak -time=` date +%Y%m%d%H ` -mysql_bin_dir/mysqldump -u root -ppwd database | gzip > $backupdir/database$time.sql.gz -# -find $backupdir -name "name_*.sql.gz" -type f -mtime +7 -exec rm {} ; > /dev/null 2>&1 - -# -``` - -脚本说明: - -> ```backupdir``` mysql备份地址 -> ```root``` mysql用户名 -> ```pwd``` mysql密码 -> ```database``` 数据库名 -> ```mysql_bin_dir``` mysql的bin路径; -> ```time=` date +%Y%m%d%H ` ```也可以写为```time="$(date +"%Y%m%d$H")" ```其中``` ` ``` 符号是TAB键上面的符号,不是ENTER左边的'符号,还有date后要有一个空格。 -> ```type f``` 表示查找普通类型的文件,f表示普通文件。 -> ```mtime +7``` 按照文件的更改时间来查找文件,+7表示文件更改时间距现在7天以前;如果是 -mmin +5 表示文件更改时间距现在5分钟以前。 -> ```exec rm {} \``` 表示执行一段shell命令,exec选项后面跟随着所要执行的命令或脚本,然后是一对儿{},一个空格和一个\,最后是一个分号。 -> ```/dev/null 2>&1``` 把标准出错重定向到标准输出,然后扔到/DEV/NULL下面去。通俗的说,就是把所有标准输出和标准出错都扔到垃圾桶里面;其中的& 表示让该命令在后台执行。 - -3、为脚本添加执行权限 - -``` shell -# chmod +x /usr/sbin/bakmysql.sh -``` - -4、设置crontab定时执行 - -``` shell -vi /etc/crontab -#在最后一行中加入: -00 3 * * * root /usr/sbin/bakmysql.sh -#表示每天3点00分执行备份 -``` - - ->注:crontab配置文件格式如下: ->分 时 日 月 周  命令 - - -5、重启crontab - -``` shell -/etc/rc.d/init.d/crond restart -``` - -这样就完了定时备份并清理前7天的备份数据 - - - -## 同步到其它服务器 - -这里使用Linux同步文件工具rsync+inotify来进行文件的同步 - -### rsync - -rsync是类unix系统下的数据镜像备份工具——remote sync。一款快速增量备份工具 Remote Sync,远程同步 支持本地复制,或者与其他SSH、rsync主机同步 - -**用法** - -``` shell -rsync src dest -``` - -这是最简单的用法,表示同步src,dest文件。(即,执行之后,dest的文件与src的相同,以src的为准) - -常用选项 -> ```-a```: 等价于-rlptgoD,归档式 -> ```-r```: 递归 -> ```-l```: 复制软件链接 -> ```-p```: 保留权限信息 -> ```-t```: 将src的修改时间,同步到dest -> ```-g```: 同步组信息(group) -> ```-o```: 同步拥有者信息(own) -> ```-D```: 保持字符与块设备文件 -> ```-z```: 启用压缩传输 -> ```–delete```:如果src没有此文件,那么dest也不能有,即在dest删除src里没有的文件。(如果你使用这个选项,就必须搭配-r选项一起) - - -``` shell -## 将本地/bak/mysqlbak/文件同步到 远程服务器 /bak/mysql/bak 目录下面 排除 mysqlbak/index目录 通过ssh端口 -rsync -vzacu /bak/mysqlbak/ root@192.168.53.86:/bak/mysqlbak --exclude "mysqlbak/index" -e "ssh -p 22" -# 将远程目录 /bak/mysqlbak下的文件同步到本地 /bak/mysqlbak/目录下 -rsync -vzrtopg --progress --delete root@192.168.53.85:/bak/mysqlbak /bak -``` - -**启用rsync服务器端同步远程文件** - -rsycn的服务端为服务器的文件接收端,rsycn的客户端为服务器的文件推动端。 - - -**rsycn的服务端/文件接收端配置** - -服务端需要开启rsyncd服务 - -添加配置文件rsyncd.conf - -``` shell -vi /etc/rsyncd.conf -#以下是全局配置 -log file = /var/log/rsyncd.log -pid file = /var/run/rsyncd.pid -lock file = /var/lock/rsyncd -[mysqlbak] #模块名,在源服务器指定这个名字 - comment = sync rsync/home #描述信息 - path = /bak/mysqlbak #备份目录 - use chroot=no #不使用chroot,不用root权限 - read only = no #设置本地备份目录为读写权限 - uid=root - gid=root - max connections=10 #客户端最大连接数 - auth users = root #指定数据同步用户 - secrets file = /etc/rsyncd.pass #指定数据同步用户信息文件 - hosts allow=192.168.53.0/85 #允许连接的客户端 - ignore errors = yes #忽略出现I/O错误 - timeout = 600 -``` - -创建认证文件 - -``` shell - vi /etc/rsyncd.pass - ##代码 - root:root #格式是用户名:密码 - #属主要有权限读这个文件,否则会报没权限 - chmod 600 /etc/rsyncd.pass -``` - -修改/etc/xinetd.d/rsync文件,disable 改为 no - -``` shell -service rsync -{ - disable = no - socket_type = stream - wait = no - user = root - server = /usr/bin/rsync - server_args = --daemon - log_on_failure += USERID -} -``` - -启动服务端 - -``` shell -rsync --daemon --config=/etc/rsyncd.conf -``` - -**rsycn的客户端/文件发送端配置** - -客户端配置简单 只需要配置密码既可 - -``` shell - vi /etc/rsync_client.pwd - ##代码 - root #只需要填写rsync服务的密码 - #属主要有权限读这个文件,否则会报没权限 - chmod 600 /etc/rsync_client.pwd -``` - -客户端同步测试 - -``` shell -/usr/bin/rsync -auvrtzopgP --progress --password-file=/etc/rsync_client.pwd /bak/mysqlbak/ root@192.168.53.86::mysqlbak -``` - -> **rsync只是一次性同步,如果需要实时同步就需要引入另一个工具了** - -### inotify - - Inotify 是一种强大的、细粒度的、异步的文件系统事件监控机制,linux内核从2.6.13起,加入了Inotify支持,通过Inotify可以监控文件系统中添加、删除,修改、移动等各种细微事件,利用这个内核接口,第三方软件就可以监控文件系统下文件的各种变化情况,而inotify-tools就是这样的一个第三方软件。 - -> Inotify只需要要按照部署在同步的客户端,当监控的文件有变化触动 rsync脚本来同步 - -安装 - -``` shell -yum install inotify-tools -``` - -配置监控的文件路径 - -``` shell -vi /etc/inotify_exclude.lst -#代码 -/bak/mysqlbak #监控目录 -@/bak/log #排除监控目录 -``` - -rsync排除监控文件目录 - -``` shell -vi /etc/rsyncd.d/rsync_exclude.lst -#代码 -src/*.html* -src/js/ -src/2014/20140[1-9]/ -``` - -客户端同步到远程的脚本rsync.sh - -``` shell -#rsync auto sync script with inotify -#variables -current_date=$(date +%Y%m%d_%H%M%S) -source_path=/bak/mysqlbak/ -log_file=/var/log/rsync_client.log -#rsync -rsync_server=192.168.53.86 -rsync_user=root -rsync_pwd=/etc/rsync_client.pwd -rsync_module=mysqlbak -INOTIFY_EXCLUDE='(.*/*\.log|.*/*\.swp)$|^/tmp/src/mail/(2014|20.*/.*che.*)' -RSYNC_EXCLUDE='/bak/rsync_exclude.lst' -#rsync client pwd check -if [ ! -e ${rsync_pwd} ];then - echo -e "rsync client passwod file ${rsync_pwd} does not exist!" - exit 0 -fi -#inotify_function -inotify_fun(){ - /usr/bin/inotifywait -mrq --timefmt '%Y/%m/%d-%H:%M:%S' --format '%T %w %f' \ - --exclude ${INOTIFY_EXCLUDE} -e modify,delete,create,move,attrib ${source_path} \ - | while read file - do - /usr/bin/rsync -auvrtzopgP --exclude-from=${RSYNC_EXCLUDE} --progress --bwlimit=200 --password-file=${rsync_pwd} ${source_path} ${rsync_user}@${rsync_server}::${rsync_module} - done -} -#inotify log -inotify_fun >> ${log_file} 2>&1 & -``` - -给脚本执行权限,执行后就可以了 - -``` shell -chmod 777 rsync.sh -./rsync.sh -``` - -## 参考: - -[Linux下同步工具inotify+rsync使用详解](http://seanlook.com/2014/12/12/rsync_inotify_setup/) - - -------------- - -**作者:纯洁的微笑** -**出处:[www.ityouknow.com](http://www.ityouknow.com)** -**版权所有,欢迎保留原文链接进行转载 :)** diff --git a/_posts/2016/2016-09-26-spring-boot-opensource-favorites.md b/_posts/2016/2016-09-26-spring-boot-opensource-favorites.md deleted file mode 100644 index 8f2df0476c..0000000000 --- a/_posts/2016/2016-09-26-spring-boot-opensource-favorites.md +++ /dev/null @@ -1,110 +0,0 @@ ---- -layout: post -title: Spring Boot 实战:我们的第一款开源软件 -copyright: java -category: springboot -tags: [云收藏] ---- - -在信息爆炸时代,如何避免持续性信息过剩,使自己变得专注而不是被纷繁的信息所累?每天会看到各种各样的新闻,各种新潮的技术层出不穷,如何筛选出自己所关心的? - -各位看官会想,我们是来看开源软件的,你给我扯什么信息干嘛,别着急,听我慢慢道来。 - -## 背景 - -浏览器收藏夹应该是我们在收藏文章、网站的第一个利器,平时遇到喜欢的网站或者文章很方便的收藏到收藏夹中;很快我们的收藏夹就满了,于是就像我这样,创建文件夹来分组两层、三层都有: - -![favorites_chrome](http://favorites.ren/assets/images/2016/favorites_chrome.jpg) - -有的也会借助百度首页导航这样的一些功能来整理自己收藏的网站,以前我记得 QQ 还有一款产品叫做网络收藏夹,用过一段时间,后来 QQ 也把这款产品给淘汰了;也尝试了去用印象笔记、有道笔记这些产品,这些产品都偏向收藏一些具体的文章或者自己整理的日志信息方面。 - -当浏览器收藏夹收藏的网站或者文章在一百份以内的时候收藏和查找问题都不是特别大。当收藏大于1000份的时候,去查找自己收藏的内容绝对是个体力活,另外还有一些文章我仅仅只是暂时保存下来,准备随后找时间看看就行,也需要收藏、整理、删除的时候就很麻烦。 - -## 产品介绍 - -于是在这样的背景下,我就想着需要做这么一款产品,可以方便随时随地的收藏我喜欢的文章或者网站,方便整理,我日后需要的时候非常方便的去检索,另外如果可以的话,我是否可以分享我自己收藏的文章或者网站,同时也可以看看大牛们或者是同行都收藏了什么文章我是否感兴趣,于是就开发了这么一款产品:**云收藏** - -核心功能点: - -- 收藏、分类、检索文章 -- 导出、导出(包活从浏览器中) -- 可以点赞、分享、讨论 -- 注册、登录、个人账户 -- 临时收藏、查看别人收藏 -- 其它... - -放产品一些截图: - -主页 -![favorites_chrome](http://favorites.ren/assets/images/2016/favorites_index.png) - -注册 -![favorites_chrome](http://favorites.ren/assets/images/2016/favorites_register.png) - -首页 -![favorites_chrome](http://favorites.ren/assets/images/2016/favorites_home.png) - -收藏 -![favorites_chrome](http://favorites.ren/assets/images/2016/favorites_collect.png) - - -## 技术点 - -这段时间我们团队主要在学习 Spring Boot,这个开源项目也就成了我们的练习新技术的一个非常好的产品,主要的技术都是和 Spring Boot相关,可以参考我以前文章 [Spring Boot 系列文章](http://www.ityouknow.com/spring-boot.html) - -### 网页端 - -[网页端收藏夹主页](https://cloudfavorites.github.io/favorites-web/) - -**收藏快捷图标** - -这个是收藏的最关键一步,一段js代码,拖入到浏览器的收藏夹,每次点击收藏的时候负责读取网站的 title、描述、网址等信息,并且提交到收藏的页面 - - -**前端** - -前端页面由[Angle - Bootstrap Admin theme](https://wrapbootstrap.com/theme/angle-bootstrap-admin-template-WB04HF123)这套主题改造而来;模版引擎使用了`thymeleaf`,可以参考这篇文章:[Spring Boot(四):Thymeleaf 使用详解](http://www.ityouknow.com/springboot/2016/05/01/spring-boot-thymeleaf.html) - -**持久层** - -数据库主要使用了 Spring Data Jpa 模版来实现,可以参考这篇文章:[Spring Boot(五):Spring Data Jpa 的使用](http://www.ityouknow.com/springboot/2016/08/20/spring-boo-jpa.html) - -**session** - -session 使用持久化技术来保存登录状态,登录一次保持需要会话30天,主要是依赖 Redis 来实现,参考:[Spring Boot(三):Spring Boot 中 Redis 的使用](http://www.ityouknow.com/springboot/2016/03/06/spring-boot-redis.html) - -**其它** - -使用`grade`做为项目的构建工具、使用了一点`webjars`、`vuejs`、`Jsoup`、`Scheduled` ... - - -### 客户端 - -客户端技术使用 react native 来开发安卓和 IOS 的 app,目前还在开发中,完成之后也会开源出来。 - - -## 未来计划做的内容 - -这个开源产品暂时只是开源了我们 Web 端产品,安卓端、IOS 端内容的开发还在进行中。 - -未来我们还会持续的来完善这些产品,做一些有意思的小功能,以下可能是我们近期准备要做的 - -- 可以自定义个人收藏页面 -- 无登录可以查看热门收藏内容 -- 首页展示热门收藏家 -- 小纸条 -- 智能推荐 -- 其它... - -大家有什么更好玩想法,也可以在建议给我们 - - -**产品主页** - -[产品地址](http://favorites.ren/) -[源码地址](https://cloudfavorites.github.io/favorites-web/) - - -**[示例代码-github](https://github.com/cloudfavorites/favorites-web)** - -**[示例代码-码云](https://gitee.com/ityouknow/favorites-web)** \ No newline at end of file diff --git a/_posts/2016/2016-10-30-python3-1024.md b/_posts/2016/2016-10-30-python3-1024.md deleted file mode 100644 index bf65b61026..0000000000 --- a/_posts/2016/2016-10-30-python3-1024.md +++ /dev/null @@ -1,251 +0,0 @@ ---- -layout: post -title: python3爬取1024图片 -category: python -copyright: python -tags: [python] ---- - -这两年python特别火,火到博客园现在也是隔三差五的出现一些python的文章。各种开源软件、各种爬虫算法纷纷开路,作为互联网行业的IT狗自然看的我也是心痒痒,于是趁着这个雾霾横行的周末瞅了两眼,作为一名老司机觉得还是应该以练带学,1024在程序员界这么流行的网站,当然拿来先练一练。 - -python自称是以自然语言的视角来编程,特点是开发快,语言简洁,没那么多技巧,大名鼎鼎的豆瓣、youtube都是使用python开发的网站,看来python在大规模使用这个方面来讲应该没有啥子问题;python也不是没有缺点在性能方面就Java、C++等老前辈还是没得比的,另外python和nodejs一样只能使用CPU单核,也是性能方面影响是因素之一。但python在特定领域表现突出,特别是脚本、爬虫、科学算法等。 - -> 好了,还是说正事如何爬取1024网站的图片 - - -## 分析 - -### 列表页面 - -首先进入1024的导航网站,随便点击一个地址进入选择图片区或者在网站地址后面添加```thread0806.php?fid=16&search=&page=```,这就是1024网站的图片区,这个爬虫就是主要抓取这个区域的所有图片,使用浏览器debug分析一下这个页面发现基本都是列表页,格式如下: - -![list](http://favorites.ren/assets/images/2016/1024list.jpg) - -在地址栏```http://xxxxxx.biz/thread0806.php?fid=16&search=&page=```后面拼1、2、3等于就是访问图片区第一页、第二页、第三页的列表页。根据这些列表页就可以爬出具体的每一个图片页的地址,类似上图的地址:```htm_data/16/1611/2114702.html``` 在地址的前面拼接上主站地址就是具体的图片页了。所以根据以上的分析:通过循环地址栏找到不同的列表页在根据列表页找到具体的图片页 - -> 地址栏->图片列表->图片页地址 - -获取列表页图片地址代码如下: - -``` python -import urllib.request,socket,re,sys,os - -baseUrl='http://xxxx.biz/' - -def getContant(Weburl): - Webheader= {'Upgrade-Insecure-Requests':'1', - 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.103 Safari/537.36',} - req = urllib.request.Request(url = Weburl,headers=Webheader) - respose = urllib.request.urlopen(req) - _contant = respose.read() - respose.close() - return str(_contant) - -def getUrl(URL): - pageIndex = 1 - for i in range(1,int(pageIndex)+1): - Weburl = URL + str(i) - contant = getContant(Weburl) - comp = re.compile(r' 在这个地址后面拼接1到N就是不同的列表页 - - -### 图片页面 - -利用浏览器debug一下页面,图片基本上都是外链地址,以http或者https开头以jpg、png、gif结尾,写个正则表达式匹配这些地址,然后交给程序下载就OK了。 - -页面代码如下: - -![page](http://favorites.ren/assets/images/2016/1024page.jpg) - -在下载过程中遇到了几个问题,就是有的页面会报403禁止访问等,应该是网站加了一些防止爬虫的手段,网上找了下加上header参数来模拟浏览器访问就解决了; - -下载单个页面代码如下: - -``` python -import urllib.request,socket,re,sys,os - -#定义文件保存路径 -targetPath = "D:\\temp\\1024\\1" - -def openUrl(url): - headers = { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/51.0.2704.63 Safari/537.36' - } - - req = urllib.request.Request(url=url, headers=headers) - res = urllib.request.urlopen(req) - data = res.read() - downImg(data) - -def downImg(data): - for link,t in set(re.findall(r'([http|https]:[^\s]*?(jpg|png|gif))', str(data))): - - if link.startswith('s'): - link='http'+link - else: - link='htt'+link - print(link) - try: - opener=urllib.request.build_opener() - opener.addheaders=[('User-Agent','Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1941.0 Safari/537.36')] - urllib.request.install_opener(opener) - urllib.request.urlretrieve(link,saveFile(link)) - except: - print('失败') - -def saveFile(path): - #检测当前路径的有效性 - if not os.path.isdir(targetPath): - os.mkdir(targetPath) - - #设置每个图片的路径 - pos = path.rindex('/') - t = os.path.join(targetPath,path[pos+1:]) - return t - -url = "http://xxxx.biz/htm_data/16/1611/2115193.html" -openUrl(url) - -``` - - -## 批量爬取 - -批量爬取有两个工作要做,第一for循环目标内的所有列表页,第二为了避免重复爬取,需要给每个页面建立唯一的文件夹,下次爬取的时候如果存在直接跳过。最后在理一下所有的爬取步骤: - -> 循环地址栏->找出图片页列表->图片页分析找出图片地址->为图片页建立唯一的文件夹->开始下载页面图片 - -完整的代码如下: - -``` python -import urllib.request,socket,re,sys,os - -baseUrl='http://xxxx.biz/' -targetPath = "D:\\temp\\1024\\" - -def getContant(Weburl): - Webheader= {'Upgrade-Insecure-Requests':'1', - 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.103 Safari/537.36',} - req = urllib.request.Request(url = Weburl,headers=Webheader) - respose = urllib.request.urlopen(req) - _contant = respose.read() - respose.close() - return str(_contant) - -def getUrl(URL): - pageIndex = 1 - for i in range(1,int(pageIndex)+1): - Weburl = URL + str(i) - contant = getContant(Weburl) - comp = re.compile(r'源代码地址:[python-crawler](https://github.com/ityouknow/python-crawler) -> **具体地址和源代码在一起** - - -## 其它 - -关于python2和python3的争论,网站争论比较大python3不兼容pyhton2,很多第三方的类库暂时还没有支持python3等等,但是对于我们新手来说,肯定是往前看果断python3. - -代码比较冗余几个地方还没有写好,还在慢慢学习中,目前只是搞的可以跑起来。还有几个问题没有解决,下载一段时间后会莫名其妙的断掉目前还么找到原因,后期看是否可以加上多线程来爬取可能会快一点,大家有什么更好的建议也可以提出来。 - - -## 参考: - -[爬取豆瓣首页图片](http://blog.csdn.net/fly_yr/article/details/51525945) - -[使用Python爬取1024上的图片](http://zzydev.com/python/2016/05/20/Python1024) - - -------------- - -**作者:纯洁的微笑** -**出处:[www.ityouknow.com](http://www.ityouknow.com)** -**版权所有,欢迎保留原文链接进行转载:)** \ No newline at end of file diff --git a/_posts/2016/2016-11-06-spring-boot-mybatis.md b/_posts/2016/2016-11-06-spring-boot-mybatis.md deleted file mode 100644 index 4f7dfb3a0a..0000000000 --- a/_posts/2016/2016-11-06-spring-boot-mybatis.md +++ /dev/null @@ -1,321 +0,0 @@ ---- -layout: post -title: Spring Boot(六):如何优雅的使用 Mybatis -category: springboot -tags: [springboot] -copyright: java -lock: need ---- - -这两天启动了一个新项目因为项目组成员一直都使用的是 Mybatis,虽然个人比较喜欢 Jpa 这种极简的模式,但是为了项目保持统一性技术选型还是定了 Mybatis 。到网上找了一下关于 Spring Boot 和 Mybatis 组合的相关资料,各种各样的形式都有,看的人心累,结合了 Mybatis 的官方 Demo 和文档终于找到了最简的两种模式,花了一天时间总结后分享出来。 - -Orm 框架的本质是简化编程中操作数据库的编码,发展到现在基本上就剩两家了,一个是宣称可以不用写一句 Sql 的 Hibernate,一个是可以灵活调试动态 Sql 的 Mybatis ,两者各有特点,在企业级系统开发中可以根据需求灵活使用。发现一个有趣的现象:传统企业大都喜欢使用 Hibernate ,互联网行业通常使用 Mybatis 。 - -Hibernate 特点就是所有的 Sql 都用 Java 代码来生成,不用跳出程序去写(看) Sql ,有着编程的完整性,发展到最顶端就是 Spring Data Jpa 这种模式了,基本上根据方法名就可以生成对应的 Sql 了,有不太了解的可以看我的上篇文章[Spring Boot(五): Spring Data Jpa 的使用](http://www.ityouknow.com/springboot/2016/08/20/spring-boot-jpa.html)。 - -Mybatis 初期使用比较麻烦,需要各种配置文件、实体类、Dao 层映射关联、还有一大推其它配置。当然 Mybatis 也发现了这种弊端,初期开发了[generator](https://github.com/mybatis/generator)可以根据表结果自动生产实体类、配置文件和 Dao 层代码,可以减轻一部分开发量;后期也进行了大量的优化可以使用注解了,自动管理 Dao 层和配置文件等,发展到最顶端就是今天要讲的这种模式了,`mybatis-spring-boot-starter` 就是 Spring Boot+ Mybatis 可以完全注解不用配置文件,也可以简单配置轻松上手。 - -> 现在想想 Spring Boot 就是牛逼呀,任何东西只要关联到 Spring Boot 都是化繁为简。 - -## mybatis-spring-boot-starter - -官方说明:`MyBatis Spring-Boot-Starter will help you use MyBatis with Spring Boot` -其实就是 Mybatis 看 Spring Boot 这么火热也开发出一套解决方案来凑凑热闹,但这一凑确实解决了很多问题,使用起来确实顺畅了许多。`mybatis-spring-boot-starter`主要有两种解决方案,一种是使用注解解决一切问题,一种是简化后的老传统。 - -当然任何模式都需要首先引入`mybatis-spring-boot-starter`的 Pom 文件,现在最新版本是 2.0.0 - -``` xml - - org.mybatis.spring.boot - mybatis-spring-boot-starter - 2.0.0 - -``` - -好了下来分别介绍两种开发模式 - -## 无配置文件注解版 - -就是一切使用注解搞定。 - -### 1 添加相关 Maven 文件 - -``` xml - - - org.springframework.boot - spring-boot-starter-web - - - org.mybatis.spring.boot - mybatis-spring-boot-starter - 2.0.0 - - - mysql - mysql-connector-java - - -``` - -完整的 Pom 包这里就不贴了,大家直接看源码 - - -### 2、`application.properties` 添加相关配置 - -``` properties -mybatis.type-aliases-package=com.neo.model - -spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true -spring.datasource.username=root -spring.datasource.password=root -spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -``` - -Spring Boot 会自动加载 `spring.datasource.*` 相关配置,数据源就会自动注入到 sqlSessionFactory 中,sqlSessionFactory 会自动注入到 Mapper 中,对了,你一切都不用管了,直接拿起来使用就行了。 - -在启动类中添加对 mapper 包扫描`@MapperScan` - -``` java -@SpringBootApplication -@MapperScan("com.neo.mapper") -public class MybatisAnnotationApplication { - - public static void main(String[] args) { - SpringApplication.run(MybatisAnnotationApplication.class, args); - } -} -``` - -或者直接在 Mapper 类上面添加注解`@Mapper`,建议使用上面那种,不然每个 mapper 加个注解也挺麻烦的 - - -### 3、开发 Mapper - -第三步是最关键的一块, Sql 生产都在这里 - -``` java -public interface UserMapper { - - @Select("SELECT * FROM users") - @Results({ - @Result(property = "userSex", column = "user_sex", javaType = UserSexEnum.class), - @Result(property = "nickName", column = "nick_name") - }) - List getAll(); - - @Select("SELECT * FROM users WHERE id = #{id}") - @Results({ - @Result(property = "userSex", column = "user_sex", javaType = UserSexEnum.class), - @Result(property = "nickName", column = "nick_name") - }) - UserEntity getOne(Long id); - - @Insert("INSERT INTO users(userName,passWord,user_sex) VALUES(#{userName}, #{passWord}, #{userSex})") - void insert(UserEntity user); - - @Update("UPDATE users SET userName=#{userName},nick_name=#{nickName} WHERE id =#{id}") - void update(UserEntity user); - - @Delete("DELETE FROM users WHERE id =#{id}") - void delete(Long id); - -} -``` - -**为了更接近生产我特地将 user_sex、nick_name 两个属性在数据库加了下划线和实体类属性名不一致,另外 user_sex 使用了枚举** - -> - @Select 是查询类的注解,所有的查询均使用这个 -> - @Result 修饰返回的结果集,关联实体类属性和数据库字段一一对应,如果实体类属性和数据库属性名保持一致,就不需要这个属性来修饰。 -> - @Insert 插入数据库使用,直接传入实体类会自动解析属性到对应的值 -> - @Update 负责修改,也可以直接传入对象 -> - @delete 负责删除 - -[了解更多属性参考这里](http://www.mybatis.org/mybatis-3/zh/java-api.html) - -> **注意,使用#符号和$符号的不同:** - - -``` -// This example creates a prepared statement, something like select * from teacher where name = ?; -@Select("Select * from teacher where name = #{name}") -Teacher selectTeachForGivenName(@Param("name") String name); - -// This example creates n inlined statement, something like select * from teacher where name = 'someName'; -@Select("Select * from teacher where name = '${name}'") -Teacher selectTeachForGivenName(@Param("name") String name); -``` - -### 4、使用 - -上面三步就基本完成了相关 Mapper 层开发,使用的时候当作普通的类注入进入就可以了 - - -``` java -@RunWith(SpringRunner.class) -@SpringBootTest -public class UserMapperTest { - - @Autowired - private UserMapper userMapper; - - @Test - public void testInsert() throws Exception { - userMapper.insert(new User("aa1", "a123456", UserSexEnum.MAN)); - userMapper.insert(new User("bb1", "b123456", UserSexEnum.WOMAN)); - userMapper.insert(new User("cc1", "b123456", UserSexEnum.WOMAN)); - - Assert.assertEquals(3, userMapper.getAll().size()); - } - - @Test - public void testQuery() throws Exception { - List users = userMapper.getAll(); - System.out.println(users.toString()); - } - - - @Test - public void testUpdate() throws Exception { - User user = userMapper.getOne(30l); - System.out.println(user.toString()); - user.setNickName("neo"); - userMapper.update(user); - Assert.assertTrue(("neo".equals(userMapper.getOne(30l).getNickName()))); - } -} -``` - -源码中 Controller 层有完整的增删改查,这里就不贴了 - -## 极简 xml 版本 - -极简 xml 版本保持映射文件的老传统,接口层只需要定义空方法,系统会自动根据方法名在映射文件中找对应的 Sql . - - -### 1、配置 - -pom 文件和上个版本一样,只是`application.properties`新增以下配置 - -``` properties -mybatis.config-location=classpath:mybatis/mybatis-config.xml -mybatis.mapper-locations=classpath:mybatis/mapper/*.xml -``` - -指定了 Mybatis 基础配置文件和实体类映射文件的地址 - -mybatis-config.xml 配置 - -``` xml - - - - - - - - - - -``` - -这里也可以添加一些 Mybatis 基础的配置 - - -### 2、添加 User 的映射文件 - -``` xml - - - - - - - - - - - id, userName, passWord, user_sex, nick_name - - - - - - - - INSERT INTO - users - (userName,passWord,user_sex) - VALUES - (#{userName}, #{passWord}, #{userSex}) - - - - UPDATE - users - SET - userName = #{userName}, - passWord = #{passWord}, - nick_name = #{nickName} - WHERE - id = #{id} - - - - DELETE FROM - users - WHERE - id =#{id} - - -``` - -其实就是把上个版本中 Mapper 的 Sql 搬到了这里的 xml 中了 - - -### 3、编写 Mapper 层的代码 - -``` java -public interface UserMapper { - - List getAll(); - - UserEntity getOne(Long id); - - void insert(UserEntity user); - - void update(UserEntity user); - - void delete(Long id); - -} -``` - -对比上一步,这里只需要定义接口方法 - - -### 4、使用 - -使用和上个版本没有任何区别,大家就看文章对应的示例代码吧 - - -## 如何选择 - -两种模式各有特点,注解版适合简单快速的模式,其实像现在流行的这种微服务模式,一个微服务就会对应一个自已的数据库,多表连接查询的需求会大大的降低,会越来越适合这种模式。 - -老传统模式比适合大型项目,可以灵活的动态生成 Sql ,方便调整 Sql ,也有痛痛快快,洋洋洒洒的写 Sql 的感觉。 - -> 文章内容已经升级到 Spring Boot 2.x - -**[示例代码-github](https://github.com/ityouknow/spring-boot-examples/tree/master/spring-boot-mybatis)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-boot-examples/tree/master/spring-boot-mybatis)** \ No newline at end of file diff --git a/_posts/2016/2016-11-20-six-years-program.md b/_posts/2016/2016-11-20-six-years-program.md deleted file mode 100644 index 4763785b5f..0000000000 --- a/_posts/2016/2016-11-20-six-years-program.md +++ /dev/null @@ -1,147 +0,0 @@ ---- -layout: post -title: 六年程序生涯 -category: life -tags: [life] ---- - -工作六年对一个程序员意味什么?在职位上:高级开发工程师?架构师?技术经理?or ... ?在能力上:各种编码无压力?核心代码无压力?平台架构无压力? or ... fuck?看着这些问号都心累。那么,六年你迷惘了吗?又走到了那个十字路口? - - -人们大都喜欢5年或者10年总结一次,但对我而言更喜欢6。六对我来讲总是一个特殊的数字,六年中一直想对自己的程序员生涯做一个回顾,总是有各种的借口飘然而过就到了今天。留点记录在这里,当自己多年以后,看看当初的自己对这个行业的理解。毕业六年,大学同学们基本上都走在了不同的路线,也走进了完全不同的生活,能在六年冲出来的现在也都小有了名气,为什么相同的学校相同的专业却走向了不同的方向呢,且听我慢慢道来。 - -> 每个程序员的经历都是一个故事 - -## 如何入坑 - -在XX的培训班上有一次我对大家这样介绍:我来自一个二流的本科院校中的一个三流专业,学校本来就是师范类的院校,自然不是特别受欢迎,我们是师范学校里面的非师范专业,而且是学校刚开的专业挂在数学系更加非主流,专业就是:信息与计算科学。我听说有的学校这个专业是计算机系的,不知准确信息。但基本上都是学数学的,带着学习一点计算机,当初报考这个专业也是因为这个名字,看起来很有科技含量,多少农村孩子都是这样报考专业的!!! - -### 大学生活 - -一般大家回顾都要说说大学生活,我的大学一年一句话来总结,大一基本上都是在网吧度过,大二基本上都在篮球场度过,大三基本上都和女朋友一起过,大四基本上都在找工作中度过,导致我走向编程这条道路的经历基本上都在大四了。放一张当时大学的图片,当然现在都已经发生了很大的变化。 - -![dx](http://favorites.ren/assets/images/2016/dx.jpg) - -有几个原因导致了我最后选择去培训机构培训Java编码,第一、对计算机比较感兴趣,大学数学课程基本没听过,都是考试应付,但对相关计算机课程很感兴趣,但学的太浅了,我不讨厌数学,但是让我想到学这么多微积分、线性代数...毕业后有个鸟用,就泄气了,没有一点动力。第二、真的不好找工作,专业几乎没有对口,同学干啥都有,有的走向了培训、当了教师,有的做了文员、公务员、银行职员,有的做了交警、还有公安,但最让我惊奇的是有一个当了律师,太惊讶了,我感觉比编程可困难多了,基本上都是各自找自己的出路。第三、大三暑假那会参加了数学建模竞赛,在小组中我负责编程的部分,那时候用的MATLAB和C语言,随着不断的练习和使用更加验证了对软件的热情和理解,也只是觉得软件应该是一个朝阳行业,慢慢的去了解了入行的标准,找工作的过程中慢慢试着去接触了一些培训机构,但是看到1w左右的培训费用,我犹豫了,那时候的1w对我来讲太重了。 - -大四那年冬天,印象很深刻,跑遍了省会城市大小招聘会场,不是简历都过不了,就是么消息;最后到应聘上几家公司,但是和自己想象中差距太远,一个是培训机构当老师,没去;一个是做管培生,去了,我靠基本上跟传销一样,什么管培生就是卖软件,记得应该叫“红利软件”,就是跑到各个交易所里面去找大爷大妈,聊天要电话,让听讲座最后引导买软件,一套软件大概是3000左右,可以提成10%;早上7点上班,各种活动游戏,8:30出发,9点左右到交易所各种找人,看着眼睛发光的大妈大爷就是目标客户。下午5点左右回来,各种培训,然后开始根据话术打电话晚上10点回家,我们学校一共去了8个人,最后留下了一个我们都想不到一个人,我们班一个文文弱弱的小女生,学习很好的那种,一干就是两年,真是人不可貌相呀,坚持了两周我就撤了。 - -### 南下深圳 - -为什么去了深圳?几个原因,我老大(初中很好的朋友,初中毕业后当兵,然后南下深圳)在这里,万一不行还有一个投奔的地方。深圳应该是当时印象中南方比较发达的城市,希望可以见见世面,找找工作,对了那年还是流感(2009年H1N1流感),疫苗刚出来,只给大四的学生先用,那天还发着低烧,也没管直接就打了。小马是我们班的一个同学,关系比较好,听我说要去深圳,特别激动说,强哥咱俩一起去闯天下去 :),因为小马家境还不错,没有吃过太多的苦,从小都没出过省,还是有点担心,但是看着小马这么激动,恰好我也有一个伴,就欣然接受了。后来我先去的,看了情况还行,就打了电话叫了小马一起过来。那时候从西安到深圳为了省钱买了硬座,应该是做了将近30个小时左右吧,吃了N多筒的泡面终于到了深圳,南方人的普通话真是听不懂呀。对了,小马最终做了一名人民警察,这就是另外的故事了。 - - -因为我先去的深圳,就先去了深圳人才大市场,当然了各种受挫。等小马来了以后我们就先进了龙岗的比亚迪工厂,我们分在了不同的车间,小马比较幸运去了电池车间,就是流水线,我分到了纸箱车间,大家可能平时觉得纸箱子没有啥,但是在没有成品之前,边缘几乎都跟刀子一样,没几天从手到胳膊全部是伤,但最重要的是我和我们车间的老大搞不到一起,整天吵架,有一次差点干了起来,那个车间老大后面瞬间站了两个人,那次之后起我就出来了。 - -听了老大的建议,我又去了龙华的富士康,有一个朋友接我,对我说那边大学生应聘一人还发一个电脑而且是坐办公室的(车间里面的人都羡慕坐办公室的,毕竟不用卖苦力;在厂里面一般工服有三种颜色:蓝领,就是普工或者技工身份进厂的;红领,就是质检,专门检查质量的,大部分从蓝领升上去;白领,一般就是大学生了做文职或者管理),我没去,我还是以普工的身份进的富士康,我不信我干不下去,最后又分到了观澜的一个小分厂里面做物流,对了就是富士康当时有名的第一跳,就在那个厂区。厂里面不像大家现在洋气的说996什么的,几乎没有假期,什么周末更别提了,早8点晚8点,半月白班,半月夜班。富士康在深圳还是福利比较好的一个工厂,包吃住用药等。 - -这个图网上找的,有点像当初那个屌丝的年华 -![dx](http://favorites.ren/assets/images/2016/fsk.jpg) - -在工厂的这段经历让我明白了,我必须要依靠一个技能来养活自己。这个技能就是搞软件! - - -### 培训班 - -根据之前的了解我大概选了两家培训机构,一家就是野马XXX,另外一家就是XX培训机构,价格都差不多,但野马有教师,XX是视频教学,当时感觉不能接受,不太靠谱。本来都拿着钱去野马那边交钱了,在付款的时候,财务说可以减免了一百元,跟我一直沟通的那位说政策搞错了,肯定不是这个。就因为这个原因,我感觉他们不够严谨,我说那我就先不报名了,然后就去了隔壁的XX看看,结果他们正在上课就让我试听了一下,里面全是大四的学生,我坐在后排听了一上午,虽然感觉还是跟不上,但是有那种建模学习的那种氛围,我就定了XX。后来我在XX都上课两周了,野马打电话说给我特殊减免3千元让我过去,最后没去,但是感觉水分真大。 - -其实XX当时在西安的培训还挺扎实的,还考了sun的证书,其实也么啥用。开始从跟不上,到处请教别人,到慢慢的理解,写各种小游戏,到最后也有学员来问我问题。那时候周六、周天可以免费在培训机构学习,我几乎周末都在哪里,毕业答辩的前两天才回去,然后又过来,毕业典礼没有去,毕业体检舍友这个帮我测血压,那个帮忙测体重,就连毕业证书、学位证书都是舍友帮忙领的。我的大四几乎和学校都没有关系。在XX认识了好几个朋友,工作到现在都联系着,有的去了华为,有的在外包,还有的搞了小公司。 - -培训机构承诺免费介绍工作,那时候的培训好像也没有让大家伪造什么工作经验,学校什么的,一般情况下只要你不是很烂,基本上都能找到工作。XX推荐了几家公司,自己也投了简历记得最后收到了2个或者3个offer,也记不得都有什么公司了,最后选择了去李嘉诚儿子的一家公司就是电讯盈科,这个公司那时候刚来西安,还算不错的公司,主要是电信方面的研发,可惜我在这公司也才呆了不到一个月。 - - -## 工作西安 - -刚入这行还是比较周折,也差点进去了另一个方向,所幸最后走向了正轨。程序员都是第一年的工作不是特别好找,过了第一年后,后面就比较轻松了。刚进电讯盈科的时候我的心态还是没有调整过来,感觉还是在大学的那种状态,进去之后是淘汰制,一个月内培训oracle,两周淘汰一次人。其实我感觉自己太不会表现了或者其它吧,最终一个月底的时候我也被淘汰了,打电话给我姐说的时候,我姐说,关键人家一共20多个人就淘汰了2个人!其中就有你!对了培训的钱,借我姐夫的。我感觉很憋屈,但我还是不认可,我是最差的。但最终我还是需要面对再找工作的问题。 - -网上海投了N份简历,电话不多,面试了几家公司后,也收到了几份Offer一个小公司不交社保,1800;有一家外包华为2100正规缴纳社保,我去了华为外包。关于薪资我给大家说两个笑话:1、我当初培训的时候一个学员给我说,有一个朋友从这里出来后,第一份工作2000,跳槽后4000,再跳槽后6000,我们一群人心里都默默的崇拜着,想着这肯定都是大牛级别的人物;2、我毕业第一份工作预期是2000左右,然后我就幻想着以后每年能涨1000元我就满足了,到了30岁我就能拿快8000了,现在真到30了才发现现在的毕业生起步价也是这个数。 - - -### 第一份工作 - -那时候我也在网上看了很多外包公司的种种不是,但我的选择不是很多,不管怎么样毕业了就不能再往家里要钱了,总得先挣钱吧。华为外包有一个变态的特点为了保密,不允许带U盘、手机等各种存储、通讯设备,上班后基本就和外界失联了。那时候是做无线上网卡的客户端,就是那时候往电脑一插就可以上网那种。每个人进来会分配一个师傅来带,比较幸运我来的时候分给了一个比较好的师傅,性格、态度,以及他工作的方式其实最后也都影响了我。 - -我们应该在研发二部,大概分了三个部门,大巴组,小巴组和定制组。定制组:就是不用写代码的那种,华为开发了一些工具通过工具可以改变客户端软件的logo,模块功能等,华为的软件真是遍布全球到处的客户都有,阿拉伯、非洲各种语言大部分的需求都是基本可以工具搞定;小巴组,就是需要改一些代码,但是工作量又不是特别多的那种;大巴组就是需要改动需要1月以上的需求。我当时分在了小巴组,大概有十几个人,其中也有很多硕士毕业的也被忽悠进来。最原始的代码都是印度阿三写的,我们都是在上面做二次开发,刚进去看了一个类代码有上万多行惊呆了,但是代码确实写的非常正规。没有什么架构文档给我们,但改动基本都是外层的皮肤了或者小按钮之类的。 - -加班非常多,但比较开心的是加班有工资,而且是按照国家标准来的,平时加班1.5倍,周六天2倍,假期三倍。这是我工作到现在最正规的加班制度了,工资只有2100,但通过加班可以拿到3000左右,加到2点都是很正常的事情,特别喜欢假期加班可以拿三倍工资,华为在西安包了N多大楼,当时在软件园三期,班车上百辆开出去还是挺壮观的。华为的中午休息文化,确实好,中午吃完饭熄灯大家都睡觉,中午趟在哪里睡一个小时,下午工作质量明显提高N倍。 - -我刚进去的时候客户端有两种一种是Java写的,一种是QT(C++的封装),慢慢的Java版本的都淘汰了,全部上线了QT。我从小巴慢慢开发了大巴需求,但到最后没有Java版本的需求了,全部用QT。Java组的大家都各种转型,有的去了另外一个js控制的项目组,我选择了留下来搞QT,开始学习C++,因为有C语言的底子,倒也不难慢慢的可以开始搞QT版本的小巴需求,但是最后我就纠结了,我以后到底是往Java方向发展呢,还是C++呢。后来终于想通了,我花了那么多钱培训java这样太亏了 ,于是选择了离职。那时候华为方的领导其实对我也特别好,还专门给了我两周时间不用上班去参加华为Java的培训,大家都带着华为的白牌子,我带着外包的黄色牌子,培训老师问了我好几次我是那个部门的,但最后我还是撤了。 - -放一张我们小巴组出游的照片 -![dx](http://favorites.ren/assets/images/2016/ysb.jpeg) - -> 第一份工作促成我从学生到职场的转变。 - - -### 第二份工作 - -当时面试有意向的公司大概是两家,一家是做GIS系统关于地理信息的公司,另外一家是XX系统,主要是做思科代理,给思科做各种软件或者给思科的硬件去做集成方案的各种软件。我选择了后者,没有别的原因,后面这家工资给的高,我就这么实在。 - -刚进公司其实比较紧张,因为半年没有做Java了,每天各种学习,各种加班最后发现其实还可以,虽然半年没有搞了但问题也不大,公司用的是hibernate和Struts基本上都是以前用过的框架。第一个项目是,smart meeting智能会议,就是大公司预定会议的一套系统,大部分的工作都在前端,那段时间让我对js有很了很大的了解,因为预定会议的系统界面都是各种拖拽。第二个项目大概就是vozimate,就是给思科的IP话机上面做应用,就是通过电话可以查询股票了、天气预报了等等,我们的这些信息都是通过爬虫抓取第三方的,过一段时间就需要调整一下爬虫策略。第一次让我对硬件和软件交互有了理解,思科的IP话机当时还是蛮先进的视频通话,各种会议都是没有问题。 - -最重要的一个项目也是我几乎入职一直在搞的项目就是UC manager,就是通过思科的电话打完短途、长途、漫游、国际漫游、转接、会议等等,凡是和打电话相关都会有,其实就是相当于联通或者移动公司话费的计费系统,当然还有路线最优路由,统计等各种功能非常多,刚开始做一些小功能,到负责一个模块,到最后整个系统都是我来负责,直到我离职的最后一天,我都在做这个项目的最后一版计费优化。正因为这个项目到北京面试的时候得到了一个高薪的机会,这个下面再讲。 - -其实在这个公司里面,项目中规中矩,代码也是主流的框架和技术,一年多的时间稳扎稳打让我对大项项目框架和设计有了很多的认识,特别是爬虫什么的让我非常兴奋。那时候中午我经常看博客园的新闻,整天都是互联网公司怎么怎么了,但是西安基本没有一家正正经经的互联网公司,于是就有了去北京想法,刚好jerry也有这样的想法13年过年后,大家纷纷提离职,准备去帝都呼吸新鲜的雾霾。 - -记得是11年十一过后入职的,当天一起入职了四名同事,我什么要强调这个呢,因为这四个同事到现在为止,都成了我职场后关系最好的四个好基友,其中有一个女孩,但我们仍然这样认为。jerry、波仔和鸽子,我们四个各有特点,jerry就是那种极客精神,喜欢各种硬件、软件,做了好几个网站,创业几次,目前创业中;波仔,天生搞笑天王,唱歌天王,在生活中带来无限的乐趣,跟他在一起永远是欢笑不断;鸽子,是女汉子或者是逍遥着,天生喜欢流浪、喝酒,拉萨、云南、日本、台湾、英国各处流浪,永远给人一种激情满满的感觉。 - -我们甚至创建了自己的户外组织Flyever,有自己的官网,甚至印了自己的队旗,logo和口号:自由 梦想!每月组织去排山,腐败、各种活动。发工资了说今天活动一下吧,十几个人就去吃饭喝酒,晚上通宵唱歌;世界末日了说去庆祝下吧,这两天心情不好,去活动一下吧!找各种借口去腐败,爬了很多山,喝了数不清多少瓶的9度。这是曾经的官网[www.flyever.cn](http://www.flyever.cn) - -放一张我们当时一个活动策划的截图 -![fly](http://favorites.ren/assets/images/2016/flyever.png) - -在放几张我们去过的地方 - -青海湖的太阳 -![fly](http://favorites.ren/assets/images/2016/qhh.jpg) - -芦苇荡 -![fly](http://favorites.ren/assets/images/2016/flys.jpeg) - - -## 北上帝都 - -来到北京的时候,我身上只剩了3000快钱,1000多在分钟寺(现在已经拆迁)租了个公寓,买了些日常用品后就剩1千多了,我专门挑互联网公司来投简历,大概头了100份左右,找了10家去面试,一周内面试完拿到了5份offer,其中有一家给的特别高就是因为我以前做了UCmanager这个项目,他们公司刚好给爱立信做项目,缺少这样项目经验的人,我犹豫了很久,毕竟工资给的很高,但最后还是放弃了,进了一家第三方支付公司。 - -### 第三方支付公司 - -选择第三方支付公司的时候,其实我还不是特别了解这个行业,只是觉得支付应该是比较不错的。这家公司也是我现在公司的母公司,刚入职的时候感觉周围一大片全都是大牛!公司还管饭,感觉特别好,没过了几天,就被同事拉进了一个XX山炮群,又开启了胡吃海喝的时代,经常私下我们几个组织着去AA聚餐,各种吹牛,各种烧烤啤酒也别有一番风味,混熟了之后,才发现和我一样大家都是屌丝,有一次部门聚餐的时候,部门经理说,大家都举下手看看大家都是那个省的,结果几乎每个省都是一两个这种,看着来自五湖四海的同事在一起工作,也是一番景象。 - -刚来公司做的是,第三方支付的前置接入系统,当时公司每天交易额刚刚上亿,服务压力非常大,每天各种报警,我们就辅助从前置开始跟踪,慢慢的对业务有了了解,后来也写了专门的压测程序来跑。再后来开始负责公司官网的改版,收银台改版,到后来开始了解J8583,银行接入平台 慢慢的才对第三方支付有了一个整体的了解,13年底的时候慢慢的兴起了很多p2p,很多公司在做对接平台,那时候大家都不懂什么是p2p,我们也不懂,也是一边学习一边搞,项目持续做了很久,我带了两三个人来做后端。最后这个项目也没有做起来,因为我们还是不了解p2p公司到底需要什么,自己琢磨的东西还是不太靠谱。 - -14初的时候总监,偷偷告诉我要封闭去做一个关于金融的项目,想让我参加,没想到正是这个项目对我的职场有如此大的影响,所以说关键时候的选择非常的重要。紧接着没过几天,就开了几个车拉着我们去了四星级酒店开始封闭开发,后来方案定了使用PHP开发之后,我又撤了回来做平台接口层的开发。大约过了一个月项目基本完成了,大家回来后项目组因为一个老总出走,带走了一个团队,剩下的开发几乎都走光了,那时候其实我也找好了工作,新的公司待遇和环境都不错,领导找我谈话,想让我负责这个项目。我对领导说我先考虑一下,等我休假回来后给回复。那段时间太累了加了很多班,请了好几天假去了青海湖大玩了几天,朋友都建议我去新的公司,后来我考虑一下还是选择了留下。 - -![hkrt](http://favorites.ren/assets/images/2016/hkrt.jpg) - - -### 互联网金融 - -那个封闭的项目就是互联网金融,那时候互联网金融已经慢慢热了起来,14年我们上线的时候应该是最后的一波热潮了。直到今天我们公司在行业的排名都在20-60之间来回。 - -14年初的时候大家搞的p2p都是网页版,app端有几个公司有,但都很基础,当时公司人力有限,就面临一个选择先做APP还是网站的问题,其实APP的问题主要是通道的问题,当时快捷支付应用到P2P公司还是非常难的。最后领导还是拍板先上APP,大家就集中人力先做APP,最后证明这是个正确的选择,现在监控我们公司的交易,几乎百分之80来自APP,第一次感觉移动互联网的浪潮就是这样来的。 - -系统刚投产的那段时间,交易量火爆,最夸张的时候1000万的标的几秒钟就满了,虽然现在平台也是这样。但当时对我们来讲还是蛮震惊的。在交易量火爆的情况下,系统出现了各种问题,首先是秒杀的时候控制不住并发,有时候会出现超卖的现象,最后各种优化,通过memcahed锁解决了这个问题,紧接着服务器又因为流量太大扛不住了,于是又上线lvs做负载。期间各种问题不断,那段时间我几乎晚上11点之前没有回过家,每次我走的时候老婆还在睡觉,回来的时候她又睡着了,周末也是各种加班,总感觉自己见不到太阳,持续了很久,但是成长也是非常的大。 - -因为我们的前端是PHP写的我又逼着自己学习了PHP,从开始能看懂,到最后可以写一点。公司慢慢上线了官网,又增加了小网页(H5),各种分布式系统改造。做各种活动,和滴滴打车做活动、和河狸家做活动、和携程做活动等等,有一次一天注册了X千个用户,惊呆了,发现羊毛党来了,又是各种限制。在后来慢慢的有黑客盯上了我们,各种骚扰,DDOS攻击,SQL注入等等。反正是能遇到的问题我们基本都遇到了,每一次问题之后,我们系统就又健壮了一些。 - -再后来要做大数据分析,我们又开始启动golang+monggodb这套方案来做大数据,刚开始也很困难,但是大家对新技术的这种渴望战胜了一切;再后来上线了dubbo做SOA服务治理,到现在启动spring boot+cloud。我们的系统也从第一代平台开始到现在第四代平台更换中,对这四代平台做一个简单的介绍: 第一代平台,主要是集中式,以快速上线为目的;第二代平台主要是分布式改造,缓解各服务压力;第三代平台主要做服务端SOA治理,后台统一账户中心;第四代微服务化改造,已达到灰度上线、动态部署集中管理的目的。 - -我也从负责Java端,到负责整个技术团队,慢慢的在领导的信任下测试交给了我,再后来分公司独立后将运维也交给了我,于是成了整个分公司的技术负责人。这就是我的故事。未来仍然有更多的挑战,感谢我们团队的兄弟姐妹,感谢工作中遇到的所有同事和领导。 - -我特别喜欢一句话在这里分享给大家: - -> 我的代码曾运行在几千万用户的机器上,作为一个程序员,还有什么比这更让人满足的呢?如果有,那就是让这个用户数量再扩大 10 倍。 - - -![zxjr](http://favorites.ren/assets/images/2016/zxjr.jpg) - -**路漫漫,欢迎大家在博文下面回复自己个人的经历,以共勉!** - -------------- - -**作者:纯洁的微笑** -**出处:[www.ityouknow.com](http://www.ityouknow.com)** -**版权所有,欢迎保留原文链接进行转载:)** - - - diff --git a/_posts/2016/2016-11-25-spring-boot-multi-mybatis.md b/_posts/2016/2016-11-25-spring-boot-multi-mybatis.md deleted file mode 100644 index 92338ebd37..0000000000 --- a/_posts/2016/2016-11-25-spring-boot-multi-mybatis.md +++ /dev/null @@ -1,206 +0,0 @@ ---- -layout: post -title: Spring Boot(七):Mybatis 多数据源最简解决方案 -category: springboot -tags: [springboot] -copyright: java ---- - -说起多数据源,一般都来解决那些问题呢,主从模式或者业务比较复杂需要连接不同的分库来支持业务。我们遇到的情况是后者,网上找了很多,大都是根据 Jpa 来做多数据源解决方案,要不就是老的 Spring 多数据源解决方案,还有的是利用 Aop 动态切换,感觉有点小复杂,其实我只是想找一个简单的多数据支持而已,折腾了两个小时整理出来,供大家参考。 - ->废话不多说直接上代码吧 - -我们以 Mybatis Xml 版本为例,给大家展示如何如何配置多数据源。 - -## 配置文件 - -Pom 包就不贴了比较简单该依赖的就依赖,主要是数据库这边的配置: - -``` properties -mybatis.config-location=classpath:mybatis/mybatis-config.xml - -spring.datasource.test1.jdbc-url=jdbc:mysql://localhost:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true -spring.datasource.test1.username=root -spring.datasource.test1.password=root -spring.datasource.test1.driver-class-name=com.mysql.cj.jdbc.Driver - -spring.datasource.test2.jdbc-url=jdbc:mysql://localhost:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true -spring.datasource.test2.username=root -spring.datasource.test2.password=root -spring.datasource.test2.driver-class-name=com.mysql.cj.jdbc.Driver -``` - -一个 test1 库和一个 test2 库,其中 test1 位主库,在使用的过程中必须指定主库,不然会报错。 - - -## 数据源配置 - -``` java -@Configuration -@MapperScan(basePackages = "com.neo.mapper.test1", sqlSessionTemplateRef = "test1SqlSessionTemplate") -public class DataSource1Config { - - @Bean(name = "test1DataSource") - @ConfigurationProperties(prefix = "spring.datasource.test1") - @Primary - public DataSource testDataSource() { - return DataSourceBuilder.create().build(); - } - - @Bean(name = "test1SqlSessionFactory") - @Primary - public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource) throws Exception { - SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); - bean.setDataSource(dataSource); - bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/test1/*.xml")); - return bean.getObject(); - } - - @Bean(name = "test1TransactionManager") - @Primary - public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) { - return new DataSourceTransactionManager(dataSource); - } - - @Bean(name = "test1SqlSessionTemplate") - @Primary - public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { - return new SqlSessionTemplate(sqlSessionFactory); - } -} -``` - -最关键的地方就是这块了,一层一层注入,首先创建 DataSource,然后创建 SqlSessionFactory 再创建事务,最后包装到 SqlSessionTemplate 中。其中需要指定分库的 mapper 文件地址,以及分库dao层代码 - -``` -@MapperScan(basePackages = "com.neo.mapper.test1", sqlSessionTemplateRef = "test1SqlSessionTemplate") -``` - -这块的注解就是指明了扫描 dao 层,并且给 dao 层注入指定的 SqlSessionTemplate。所有`@Bean`都需要按照命名指定正确。 - - -## dao 层和 xml层 - -dao 层和 xml 需要按照库来分在不同的目录,比如:test1 库 dao 层在 `com.neo.mapper.test1` 包下,test2 库在`com.neo.mapper.test2` - -``` java -public interface User1Mapper { - List getAll(); - UserEntity getOne(Long id); - void insert(UserEntity user); - void update(UserEntity user); - void delete(Long id); -} -``` - -xml 层 - -``` xml - - - - - - - - - - - id, userName, passWord, user_sex, nick_name - - - - - - - - INSERT INTO - users - (userName,passWord,user_sex) - VALUES - (#{userName}, #{passWord}, #{userSex}) - - - - UPDATE - users - SET - userName = #{userName}, - passWord = #{passWord}, - nick_name = #{nickName} - WHERE - id = #{id} - - - - DELETE FROM - users - WHERE - id =#{id} - - - -``` - - -## 测试 - -测试可以使用 SpringBootTest,也可以放到 Controller中,这里只贴 Controller 层的使用 - - -``` java -@RestController -public class UserController { - - @Autowired - private User1Mapper user1Mapper; - - @Autowired - private User2Mapper user2Mapper; - - @RequestMapping("/getUsers") - public List getUsers() { - List users=user1Mapper.getAll(); - return users; - } - - @RequestMapping("/getUser") - public UserEntity getUser(Long id) { - UserEntity user=user2Mapper.getOne(id); - return user; - } - - @RequestMapping("/add") - public void save(UserEntity user) { - user2Mapper.insert(user); - } - - @RequestMapping(value="update") - public void update(UserEntity user) { - user2Mapper.update(user); - } - - @RequestMapping(value="/delete/{id}") - public void delete(@PathVariable("id") Long id) { - user1Mapper.delete(id); - } - -} -``` - -Mybatis 注解版本配置多数据源和 Xml 版本基本一致,搭建可以参考文末的示例项目。 - -> 文章内容已经升级到 Spring Boot 2.x - -**[示例代码-github](https://github.com/ityouknow/spring-boot-examples/tree/master/spring-boot-mybatis)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-boot-examples/tree/master/spring-boot-mybatis)** diff --git a/_posts/2016/2016-11-30-spring-boot-rabbitMQ.md b/_posts/2016/2016-11-30-spring-boot-rabbitMQ.md deleted file mode 100644 index b04e681833..0000000000 --- a/_posts/2016/2016-11-30-spring-boot-rabbitMQ.md +++ /dev/null @@ -1,437 +0,0 @@ ---- -layout: post -title: Spring Boot(八):RabbitMQ 详解 -category: springboot -tags: [springboot] -copyright: java -lock: need ---- - -RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。 - -消息中间件在互联网公司的使用中越来越多,刚才还看到新闻阿里将 RocketMQ 捐献给了 Apache,当然了今天的主角还是讲 RabbitMQ。消息中间件最主要的作用是解耦,中间件最标准的用法是生产者生产消息传送到队列,消费者从队列中拿取消息并处理,生产者不用关心是谁来消费,消费者不用关心谁在生产消息,从而达到解耦的目的。在分布式的系统中,消息队列也会被用在很多其它的方面,比如:分布式事务的支持,RPC 的调用等等。 - -以前一直使用的是 ActiveMQ,在实际的生产使用中也出现了一些小问题,在网络查阅了很多的资料后,决定尝试使用 RabbitMQ 来替换 ActiveMQ,RabbitMQ 的高可用性、高性能、灵活性等一些特点吸引了我们,查阅了一些资料整理出此文。 - -## RabbitMQ 介绍 - -RabbitMQ 是实现 AMQP(高级消息队列协议)的消息中间件的一种,最初起源于金融系统,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。 RabbitMQ 主要是为了实现系统之间的双向解耦而实现的。当生产者大量产生数据时,消费者无法快速消费,那么需要一个中间层。保存这个数据。 - -AMQP,即 Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。AMQP 的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。 - -RabbitMQ 是一个开源的 AMQP 实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP 等,支持 AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。 - - -### 相关概念 - -通常我们谈到队列服务, 会有三个概念: 发消息者、队列、收消息者,RabbitMQ 在这个基本概念之上, 多做了一层抽象, 在发消息者和 队列之间, 加入了交换器 (Exchange). 这样发消息者和队列就没有直接联系, 转而变成发消息者把消息给交换器, 交换器根据调度策略再把消息再给队列。 - -![](http://favorites.ren/assets/images/2016/RabbitMQ01.png) - -- 左侧 P 代表 生产者,也就是往 RabbitMQ 发消息的程序。 -- 中间即是 RabbitMQ,_其中包括了 交换机 和 队列。_ -- 右侧 C 代表 消费者,也就是往 RabbitMQ 拿消息的程序。 - -那么,_其中比较重要的概念有 4 个,分别为:虚拟主机,交换机,队列,和绑定。_ - -- 虚拟主机:一个虚拟主机持有一组交换机、队列和绑定。为什么需要多个虚拟主机呢?很简单, RabbitMQ 当中,_用户只能在虚拟主机的粒度进行权限控制。_ 因此,如果需要禁止A组访问B组的交换机/队列/绑定,必须为A和B分别创建一个虚拟主机。每一个 RabbitMQ 服务器都有一个默认的虚拟主机“/”。 -- 交换机:_Exchange 用于转发消息,但是它不会做存储_ ,如果没有 Queue bind 到 Exchange 的话,它会直接丢弃掉 Producer 发送过来的消息。 -这里有一个比较重要的概念:**路由键** 。消息到交换机的时候,交互机会转发到对应的队列中,那么究竟转发到哪个队列,就要根据该路由键。 -- 绑定:也就是交换机需要和队列相绑定,这其中如上图所示,是多对多的关系。 - - -### 交换机(Exchange) - -交换机的功能主要是接收消息并且转发到绑定的队列,交换机不存储消息,在启用ack模式后,交换机找不到队列会返回错误。交换机有四种类型:Direct, topic, Headers and Fanout - -- Direct:direct 类型的行为是"先匹配, 再投送". 即在绑定时设定一个 **routing_key**, 消息的**routing_key** 匹配时, 才会被交换器投送到绑定的队列中去. -- Topic:按规则转发消息(最灵活) -- Headers:设置 header attribute 参数类型的交换机 -- Fanout:转发消息到所有绑定队列 - - -**Direct Exchange** - -Direct Exchange 是 RabbitMQ 默认的交换机模式,也是最简单的模式,根据key全文匹配去寻找队列。 -![](http://favorites.ren/assets/images/2016/rabbitMq_direct.png) - -第一个 X - Q1 就有一个 binding key,名字为 orange; X - Q2 就有 2 个 binding key,名字为 black 和 green。_当消息中的 路由键 和 这个 binding key 对应上的时候,那么就知道了该消息去到哪一个队列中。_ - -Ps:为什么 X 到 Q2 要有 black,green,2个 binding key呢,一个不就行了吗? - 这个主要是因为可能又有 Q3,而Q3只接受 black 的信息,而Q2不仅接受black 的信息,还接受 green 的信息。 - - -**Topic Exchange** - -_Topic Exchange 转发消息主要是根据通配符。_ 在这种交换机下,队列和交换机的绑定会定义一种路由模式,那么,通配符就要在这种路由模式和路由键之间匹配后交换机才能转发消息。 - -在这种交换机模式下: - -- 路由键必须是一串字符,用句号(`.`) 隔开,比如说 agreements.us,或者 agreements.eu.stockholm 等。 -- 路由模式必须包含一个 星号(`*`),主要用于匹配路由键指定位置的一个单词,比如说,一个路由模式是这样子:agreements..b.*,那么就只能匹配路由键是这样子的:第一个单词是 agreements,第四个单词是 b。 井号(#)就表示相当于一个或者多个单词,例如一个匹配模式是 agreements.eu.berlin.#,那么,以agreements.eu.berlin 开头的路由键都是可以的。 - -具体代码发送的时候还是一样,第一个参数表示交换机,第二个参数表示 routing key,第三个参数即消息。如下: - -``` -rabbitTemplate.convertAndSend("testTopicExchange","key1.a.c.key2", " this is RabbitMQ!"); -``` - -topic 和 direct 类似, 只是匹配上支持了"模式", 在"点分"的 routing_key 形式中, 可以使用两个通配符: - -- `*`表示一个词. -- `#`表示零个或多个词. - -**Headers Exchange** - -headers 也是根据规则匹配, 相较于 direct 和 topic 固定地使用 routing_key , headers 则是一个自定义匹配规则的类型. -在队列与交换器绑定时, 会设定一组键值对规则, 消息中也包括一组键值对( headers 属性), 当这些键值对有一对, 或全部匹配时, 消息被投送到对应队列. - -**Fanout Exchange** - -Fanout Exchange 消息广播的模式,不管路由键或者是路由模式,_会把消息发给绑定给它的全部队列_,如果配置了 routing_key 会被忽略。 - -## Spring Boot 集成 RabbitMQ - -Spring Boot 集成 RabbitMQ 非常简单,如果只是简单的使用配置非常少,Spring Boot 提供了`spring-boot-starter-amqp` 项目对消息各种支持。 - - -### 简单使用 - -1、配置 Pom 包,主要是添加 `spring-boot-starter-amqp` 的支持 - -``` xml - - org.springframework.boot - spring-boot-starter-amqp - -``` - -2、配置文件 - -配置 RabbitMQ 的安装地址、端口以及账户信息 - -``` properties -spring.application.name=Spring-boot-rabbitmq - -spring.rabbitmq.host=192.168.0.86 -spring.rabbitmq.port=5672 -spring.rabbitmq.username=admin -spring.rabbitmq.password=123456 -``` - -3、队列配置 - -``` java -@Configuration -public class RabbitConfig { - - @Bean - public Queue Queue() { - return new Queue("hello"); - } - -} -``` - -3、发送者 - -rabbitTemplate 是 Spring Boot 提供的默认实现 - -``` java -@component -public class HelloSender { - - @Autowired - private AmqpTemplate rabbitTemplate; - - public void send() { - String context = "hello " + new Date(); - System.out.println("Sender : " + context); - this.rabbitTemplate.convertAndSend("hello", context); - } - -} -``` - -4、接收者 - -``` java -@Component -@RabbitListener(queues = "hello") -public class HelloReceiver { - - @RabbitHandler - public void process(String hello) { - System.out.println("Receiver : " + hello); - } - -} -``` - -5、测试 - -``` java -@RunWith(SpringRunner.class) -@SpringBootTest -public class RabbitMqHelloTest { - - @Autowired - private HelloSender helloSender; - - @Test - public void hello() throws Exception { - helloSender.send(); - } - -} -``` - -> 注意,发送者和接收者的 queue name 必须一致,不然不能接收 - - - -### 多对多使用 - -一个发送者,N 个接收者或者 N 个发送者和 N 个接收者会出现什么情况呢? - -**一对多发送** - -对上面的代码进行了小改造,接收端注册了两个 Receiver,Receiver1 和 Receiver2,发送端加入参数计数,接收端打印接收到的参数,下面是测试代码,发送一百条消息,来观察两个接收端的执行效果 - -``` java -@Test -public void oneToMany() throws Exception { - for (int i=0;i<100;i++){ - neoSender.send(i); - } -} -``` - -结果如下: - -``` xml -Receiver 1: Spring boot neo queue ****** 11 -Receiver 2: Spring boot neo queue ****** 12 -Receiver 2: Spring boot neo queue ****** 14 -Receiver 1: Spring boot neo queue ****** 13 -Receiver 2: Spring boot neo queue ****** 15 -Receiver 1: Spring boot neo queue ****** 16 -Receiver 1: Spring boot neo queue ****** 18 -Receiver 2: Spring boot neo queue ****** 17 -Receiver 2: Spring boot neo queue ****** 19 -Receiver 1: Spring boot neo queue ****** 20 - -``` - -根据返回结果得到以下结论 -> 一个发送者,N个接受者,经过测试会均匀的将消息发送到N个接收者中 - - -**多对多发送** - -复制了一份发送者,加入标记,在一百个循环中相互交替发送 - -``` java -@Test - public void manyToMany() throws Exception { - for (int i=0;i<100;i++){ - neoSender.send(i); - neoSender2.send(i); - } -} - -``` - -结果如下: - -``` xml -Receiver 1: Spring boot neo queue ****** 20 -Receiver 2: Spring boot neo queue ****** 20 -Receiver 1: Spring boot neo queue ****** 21 -Receiver 2: Spring boot neo queue ****** 21 -Receiver 1: Spring boot neo queue ****** 22 -Receiver 2: Spring boot neo queue ****** 22 -Receiver 1: Spring boot neo queue ****** 23 -Receiver 2: Spring boot neo queue ****** 23 -Receiver 1: Spring boot neo queue ****** 24 -Receiver 2: Spring boot neo queue ****** 24 -Receiver 1: Spring boot neo queue ****** 25 -Receiver 2: Spring boot neo queue ****** 25 -``` - -> 结论:和一对多一样,接收端仍然会均匀接收到消息 - - -### 高级使用 - - -**对象的支持** - -Spring Boot 以及完美的支持对象的发送和接收,不需要格外的配置。 - - -``` java -//发送者 -public void send(User user) { - System.out.println("Sender object: " + user.toString()); - this.rabbitTemplate.convertAndSend("object", user); -} - -... - -//接收者 -@RabbitHandler -public void process(User user) { - System.out.println("Receiver object : " + user); -} -``` - -结果如下: - -``` xml -Sender object: User{name='neo', pass='123456'} -Receiver object : User{name='neo', pass='123456'} -``` - - -**Topic Exchange** - -topic 是 RabbitMQ 中最灵活的一种方式,可以根据 routing_key 自由的绑定不同的队列 - - -首先对 topic 规则配置,这里使用两个队列来测试 - -``` java -@Configuration -public class TopicRabbitConfig { - - final static String message = "topic.message"; - final static String messages = "topic.messages"; - - @Bean - public Queue queueMessage() { - return new Queue(TopicRabbitConfig.message); - } - - @Bean - public Queue queueMessages() { - return new Queue(TopicRabbitConfig.messages); - } - - @Bean - TopicExchange exchange() { - return new TopicExchange("exchange"); - } - - @Bean - Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) { - return BindingBuilder.bind(queueMessage).to(exchange).with("topic.message"); - } - - @Bean - Binding bindingExchangeMessages(Queue queueMessages, TopicExchange exchange) { - return BindingBuilder.bind(queueMessages).to(exchange).with("topic.#"); - } -} -``` - - -使用 queueMessages 同时匹配两个队列,queueMessage 只匹配 "topic.message" 队列 - -``` java -public void send1() { - String context = "hi, i am message 1"; - System.out.println("Sender : " + context); - this.rabbitTemplate.convertAndSend("exchange", "topic.message", context); -} - -public void send2() { - String context = "hi, i am messages 2"; - System.out.println("Sender : " + context); - this.rabbitTemplate.convertAndSend("exchange", "topic.messages", context); -} -``` - -发送send1会匹配到topic.#和topic.message 两个Receiver都可以收到消息,发送send2只有topic.#可以匹配所有只有Receiver2监听到消息 - -**Fanout Exchange** - -Fanout 就是我们熟悉的广播模式或者订阅模式,给 Fanout 交换机发送消息,绑定了这个交换机的所有队列都收到这个消息。 - -Fanout 相关配置 - -``` java -@Configuration -public class FanoutRabbitConfig { - - @Bean - public Queue AMessage() { - return new Queue("fanout.A"); - } - - @Bean - public Queue BMessage() { - return new Queue("fanout.B"); - } - - @Bean - public Queue CMessage() { - return new Queue("fanout.C"); - } - - @Bean - FanoutExchange fanoutExchange() { - return new FanoutExchange("fanoutExchange"); - } - - @Bean - Binding bindingExchangeA(Queue AMessage,FanoutExchange fanoutExchange) { - return BindingBuilder.bind(AMessage).to(fanoutExchange); - } - - @Bean - Binding bindingExchangeB(Queue BMessage, FanoutExchange fanoutExchange) { - return BindingBuilder.bind(BMessage).to(fanoutExchange); - } - - @Bean - Binding bindingExchangeC(Queue CMessage, FanoutExchange fanoutExchange) { - return BindingBuilder.bind(CMessage).to(fanoutExchange); - } - -} -``` - -这里使用了 A、B、C 三个队列绑定到 Fanout 交换机上面,发送端的 routing_key 写任何字符都会被忽略: - -``` java -public void send() { - String context = "hi, fanout msg "; - System.out.println("Sender : " + context); - this.rabbitTemplate.convertAndSend("fanoutExchange","", context); -} -``` - -结果如下: - -``` xml -Sender : hi, fanout msg -... -fanout Receiver B: hi, fanout msg -fanout Receiver A : hi, fanout msg -fanout Receiver C: hi, fanout msg -``` - -结果说明,绑定到 fanout 交换机上面的队列都收到了消息 - -> 文章内容已经升级到 Spring Boot 2.x - - -**[示例代码-github](https://github.com/ityouknow/spring-boot-examples/tree/master/spring-boot-rabbitmq)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-boot-examples/tree/master/spring-boot-rabbitmq)** - -## 参考 - -[RabbitMQ 使用参考](https://www.zouyesheng.com/rabbitmq.html) - -[RabbitMQ:Spring 集成 RabbitMQ 与其概念,消息持久化,ACK机制](https://github.com/401Studio/WeekLearn/issues/2) diff --git a/_posts/2016/2016-12-02-spring-boot-scheduler.md b/_posts/2016/2016-12-02-spring-boot-scheduler.md deleted file mode 100644 index 7e80fc0322..0000000000 --- a/_posts/2016/2016-12-02-spring-boot-scheduler.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -layout: post -title: Spring Boot(九):定时任务 -category: springboot -tags: [springboot] -copyright: java ---- - -在我们开发项目过程中,经常需要定时任务来帮助我们来做一些内容, Spring Boot 默认已经帮我们实行了,只需要添加相应的注解就可以实现 - - -## 1、pom 包配置 - -pom 包里面只需要引入 Spring Boot Starter 包即可 - -``` xml - - - org.springframework.boot - spring-boot-starter - - - org.springframework.boot - spring-boot-starter-test - test - - -``` - - -## 2、启动类启用定时 - -在启动类上面加上`@EnableScheduling`即可开启定时 - -``` java -@SpringBootApplication -@EnableScheduling -public class Application { - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } -} -``` - - -## 3、创建定时任务实现类 - -定时任务1: - -``` java -@Component -public class SchedulerTask { - - private int count=0; - - @Scheduled(cron="*/6 * * * * ?") - private void process(){ - System.out.println("this is scheduler task runing "+(count++)); - } - -} -``` - - -定时任务2: - -``` java -@Component -public class Scheduler2Task { - - private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); - - @Scheduled(fixedRate = 6000) - public void reportCurrentTime() { - System.out.println("现在时间:" + dateFormat.format(new Date())); - } - -} -``` - - -结果如下: - -``` xml -this is scheduler task runing 0 -现在时间:09:44:17 -this is scheduler task runing 1 -现在时间:09:44:23 -this is scheduler task runing 2 -现在时间:09:44:29 -this is scheduler task runing 3 -现在时间:09:44:35 -``` - - -## 参数说明 - -`@Scheduled` 参数可以接受两种定时的设置,一种是我们常用的`cron="*/6 * * * * ?"`,一种是 `fixedRate = 6000`,两种都表示每隔六秒打印一下内容。 - -**fixedRate 说明** - -- `@Scheduled(fixedRate = 6000)` :上一次开始执行时间点之后6秒再执行 -- `@Scheduled(fixedDelay = 6000)` :上一次执行完毕时间点之后6秒再执行 -- `@Scheduled(initialDelay=1000, fixedRate=6000)` :第一次延迟1秒后执行,之后按 fixedRate 的规则每6秒执行一次 - -> 文章内容已经升级到 Spring Boot 2.x - -**[示例代码-github](https://github.com/ityouknow/spring-boot-examples/tree/master/spring-boot-scheduler)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-boot-examples/tree/master/spring-boot-scheduler)** - - diff --git a/_posts/2016/2016-12-30-springcloud-collect.md b/_posts/2016/2016-12-30-springcloud-collect.md deleted file mode 100644 index 89da6c98ad..0000000000 --- a/_posts/2016/2016-12-30-springcloud-collect.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -layout: post -title: Spring Cloud 学习资料汇总 -category: springcloud -tags: [springcloud] ---- - -收集 Spring Cloud 相关的学习资料 - - -> 学习 Spring Cloud 首先需要了解 Spring Boot,不了解 Spring Boot 的同学戳这里[Spring Boot学习资料汇总](http://www.ityouknow.com/springboot/2015/12/30/springboot-collect.html) - - -**重点推荐:[Spring Cloud 中文索引](http://springcloud.fun/)** - - -## 推荐博客 - -- [纯洁的微笑 Spring Cloud 系列文章](http://www.ityouknow.com/spring-cloud) -- [windmt一spring cloud](https://windmt.com/tags/Spring-Cloud/) -- [方志朋 Spring Cloud 专栏](http://blog.csdn.net/column/details/15197.html) -- [许进 跟我学 Spring Cloud](http://xujin.org/categories/%E8%B7%9F%E6%88%91%E5%AD%A6Spring-Cloud/) -- [liaokailin的专栏 Spring Cloud](http://blog.csdn.net/liaokailin/article/category/6212338) -- [猿天地尹吉欢 Spring Cloud](http://cxytiandi.com/blog/detail/17470) -- [唐亚峰 Battcn 起来学SpringCloud](https://blog.battcn.com/categories/SpringCloud/) -- [yjclsx spring cloud之路](https://blog.csdn.net/column/details/24531.html) -- [aoho spring cloud](http://blueskykong.com/tags/Spring-Cloud) -- [江南一点雨 Spring Cloud ](https://wangsong.blog.csdn.net/column/info/17373) - - -## 开源 - -- [纯洁的微笑的 Spring Cloud 示例](https://github.com/ityouknow/spring-cloud-examples) -- [spring cloud + vue 全家桶实战,模拟商城,完整的购物流程](https://github.com/paascloud/paascloud-master) -- [PiggyMetrics-一个供个人处理财务的解决方案](https://github.com/sqshq/PiggyMetrics) -- [基于Spring Cloud Netflix的TCC柔性事务和EDA事件驱动示例](https://github.com/prontera/spring-cloud-rest-tcc) -- [方志朋 SpringCloudLearning](https://github.com/forezp/SpringCloudLearning) -- [一套基于springcloud + mybatis + vue全家桶](https://github.com/OptionalDay/spring-cloud-vue) -- [cloudE 基于spring cloud的分布式系统架构](https://github.com/vangao1989/cloudE) -- [shop spring cloud最佳实践项目实例](https://github.com/lrwinx/shop) -- [Cloud-Admin是国内首个基于Spring Cloud微服务化开发平台](https://gitee.com/minull/ace-security) -- [spring-boot-cloud综合练手项目](https://github.com/zhangxd1989/spring-boot-cloud) -- [基于Spring Cloud的在线考试系统](https://gitee.com/wells2333/spring-cloud-online-exam) -- [基于SpringCloud的微服务架构实战案例项目,以一个简单的购物流程为示例](https://github.com/backkoms/simplemall) -- [XxPay 使用Spring Cloud实现的聚合支付](https://gitee.com/jmdhappy/xxpay-master) -- [FCat项目基于 Angular 4 + Spring Cloud 的企业级基础功能框架](https://gitee.com/xfdm/FCat) -- [基于Spring Cloud、oAuth2.0开发基于Vue前后分离的开发平台](https://gitee.com/log4j/pig) - - -## 网站 - -- [Spring Cloud 官网](http://projects.spring.io/spring-cloud/) -- [Spring Cloud 中国社区](http://springcloud.cn/) -- [Spring Cloud 中文网](https://springcloud.cc/) -- [网易云课堂 Spring Cloud 视频](http://study.163.com/courses-search?keyword=Spring%20Cloud) -- [Spring Cloud 参考指南- 英文版](https://projects.spring.io/spring-cloud/spring-cloud.html) -- [Nepxion](https://github.com/Nepxion/Aquarius) - - -## 其它 - -- [Spring Boot 中文索引](https://github.com/ityouknow/awesome-spring-boot) -- [程序员导航网站](http://tooool.org/) -- [IT行业中文资源大全](https://github.com/ityouknow/awesome-list) - - - - -**欢迎大家推荐更多的资料** \ No newline at end of file diff --git a/_posts/2017/2017-01-01-2016-end-of-the-year.md b/_posts/2017/2017-01-01-2016-end-of-the-year.md deleted file mode 100644 index 8eb2c025ec..0000000000 --- a/_posts/2017/2017-01-01-2016-end-of-the-year.md +++ /dev/null @@ -1,159 +0,0 @@ ---- -layout: post -title: 2016颠倒梦想,2017静心前行 -category: life -tags: [life] ---- - - - -> 世人都晓神仙好,惟有功名忘不了! 古今将相在何方?荒冢一堆草没了. - - -*-- 出处 [《好了歌--跛足道人》](http://liuyue.ren/2016/12/03/%E5%A5%BD%E4%BA%86%E6%AD%8C/)* - - -![](http://favorites.ren/assets/images/2017/haze.jpg) - -今天是2017年的第一天,早上打开朋友圈不出所料,各种欢送16,又是酒吧,又是灯火,热闹纷繁,貌似都在兴高采烈的迎接新年;朝着窗户向南望去,北京一片云山雾里,打开窗户拍了一张照片(上图),一股厚重的雾霾味迎面而来,各种酸爽嘴里一股沙子味。2016年的最后一天和2017年的第一天都只能待在家里,出去都是在探险;在这新旧年交替的时光中我的心情犹如这张图片一样,厚厚的迷雾,迟迟不能散去。 - - -我想从三个方面来回顾我的2016:环境、技术、生活 - - -## 天朝的环境 - -这里的环境指两方面,社会环境和自然环境。但两者都和这雾霾一样越来越浓厚。 - - -### 社会环境 - -这两天看了一部电影叫做[《驴得水》](https://movie.douban.com/subject/25921812/),一部被噎得说不出话的片子,2016年国产电影评分最高的一部电影。看完后心里一直不是很舒服,这分明就是借古喻今,讽刺我们当下的生活,看完之后在豆瓣翻了翻影评,不知道是被和谐了还是怎么找都是不痛不痒的感觉,看了知乎的影评之后才发现,难道现在高质量的影迷都在知乎上面来发表评论了吗?电影的细节就不在这里说了,大家自行观看,但知乎中有一个评论让我印象特别深刻 - ->每个曾经特立独行的人,都抛弃了曾经的自己。 ->每个曾经想改变中国的人,都被中国所改变。 - -大家都知道我们中国有着各种的墙,在互联网行业有着大名鼎鼎的GFW,屏蔽这谷歌、facebook如此优秀的站点,听说有一阵子要屏蔽github,李开复等大V极力反对才作罢,我在想你怎么不把互联网全部屏蔽了呢,就和朝鲜一样,只有内网,我们现在都到了一个什么样的世界,还是和老的思维一样,以为有了GFW就可以隐瞒真相、屏蔽一切了吗,现在的世界已经极度扁平化,愚民教育已经走向终结。驴得水的高分也不就是一种体现吗? - - -我们的电影也有类似的墙:电影审查制度。就是每部电影上映之前都需要过审,一群的大着肚子,不喑世事的官员,往那儿一坐,这个太消极了,那个太恐怖了,那个太揭露真相了!凡是有一点讽刺政府、有一点和党有所出入的统统毙掉,那怕外国中了大奖,国内一概不许,我们是中国特色社会主义。中国之所以拍不出很多优秀、深刻的电影和这个制度息息相关,看看韩国的熔炉、恐怖直播,真是感觉中国的电影和韩国整整差了10年,我们还是如此的不自信!我一度担心驴得水过不了审,于是导演特意设定了年代1942,特意在结尾的时候奔向了延安,中国好的电影不都是这样才能上映吗?电影里面校长女儿最后说的那句话: - ->**过去的如果都让它就这么过去,以后只会更糟。** - - -### 自然环境 - -上次坐出租车,和一位老司机聊天的时候说起雾霾,刚开始的时候大家都以为是雾,也没啥大不了都认为不伤害身体,后来美国大使馆说这是霾,然后中国极力抗议说这是干涉内政(这和内政有毛线关系呀),后来大家都知道了包不住了开始公布pm2.5,雾霾是一天比一天严重,而且不只是北京,回老家西安也是一个德行,坐火车南下武汉一路过去全是雾霾,大半个中国估计都是。我们真的拿雾霾没有办法了吗?不是,apec蓝 大家都体会了吧,这就是我朝的力量,但是为什么不能长久呢?《苍穹之下》大家应该都看了吧,既得利益者不会这么轻易的放弃自己的筹码。每次看到北京的雾霾,就想到之前看的特别好看的一个电影叫《迷雾》,想着如果在北京来拍摄都不需要什么特景,原地取景即可。我一直在想,中南海的食物和水是特供的,但是空气也能特供吗? - - -在微信上面看了一篇文章[《纪录片海外夺奖背后:中国的污染,比你想象的更严重》](http://mt.sohu.com/20161227/n477034997.shtml),本来想给大家贴出微信原文出来,刚才看的时候已经被屏蔽了,这也是另一道墙起的作用[《网络审查》](https://zh.wikipedia.org/wiki/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD%E7%BD%91%E7%BB%9C%E5%AE%A1%E6%9F%A5),正是这篇文章让我认识到了王久良这样的人物,我一直在想做什么是有意义的事情,这个绝对是!这个纪录片的名字是[《塑料王国》](http://v.youku.com/v_show/id_XMTQ5NDY4OTYzMg==.html)现在优酷上还可以看,像这种作品国外都是得各种奖,但是放到了中国,可能过两天大家又看不了了。看完这部纪录片之后的感觉用四个字来形容:触目惊心! - - -![](http://favorites.ren/assets/images/2017/plastic.png) - -我们从国外高价把垃圾收回来,整理分类在做成塑料产品来用,农民为了挣钱,什么身体健康、环境污染能顾得上吗?这是他们道德低下吗,他们不知道这些垃圾有毒吗?他们都知道,但是他们没有办法,需要活着,需要生存!我原来一直以为我们家乡那边就算贫穷的了,看了这个记录片,在结合兰州[《盛世中的蝼蚁》](http://relatos.top/2016/09/14/%E7%9B%9B%E4%B8%96%E4%B8%AD%E7%9A%84%E8%9D%BC%E8%9A%81/)的这些事情,才知道贫穷在中国还是不少,如今北京高楼大厦,高铁、飞机、互联网高速发展,中国经济飞速前进,这些光鲜靓丽的背后,谁来考虑他们的生存状况! - - - ->怎样对待弱势群体,是一个社会最柔软的部分,恰恰也是一个社会最强大的部分。 ->一个国家是不是真的强大,一定不是出了多少英明领袖,造了多少核弹,有多少外汇储备,在奥运会拿了多少金牌,GDP增长率多高……,这些和杨改兰们没有毛线关系。 ->一个社会的文明程度就是看怎么对待你的弱势群体! - - -雷洋事件我们也看到了结果吧!我们什么时候能够真正的进入一个法治的国家,明文的国家! -以前大家谈起中国各种不好的时候,我深不以为然!我始终以国家为自豪,中国各种变强,反腐大块人心,但是我们仍然还差太多!我有一腔热血,愿意洒在这片土地上,但是这片土地缺乏土壤! - -> 天下兴亡,匹夫有责!爱之深,恨之切! - - - -## 变幻的技术 - -技术总是变化的很快,前端更是一年三个样! - - -### 关于博客 - -终于搭建了自己的博客系统[www.ityouknow.com](http://www.ityouknow.com/),博客园虽然是一个很好的交流的地方,但是对于我们程序员来讲,能有自己的博客系统,那感觉还是不一样,我的博客系统是基于jekyll改造的。jekyll最大的优点是可以托管到github上面,在结合markdown的这样书写方式可以非常方便的写文章,有一种用编码的方式来写文章的感觉,特别好,每次写完文章直接github提交,就自动发布到博客系统中了,自己在网上找了一个 jekyll的主题 [Yummy-Jekyll](https://github.com/DONGChuan/Yummy-Jekyll)搭配最后搞定。现在写文章的过程就是:首先在sublime上以markdown的方式写好文章,然后提交到GitHub上面,最后同步到博客园里面。 - - -2016年也算是我写博客的元年,很早以前就注册了博客园大都是看看文章和新闻很少自己写,一方面觉得自己水平太差,另一方面嫌麻烦。2015年开始零零星星的写了几个,2016才慢慢的找到了感觉,文章的质量不敢说怎么好,但是16年也算是认认真真的来对待它。技术类的文章主要有: - - -- [jvm系类文章](http://www.cnblogs.com/ityouknow/category/437541.html) -- [spring boot系类文章](http://www.ityouknow.com/spring-boot) -- [系统架构类文章](http://www.cnblogs.com/ityouknow/category/809108.html) - - -有两篇文章进入了博客园的最多推荐: - -- [程序员该用哪种姿势来理财](http://www.cnblogs.com/ityouknow/p/5772248.html) -- [六年程序生涯](http://www.cnblogs.com/ityouknow/p/6084208.html) - -其中《六年程序生涯》更是被```程序猿```等公共账号所转载是我始料未及的。 - - -其实通过这些文章发现,博客园里大家还是更喜欢程序员生活感悟带来的一些共鸣,今年也计划会多写一些这方面的文章,我的技术文章还需要更努力。写文章的过程也是和圆友不断交流的过程,正是很多的园友的留言、点赞或者更进一度的沟通,让我才更有动力一直写下去。 - - -### 技术方面 - -由于我司以前都是分布式的开发模式,随着子系统的增多,系统直接的调用越来越复杂,如果更新一个子系统或者更新一个子系统的地址,需要更改很多配置文件重启相关好几个子系统,一直在寻找SOA的解决方案,就用到了DUBBO,我们首先是在后台统一账户中心中做了使用,发现效果还比较好,稳定性也不错,于是在做众筹平台的时候全面启动了dubbo,开发的过程到也没有什么说的,等到了最后投产后,出现问题了。 - -首先是tomcat动不动就内存溢出直接down掉了,然后跟踪日志也没有什么特殊的现象,然后添加脚本在内存溢出的时候让down出内存堆栈信息,发现有很多的对象没有释放,最后才发现在脚本中禁止了代码的GC,但是dubbo框架里面都是启动了Nio需要自己来触发GC,因此导致溢出。去掉脚本中```-XX:+DisableExplicitGC```解决,解决后又出现另外的问题,反正是填了很多坑,最后稳定下来,有时间这方面也终结一个文章。 - - -在开发众筹app的时候因为时间问题也采用了混合开发的模式,原生+vue+html这样的技术桟,APP的主框架和所有的第一页面全部采用原生代码开发,其它二级页面全部是vue+html,那时候vue也才刚刚火起来,对比了react和angular最后还是选择了vue,在后来一点博客园就出现了很多VUE的文章,写的非常好。但是我们初期找了很多Demo来参考,还是填了很多坑才最终投产上线。 - - -在后来就遇到了spring boot,找了几个项目练手之后发现使用起来特别爽,明显的简化了开发模式,而且结合spring cloud可以非常容易,简单的构建一套均衡负载、容错、稳定的平台,注册中心、服务中心、配置中心,路由等一套体系用起了特别爽!相比较起dubbo,spring就是全家桶,微服务的需求几乎都考虑到了,dubbo很久都不更新了,因此我们就全部的拥抱了spring boot,为了更好的学习spring boot我们还开源了一个网站 [云收藏](http://www.favorites.ren/),可以收藏和查看技术文章。 - -![](http://favorites.ren/assets/images/2017/Springcloud.png) - -我也在github上面分享了一些关于spring boot的学习案例 [spring-boot-starter](https://github.com/ityouknow/spring-boot-starter),其中开源的云收藏网站的代码最受欢迎 [网站源码](https://github.com/cloudfavorites/favorites-web),文章介绍:[我们的第一款开源软件](http://www.ityouknow.com/springboot/2016/09/26/springboot%E5%AE%9E%E6%88%98-%E6%88%91%E4%BB%AC%E7%9A%84%E7%AC%AC%E4%B8%80%E6%AC%BE%E5%BC%80%E6%BA%90%E8%BD%AF%E4%BB%B6.html) - - -## 关于生活 - -生活总是让你不可捉摸 - -> 以前我们看电视剧总是感觉这他妈太狗血了吧,但随着年龄的增大才发现生活中的剧情远远比电视剧更狗血。 - - -关于亲人,不管我们身在哪里,亲人永远都是内心最柔软的那个地方,特别是北漂程序员这个群体,远离家乡,一年中和父母、亲人相聚的时间有一个月吗?无论如何多给家里打打电话,多回趟家和父母聚聚,很有可能某个时候,就再也来不及了。2016年是我最沉重的一年,不堪回首。 - -我总说6是我特殊的数字,16年生活中的事情太多,来不及梳理。 - -今年参加了好几个非常好的朋友婚礼,也和女友领了证。好几个朋友生了孩子,一起来北京的很多兄弟,又离开了北京,每年都是这样,一群人奔向北京,一群人选择离开北京,有人为了理想,有人选择了生活。 - -今年在6月份在公交驾校报名学了车,其实朋友包括女友早都学了车,自己一直觉得反正买不起,买了也养不起一直没有学,这次呢是因为想着以后和朋友周末可以租车去玩比较方便于是就学了,科目一没有难度,主要是科目二的倒库最麻烦的,我当初也算是下了功夫,记录了倒车入库的各种知识点,只是最后其实也没啥用,全凭了手感,分享一小段当时记录的: - -- 右倒车入库: -右倒车至后窗1/3处向右打满方向盘,查看右后视镜 根据后轮和右停车脚的距离选择放轮 直到右后脚可以入库 看左前后视镜看到左库脚 方向盘放正 倒车至 倒库前沿线在左后视镜下面位置停车 -- 右倒车出库: -摘倒档挂一档 往前开 看到左后轮出库脚 方向盘左打满 - -关于身体,现在锻炼真的是越来越少了,刚来北京的时候还和朋友打打篮球,没事去游游泳,现在呢,16年除了游泳去过几次,别的活动几乎都拉下来了,做为程序员真的需要多锻炼身体。 - -> 亲人永远都是你内心中最柔软的那个地方 - -其它的事情也不愿意在提起了 - -## 2017年 - -生活,计划永远赶不上变化 - -17年也么什么想计划的,想多陪陪家人,多出去转转。搞好生活,处理好工作,本是平凡的生活,平凡的世界。 - -如果有希望,那就希望环境能变好一点,生活能多一些乐趣,亲人都健健康康一些。 - -![fly](http://favorites.ren/assets/images/2016/qhh.jpg) - - -------------- - -**作者:纯洁的微笑** -**出处:[www.ityouknow.com](http://www.ityouknow.com)** - - - diff --git a/_posts/2017/2017-01-10-ten-billion-architecture-history.md b/_posts/2017/2017-01-10-ten-billion-architecture-history.md deleted file mode 100644 index 5d09bea896..0000000000 --- a/_posts/2017/2017-01-10-ten-billion-architecture-history.md +++ /dev/null @@ -1,160 +0,0 @@ ---- -layout: post -title: 从零到百亿互联网金融架构发展史 -category: arch -tags: [arch] ---- - -回想起从公司成立敲出的第一行代码算起到现在也快三年了,平台的技术架构,技术体系也算是经历了四次比较重大的升级转化(目前第四代架构体系正在进行中),临近年底也想抽出时间来回顾一下,一个小公司从最开始的零交易到现在交易量超过百亿背后的技术变迁。 - -## 总体介绍 - -在互联网金融行业一百多亿其实也算不上大平台,也就是二级阵营吧,其实每次的架构升级都是随着业务重大推进而伴随的,在前一代系统架构上遇到的问题,业务开发过程中积累一些优秀的开发案例,在下一代系统开发中就会大力推进架构升级。一方面可以平滑过度,一方面公司资源可以大力支持,同时技术的小伙伴们可以使用到前沿的技术,更有开发的成就感,就这样我们大概9个月对系统架构做一次升级,就到了我们现在的这套架构中。 - -很多网友经常会问,你们平台的TPS是多少呀,最大并发是多少呀,性能怎么样,说实话我们是一个小公司,最夸张也就上万人同时抢标,但是做为一个中型的互联网金融平台要做的事情也真的不少,远远不只是这些参数可以说的清楚;我们也不是什么高大上的平台,使用的技术也是目前比较主流开源产品,但在公司不断发展的过程中也遇到了很多的问题,也尽量去使用比较主流的、开源的、适合我们的一些解决方案来构建整个系统,在这里分享平台发展背后技术换代的变化,同时希望和大家多做一些交流,多提一些建议。 - - -我们进行了四次大的架构变化,每代架构都用一句话来总结: - ->- 第一代架构特点:业务比较集中、功能满足投资理财需求、快速上线 ->- 第二代架构特点;分布式系统改造,平台化初具规模,各项垂直业务系统搭建上线、产品端极大丰富用户投资、大数据平台研究并使用 ->- 第三代架构特点;SOA治理,使用zookeeper作为注册中心,dubbo做监控和调度中心;cas实现单点登录,使用shiro做权限控制 ->- 第四代架构特点;全面启用微服务开发模式,springboot+springcloud技术桟做为第四代架构技术支撑 - -下面做详细介绍 - -## 第一代系统架构 - -2014年应该算是互联网金融元年,在之前其实已经有很多互联网公司用着各种模式在生存,一直不温不火,但是到2014年突然火爆了起来,首先是网贷之家,网贷天眼这种第三方网站流量突然暴增,接着是媒体报道不断跟进,再后来各种互联网金融公司获得XXX美元投资的报道越来越多,政策也慢慢明朗,于是很多大型公司(集团)也就趁着这股热潮跟进,其中就包括我们。 - -第一代系统最主要就是抢时间,公司希望用最短的时间内保证系统上线,那时候移动浪潮已经启动,于是决定优先上线移动端,网站可以暂不考虑。公司当时有PHP和Java两种开发语言技术储备,因为PHP在快速开发上面有着非常大的优势,因此决定采用前端PHP+后端Java这种模式。系统分成了三层:用户层:安卓和IOS移动端;接口层:php提供用户和交易接口;后端:后端有两部分,后台和定时系统。后台用PHP开发和接口层公用了一个系统,另一个是定时系统,负责计息、派息、到期等定时任务等使用了java开发。 - -基础服务和中间件,mysql做了最基本的主从来支持,第一代系统只是使用了mysql的主库,从库只是同步备份;memcached用来处理用户抢标的并发问题,也只用了这一块;ActiveMQ用来使用二级市场的转让撮合以及其它一些异步消息通知。项目部署:php使用apache部署,定时服务使用tomcat6来做应用服务器,使用lvs来做前端apache的负载,基本上第一代也就这些技术了,下面是第一代系统的架构图。 - - -![](http://favorites.ren/assets/images/2017/architecture/framework1.jpg) - -第一代系统上线之后,网站和H5(手机浏览器或者微信端)系统建设就变的特别突出,作为一个互联网金融公司没有官网不能忍,于是马不停蹄的又开始开发网站和H5系统,在这个期间PHP之前做的后台这块摘了出来,用java从新规划了一版,至此PHP就负责了网站、APP接口、H5这三个系统,三个系统共用一个核心交易,java这边负责后台管理和定时服务,我们一般给这个架构叫做1.1代架构。 - -第1.1代系统架构图,绿色部分为变动部分 - - -![](http://favorites.ren/assets/images/2017/architecture/framework1.1.jpg) - - -> 第一代系统的缺点是业务过于集中,仓促上线,后期问题较多 - - -## 第二代系统架构 - -第二代系统的背景是随着公司业务量的快速发展,很多初期所欠的技术债务统统爆发,线上出现了很多问题,最严重的一次是给个别用户重复派息,各种被骂,现在记忆犹新。另一方各业务部门需求不断,公司产品需求不断,所以这个阶段就是忙着修复各种生产问题,一边还需要开发垂直业务系统。那段时间差点被逼疯了,第一代系统是封闭开发,回来还没缓过劲,这边又赶马上架,真是疼并快乐着。 - -第一个垂直子系统上线的是:合同系统,当时用户投标后没有一个合同,很多用户很不放心,就把优先级提到了前面。后来就单合同系统就改了三个版本,第一个版本只是生成pdf,第二阶段上线电子签章,第三个阶段加水印,自定义动态生成pdf;紧接着开发积分系统:用户邀请,投资等生产积分,用来兑换抵现卷等;抽离出消息系统:站内消息、短信、邮件等;上线监控系统、业务监控和服务监控,业务失败预警;各业务部门继续不断提需求,上线财务系统:财务人员统计核算金额;风控系统:监控异常用户,异常交易;给销售开发了销售系统;因为和很多第三方系统对接,又开发了对外接入系统。 - -一代系统做的很赶,产品界面又很烂,随即启动规划了网站2.0、APP2.0、H52.0,针对前端系统的需求,在后端开发了CMS系统来发布项目、公司的公告新闻等;第二代产品端普遍规划了很多大数据分析的一些需求,会在官网展示全量数据分析后投资偏好、投资的金额都跑到哪里去,前端用地图来展示,对于个人也会有还款日历,代收数据分析等,因为需要跑全量数据,在规划的时候都是设计离线来处理,将数据从mysql从库同步到mongodb的集群中,利用mongdo的mapreduce技术来处理大量的数据,于是我们的数据库层就变成下面的这个架构 - - -![](http://favorites.ren/assets/images/2017/architecture/db.jpg) - - -mysql实时同步到mongodb,我们使用的是[tungsten-relicator](https://github.com/vmware/tungsten-replicator)这个工具,会在mysql服务器端启动一个监控agent,实时监控mysql的binlog日志,同时在mongodb的服务器端也起了一个服务端,agent监控到数据变化后传送给服务端,服务端解析后插入到mongodb集群中以达到实时同步的效果,如上图,当初写了一篇文章来介绍:[大数据实践-数据同步篇tungsten-relicator(mysql->mongo)](http://www.cnblogs.com/ityouknow/p/4918164.html),其实这个工具在使用中,也不是特别的稳定,但是当初的选择方案并不多,幸好后期慢慢的熟悉后算是稳定了下来。 - -数据清洗系统我们大胆的使用了golang来开发,当时使用的golang版本是1.3吧,现在都1.8了,以前也是没有接触过也是锻炼了队伍,好在golang语言本身非常简洁和高效,虽然踩了N多坑,但是最终我们还是按时投产了;后来又使用了golang开发了一个后台,是在beego框架的基础上来做的。大数据分析系统后来又升级了一代,在前端的各业务系统,UI用户层做了很多埋点来收集用户数据,通过activeMQ传输接收最后存储到mongodb,在进行数据清洗,将清洗后的结果存入到结果库中,供前端业务系统使用;后来利用beego+echart重新做了一版数据分析系统。 - -大数据系统的架构图 - - -![](http://favorites.ren/assets/images/2017/architecture/bigdata.jpg) - -因为后端数据库的压力不断增大,后端管理系统、业务系统均作了主从分离;后台管理系统增加缓存,启动了redis做缓存;使用nginx搭建了独立的图片服务器;第二代系统开发过程中,也是公司发展最快的阶段,上线了N多的活动。 - -第二代系统架构图: - - -![](http://favorites.ren/assets/images/2017/architecture/framework2.jpg) - - -> 第二代架构上线了各业务系统,做了主从分离,搭建了大数据平台为以后更多的数据处理提供了技术基础 -> 缺点:各业务系统切分之后,各项目之间调用复杂;后台系统繁多、各系统之间有单独的账户系统,运营需要来回切换完成平台运营监控 - - -## 第三代系统架构 - -第二代系统开发完成之后,留给我们了三个问题很痛苦,第一个是随着业务系统不断增多,系统之间的调用关系成指数级别上涨,在第三代系统初期,我们又开发了很多基础组件,更是加剧了这个问题;第二个问题和第一个问题相辅相成,系统之间调用关系太多,如果移动其中一个子系统,可能需要修改关联系统的配置文件,重新启动服务,经常因为更新一个系统,其它系统也需要被动更新,投产和回退切换很复杂;第三个问题是我们开发了很多的后台系统,但是账户没有统一,每个子系统有各自的账户中心,运营和业务人员需要来回登录才能完成日常工作,随着业务量增大这个问题也日益突出。 - - -于是又开启调研、系统选型等,解决第一个问题就是引入SOA服务治理,通过服务的注册和发现解决系统之间的解耦,当时考察了很多,最后选型dubbo,原因无它,有大量群众使用基础该趟的水都趟过了。解决第二个问题就是引入配置中心,当时调研了360的Qihoo360/QConf、Spring的spring-cloud-config、淘宝 的diamond、还有百度的disconf,最后纠结半天选定了disconf,完美和spring cloud擦肩而过,但是正是从这里开始让我们注意到了spring-cloud、Spring-boot为第四代的架构选型留下了伏笔,其实最后disconf也只是在少部分项目使用,也没完全推广开;解决第三个问题就是账户中心,使用了cas实现单点登录,shiro做权限控制,dubbo来提供登录后权限列表等服务端接口。 - -改造后的架构图 - - -![](http://favorites.ren/assets/images/2017/architecture/framework3.1.jpg) - - -在这个基础上面,我们又抽离出来很多基础组件,comomn组件处理共用的基础类,包含字符类、日期类、加密类....,搭建了fastDFS集群来处理文件系统,做了redis集群的测试;单独开发了定时调度系统,将所有的定时任务统一集成到调度系统,那个系统需要定时任务都可以在页面自动添加调度策略;前端PHP做了系统改造,主从分离、静态优化等 - - -在后来,公司又启动众筹平台的建设,这次系统完全采用java语言开发,app端采用混合开发模式,其中APP的所有一级页面全部采用原生开发,所有的二级页面都是H5+vue这种模式,后端全部采用dubbo做服务化,最终的架构如下: - - -![](http://favorites.ren/assets/images/2017/architecture/framework3.jpg) - -图里面系统只罗列一部分,使用其它服务来代替 - - -> 第三代系统启动了SOA服务治理,引入统一账户中心、基础组件;缺点是开发环境较复杂 - - -## 第四代系统架构 - - -人总是不满足,技术呢也总是希望可以使用最好架构体系,在第三代系统架构的开发中,了解到了spring cloud和spring boot,在不断的学习之后,越发的感觉到springboot的便利性,快速开发的优点甚是喜爱,spring cloud体系也完全满足一个大型系统需要考虑的方方面面,微服务的概念不断的被提出来,以上为技术背景;另一方面国家开始严格要求P2P公司必须接入银行存管,分析了银行的相关接口后发现如果严格按照规则走,我们的系统需要大改造,同时公司为了满足监管要求,又开发出白条相关产品也是一个大项目,趁着以上的两个背景,我们决定在进行银行存管和白条项目的同时全面拥抱微服务。 - - -至于为什么我们要抛弃dubbo转而全面拥抱spring cloud原因有三,1、dubbo多年都没有更新了,spring cloud不断的在更新升级;2、dubbo主要做服务治理和监控,spring cloud几乎考虑了微服务所需要方方面面,比如统一配置中心、路由中心;3、spring cloud更是无侵并且完美和spring其它项目整合,开发效率更高。 - - -既然选定了使用spring boot+spring cloud来改造,微服务技术选型这边就定了下来,那么如何开启改造呢,毕竟在进行新一代系统改造的同时也不能影响原有业务,其中最主要的问题就是最初的系统虽然都是按照分布式的开发模式来进行,由于老系统的原因有的系统还是共用了一个数据库,微服务要求每个独立的子系统有自己独立的库操作,别的系统如果需要修改或者查询子系统的数据,需要根据服务间接口调用来获取。因此计划先从新开发的项目和需要改造的项目中启用springcloud项目,别的系统暂时先通过路由器模式来通讯,最终的系统架构图如下: - - - -![](http://favorites.ren/assets/images/2017/architecture/framework4.jpg) - - -在架构的这条路上面没有终点,变化就是永远的不变,架构的升级更是为了更好的支撑业务,二者相辅相成。 - - -## 开源软件 - -在这几年中我们也想对开源世界做一点点贡献,总共开源了两个软件: - -**generator-web** - -在项目中大量使用了mybaits,我们对mybaits的generator做了改造,并且做了一个系统界面,方便根据相关参数自动生产相关代码(只需要设计好表结构,系统会自动生成mappper、Entity、dao层的代码),最后也开源了出来 - -[generator-web](https://github.com/justdojava/generator-web) - -界面如下: - - -![](http://favorites.ren/assets/images/2017/architecture/generator.jpg) - - - -**云收藏** - -为了锻炼技术学习springboot我们开发了一个云收藏的开源软件,使用的技术主要是```springboot```/```Spring data jpa```/```redis```/```thymeleaf```/```gradle```,功能主要是可以帮助用户在云端收藏、分享和整理自己的收藏夹。 - - -[favorites-web](https://github.com/cloudfavorites/favorites-web) - - -![favorites_chrome](http://favorites.ren/assets/images/2016/favorites_home.png) - - - -------------- - -**作者:纯洁的微笑** -**出处:[www.ityouknow.com](http://www.ityouknow.com)** -**版权所有,欢迎保留原文链接进行转载:)** diff --git a/_posts/2017/2017-01-18-find-new-world.md b/_posts/2017/2017-01-18-find-new-world.md deleted file mode 100644 index 0184feaf01..0000000000 --- a/_posts/2017/2017-01-18-find-new-world.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -layout: post -title: 发现另外一个世界-网盘关闭背后 -category: life -tags: [life] -lock: need ---- - -据说网络中的我们能搜索浏览到的信息只有4%,96%的信息都隐藏在另外的一个世界之中,又由于我国著名的GFW,那么我们所能接触和搜索到的信息就更少了,人总是有非常大的好奇心,想去窥探另外的一个世界,作为一名IT行业的从业人员,更是有网上探索的精神。本篇就将介绍我是如何去发现另外一个世界的过程。 - -![](http://favorites.ren/assets/images/2017/webDeepDark.jpg) - -> 想要探索另外一个世界,第一步需要突破的就是GFW - - -## 科学上网之路 - -围城里面有这么一句话,“在墙外的想到墙内去,墙内的想到墙外来”。但是对于GFW这到墙来讲,又有那些人想到墙里面来吗?平时我们可能也习惯了不能上facebook,twitter之类的,因为国内都有类似的替代品;也习惯了各种游行示威消息被屏蔽,因为这样避免国内民主情绪;这道墙在这里可能对生活也没有啥影响,偶尔翻墙出去了,发现也不一定就能找到你所喜欢的东西,注册了facebook半年没有几个朋友,习惯了免费的网盘会使用付费的Dropbox吗?要不通过什么XX门类似的翻墙软件全TM都是反我天朝各种偏激,对着这些新闻看看也只能呵呵了。 - -好像时间长了,慢慢的都已经麻木了,不翻墙就不翻墙也影响不了啥;但是就是因为这道墙你会感觉到不自由,心里的那种不自由的劲儿那你就是百般的不爽,特别是对于我们这些搞IT的,实在是忍无可忍,百度一个问题的时候TMD我都不知道搜出来的是什么,对于谷歌可以精准找到我想要的东西,快速的解决问题。基于各种原因吧,慢慢的走上了探索如何翻墙的各种路子,可以分几个阶段来描述。 - -### 第一个阶段(2006-2010年) - -上大学那会,啥都不会,在到处瞎溜达,类似无界、XX门的一些小软件吧,不知道网上那块找的这些软件,打开就是各种反动思想什么的,好像也不能谷歌或者我那时候都不知道有谷歌,最后看看热闹就算了。这些小软件都有一些共同的特征,应该是各种国外反我党做的软件吧,质量不咋的,就算连通之后上网可以用龟速来形容,另外打开之后全是反我党的各种宣传,类似 XXX的真相、XXX退党申明、XXX内幕,反正我对这些完全不感冒,连接上之后也会尝试这去访问国外的网站,但都是乱看,后来就慢慢没有兴趣了。刚搜索了下现在还有这软件,真是服了。 - - -### 第二个阶段(2010-2014年) - -工作以后对互联网了解多了一些,想去注册什么facebook、twitter之类的网站,也想使用谷歌搜索,就继续进行了研究,在这个阶段算是尝试的最多的了。大概可以分为三类:网站直接翻墙、浏览器插件翻墙、第三方的VPN。 - -网站直接翻墙,只是提供了谷歌的搜索功能,还是不能访问国外的网站。记得有一个的香港谷歌站点可以进行搜索使用,过了一段时间后就被封了;网上又有一些人共享了一些小网站或者代理网站,背后不知道是通过代理还是什么,可以进行谷歌搜索,断断续续的使用了一段时间,后来又被掐断了。 - -浏览器插件翻墙,一般是通过安装浏览器插件代理来实现翻墙,可以访问国外站点。360、百度之类的浏览器公司都推出类似的插件来推广,需要下载他们的浏览器以及特定插件,就可以进行谷歌搜索、访问国外站点。但是使用这些插件都是有限制的,比如说限制访问国外站点网速,或者三天之内需要打开一下hao123.com,或者做一些什么任务。后来网上也推出了蓝灯等一些软件,通过chrome插件的形式,来提供翻墙的服务,断断续续的使用了一段时间,很不稳定最后也是放弃了。 - -第三方VPN服务,很多小公司专门提供vpn包月,包年的套餐,需要按照一个独立的客户端,登录账户之后就可以连接日本、香港或者欧美这些国家的代理服务器来翻墙。通常这些vpn都会提供试用的功能,比如可以免费体验XXXM流量的使用,或者每天签到可以领取20M代理使用的流量类似这样的促销,时不时的提醒你重新登录一下或者免费流量试用完了也挺烦人的,但只是偶尔谷歌一下还是够用的。像稍微比较有名的有红杏、greenvpn等大概一年就是一百多元把,缴费后的使用一般还算是比较稳定的,但有时候如果国家稍微查一下就需要关闭一段时间或者换一个IP地址之类的。 - - -### 第三个阶段(2015-现在) - -做为一名IT从业人员,如果仅仅只是使用别人服务的话那就太low了,于是想着买一些国外的服务器来自己搭建代理。无意中听说了搬瓦工,网上搜索了一下,大于每年128元就可以买一个国外的服务器,当然了配置很低,但是流量不错呀每个月500G,也可以装一些博客什么的,都没有问题。于是根据网上的教程,购买机子、搭建shadowsocks服务端、客户端,搭建过程总体比较顺利,完了网上一搜什么facebook、google等嗖嗖的,各种爽呀。 - -板瓦工支持支付宝付款,购买完成之后也可以将代理切换到美国不同的城市来提供代理,当然还有很多其它的国外服务器厂商,但这个应该是最便宜的,我使用的半年多除过G20时期的时候出过问题,其它时间还是比较稳定的;最主要的是我们同时用于了一个国外的服务器,可以放一些小的网站之类的,也可以几个人共同买一个,反正我每个月500G的流量,从来都没有使用超过10%。 - -如果还嫌不够方便还有两种办法:1、国内在买一台服务器,上面搭建一个客户端代理,我们的设备只要连接上国内的代理服务器就可以上网了,这种方式完全可以提供对外的服务了。2、使用openWrt来刷路由器然后在结合shadowsocks配置,让这台路由器具备了翻墙的能力,接下来只要连接这台路由器的设备都可以轻松实现翻墙。 - - -搬瓦工的价格 -![](http://favorites.ren/assets/images/2017/bwg.jpg) - - -## 暗网 - - -### 什么是暗网 - -暗网(英语:Darknet或Dark Web)通称只能用特殊软件、特殊授权、或对电脑做特殊设置才能连上的网络,使用一般的浏览器和搜索引擎找不到暗网的内容。暗网的服务器地址和数据传输通常是匿名、匿踪的。与此相对,一般常用的互联网由于可追踪其真实地理位置和通信进行人的身份被称为“明网”(英语:Clearnet)。 - -暗网虽然通常需要借助公开的互联网当作线路,但它们通常使用非常规的网络传输协议和端口(常规的互联网传输协议是HTTP/HTTPS,分别使用端口80/443),有些使用分布式网络架构或层层转传来混淆来源,使得第三人难以知悉有网络交流正在发生,就算知悉也难以追踪通信参与者的真实位置和身份;再搭配全程加密传输,使得就算第三人拦截了通信也难以解析内容。 - -暗网有许多种类,所有暗网的集合组成了深网的一部分。常见的小规模暗网是朋友之间使用P2P分享文件。目前最大规模的暗网则是洋葱网络,只能通过tor来访问。 - - -### TOR - -TOR 是洋文 (The Onion Router)的缩写,中文又称“洋葱网络/洋葱路由”。简单而言,这是一款专门设计用于隐匿上网身份的工具。设计 TOR 的主要目的是为了上网隐匿身份。所以,跟其它的翻墙工具不同——它的重点功能是"隐匿性","翻墙"只是顺带的功能。 Tor最初由美国海军研究实验室(US Naval Research Laboratory)赞助。2004年的后期,Tor成为电子前哨基金会(Electronic Frontier Foundation,EFF)的一个项目。在2005年下半年,EFF虽然不再赞助Tor项目,但他们继续维持Tor的官方网站 。 - -Tor支持动态代理链(通过Tor访问一个地址时,所经过的节点在Tor节点群中随机挑选,动态变化,由于兼顾速度与安全性,节点数目通常为2-5个),因此难于追踪,有效地保证了安全性。另一方面,Tor 的分布式服务器可以自动获取,因此省却了搜寻代理服务器的精力。Tor浏览器在后台启动Tor进程并透过其连接网络。一旦程序断开连接,Tor浏览器便会自动删除隐私敏感数据,如cookie和浏览历史记录。 - -![](http://favorites.ren/assets/images/2017/Tor_Browser_Bundle_start_page.png) - -### 暗网有什么 - -暗网有着人性最黑暗的一面,盗版、黑客、毒品、军火、血腥、暴力、色情、阴谋论、职业打手,甚至是恐怖分子。有人说“深网”是网络世界的罪恶天堂,也有人说,“深网”是人性最深处的黑暗。但是国内目前通过tor浏览器已经不能访问暗网了,貌似又被封闭了,需要启动前端代理,也就是前面说的代理服务器来使用。 - -暗网的网站一般都是以.onion为后缀的网站,现在如果登录进去会有很多类似的导航网站,对于普通人来讲,以我个人尝试的几次情况来看,大部分人登录进去也就是看看热闹,里面有一部分侵入工具、黑客交流网站,一部分就是各种爱情动作片的网站、还有各种盗版的软件、论坛等等,但国内使用起来反应巨慢,根本没发正常使用,也许也是因为了解的不够多。 - - -## Resilio Sync - -最后给大家介绍大名鼎鼎的Resilio Sync, Resilio Sync的前身是BitTorrent Sync 是一款可以取代网盘的P2P共享软件,它和BT下载一样,都是BitTorrent公司发明的玩意儿,通过P2P协议来进行传输。而这个软件最厉害的地方在于,同步时无需第三方服务器,直接点对点加密传输,所以安全性无敌。使用按照可以参考这篇文章[Sync 完整中文版图文教程](http://wherebt.com/blog/2016/blog2.html),这个软件最大的好处就是完全去中心化,根本就无法监控。 - -> 使用Resilio Sync两大用处:同步自己文件和分享自己的文件 - -### 同步文件 - -这段时间最疼恨的就是各种国内网盘关闭,剩下的几家不是收费,就是限流用起来非常的不爽。如果你使用网盘只是为了同步数据,那么使用Resilio Sync可以完全将它替代,而且速度不受中心服务器各种影响,只和自己网络带宽有关。如果想同步本机文件,可以在自己的电脑上面设置一个文件夹,生成一个对应的key或者二维码,手机或者pad扫二维码或者输入key就可以直接同步了。公司内部的文件也可以采取这样的方式,在服务器上面划分一块区域,大家把需要共享的文件都可以放到一起来共享,也可以设置只读或者读写的权限。 - - -### 分享文件 - -这个目前是大家最喜欢使用的功能了吧,所有自己分享的内容都依赖于key,于是网站会有人分享各种各样的key,IT类的电子书,最新的电影合集key、还有专门用来导航的key;以前用百度云盘来分享东西,涉及到什么最新的电影之类的应该很快就被和谐了,但是使用它来分享那是决定不会发生的事情,因为完全去中心化,没有服务器会存储你分享的内容,只是通过这个软件来同步。 - -因为是去中心化,那么数据都是从个人到个人,如果你下载了这个内容,并且软件打开着,那么你也会为这个资源服务的一部分,一方面你是资源的下载者,一方面又是资源的上传者,软件会根据最优的算法来从你最近的几个用户的电脑里面来下载。于是也催生了一部分专业分享key的网站,这里有一个导航的网站里面的网站都是专业分享key的网站,质量参差不齐。[wherebt.com](http://wherebt.com/) - - -我也是无意查找一个软件的时候发现这个工具的,使用了之后感觉像发现了另外一个世界,有各种盗版的书籍、盗版的高清电影、有反我党的各种书籍资料、有各种小电影、哪怕前一阵子流行的X宝10G,历年被脱裤的数据库资源(大概100多G吧,利用这些资源都可以建各种社区库的网站了),当然还有各种病毒。 - - -![](http://favorites.ren/assets/images/2017/ResilioSync.jpg) - - -自由、开发、无监管可以体现出来互联网人这种无拘无促的精神,但也同时会带来一些问题,比如会有暴力、色情、病毒等,工具(技术)是无罪的,主要是人如何去使用它! - - -------------- - -**作者:纯洁的微笑** -**出处:[www.ityouknow.com](http://www.ityouknow.com)** -**版权归作者所有,转载请注明出处** \ No newline at end of file diff --git a/_posts/2017/2017-02-06-one-production-accident-optimization-experience.md b/_posts/2017/2017-02-06-one-production-accident-optimization-experience.md deleted file mode 100644 index c97abfa3c4..0000000000 --- a/_posts/2017/2017-02-06-one-production-accident-optimization-experience.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -layout: post -title: 一次生产事故的优化经历 -category: arch -tags: [arch] ---- - -在一次正常的活动促销之后,客服开始陆续反馈有用户反应在抢标的时候打不开网页或者APP,在打开的时候标的就已经被抢光了,刚开始没有特别的上心,觉得抢标不就是这样吗,抢小米手机的时候也不就这样吗?随着活动继续推进,有更多的用户强烈抗议,用户领了加息卷或者抵现卷之后抢不上标的,认为是平台作假故意不让使用以达到节省资源。 - - -## 分析过程 - -其实以前也会有陆续的用户反馈不减少,给客户以小米抢手机为例子忽悠了过去,这次用户反馈太过强烈,才让我们重视了起来。我们前端一共三款产品,app、官网、H5,其中app使用量最大,官网其次,H5平时使用量极少但是做活动期间流量会暴增(活动一般都是H5游戏居多,H5也便于推广营销),前端的三款产品都是分别使用lvs负载到后端的两台web服务器中(如下图),这次用户反馈基本在web和app端,所以重点观察这四台服务器。 - - -![](http://favorites.ren/assets/images/2017/optimize/once1.png) - -首先怀疑网络带宽是否被涌满,找到网络工程师通过工具来监控,在抢标的时候带宽最高使用率只有70%左右,随排除之;再次怀疑web服务器是否抗不住了,使用top命令查看官网负载的两台服务器,在抢标的瞬间会飙到6-8左右,抢标后也慢慢的恢复了正常,app两台服务器高峰到10-12,随后也恢复正常。 - -跟踪web服务器业务日志,发现在数据库更新层报请求不到新的数据库连接或者数据库连接已经用完,认为是数据库的最大连接数太小,于是调整mysql数据库最大连接数为以往的3倍;下次抢标的时候继续观察业务日志,发现已经不报数据库链接的相关错误了,但还是很多用户反馈抢标时候打不开页面。 - -继续跟踪web服务器,在抢标时使用命令(```ps -ef|grep httpd|wc -l```)查看httpd得连接数有1千左右,随机查看apache配置文件中设置的最大连接数为1024(apache默认的最大连接数为256),原来抢标期间连接数已经到达最大连接数,很多用户在抢标的过程中已经获取不到http连接导致页面无响应或者app一直在等待中。于是调整apache配置文件中的最大连接数为1024*3。 - -在抢标过程中继续观察,apache的连接数在抢标的时候仍然可以飙到2600-2800之间,根据客服反馈,仍然有很多用户反馈抢标的问题,但比之前稍微好一点,但是有零星的用户反馈已经抢到标的,最后又给回退了。然后继续观察数据库服务器,使用top命令和MySQLWorkbench查看mysql主库和从库的各项负载吓一跳(如下图),mysql服务器主库的各项指标均已经达到峰值,而从库几乎没有太大压力。 - -![](http://favorites.ren/assets/images/2017/optimize/mysql_before.jpg) - -跟踪代码发现,三端的业务代码全部连接主库,从库只有后台的查询业务在使用,于是立刻启动改造;将除过抢标过程中的查询外,其它页面或者业务的所有查询改造为查询从库,改造之后观察,发现主库的压力明显减少,从库的压力开始上来了。如下图: - -![](http://favorites.ren/assets/images/2017/optimize/mysql_after.jpg) - - -根据客服的反馈,改造之后抢到标回退的问题几乎没有了,抢标过程中页面打不开或者打开慢的问题有一定的缓解但仍有部分用户反馈此问题,根据上面各项目分析结果得出: - -- 1 负载的两台服务器均已经达到处理的极限,需要配置更多的服务器来负载。 -- 2 mysql主库的压力明显减少,但是从库的压力却上去了,需要将现在的一主一从已从改为一主多从的模式。 -- 3 彻底解决这些问题,需要综合考虑平台的整体优化,如:业务优化(去掉业务中热点)、增加缓存、部分页面静态化(可以使用雅虎和谷歌的前端优化规则,网上也有很多的测试网站可以评测)等等。 - -> 当时根据这些情况写了一份优化的报告,见下文: - - -## 优化报告 - -### 1 背景 - -随着公司业务不断发展,业务量和用户量的激增,官网pv也从最初的xxx-xxx到现在的xxx-xxxx,APP活跃用户更是大幅增加;因此也对平台目前的技术架构有了更大的挑战。特别是近期平台标源紧张的情况下,满标的时间更是越来越短。服务器的压力也越来越大;因此需要升级目前的系统架构,以支持更大的用户量和业务量。 - - -### 2 用户访问示意图 - -![](http://favorites.ren/assets/images/2017/optimize/userVisit.jpg) - -目前平台有三款产品面对用户,平台官网、平台APP、平台小网页;其中平台官网和平台APP的压力比较大。 - - -### 3 存在的问题 - -用户抢标的时候问题集中在以下几个方面 -1、网页或者APP打不开 -2、网站或者APP打开慢 -3、抢标过程中转账成功后,因为服务器负责压力大更新失败,再次退款 -4、数据库连接数用完,导致满标后添加投资记录失败,回退标的进度 - -### 4 分析 - -通过对近期的服务器参数,并发量,以及系统日志等进行深入的分析,得出: -1、平台官网、平台APP抢标过程中服务器压力巨大,其中平台APP问题更加突出,抢标高峰期间单台APP服务器apache最大连接数已经接近2600,接近apache最大的处理能力 - -2、数据库服务器压力巨大。数据库压力主要在两个时期比较突出 -1)当平台做活动的时候,官网、小网页、APP访问量巨增,导致数据查询量跟着巨增,当到达数据库处理极限时,就会表现出网站打开慢等问题; -2)当用户抢标的时候,用户抢标的压力又分为两个阶段:抢标前和抢标中。抢标前,因为满标速度很快,用户提前打开抢标页面不断刷新,这样数据库的查询压力会不断增大,如果抢标的用户量非常大,会导致在抢标之前将数据库连接数用完;抢标中,单次购买大概会涉及15张左右表进行更改查询,每个标的份额1000万大概每次会有100-200人左右购买完成满标,以中间值150人计算,在几秒的时间内需要对数据更新2000-3000次(仅仅是更新,不包括查询 ),产生大量并发,可能会导致更新失败或者连接超时,从而影响到用户投标和系统正常满标。 - - -### 5 解决方案 - -1、web服务器解决方案 -单个用户访问web服务的示意图 - -![](http://favorites.ren/assets/images/2017/optimize/once1.png) - -目前网站和平台APP均是采用了两台服务来做均衡负载,每台服务器中安装了apache来做服务端接受处理,每台apache最大可以处理大约2000条连接。因此理论上目前网站或者APP可以处理大于4000个用户请求。如果要支持同时1万的请求,则需要5台apache服务器来支持,因此目前缺少6台web服务器。 -升级服务器后的访问示意图 -![](http://favorites.ren/assets/images/2017/optimize/once2.png) - - -2、数据库解决方案 -当前数据库的部署方案 -![](http://favorites.ren/assets/images/2017/optimize/once3.png) - - -1)主从分离解决主库80%的查询压力。目前平台官网、APP均连接mysql主库导致主库压力倍增,把服务中的查询全部迁移到从数据库可以大量减轻主库的压力。 - -2)增加缓存服务器。当从库查询到达峰值的时候,也会影响主从的同步,从而影响交易,因此对用户经常使用的查询进行缓存以达到减少数据库的请求压力。需要新增三台缓存服务器搭建redis集群。 - ![](http://favorites.ren/assets/images/2017/optimize/once4.png) - -3、其它优化 -1)官网首页静态化,从cnzz统计来分析,首页占比网站的整体访问量的15%左右,对于首页不经常变动的数据通过静态化来处理,提升官网打开的流畅度。 - -2)apache服务器的优化,开启gzip压缩,配置合理的链接数等 - -3) 去掉投资过程中的更新热点:标的进度表。每次投标成功或者失败都需要对标的进度表进行更新,多线程更新的时候就会出现乐观锁等问题。去掉过程中的更新,只在满标后将标的进度信息保存在标的进度表,优化投资过程中对数据库的压力。 - - -### 6 服务器升级方案 - -1、平台最大的压力来自于数据库,需要将现在的一主一从,改为一主四从。官网/app/小网页产生的大量查询,由虚IP分发到三台从库,后台管理查询走另外的一个从库。数据库需要新增三台服务器 -数据库升级后的示意图 -![](http://favorites.ren/assets/images/2017/optimize/once5.png) - -2、增加缓存减少数据的压力,需要新增两台大内存的缓存服务器 -![](http://favorites.ren/assets/images/2017/optimize/once6.png) - -3、需要新增三台web服务器分解用户访问请求 - -**app需要新增两台服务器** -在抢标过程中app服务器压力最大,需要新增两台服务器,配置完成后的示意图 -![](http://favorites.ren/assets/images/2017/optimize/once7.png) - -**官网需要新增一台服务器** -官网在抢标过程也有一定的压力,需要新增一条服务器,完成后示意图如下: -![](http://favorites.ren/assets/images/2017/optimize/once8.png) - -> 总合计之后需要购置8台服务器,其中有两台要求有大内存(64G以上) - -**公众号回复“优化”,可下载完整版word优化报告** - - -*备注:所有优化方案投产后,问题解决,抢标无忧!* - -------------- - -**作者:纯洁的微笑** -**出处:[www.ityouknow.com](http://www.ityouknow.com)** -**版权归作者所有,转载请注明出处** \ No newline at end of file diff --git a/_posts/2017/2017-02-09-a-tragedy-caused-by-dns-cache.md b/_posts/2017/2017-02-09-a-tragedy-caused-by-dns-cache.md deleted file mode 100644 index 28109ab509..0000000000 --- a/_posts/2017/2017-02-09-a-tragedy-caused-by-dns-cache.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -layout: post -title: 一次dns缓存引发的惨案 -category: arch -tags: [arch] ---- - -时间2015年的某个周六凌晨5点,公司官方的QQ群有用户反馈官网打不开了,但有的用户反馈可以打开,客服爬起来自己用电脑试了一下没有问题,就给客户反馈说,可能是自己网络的问题,请过会在试试。早点8点,越来越多的用户反馈官网无法打开,并且有部分用户开发反馈app也打不开了,客服打电话叫起了还在梦乡中的我。 - - -## 分析定位 - -被客服叫起来之后,一脸懵逼,不知道什么情况,给客服回复,知道了,立刻排查,待会有消息及时沟通。用凉水洗了一把脸清醒了一下,立刻根据经验回忆这两天生产投产的情况:上线了XX模块,不影响、修复了XXbug,应该也不影响、刚给服务器配置了https,看起来好像有点关系,但是app暂时没有投产https,怎么也出现问题,排除之。打开电脑核查了最近的投产记录应该都不至于发生这么严重的问题,随怀疑是不是网络方面有问题,立刻打电话叫起来运维经理以及相关人等一起排查。 - -一边让网络和运维排除问题,一边再次核查了web服务器、数据库服务器、业务日志、数据库日志,以及其它的一些监控数据,各项皆正常。试着在本机ping了一下域名确实不通,更加怀疑是网络问题,尝试这直接使用外网访问,可以打开没有问题,可以基本确认服务没有问题,但运维部反馈网络设备什么都正常,肯定是你们投产代码出问题了,各方硬着头皮继续在排查。 - -9点,群里开始有大规模的用户反馈官网和app都打不开了,更有部分用户煽动,XXX公司跑出了(15年很多p2p公司跑路,导致用户都成了惊弓之鸟,稍微有问题便害怕公司跑路,个个都锻炼成了监控高手,天天看,实时刷,凌晨起来尿尿也都顺便看一下app上的今日收益),客服400热线基本被打爆了。一边继续排查问题,一边上报此问题给总监、公司各高管,给客服建议,给用户解释,IDC机房网络抖动,技术正在紧急解决,资金和数据都没有任何影响,稍安勿躁。 - -10点,开发和运维反复的检查后,开始怀疑dns解析有问题,但具体是什么问题还不清楚,CTO决定:1、大家都打车往公司走,来公司集体解决 2、在各QQ群、微信群给用户群发解释xxx问题,安抚客户。在车上的时候重新梳理了一下用户的整个访问流程,如下图: - - -![](http://favorites.ren/assets/images/2017/optimize/user_dns_visit.jpg) - - -到公司后,根据这个思路大家在一起验证了一下,通过外网IP和内网IP访问公司所有服务都正常,但是通过域名访问不行,另外监控服务器、防火墙、网络设备日志都正常,因此断定是DNS解析出现问题。 - - -## 攻坚问题 - -既然确实是DNS解析问题,那么问题又来了?为什么DNS解析会出现问题?如何去解决这个问题?一边给万网提工单,我们也自己测试一下电信、移动、联通在不同的网络运营商下面的访问情况,发现只有在联通网络的环境下DNS解析不了。根据客服得到的反馈也验证了这个情况,电信和移动用户反馈很少,联通用户反馈最多。于是我们又开始给联通打电话,刚开始联通不受理我们的这个请求,于是又开始以用户的身份打电话给联通公司让立刻解决不能上网的问题。 - -于是就开始了万网和联通的扯皮大战,万网说从他们那边查看DNS解析都正常,一起指标都正常,我们又给联通打电话联通说我们已经知道了,待会由专业的人给我们回复,过了一会联通的网络工程师回复说,像这种情况一般都是域名解析的问题。早上10:30到公司开始短短的6各小时内,我们几个轮流给联通公司合计供打了近50、60通电话,给万网提了N个工单,接了N个电话。 - -期间领导也开始动用各种关系,联通内部的朋友、网络运维界的大拿帮忙来定位解决,我们也尝试了很多的办法,比如,使用```ipconfig/flushdns```命令清除本机的DNS缓存、在万网的官网把DNS解析重新更新一边、删除在重新添加等等,也不是完全没有收获。我们一直想找一个可以测试各个地方、运营商网络的办法,终于在各方推荐和搜索的情况下找了[17ce](http://www.17ce.com/) 和 [360奇云测](http://ce.cloud.360.cn/)两个网站,感觉非常实用,在以后的网络定位中,成了我必备使用的工具,可以非常方便的监控各个运营商、各个地区网站的访问是否通不通、访问的速度快不快等问题,截图如下: - - -![](http://favorites.ren/assets/images/2017/optimize/17ce.jpg) - - -我们也发现,公司的其它域名也都访问正常,就是官网的这个域名和相关的子域名不通。期间很多人都问了一个问题就是你们的域名有没有忘了缴费,刚开始大家也都问了运维这边说是没有这个问题,直到中午12:30的时候在我们再三的追问下才说8点多的时候登录上万网的时候显示这个域名是欠费状态,但是他已经立刻把费用补了上去了。哎呀差点把我们气死,问了不是域名到期有提示的吗?才知道因为上一个运维经理走后,他们没有及时的更新万网的电话和邮箱导致提示邮件和短信也没有收到。 - -通过和万网、联通公司、领导的相关朋友沟通以及我们的测试观察,初步明白了这个事情的原因:域名忘记缴费导致万网的DNS解析被停止,用户本机或者DNS服务器有缓存,所以部分用户可以访问部分用户不能访问;缴费过后万网的DNS已经进行了更新和推送,但是DNS解析有很多的层级需要一级一级的往下面发送更新,有的层级并没有更新到,导致部分没有更新到的DNS服务商下面的用户不能访问官网。 - -和万网进行了沟通,问最延迟的情况所有的DNS更新到最新的时间,回答是48小时内肯定都会好的,但是我们等不起呀,随着时间的推移越来越多的用户发现问题,QQ群、微信群已经沸腾,董事长也开始关注次问题,有的客户直接在群里面说,你们的技术太不给力了(像这种还是委婉的,有的直接打电话骂人)... - - -## 临时解决方案 - -不断的通过17ce测试发现,大部分地区的网络都已经恢复,就剩北京联通和部分地区联通网络环境下不通,也说明了这几个地区下的DNS解析记录没有被更新。那么既然我们在上面已经定位出了问题,又了解是什么原因,就想着试着换个DNS解析服务器会不会好一点呢,于是我们把本地的DNS地址换成8.8.8.8(谷歌的DNS服务解析)发现好了!于是赶紧先写解决手册发给着急的客户来使用。 - -官网的用户可以通过更改DNS来解决访问的问题,APP怎么办呢?没有办法我们也不能等,直接找开发人员把客户端调用的地址由域名暂时先改为外网的IP地址打一个版本供用户临时使用。安卓还比较好办,直接让用户下载安装使用还好,但是IOS那时候的审核最少都需要一周黄花菜都凉了。其实iPhone手机可以单独设置DNS的,我们进行了设置和测试后发现也可以实现,于是马上更新到手册中发送给客服发送到群里面给用户使用。 - -**[点击下载当时写的DNS更新手册](http://favorites.ren/assets/files/2017/解决联通用户上网手册.doc)** - -有人说直接让用户使用外网就行了吗,使用外网首页打开到是没有问题,但是各系统之间调用,相关配置文件里面写的也都是域名的地址,如果硬改的话可能会引发另外的问题。第一天搞完就10点多了,中间就4点吃了一顿饭,打了N个电话大家都非常累,于是当天就先这样了,第二天大家一早到公司继续跟进。 - -第二天到公司经过17ce测试发现所有的节点都已经通了就剩北京联通的两个接点没响应,但是北京是我们的大本营,绝大部分的用户都是北京的,继续和万网、联通沟通看怎么能彻底的解决这个问题,另一方面做好最坏的打算,如果一直不通怎么办。在生产环境中梳理所有使用域名的配置文件,做好随时可以直接更新为外网地址而不能影响服务,app完整的重新做一个版本,做好随时可以投产让用户强制升级到外网直连的版本。 - -到第二天晚上10点的时候,北京联通的这两个节点还是不通,和领导进行了商议如果到周一早上8点来的时候这两个网络还是不能通的话,就上线改造好的系统和APP强制升级(因为当时周末还没有标的,周内才有发标计划)。第三天早上起来的第一件事情就是拿起手机,查看自己的联通网络是不是可以登录上官网,结果通了!皆大欢喜。 - - -*俗话说真理是愈辩愈明,经过了这次事故,也彻底的让我了解了DNS解析的整个过程。* - - -## DNS 解析流程 - -DNS( Domain Name System)是“域名系统”的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,它用于TCP/IP网络,它所提供的服务是用来将主机名和域名转换为IP地址的工作。俗话说,DNS就是将网址转化为对外的IP地址。 - -dns从用户访问到响应的整个流程 - -![](http://favorites.ren/assets/images/2017/optimize/dns.jpg) - -- 第一步:浏览器将会检查缓存中有没有这个域名对应的解析过的IP地址,如果有该解析过程将会结束。浏览器缓存域名也是有限制的,包括缓存的时间、大小,可以通过TTL属性来设置。 -- 第二步:如果用户的浏览器中缓存中没有,操作系统会先检查自己本地的hosts文件是否有这个网址映射关系,如果有,就先调用这个IP地址映射,完成域名解析。 -- 第三步:如果hosts里没有这个域名的映射,则查找本地DNS解析器缓存,是否有这个网址映射关系,如果有,直接返回,完成域名解析。 -- 第四步:如果hosts与本地DNS解析器缓存都没有相应的网址映射关系,首先会找TCP/ip参数中设置的首选DNS服务器,在此我们叫它本地DNS服务器,此服务器收到查询时,如果要查询的域名,包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析,此解析具有权威性。 -- 第五步:如果要查询的域名,不由本地DNS服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个IP地址映射,完成域名解析,此解析不具有权威性。 -- 第六步:如果本地DNS服务器本地区域文件与缓存解析都失效,则根据本地DNS服务器的设置(是否设置转发器)进行查询,如果未用转发模式,本地DNS就把请求发至13台根DNS,根DNS服务器收到请求后会判断这个域名(.com)是谁来授权管理,并会返回一个负责该顶级域名服务器的一个IP。本地DNS服务器收到IP信息后,将会联系负责.com域的这台服务器。这台负责.com域的服务器收到请求后,如果自己无法解析,它就会找一个管理.com域的下一级DNS服务器地址给本地DNS服务器。当本地DNS服务器收到这个地址后,就会找域名域服务器,重复上面的动作,进行查询,直至找到域名对应的主机。 -- 第七步:如果用的是转发模式,此DNS服务器就会把请求转发至上一级DNS服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根DNS或把转请求转至上上级,以此循环。不管是本地DNS服务器用是是转发,还是根提示,最后都是把结果返回给本地DNS服务器,由此DNS服务器再返回给客户机。 - -> 这个事情发生后给了我们很大的教训: -> 第一、流程管理有漏洞,离职交接不到位; -> 第二、危机处理不成熟,影响公司声誉; -> 第三、监控机制不完善,像外网不通的这种问题,应该提前设置监控措施。 - -*有时候非常的严重的问题,就是你常常忽略的小不点* - -------------- - -**作者:纯洁的微笑** -**出处:[www.ityouknow.com](http://www.ityouknow.com)** -**版权归作者所有,转载请注明出处** \ No newline at end of file diff --git a/_posts/2017/2017-02-12-a-script-caused-bloody-case.md b/_posts/2017/2017-02-12-a-script-caused-bloody-case.md deleted file mode 100644 index f38fa578b3..0000000000 --- a/_posts/2017/2017-02-12-a-script-caused-bloody-case.md +++ /dev/null @@ -1,201 +0,0 @@ ---- -layout: post -title: 一个脚本引发的血案 -category: arch -tags: [arch] ---- - -我们本身是一家互联网金融公司,公司的主流业务就是p2p,因为各种原因吧,15年底启动建设众筹平台。考虑到前期开发过程中的一些弊端和架构经验,本次架构引用了dubbo做soa服务的治理,web容器nginx+tomcat,后端语言采用java,框架选择spring+mybaits,前端模板引擎使用的是btl,app采用原生+h5的模式。这个架构可以参考我之前写的文章[从零到百亿互联网金融架构发展史](http://www.ityouknow.com/arch/2017/01/10/ten-billion-architecture-history.html)中的第三代系统架构,之前的文章主要介绍了架构的变迁,本篇文章主要介绍在第三代平台中遇到的问题以及解决方法。 - - -首先介绍一下众筹系统的部署架构(如下图),网站和app请求都是首先到最前端的nginx,如果只是静态内容的访问nginx直接处理后返回;动态请求分别转发到后端的tomcat前端服务层,前端服务层只关注页面端业务逻辑不涉及数据库的操作,如果只是页面框架渲染以及不涉及数据库的请求,在前端服务层直接处理返回;如果涉及到数据库操作或者核心业务逻辑,前端服务层通过dubbo调用后端的接入层服务或者核心层服务。 - - -![](http://favorites.ren/assets/images/2017/optimize/zhongchou.jpg) - -上线在生产测试期间,发现tomcat过一段时间就会莫名奇妙的down掉,特别是后端的tomcat down掉的频率比较高。后端的tomcat down掉之后对前端的页面展示没有影响,会影响后端的交易。 - -## jvm参数配置 - -查看tomcat业务日志,报错如下: - -``` properties -2016-04-14 12:01:55,025 - org.jboss.netty.channel.DefaultChannelPipeline -59679839 [New I/O worker #29] WARN null - [DUBBO] An exception was thrown by a user handler while handling an exception event ([id: 0x5f980c11, /xxx:55386 => /xxx:6666] EXCEPTION: com.alibaba.dubbo.remoting.ExecutionException: class com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler error when process received event .), dubbo version: 2.8.4, current host: xxx -com.alibaba.dubbo.remoting.ExecutionException: class com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler error when process caught event . - at com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler.caught(AllChannelHandler.java:67) - at com.alibaba.dubbo.remoting.transport.AbstractChannelHandlerDelegate.caught(AbstractChannelHandlerDelegate.java:44) - at com.alibaba.dubbo.remoting.transport.AbstractChannelHandlerDelegate.caught(AbstractChannelHandlerDelegate.java:44) - at com.alibaba.dubbo.remoting.transport.AbstractPeer.caught(AbstractPeer.java:127) - at com.alibaba.dubbo.remoting.transport.netty.NettyHandler.exceptionCaught(NettyHandler.java:112) - at com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter$InternalDecoder.exceptionCaught(NettyCodecAdapter.java:165) - at org.jboss.netty.channel.Channels.fireExceptionCaught(Channels.java:525) - at org.jboss.netty.channel.AbstractChannelSink.exceptionCaught(AbstractChannelSink.java:48) - at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:296) - at com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter$InternalDecoder.messageReceived(NettyCodecAdapter.java:148) - at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:268) - at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:255) - at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:88) - at org.jboss.netty.channel.socket.nio.AbstractNioWorker.process(AbstractNioWorker.java:109) - at org.jboss.netty.channel.socket.nio.AbstractNioSelector.run(AbstractNioSelector.java:312) - at org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:90) - at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:178) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) - at java.lang.Thread.run(Thread.java:745) -Caused by: java.lang.OutOfMemoryError: unable to create new native thread - at java.lang.Thread.start0(Native Method) - at java.lang.Thread.start(Thread.java:714) - at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:949) - at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1360) - at com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler.caught(AllChannelHandler.java:65) - ... 19 more -``` - -查看output日志,发现其中有这么一句。 - -``` xml -SEVERE: The web application [/xxx] appears to have started a thread named [DubboResponseTimeoutScanTimer] but has failed to stop it. This is very likely to create a memory leak. -``` - -根据日志提示貌似有内存泄露,以前确实还没有碰到过这个错误,一片迷茫。重新启动后,先用命令```jstat -gc xxx 1000 30```查看java 进程的gc情况,发现在30秒的世界内minor gc了n次,随怀疑年轻代内存配置少了,查看个区域内存的配置参数如下: - -``` properties --Xms10g -Xmx10g -XX:PermSize=1g -XX:MaxPermSize=2g -Xshare:off -Xmn1024m -``` - -按照年轻代为堆内存为百分之三的原则修改为```-Xmn4g```,重新启动观察之后mimor gc的频率确实有所下降,测试大约过了3小时候之后又反馈tomcat down掉了,继续分析启动参数配置的时候发现了这么一句```-XX:-+DisableExplicitGC```,显示的禁止了```System.gc()```,但是使用了java.nio的大量框架中使用```System.gc()```来执行gc期通过full gc来强迫已经无用的DirectByteBuffer对象释放掉它们关联的native memory,如果禁用会导致OOM,随即怀疑是否是这个参数引发的问题,在启动参数中去掉它。 - -为了防止再次出现异常的时候能更加详细的分析堆内存的使用情况,在启动参数中添加了```-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/logs/java/```,当tomcat down的时候让输出堆内存文件,一边也启动jvisualvm工具来实时的监控内存各个线程的使用情况。 - - -## 数据库连接池 - -继续使用压测工具来压测,在压测的过程中发现名为```com.mchange.v2.resourcepool.ssync.ThreadPoolAsynchronousRunner$PoolThred-#xxx```的线程不断的增长,并且后台tomcat报错如下: - -``` properties -2016-04-13 16:55:15,175 - com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport -83649035 [New I/O worker #27] WARN - [DUBBO] Thread pool is EXHAUSTED! Thread Name: DubboServerHandler-xxx:6666, Pool Size: 200 (active: 200, core: 200, max: 200, largest: 200), Task: 692 (completed: 492), Executor status:(isShutdown:false, isTerminated:false, isTerminating:false), in dubbo://xxx:6666!, dubbo version: 2.8.4, current host: xxx -2016-04-13 16:55:15,176 - com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport -83649036 [New I/O worker #27] WARN - [DUBBO] Thread pool is EXHAUSTED! Thread Name: DubboServerHandler-xxx:6666, Pool Size: 200 (active: 200, core: 200, max: 200, largest: 200), Task: 692 (completed: 492), Executor status:(isShutdown:false, isTerminated:false, isTerminating:false), in dubbo://xxx:6666!, dubbo version: 2.8.4, current host: xxx -2016-04-13 16:55:15,177 - org.jboss.netty.channel.DefaultChannelPipeline -83649037 [New I/O worker #27] WARN - [DUBBO] An exception was thrown by a user handler while handling an exception event ([id: 0x2f345d45, /192.168.57.20:36475 => /xxx:6666] EXCEPTION: com.alibaba.dubbo.remoting.ExecutionException: class com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler error when process received event .), dubbo version: 2.8.4, current host: xxx -com.alibaba.dubbo.remoting.ExecutionException: class com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler error when process caught event . - at com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler.caught(AllChannelHandler.java:67) - at com.alibaba.dubbo.remoting.transport.AbstractChannelHandlerDelegate.caught(AbstractChannelHandlerDelegate.java:44) - at com.alibaba.dubbo.remoting.transport.AbstractChannelHandlerDelegate.caught(AbstractChannelHandlerDelegate.java:44) - at com.alibaba.dubbo.remoting.transport.AbstractPeer.caught(AbstractPeer.java:127) - at com.alibaba.dubbo.remoting.transport.netty.NettyHandler.exceptionCaught(NettyHandler.java:112) - at com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter$InternalDecoder.exceptionCaught(NettyCodecAdapter.java:165) - at org.jboss.netty.channel.Channels.fireExceptionCaught(Channels.java:525) - at org.jboss.netty.channel.AbstractChannelSink.exceptionCaught(AbstractChannelSink.java:48) - at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:296) - at com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter$InternalDecoder.messageReceived(NettyCodecAdapter.java:148) - at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:268) - at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:255) - at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:88) - at org.jboss.netty.channel.socket.nio.AbstractNioWorker.process(AbstractNioWorker.java:109) - at org.jboss.netty.channel.socket.nio.AbstractNioSelector.run(AbstractNioSelector.java:312) - at org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:90) - at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:178) - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) - at java.lang.Thread.run(Thread.java:745) -Caused by: java.util.concurrent.RejectedExecutionException: Thread pool is EXHAUSTED! Thread Name: DubboServerHandler-xxx:6666, Pool Size: 200 (active: 200, core: 200, max: 200, largest: 200), Task: 692 (completed: 492), Executor status:(isShutdown:false, isTerminated:false, isTerminating:false), in dubbo://xxx:6666! - at com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport.rejectedExecution(AbortPolicyWithReport.java:53) - at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:821) - at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1372) - at com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler.caught(AllChannelHandler.java:65) - ... 19 more - -``` - -根据这些信息随怀疑数据库连接池有问题,为了更好的监控连接池的使用,因此前期使用c3p0也会出现的一些问题,所以我们决定将数据库连接池替换成druid,已经在别的项目中使用测试过,因此非常快速的更换投产。投产后继续用压测工具来测试,根据druid的后台监控页面发现(项目地址/druid/index.html),每次前端掉用一次数据库连接就加一次,执行完成之后数据库连接并没有释放。如下图红色区域,我们将数据库连接池调整成1000,不一会就占满了。 - - -![](http://favorites.ren/assets/images/2017/optimize/druid.jpg) - -根据这些信息判断出,数据库执行sql后肯定没有释放数据库连接,导致数据库连接池用满后,后续的线程无法执行,检查代码之后发现果然有问题,请看下方代码,我们最先使用的是SqlSessionFactory,如果使用SqlSessionFactory,在执行完sql后必须要执行```session.close()```来关闭连接,才会被连接池重新回收。 - -``` java -public class SessionFactory { - @Resource - private SqlSessionFactory coreSqlSessionFactory; - protected SqlSession getSession() { - return coreSqlSessionFactory.openSession(); - } -} -``` - -``` java -public class BaseDao extends SessionFactory{ - public void add(Entity entity) { - this.getSession().update(entity.getClass().getSimpleName()+"."+Thread.currentThread().getStackTrace()[2].getMethodName(), entity); - } -} -``` - -但是使用SqlSessionTemplate却不用手动执行代码来关闭session,因此我们把上面SessionFactory类中的代码改成SqlSessionTemplate(如下),此问题便解决了。 - -``` java -public class SessionFactory { - @Resource - public SqlSessionTemplate coreSqlSession; - protected SqlSessionTemplate getSession() { - return coreSqlSession; - } -} -``` - -## 诡异的脚本 - -做完上面的优化之后,我们感觉问题应该解决了,但过了一段时间后tomcat又诡异的挂了,继续分析gc情况,分阶段使用``` jmap -dump:live,format=b,file=dump.hprof xxx```命令生成堆转储快照来对比堆内存使用情况,监控线程使用情况,均发现没有问题。这个问题困扰了我们好几天,每天都监控这端口,一但发现tomcat down之后马上通知运营人员重启。一方面我们也查阅了各种资料,到网上查找各种tomcat自动down的原因,一一在我们服务器进行了测试、修复均不起作用。 - - -终于在google各种tomcat down原因的时候发现了这么一篇文章[Tomcat进程意外退出的问题分析](http://ifeve.com/why-kill-2-cannot-stop-tomcat/),立刻想起了我们最近使用的一个脚本来,因为我们的tomcat禁止了通过bat文件来关闭,因此为了启动方便我们写了一个脚本文件,方便通过脚本来启动、停止、重启tomcat文件,这是这个脚本导致tomcat down的原因,不不,不叫原因叫元凶!脚本内容如下: - -``` sh -#!/bin/sh -# eg: tomcat.sh start xxx -# -proc_dir="/usr/local/xxx/tomcat-zc-web/bin" -proc_name=$2 -if [ x$proc_name != x ] -then - proc_dir=${proc_dir//xxx/$proc_name} -fi -#echo $proc_dir -function stop () { - kill -9 `ps -ef |grep $proc_dir |grep -v grep|awk '{print $2}'` -} - -function start () { - cd $proc_dir - ./startup.sh - tail -300f /usr/local/logs/tomcat-business/$proc_name.log -} - -case $1 in - start) - start;; - stop) - stop;; - restart) - stop - start;; -esac -``` - -就是因为```tail -300f /usr/local/logs/tomcat-business/$proc_name.log```这一句导致的问题,在别的项目使用的时候其实是没有这一句的,一般在使用的步骤是: - -- 1 执行```tomcat.sh start xxx```启动tomcat, -- 2 执行```tail -300f /usr/local/logs/tomcat-business/xxx.log``` 查看启动日志是否成功。 - -在这次投产的时候为了省一步操作,就将执行查看日志的命令,直接加在了启动命令的后面,当执行```tomcat.sh start xxx```这个命令的时候,即启动的tomcat,也自动会打印出tomcat的日志,那时候的想法非常好。 - -原因是,使用脚本命令启动后因为使用了```tail -300f xxx``` 命令,tomcat的进程会成为shell脚本的子进程,这样的话,如果shell脚本停止的话,系统会自动杀掉tomcat进程导致tomcat down掉,在我们的脚本中去掉这条命令tomcat就正常了,更深层次的原因参考[Tomcat进程意外退出的问题分析](http://ifeve.com/why-kill-2-cannot-stop-tomcat/)这篇文章,文章的内容还是分析的比较透彻,最后感觉阿里的技术真的很牛X,这篇文章也是出自于阿里的员工。 - -> 经历这么些波折,后续的tomcat服务终于稳定了下来 - - -------------- - -**作者:纯洁的微笑** -**出处:[www.ityouknow.com](http://www.ityouknow.com)** -**版权归作者所有,转载请注明出处** \ No newline at end of file diff --git a/_posts/2017/2017-02-15-internet-financial-war-hacker.md b/_posts/2017/2017-02-15-internet-financial-war-hacker.md deleted file mode 100644 index 72cd5e15a4..0000000000 --- a/_posts/2017/2017-02-15-internet-financial-war-hacker.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -layout: post -title: 互联网金融大战黑客 -category: arch -tags: [arch] -lock: need ---- - -在互联网行业里,如果你们的系统还没有被黑客们练过,说明你们的系统还不够成熟。 - -在以往的工作经历中,大都做后端服务,较少经受到黑客们的光顾。但是自从2014年进入互联网金融行业之后,和黑客们打交道已经成了我们日常工作的一部分。2015年应该是互联网金融行业受黑客攻击最多的一年,各互金公司都深受其害,其中网贷之家有一段时间被黑客攻击的太厉害,连续几天网站都无法打开。部分互金公司选择了出钱消灾,让极客们尝到了甜头,吸引了更多的黑客们跃跃欲试。 - -当然了我们也未能幸免,什么DDOS攻击、SQL注入、寻找系统漏洞等几乎都被经历过了,有的黑客还比较好,应该是出于善意或者展示自己,将漏洞放到乌云上面或者漏洞盒子里面让厂商来修复。但更多的是一些黑产完全就是威胁、敲诈想捞一笔钱。 - -先看看下面这位吧: - - -![](http://favorites.ren/assets/images/2017/optimize/hacker.png) - -这个家伙潜伏到我们公司的客户群里面,冒充我们的客户代表将头像和资料替换成一样,然后给群里所有的客服发送信息,想让客服把公司内部的后台地址发给他,想通过这种方式来寻找突破口,当然了这个算是里面的小菜鸟吧。 - - -## DDOS攻击 - -DDOS攻击我们也是遇到了很多次,确实也没有比较好办法,最后都是通过一些笨办法来尽量的避免,先说说我们的经历吧。有一次我正在敲代码,客服QQ又闪烁了起来,还没来得及打开查看信息,客服经理电话就直接打了过来,立刻就有了一种不祥的预感,说官网打不开了,后台也登录不了。 - -挂了电话,我在本机进行了测试果然不行,立刻准备登录VPN查看服务器各项指标,结果登录不上去,马上上楼找运维经理,他也登录不上,刚准备给机房打电话的时候,机房来电话了,说我们的一个IP正经历着1G多的流量访问,问我们是否正在做什么活动,刚话没有说完就说流量已经到5G,不到一分钟之后流量已经到达18G之多。因为我们的机房和集团共用了一个宽带入口,结果陆续的集团上面反馈他们的网站、服务也都出现了问题,机房服务商害怕引起更大的冲击,直接把我们官网对外的IP封掉,集团的其它业务才慢慢都恢复了过来,我们也紧急的更换了外网IP,重新切换了域名解析才恢复。 - -事后我们根据apache分析了日志,流量来自N多个不同的IP地址根本无法应对,因为这次攻击让领导们重视了起来,才将公司机房的网络和公司集团彻底分离,这样的话不管那方受到大流量攻击都不会相互影响,我们也想了一些笨办法,因为上次我们更换了外网IP之后攻击也就停止了,那么我们认为肯定是针对我们外网来攻击的,所有我们就多准备了6个外网IP,当监控到对某一个外网进行攻击的时候马上切换到另一个外网地址,就这样跟他们玩,可以起到非常有限的一点作用,如果黑客真的想跟我们玩,这个办法就像是小孩子捉迷藏。 - -**周年庆的DDOS攻击** - -还有一次我们正在做周年庆活动,突然有人在QQ群里面给我们客服说了一句,叫你们的技术负责人来找我,然后我们的网站就挂了,我还保留了当时的一个截图如下: - - -![](http://favorites.ren/assets/images/2017/optimize/hacker02.png) - -完了之后客服就来找我,然后按照往常的策略处理完之后,我根据客服给我的QQ号码加上了那个人,开口就来吓我,我依稀记当年的对话如下: - -> 黑客:你是平台的技术负责人吗? -> 我:算是吧 -> 黑客:你信不信我可以让你们官网在5秒之内挂掉? -> 我:...(沉默,还真害怕又把官网搞挂了) -> 黑客:你们的官网漏洞很大 -> 我:如果有好的建议请您赐教 -> 黑客:你们的服务器是不是什么防护软件都没有装? -> 我:...(继续沉默,这会在想不会是那个安全厂商来推广产品的吧,当然我们基础的防护肯定有) -> 黑客:我们有非常多的肉鸡,想攻击谁,几秒之内肯定搞定 -> 我:... -> 黑客:我们已经给很多互联网金融行业做了渗透测试,花点钱帮买你们平安,保证以后不会在出事情 -> 我:... -> 黑客:免费的策略也有很多,比如360、百度云的安全产品可以免费低档10G左右的流量 -> -> ......(中间省略) -> -> 黑客:我说了这多,你们也是不是给包烟钱,表示表示。 -> -> ...... - -*后来也和领导进行了商议,坚决不能给他们钱,不能助涨这种嚣张气焰,实在不行就报警!* - -曝光一下当年使用的假QQ号,刚查了下变了个头像和描述,如下: - - -![](http://favorites.ren/assets/images/2017/optimize/hacker01.jpg) - -后来我一直在想为什么DDOS攻击总是喜欢根据外网IP来攻击呢,慢慢好像是理解了。如果针对域名来攻击的话,那不就是攻击到域名商的服务器,一般域名商比较强大,黑客不太搞的定,也确实没有必要。当然记的前一段时间,某著名域名服务商被攻击,导致国外twitter等著名的互联网公司访问中断到达半天以上,还是很严重的。但是对于我们这些小公司,倒不至于搞这么大的动作。 - -到底如何正确的防止DDOS攻击: - ->- 第一种方案,隐藏服务器外网地址,服务器前端加CDN中转,免费的有百度云加速、360网站卫士、加速乐、安全宝等,如果资金充裕的话,可以购买高防的盾机,用于隐藏服务器真实IP,域名解析使用CDN的IP,所有解析的子域名都使用CDN的IP地址。此外,服务器上部署的其他域名也不能使用真实IP解析,全部都使用CDN来解析。 - ->- 第二种方案,买一些安全产品来进行流量清洗,主要是阿里云、腾讯云这种大厂商提供的一种服务。 - ->- 第三种方案,有很多的防火墙产品声称可以防止Ddos攻击,但是我个人使用感觉效果非常有限。 - -## SQL注入 - -我们的官网使用的是PHP开发,因为框架比较老旧的原因,存在着一些SQL注入的点,我们发现了一些进行了修补,没想到还是被一些黑客找到了突破点,这块还是比较感谢一些黑客在漏洞盒子上面提交的bug(如下图),最后我们根据提示进行了紧急修复,后来也在WAF防火墙配置了一些拦截SQL注入的策略,起到双保险的作用。 - - - -![](http://favorites.ren/assets/images/2017/optimize/sql.jpg) - - -我一直在想为什么PHP一般比较容易出现SQL注入呢,而Java较少暴漏出来SQL漏洞的情况,我估摸着有两方面的原因:第一,PHP一般前端使用较多,受攻击的机会更多一些,Java一般做为后端服务攻击的可能性会比较少;第二,PHP框架较多而且很杂,很多早期的框架并没有特别考虑SQL注入的情况,Java大量普及了mybaits\hibernate这种orm框架,框架本身对常见的SQL注入有防范的功能,但不是说mybaits/hibernate框架就没有被sql注入的可能,大部分场景下是OK的。另外参数化查询可以有效的避免SQL注入。 - -通过一段时间的学习,我发黑客一般先使用工具对网站做整体的扫描类似Acunetix,在根据扫描出来的漏洞做个大概的分析,但是比较深入的漏洞都需要根据网站的业务在进行调整,比如sql注入会根据页面的查询使用sqlmap等工具来进一步的渗透。当然我对这方面还是外行,描述的可能不够清晰。 - - -## 短信攻击 - -验证码漏洞,我们当初有一个很小的失误,有一个程序员在H5的小网页中将发送短信验证码返回了前端,最后被haker发现了,利用这个漏洞可以给任意的用户重置登录密码。那一晚上我们几百多个用户收到了密码重置的短信,虽然黑客最终也没有改用户的密码,只是发短信玩了一把,但是对于平台来讲无形的价值(信誉度)损失不小。 - -短信攻击,现在的网站几乎都有发送短信或者短信验证码的功能,如果前端不做校验,haker会随便写一个for循环来发短信,一般系统的短信会进行全方位的防控,比如:1、前端加验证(字符验证码,有的是拖拽的动画);2、后端根据用户或者IP加限制,比如用户一分钟只可以发送一条短信,忘记密码的短信一天只能发送10条、一个IP地址限制每天只能发送100条短信等。 - - -## 如何防范 - -黑客攻击公司的网站未必完全是一件坏事,一方面说明了公司已经吸引到了很多人的关注,另一方对我们技术团队是一次检验,黑客有时候会给你带来完全不同的一种思路想法。但是被黑客攻击而影响了业务,那就不是一件愉快的事情了,如果攻击导致频繁的出现问题,对内对外都会造成大的影响,那就是严重的事件了。 - -安全防范是一个全面且持久的工程,也是需要在硬件和软件上都要投入的一件事情。 - -### 硬件方面 - -需要做好网络安全规划,机房常用的安全设备有:VPN服务器、防火墙、WAF防火墙。 - -- VPN服务器:主要用于运维人员通过口令可以登录到机房内网中进行日常运维工作,也可以用做不同机房网络互通,保证在特定的网络下信息传输安全。 -- 防火墙 :外网访问进入机房的第一道大门,负责三层网络的安全检测,隔离内网和外网环境,外网为非信任区,内网为信任区。 -- WAF防火墙:最重要的安全设备之一,WAF防火墙主要是对Web特有入侵方式的加强防护,如DDOS防护、SQL注入、XML注入、XSS等。属于应用层的安全防护,我们经常遇到的黑客攻击行为,主要就靠它的防护能力。 - - -> 现在的防火墙技术更新特别快,以前的防火墙都是基于特征库来进行防护的,最新一代的防火墙可以根据平台的用户行为来检测分析是否为攻击行为。 - -### 软件方面 - -软件方面的防护主要有两方面,一方面使用证书保证传输层的数据安全,另一方面所有对外的接口都需要做好安全规划。 - -- HTTPS证书:在web服务器部署HTTPS证书,保证用户在交互的过程中数据没有被篡改。 - -- 网络规划:所有非必须的服务都不要开放外网访问,需要开放外网访问的服务器仅开放需要的端口,比如常用的80。和合作公司有数据交互或者接口调用的需求,需要绑定固定的外网访问地址。 - -- 技术选择:选择成熟的框架可以避免很多安全问题,早期的很多PHP框架根本就没有考虑SQL注入的相关问题,当初strust2的安全漏洞多少企业被坑。选择成熟稳定的技术体系可以避免很多低级的问题。 - - -------------- - -**作者:纯洁的微笑** -**出处:[www.ityouknow.com](http://www.ityouknow.com)** -**版权归作者所有,转载请注明出处** diff --git a/_posts/2017/2017-02-16-ten-billion-gold-platform-firefighting-story.md b/_posts/2017/2017-02-16-ten-billion-gold-platform-firefighting-story.md deleted file mode 100644 index 5774ea5e32..0000000000 --- a/_posts/2017/2017-02-16-ten-billion-gold-platform-firefighting-story.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -layout: post -title: 百亿互金平台救火故事 -category: arch -tags: [arch] ---- - - -多年前,又是周六客服打电话过来,平台官网不能访问,app完全无法打开,客户在QQ群和微信群中各种反馈,说平台是不是跑路了?客服的多条400热线完全被打爆,电话已经接不过来... - - -## 前言 - -一直以来总是想以什么方式去记录下自己在互金行业的这段经历,趁着自己还记得清楚,还能找到一些资料原型,一方面可以分享出来供大家参考,但是更重要就是多年以后我可以根据这些文章回忆起来自己的那段激情岁月。 - -想了很久但一直没有实施,后来觉得应该从架构的角度来梳理一篇文章,就写了《[从零到百亿互联网金融架构发展史](http://www.ityouknow.com/arch/2017/01/10/ten-billion-architecture-history.html)》这篇文章;最后认为只有实战出来的东西以及解决问题的过程,才是工作中最宝贵的经验,应该把它分享出来,在梳理的过程中有三起事故和黑客攻击事件比较有代表性,就整理出了下面这四篇文章,本篇文章从整体来回忆一下一路走过来所经历过的救火故事。 - -- [一次生产事故的优化经历](http://www.ityouknow.com/arch/2017/02/06/one-production-accident-optimization-experience.html) -- [一次dns缓存引发的惨案](http://www.ityouknow.com/arch/2017/02/09/a-tragedy-caused-by-dns-cache.html) -- [一个脚本引发的血案](http://www.ityouknow.com/arch/2017/02/12/a-script-caused-bloody-case.html) -- [互联网金融大战黑客](http://www.ityouknow.com/arch/2017/02/15/internet-financial-war-hacker.html) - -作为一个互联网金融平台,涉及到用户资金,任何的服务(资金)差错用户都是不可容忍的,用户不懂什么是数据库,不知道什么网络不通,就是一会看不到钱在app里面展示都会觉得不安。在已经有很多P2P公司跑路的前提下,用户个个已经被锻炼成为福尔摩斯侦探,每天打开app查看收益,监控着平台一切,甚至半夜升级断网十分钟,也会被用户察觉,直接就发到群里面,更有甚者直接在QQ群或者微信群中说你们的技术行不行! - -我们常说的互联网工作经验,一方面是开发经验,但其实更重要的是处理问题的能力。那么处理问题的能力怎么来呢,就是不断的去解决问题,不断的去总结经验,其中处理生产环境中问题的经验更甚,因为在处理生产环境中对个人的压力和临危应变的能力要求最高,你不但需要面临千万个用户反馈,客服不时得催促而且旁边可能就站了N个领导在看着你,一副你行不行的样子要求立刻马上解决问题!这个时候你的操作就非常重要,稍有不慎便会引发二次生产事故。 - -说了这么多,只是想说明,生产事故对技术综合能力要求颇高,更是锻炼处理问题能力最佳时机!下面给大家介绍我们从零开发到现在百亿交易量所遇到的几次关键事故,有大有小挑出一些比较有代表性的事件来分享。 - - -## 并发满标 - -公司系统刚上线的时候,其实没有经历过什么大量用户并发的考验,结果公司做了一个大的推广,涌入了一批用户来抢标,共1000万的标的几乎都在10秒之内搞定,大概会有上万左右的用户会同时去抢标,平均每秒大概有千人左右的并发,满标控制这块没有经过大的并发测试,上来之后就被打垮了,导致得结果是什么呢,1000万的标的,有可能到一千零几万满标,也有可能会九百多万就满标,也就说要不就是多了一些,要不就是少了一些,就满标了。 - -这就会很尴尬,因为借款用户就借款一千万整,那么多出来的钱不能给用户回退,因为用户好不容易才抢上了,无端退了用户也闹;少了也是问题,用户借款一千万,少了几十万也不行,如果缺的少了可以想办法找一些有钱的客户直接给买了,多了就必须重新放出来让用户投资,非常影响士气,这个问题困扰了我们有一段时间。 - -购买标的流程图,不知道大家是否能根据此图发现问题呢? - - -![](http://favorites.ren/assets/images/2017/optimize/buy.png) - -**超募** - -为何会产生超募?在最早前的版本中没有使用乐观锁来控制,如果在最后购买的用户一单出现并发,就会出现超募,比如最后剩余30000份的购买份额,因为并发量特别大,可能同时会有十几个用户拿到了剩余30000份余额的可购买额度,有的买1000份、有的买上3000份、有的买上20000份都会驱动满标,所以最后导致了超募。 - -针对这个问题,主要是引入了memcached乐观锁的概念(底层主要是```cas```、```gets```两个命令),在发标的时候存入标的总份额,当用户购买的时候首先去锁定用户购买的份额,因为乐观锁的原因,如果同时有两个用户拿到份额的时候保证只有一个最后可以更新成功(锁定份额),(锁定份额)失败直接返回,这样就保证了在入口的时候就直接屏蔽了部分并发的请求。 - - -**少募** -为何产生少募?少募是可能1000万的标的突然到980万就给满标了,这是因为在超募情况下我们完善了代码,用户一进来首先就是锁定购买份额,只有锁定购买份额才能进行下面的流程,如果锁定购买份额失败直接返回,这样虽然保证了在1000万份额在购买初期必须每一个用户只能锁定一份,但是在高并发的情况下,因为购买流程中有十几个分支,每一个分支失败就会退回锁定的份额,这样就会导致这样的现象,就是可能是并发一上来,马上就满标了,过了一会进度又回退回来了。 - -少募主要是因为分支失败回退导致的,一方面我们分析了容易导致回退热点,因为在用户抢标的时候会给用户实时的展示标的进度,在很早的版本中直接就是存入到一个标的进度表里面,并且采用了乐观锁,如果并发一高就频繁的更新失败导致回退,因此优化了标的进度这块,直接去掉了标的进度表,实时根据查询来展示标的进度(可以有延迟,有缓存);另一方面在回退份额的时候在次判断试下memcached的份额和标的的状态,如果份额不为零并且标的状态是满标,马上自动更新状态保证后续用户可以立即购买再次驱动满标。 - - -*做了以上的两种优化后,我们还遇到了其它的一些小问题,在不断的优化过程中,终于稳定下来;在后期版本中将考虑使用MQ队列或者redis队列来处理抢标更合理对用户也更公平一些。* - - -## 重复派息 - -15年的某一天看到一个新闻说是陆金所的一个用户发现自己银行里面突然多了很多钱,没过多久又被扣走了,然后收到陆金所那边的解释,说是给用户还本派息的时候程序出现了问题导致还本派息两次,当他们程序员发现了此问题后紧急进行了处理,用户当然闹了呀,就上了新闻,当然陆金所通道能力确实比较强可以直接从用户卡里面扣,当大家都兴致勃勃的谈论这个话题的时候,我却有一股淡淡的忧伤,为什么呢?因为这个错误我们也犯过,具体说就是我搞的,大家可不知道当时的心里压力有多大! - -事情是这样子的,我们使用的第三方支付的扣款接口不是特别的稳定,于是我们前期就对接了两种不通的扣款接口,平时前端投资的时候走一个接口,后端派息或者还本的时候走另外的一个接口,在初期的时候扣款接口不稳定,因此在给用户跑批的时候经常会有个别用户失败,需要手动给失败的用户二次派息。做为一个有志向的程序员当然觉得这种方式是低效的,于是将程序改造了一下,在后端派息的时候当第一种扣款失败的时候,自动再次调用第二种扣款接口进行扣款,当时想着这种方式挺好的,各个环境测试也没有问题,上线之后监控过一段时间也运行稳定。 - -当我感觉一切都很美妙的时候,事故就来了,突然有一天客服反馈说有的用户说自己收到的利息感觉不对,好像是多了(真的是太感谢这个用户了),我登录后台看了一下派息的流水复核了一遍,果然利息被重复派了,一股冷水从头而下,把当天所有的用户派息记录和到期记录都进行了检查,影响了70多个用户,导致多派息了6万多元,幸亏只是派息出了问题,如果是到期的话金额会翻N倍,其中70多个人里面有几个进行了体现、几个进行了再次投资,绝大部分用户在我们发现的时候还不知情,金额也没有动。 - -怎么处理呢,当然不能直接就动用户的钱了,给每个重复派息的用户打电话,说明原因赠送小礼物,请求谅解后我们把重复派过的利息再次调回来。大部分用户进行了核对之后都还是比较配合的,但是肯定有一些用户不干了,当然也不能怪客户,都是我的原因,有的客户需要上门赔礼道歉,有的客户需要公司出具证明材料,我们的老板亲自给客户打了N个电话被客户骂了N遍,我心里压力可想而知,其中有一个客户特别难缠,各种威胁说既然到了我的账户里面肯定是我的,你们的失误不应该让他来承担,折腾了很久,还是不能怪客户。可能会说有的互联网公司经常出现这种问题后就送给客户了,哎,我们是小公司呀!这个噱头玩不起。 - -到底是什么原因呢,事后进行了复盘也给领导做了汇报,平时都是首先进行派息的定时任务,过一个小时之后进行到期的定时任务,当天的派息标的比较多,跑了一个半小时,就导致了派息和到期的两个定时任务同时进行,转账有了并发,第三方支付的接口不稳定给我们返回的失败,其实有的是成功的,就导致了我们进行了二次的扣款尝试引发了此问题。这个事情给我带来了非常大的教训,对于金融扣款的这种事情一定需要谨慎,那怕付款引发报警之后再人工处理,也不能盲目重试可能引发雪崩效应。 - - -## 杂七杂八 - -还有就是其它一些零碎的问题了,记的有一次对用户的登录过程进行优化,导致有一块判断少了一个括号结果用户在那两个小时内,只要输入账户,任意密码就可以登录了,幸好及时发现这个问题,正是这个问题才导致了我们正式确立了规范的上线流程,为以后的上线制度建定了基础。 - -还有一次我们在模拟用户投资一种标的时候,留了一个入口通过http就可以调用,测试也没有问题,有一天正好给领导演示呢,就在次用http请求的方式在浏览器执行了一下,前端就会看到自动投标的过程,因为生产的数据有点多,投标的过程有点长,我们为了加快进度,找了好几个人同时来执行这http请求,导致最后出现了问题,最后发现写测试脚本的这个同事根本就没有考虑并发的情况,才导致出现了问题。 - -也做了很多的活动,记得做一个网贷之家的一个活动的时候,活动上线比较紧张,我们团队曾经连续工作超过30个小时(一天一夜再一天),当天晚上我2点左右写完程序,测试从2两点测试到早上9点,最终确认没有任何问题,才进行投产。半夜公司没有暖气,我们实在冻的不行了,就在办公室跑步,从这头跑到那头,第二天上线之后,又害怕出现问题,监控了一天,确认没有任何问题,才到下午正常下班回家,那时候真是激情满满呀。 - -说到做活动肯定少不了羊毛党,说哪一家互金公司没有遇到过羊毛党那很少见,而且现在的羊毛党规模简直逆天了,我们用户里面就有一个羊毛党在两三天之内邀请了六七千位用户,如果说邀请一个用户送1元,那这个用户就可以搞几千块一次,而且有很多专业的网站、QQ群、微信公共账号都是他们的聚集地,哪天哪个平台有活动门清,他们写的淘羊毛操作手册有时候比我们官网的帮助文档还清晰,所以做活动的时候要考虑特别周全,各种限制,有封顶、有预案、讲诚信,只要是符合我们活动规则的坚决按照流程走。 - -还有一个有趣的事情,app推送,一次我在公交车上就看到xx盒子app弹出hhhhh的推送,这个事情我们也搞过,因为在调试的时候生产和测试就差了一个参数,有时候开发人员不注意就把生产参数部署到uat环境了,测试一发送就跑到生产了,这方面只能严格按照流程管理来防止。 - -其实还很多问题:mongodb集群和mysql的同步出现的一些状况、后台大量数据查询下的sql优化、golang使用mapreduce碰到的问题... 限于篇幅这里就不一一清晰的描述了。 - -其实每次的出现问题都是对团队一次非常好的锻炼机会,通过发现问题,定位问题,解决问题,再次回过头来反思这些问题;重新梳理整个环节, -举一反三避免下次再次出现类似的问题。正是因为经历这些种种的困难、考验才让团队变的更强大更稳定,也更体现了流程的重要性,更是避免再次发生类似问题。 - - - -## 总结 - - -古人有云,胸有惊雷而面如平湖者,可拜上将军。在互联网行业对大牛的要求也同如此,特别是技术的负责人,在面对生产事故的时候,一定首先是安抚同事,静下心来找到问题本质再去解决,而不是不断去施加压力催促解决,重压之下很多心里承受能力稍弱的队友,更加慌乱而不利于解决问题或者引发二次事故。 - -在看淘宝双十一视频中,有一段特别受到感触,在双十一初期,虽然技术团队做了很多的准备,但是在零点过后流量瞬间涌入,服务被打垮,部分用户投诉刷新不出网页,紧接着隔壁同事也都反馈网站打不开,在大家都在慌乱中,xx一拍桌子大喊一声,大家都别动,三分钟之后再说,过了几分钟之后服务慢慢部分恢复了正常。后来回忆说,当时虽然服务瘫痪,但是监控还是有部分得业务成功,说明系统并没有被压垮,而此时的任何操作都有可能引发更大的问题,从此之后此人一战成名,成为阿里大将。 - - -互联网平台发展大抵都会经历三个阶段: - -- 1、上线初期,此阶段问题最为繁多,生产事故不断,系统快速迭代优化。有人说为什么不测试到完全没有问题再投产吗?说实话在互联网行业这个很难,小公司很难做到生产环境和测试环境一致,成本太高;时间紧迫,一般都是很短的时间内要求上线,上线之后在快速迭代。另外互联网本就是一个快速试错的行业,错过半年时间可能风口早过; - -- 2、发展期,此阶段主要业务模式已经得到验证,系统出现问题的频繁度较少,低级错误减少,但此时是用户量和交易量不断爆发的时候,对系统性能、高并发的要求又上来了,所以此时出现的问题大多都是性能的问题; - -- 3、成熟期,发展期过后系统相对比较平稳,用户量和交易量都已经慢慢稳定下来,生产问题越来越少,出现问题几乎都是细小的bug,这个阶段也是公司最忽略技术的阶段,恰好我们公司就处于这个阶段,在这个阶段就需要静下心来,组织架构升级,补齐在初期和发展期所欠的技术债务,做好公司在升下一个量级的技术准备。 - - -> 所有的这些问题几乎都集中在14年底到15年初的这个阶段,15年后半年开始到现在平台慢慢稳定了下来,到现在几乎没有再出现过类似的问题,也因为几乎都是两年前的事情,有很多记的不是特别清楚了,写的比较粗糙望见谅。 - - -------------- - -**作者:纯洁的微笑** -**出处:[www.ityouknow.com](http://www.ityouknow.com)** -**版权归作者所有,转载请注明出处** - - - diff --git a/_posts/2017/2017-03-22-duoshuo-close.md b/_posts/2017/2017-03-22-duoshuo-close.md deleted file mode 100644 index 4ce4bd7f84..0000000000 --- a/_posts/2017/2017-03-22-duoshuo-close.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -layout: post -title: 多说关闭了 -category: other -tags: [other] ---- - -突然收到多说的官方邮件,说多说要关闭了,心中一番感慨,使用多说也近一年了,我的个人博客的第一个读者评论就这里,和很多素问谋面网友的交流在这里,很多回忆在这里。 - -官方的邮件如下: - -``` -重要通知:多说即将下线 - -你好! - -因公司业务调整,非常遗憾的向大家宣布多说项目即将关闭。 - -我们将于2017年6月1日正式关停服务,在此之前您可以通过后台的数据导出功能导出自己站点的评论数据。 - -对此给您造成的不便,我们深表歉意,感谢您的一路相伴。 -``` - -因为我的博客系统原生就带的是disqus所以切换到是没有什么技术问题,一分钟搞定,接下来就看如何将多说历史中的评论导入到disqus中,目前网上有一些教程,我也会在后面做些尝试;但是disqus有个很多的问题,被墙了,不过没有翻墙的网络,文章下面的评论就不会出来。 - -这些年,中国基础服务公司,慢慢都倒闭了,前一阵的很多网盘大家还记忆尤甚,接下来又是多说。 - -最后使用这篇文章中的办法,成功导入到disqus [弃用多说, 多说转 Disqus](http://lukang.me/2016/duoshuo2disqus.html),但是转化后的xml有一个小bug,它会默认对所有的链接后面添加.html,需要手动改一下既可。 - -发此文记录此事。 - - -*20170630日记* - -我就是一个折腾的命,最近发现很多博客使用了网易云跟贴,大概看了一下,使用起来还不错。又换回到了网易云跟贴。 - -说一些替换成网易云跟贴的步骤吧,其实很简单: - - -1、登录网易云跟帖的网站[https://disqus.163.com/](https://disqus.163.com/) - -2、注册相关账户或者直接使用网易账户登录 - -3、创建网站信息 - -4、获取对于的代码 - -5、直接贴到Jekyll post页面下方和你想要的页面中 - - - -最后看了一下也挺清爽的,比有言好多了,注册的时候需要备案号,太恶心了。 - - -看看效果吧,就在文章下方。 - -*20170713日记* - -*一声叹息,刚看到下方这个邮件* - -``` -亲爱的云跟贴用户,您好: - -很遗憾地通知您,网易云跟贴产品将于2017年8月1日停止服务。在此之前您可以通过官方网站(disqus.163.com)后台的数据导出功能导出自己站点的跟贴数据。感谢您一直以来对云跟贴的支持与厚爱,对于此次产品关停给您带来的不便我们深表歉意,希望在网易其他产品与业务上,我们能继续为您服务! - -网易云跟贴 -2017年7月6日 -``` - -我还能相信国内的企业吗?又换回disqus吧,额的神仙呀! - - -*20171122日记* - -看到[码志的博客](http://mazhuang.org/) ,发现gitalk作为评论框还是挺不错的,换了过来。 - - -毕竟有的时候我自个都打不开disqus,但是仍然留有disqus,作为懒加载的方式提供出来。 - - diff --git a/_posts/2017/2017-03-26-programmer-confused.md b/_posts/2017/2017-03-26-programmer-confused.md deleted file mode 100644 index 23155410fc..0000000000 --- a/_posts/2017/2017-03-26-programmer-confused.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -layout: post -title: 你看那个人他像一条狗 -category: life -tags: [life] -lock: need ---- - -我好像感觉还很年轻,才刚刚毕业,对未来一片憧憬,耳边才响起小马说,走!咱俩一起闯世界去。我仍然感觉我还是小伙子,但回家后才发现印象中那些屁颠屁颠的小男孩,他们才是正年轻真小伙,此刻我已经30岁了。 - -以前我在想30岁是什么感念。大着啤酒肚、人生过半、生活稳定开着车子。此刻我均没有。。。 - - -![](http://favorites.ren/assets/images/2017/life/likedog.jpg) - -30岁,到了传说中程序员最应该迷茫的年龄了,那么我迷茫吗,没的说,按照华为34岁就要劝退的要求,我还有4年的程序生涯。 - -为什么30岁的程序员就应该迷茫呢?30岁正是经过了七八年的职场生涯,技术、经验、职业素养等各方面都到了一个比较充沛的阶段。如果前几年不是在混日子,到了现在,踏踏实实干活的一般会成为公司的核心开发,潜心研究技术的应该会成为架构师,有领导才能的也许是小领导或者项目负责人。按道理来讲30岁正是人生的最黄金年龄,也是事业发展的核心阶段,如果在这个阶段抓住机遇能有突破,对整个职业生涯影响可谓不小。 - -转眼一想,三十岁也确实该迷茫,三十岁了,你在熬几个通宵试试?父母年纪大了或者有了儿女真是到了上有老下有小的阶段,很大的一部分精力需要放到家庭里面,这个阶段你就是家里的顶梁柱,可能家里最大的收入就来源于你。如果你和我一样来自于农村,没有什么家底,回家之后你会看到昔日里你感觉混的很一般的同学,买了房和车,日子过的很悠闲,然而你每个月工资一万两万的收入使照样无房无车无存款,然后你会想,这么多年自己也一直没有歇着,每天也都坚持和努力,但现实和理想仍然差了那么一截? - -想起了上一段时间看到的一个新闻,当大家都骂此男子不守规矩的时候,有人从另一方面进行了解读,心情比较复杂。 - - -![](http://favorites.ren/assets/images/2017/life/30.jpg) - -中国的IT行业还很年轻,真正发展的时间也不过二十多年,而且中国互联网发展的速度非常的快,往往在其它行业需要积累N年才可以职业级别,到互联网公司几年就完成了。很多写程序很牛逼的程序员没过两年就都多多少少的被提拔到了管理的职位,有好处也有坏处,好处是可以综合的锻炼自己,坏处是没有太多的机会去做深入的研究,大公司可能还好一点。在这个快速的发展过程中,选择多了起来,也容易产生迷茫。 - -就像研究技术一样,探究迷茫的本质是什么?迷茫本质是未来的不确定性,典型的代表就是在你人生最关键的几个选择点上,比如:毕业时考研还是上班、工作后选择大公司还是创业公司、工作几年后走管理路线还是技术路线... - -诚然每个人每个阶段的迷茫都是不一样的层次也是有所不同,很多线上或者线下的朋友也会一起聊聊自己现阶段的困惑,有的会说,我现在进入了一个瓶颈,不知道如何去突破了;有的编程语言已经换了两次了,然后还问,现在转Java还有前景吗?我自己其实也不敢妄言,也只是结合自己的情况给了一些建议,也未必正确。 - -所以迷茫是不分年龄的,到那个阶段都会有那个阶段的迷茫,只是到了30岁迷茫的情况会更复杂一些,回顾一下自己的职业生涯的几个关键的迷茫点,希望可以给同样阶段的你有所启示。 - -## 大学毕业 - -最近我司在招聘中级Java工程师,在boss直聘上面发布了一个岗位需求,半天的时间来了100多封简历,突然感觉刚毕业程序员现在竞争很激烈,从我毕业到现在吧,有一个感触刚毕业的程序员是最难找工作的,工作1-6年中找工作的溢价能力是不断提高的,多工作一年出来找工作的机会就越多,企业也热于招聘有丰富经验的程序员,工作6年以上,也就是30左右了吧,如果在前6年中都在混日子,或者都在外包公司,或者业务6年几乎都没有太大变化的公司中,那么6年后受欢迎度会越来越低。 - -所以刚毕业的第一家公司,最重要的是有一份工作可以让你去学习和实践,毕竟大学课程和实际工作差别还是比较大,如果有选择的情况下,尽量选择自己喜欢的行业,比如对游戏感兴趣,那么就多投一些游戏公司,对电商感兴趣多投一些电商公司,如果没有太多选择的情况下,尽快选择一家有技术氛围的公司,那么如何在面试的时候判断这家公司有技术份额呢?第一看公司的面试过程,对面试人的态度是否真诚,如果面试者一副牛逼呼呼的感觉,基本上面技术氛围不咋的,越是牛逼的人越低调;第二看面试过程中考察的知识点都是什么,一般面试者官都会关于公司的项目技术基础来问一些题目;最后主动问一下公司的技术栈都有哪些,自己入职后负责那部分? - -对于这个阶段的同学来讲,最重要的是尽快去入职工作,积累经验。对了,尽量不要去外包公司,很难赚取到什么经验,而且会累死。 - -## 工作两三年 - -先说说我工作两三年的那个阶段吧,就是工作两三年后,技术积累也有了,项目中遇到的问题基本上也都能解决,但是当时我们是一家偏传统的企业,我做的项目基本上就是给各个公司的IT管理员或者企业领导来用的,基本上使用者不超过10个,那时候我就特别想让自己做的东西可以让千万万的用户来使用,这样我才觉得我的工作更有价值,而不是做了大半年的项目给几个人用,而且几乎没有反馈。 - -于是在公司了两三年后,我换工作的时候目标就稍微明确了一些,做自己产品的公司,并且未来这些产品可以服务于大量的用户,这种类型的公司基本就都是互联网公司了,当时放眼西安几乎没有什么像样的互联网公司,大家都知道北京是互联网公司最多的地方,因此依然决定来北漂寻找一份互联网公司的工作,当时只是不想去传统企业,但是到底是进入什么样的互联网公司我并不清楚,机缘巧合进入了第三方支付行业,那时候我感觉这就是我当时想要工作。 - -如果你不知道自己喜欢什么样的行业,那么你至少要知道什么是你不喜欢的,这样在选择的时候也能帮你做筛选。 - -两三年之后应该是程序员最黄金的年龄,在这个阶段积累技术应该是最快的,如果你在自己的岗位中呆了一年多了,感觉还是没有学到什么东西,建议跳槽,有时候如果你自己没有能力去驱动自己去改变的时候,那就靠外部环境来驱动你,真正的经验、解决问题的能力都是在实战中学习到的,去了新的公司打破自己原有的技术盲区。 - -## 管理or架构 - -按道理来讲,在公司4年到6年的这个阶段,基本都到了中级or高级开发工程师的阶段,也是正练代码的时候,也是从how to do 做why to do 的阶段,到了可以慢慢做一些底层或者原理的研究阶段,但是在中国,基本上程序写的好的在这个阶段慢慢都会让去做一部分管理的工作,比如项目经理,小组组长等,有可能涉及到沟通或者其它的杂事太多,相应的编程的时间就少了,也容易产生迷茫?可以潜心研究技术,不太愿意做沟通或者撕X的,对技术保持热爱的可以走向架构师的这个方向。 - -其实我在工作4年多的时候,心里面的标准方向就是想做一名架构师,幻想着可以指点项目架构的江山,但是很快我发现在中小型公司里面不太现实,技术架构级别上都是技术经理在做决策,有架构师职位也基本上要不是偏高级的编程人员,要不也是做了一部分的管理职能,没有纯粹的架构师职位,后来我司成立互联网金融项目,领导就以可以让我独立负责所有技术为诱惑,后来就走向了技术经理的这条路子,不过后来确实可以按照自己的想法去构建整个平台的时候,感觉还是挺不错的。 - -不是技术好的都可以做管理,有时候也和个人的气质有很多的关系,有的人在为人处世方面确实比较擅长,有的人却对技术有着天生的敏感性,做了管理之后,和很多不同的人做了深度的交流了解,你会发现把合适的人放到合适的位置是多么的重要,可以省很多的心。因此在这个阶段需要对自己有所了解,自己擅长或者喜欢做那个方向,另外沉下心来研究技术,实践技术,等待机会,因为公司里面提拔的人才基本上都是老员工多一些,因为经历了多年的一起抗战感情上信任的住,技术能力也比较了解,频繁跳槽比较难一下进入高级一点的职位。 - -在这个阶段最好能选择一个自己喜欢的行业来稳定下来,比如游戏以后换工作的时候也是游戏方向,比如你喜欢金融,以后的工作尽量都和金融相关的,有行业积累的情况下,对以后的发展非常重要,如果这两年做金融,过两年又是通讯各种换,到头来行业背景几乎是零,因为技术的本质还是为解决实际问题,如果你在某个行业多年,那么你对行业的系统有全面的了解,开发过程中也会避免很多问题,这就是为什么很多公司招聘的后面都会有一条,有xxx工作经验者优先的原因。 - -## 30岁程序员 - -我身边有一部分程序员到了30岁左右转行了?当然很少的一部分转到了完全不相干的行业里面去了,理由是写程序太累30岁以后身体就不行了,还有相当一部分转业到IT的其它岗位了,比较多有:产品、测试、售前、需求管理等等。其实我觉得还是对程序的热爱不够,或者是本身的兴趣点就不在这里,这里就不讨论30岁程序员到底还适不适合编程的这个话题,因为我现在还在写代码,我也热爱写代码完全没一点问题,[脉脉中此话题的讨论](https://maimai.cn/article/topic?id=1669) - -有一段时间,半夜总是失眠,躲进卫生间里面点上一支烟,对镜子问自己这是你想要的生活吗?在北京4号线,身体可以保持倾斜45度而不倒,这不是魔术是生存。地铁里面男男女女以各种姿势贴在一起,站在门口不用走自动被挤进去,都拼上自己一天的力气演绎这上班的第一课。每次我看着地铁里面急匆匆的路人,我就想他们的生活是什么样的?16年冬天的时候我差点被北京的雾霾给逼疯了,白天是黑夜,夜晚也是黑夜,每到周末就是大雾霾,感觉自己好像被囚禁了一样,在房子里面坐立难安,不知道哪一天我会不会因为雾霾而离开北京。 - -隔一段时间朋友聚会就会发现又有一位朋友回老家了,每次走一个朋友心里就咯噔一下,想想自己为什么还在坚持,回家好像生活很悠闲,有一刻真的有回家的想法,但是下一刻立刻被我否了,首先经济条件还达不到回家养老的程度,第二老家根本没有自己喜欢的什么互联网公司,第三,我可能也习惯这种大城市的生活,方便、快捷、甚至拥堵。 - - -![](http://favorites.ren/assets/images/2017/life/daling.jpg) - - -就像刚毕业在西安的那种感觉一样,刚毕业那会,有少一部分人去了北京、上海,大部分都在西安,然后我们在西安的这部分人,每次有一个离开西安去别的地方的时候我们就聚一下,每年我都可以送走几个人,到了最后红柯走的时候就我们两个人一起吃了个饭,如今那些在北京上海的朋友都慢慢的回了老家,而当时留在西安的我,却一直漂在了北京。 - -这就是我30岁的迷茫,要不要重新选择拼搏一把,要不要离开这个热爱又无奈的城市。你呢? - -## 最后 - -那么迷茫就不对吗?从某种角度来讲,迷茫是一件好事情,说明你对现状进行了思考,对目前状态有改变的期望,同时又对接下来改变后的未来不确定性,所以才迷茫。所以说迷茫不是错,迷茫之后没有行动没有改变才是问题。可以给自己一段时间,在这个时间内,进行充分的了解选择后的结果,以及对自身定位,在这个时间段之后,就果断的行动,不要拖拉。 - -回想自己这几年的迷茫,刚毕业那会迷茫要不要进入IT行业,要。工作两三年迷茫要不要离开家乡去互联网公司?去。来北京后,要不要跟着公司搞互联网金融,搞了。去年朋友说创业去不去,思索良久,么去;那么现在呢,要不要继续在北京吸霾,还是没有想好,要不要进入一个新的领域去拼搏,也是没想好? - -对了,这几年好像过一年两年我就要迷茫一阵子,但是现在回想起来刚开始的迷茫却和现在迷茫又了大不同,思考的层次也不同,没事迷茫的这种频率还是保持了下来;在回想自己不迷茫是什么时候呢?下决心进入IT行业,潜心学编程的时候,刚工作热情工作的时候,天天加班搞互联网金融的时候,有一个共性,那就是飞速成长学习的时候,打破自己壁垒的时候,所以现在的迷茫可能就是慢了下来,又要重新选择了吗。 - -所以呢,迷茫是一件好事吗?从某种角度来讲,我认为是的。 - -那么作为程序员的你,还在迷茫吗? - - -![](http://favorites.ren/assets/images/2017/life/look.jpg) - -------------- - -**作者:纯洁的微笑** -**出处:[http://www.ityouknow.com/](http://www.ityouknow.com/career/2017/03/26/programmer-confused.html)** -**版权归作者所有,转载请注明出处** - - - - - - - - - - - - - - - - - diff --git a/_posts/2017/2017-04-21-house-rented.md b/_posts/2017/2017-04-21-house-rented.md deleted file mode 100644 index 21e504506d..0000000000 --- a/_posts/2017/2017-04-21-house-rented.md +++ /dev/null @@ -1,174 +0,0 @@ ---- -layout: post -title: 那些年,我们租过的房子 -category: life -tags: [life] ---- - -在北京有流传着这几句话:没有遇到黑中介的北漂都不算是真正的北漂,没有经历过黑中介的北漂不足以谈人生。其实根据这两句话就可以基本了解北京的租房生活是何等的艰辛,但北京租房只是全国大租房的一个缩影,中国有着全世界最多的人口,而北京有着全中国最大的的流动人口,大量年轻人来到大城市寻找梦想,租住着价格高昂却狭小昏暗的房间,而这几十平米的空间,却是大多数北漂奋斗十几年都买不下来的,所以在北京这些租房群体也有了专有的名词:蜗居、 蚁族、鼠族、胶囊公寓等等。 - - -![](http://favorites.ren/assets/images/2017/life/zhongjie.jpg) - -身边朋友租房的遭遇也各种各样。有住到一半的时候,房东突然一个电话说儿子要结婚给一周的时间腾空房间;有住进去几个月房东随意涨价10%,不接受就走人,押金也就别想要了,随便找个理由就给扣下了。还有各种黑中介,我有一个朋友和中介打官司,每次法官传唤,中介都不去,最后也是不了了之;有的合同已经签了,出了问题,签合同的销售早离职了,公司不认说是销售人员自己签的合同没和公司报备。总之就是只有你想不到、没有他们使不到的手段,简直都可以召集北漂们写一个租房防坑手册了。 - -回顾这十几年我自己的租房经历,可以说在我人生每个不确定的时候,就是我变换租房的时候。它代表了我的人生轨迹,见证了我从小县城到省市再到北京的成长。 - - -## 一、小县城 - -在我们那里,县城周边的孩子初中时候就需要去县里面读书了,距离县城近的可以骑自行车,但大部分人都是选择在县城里面租个20个平方米左右的房子住,一个月40元左右,当然了那是2003年,我也才13岁。一般这20平米的空间里面需要摆放两张小床,两套灶具以及两个人的其他生活用品。那时候做饭用的是蜂窝煤炉子,那时候每月40块钱的房租在小县城也不算便宜,所以有两张小床就可以找个人来合租,当然了有个人合租对一个十二三岁的小孩来说也能减少些孤单。 - -初中住在城东,和我一个远房表哥住在一起。刚开始不会做饭,只会熬稀饭,熬一锅吃三天,或者是下干面条,下一大锅干面条,放点辣椒,泼上一勺油,就成了美味的油泼面,当时就会做这两样东西,吃了一段时间我的表哥和父母实在看不下去了,于是我爸每天中午就过来指导我做饭,慢慢的家常便饭就没啥问题了。 - -当时我们住在房东的四楼,四楼就只有两家住户有一个小阳台,站在阳台上面可以看到东街最繁华的十字路口卖着各种小吃,那时候房东11点就锁了大门,我们经常因为贪玩回不去,就想办法用卡片撬开锁头或者从二楼翻过去。记得我有一次从对面家属楼的楼道外面直接翻到了四楼,现在想想都后怕,四楼摔下去不废也残呀,人生的很多第一次都在这个阶段。 - - -我初中正是叛逆期,那时候刚好脱离了父母的管制,特别地嚣张自在,那些“坏孩子”才干的事情像打牌、抽烟、旷课、打架我都是那个时候学会的。初中的叛逆导致我学习也不是特别好,考上了城西的一个高中,我们简称西中,学校不好,我就被变成了里面的好学生。 - -刚刚用百度街景地图看了一下当初的房子,那时外边小三层现在已经变成了十几层的洋楼,变化真大。 - -![](http://favorites.ren/assets/images/2017/life/chuzhong.jpg) - -高中住在城西,房东是一个二层的小洋楼,房东住在一楼我们住在二楼,楼上其实没有几间房子,所以大家都很熟悉,闲的时候在一起聊聊天,洗洗衣服。高中的三年我换了两个室友,第一个来的时候还不认识就这样搭伙住在一起了,那个家伙特别爱看小说,这个爱好和我相当的一致,我俩就进入了各种武侠世界,甚至办了当时书店借书的金卡,每天借书两毛钱,但这个家伙看书有点走火入魔,天天看小说,最后都不去上学了,就在宿舍专门看小说,最后被他爸从老家敢过来打了一顿带回家了。 - -我独自住了一段时间,就迎来了我的发小来合租,我的初中同学也住到了隔壁,从此三人各种熟悉,一起做饭、一起出去转悠,听各种流行歌曲,在每天晚上10点的时候,我和飞各自躺在自己的床上,收音机里面播放着中国之声,我俩一人点上一支烟,要么躺着静静的听广播,要么一起畅谈人生和理想,想着将来如何如何,将来在那个时候好像很远。。。 - -有一个有趣的巧合,我初中和高中租房都是分别三年没有换过地方,两个房东的女儿都和我是同级也都认识,学习也都比较好。当然那时候也曾经幻想过和房东的女儿发生点什么,但在那个年代一个好学生和一个坏学生是不可能有交集的,所以其实什么都没有发生 :) - - -高中我就住在这个沟里面 - -![](http://favorites.ren/assets/images/2017/life/gaozhong.jpg) - - -## 二、大西安 - -西安,是我搬家最为疯狂的地方,西安也是传说中蚁族的聚集地,很多大片的城中村是西安租房人的缩影,西安著名的城中村有沙井村、八里村、西辛庄、杨家村、边家村等等,其中鱼化寨被称为小香港,里面的繁华程度不可想象,各种小吃应有尽有,甚至有些旅游的都要过来参观一下,我在鱼化寨呆了两年,记忆很深刻。西安的城中村大都是私人自己盖或者加盖的,大概都是15-40平方米左右的楼房小间,好一点的都会有卫生间和简易的厨房,但是因为盖的特别密,在楼层不靠窗的情况下,大都光线很差。记得沙井村还拍过一个电影叫做“沙井村之恋”,也是城中村生活的代表,想了解西安城中村生活的可以看看。这些城中村现在要么已经被拆迁要么处在拆迁中已经发生了很大的变化。 - - -沙井村 - -![](http://favorites.ren/assets/images/2017/life/shajingcun.jpg) - -*第一间房子-双水磨-一个月* - -在西安租住的第一个房子,那时候大四还没毕业,各种奔波之后选择来西安培训学习编程,自然面临着租房的问题,我选择的是高新区的一家培训机构,当然也就在距离培训上课不远的丈八北路这一带来选择租房。茶张村、双水磨、南窑头一路看过去,最终在双水磨选择了一家,租住的房子就在我朋友的楼上,我记得当时一个月是160还是180块钱,就10平米左右吧,里面除了一张床一个桌子外啥都没有了,好在有朋友做伴,这对于刚来西安的我也有个说话的人。但没想到,我刚搬来了两周朋友就去了青海,他把各种东西往我这一寄存就撤了。更么想到的是两周后我也搬走了。 - -*第二间房子-青龙寺-两个月* - -因为发生了一些意外,我最终没有去高新区的那家培训机构,而是选择了另一家培训机构,在西安交大出版社的楼上上课,双水磨的房子距离上课的地方太远,于是就退掉了双水磨的房子,打算在交大附近找一个房子,最终根据培训机构的同学的建议在青龙寺一带租房子。找的时候比较着急,顺着巷子走了很远,最后选择了一家,180元一个月,也是很小的一个房间,房东还是个基督教徒,经常有一堆教友在楼下聚会。这房子的缺点:晚上11点关门,不给大门钥匙,光线一般。 - - -*第三间房子-青龙寺-三个月* - -在培训机构里面迅速交到几个好朋友,于是我又决定搬到他们的那栋楼上,190元一个月,这房子背后就是青龙寺公园,光线很好。但缺点也很明显,因为选择的四楼是楼顶并且是夏天,每天晚上就像烤红薯一样热醒好几次,早上睡起来之后床上就是一个人字,现在想想那个夏天简直天天晚上就是蒸桑拿。。。房东是个老太太人还不错,我在这里住了将近四个月直到找到了工作。 - -而此时也是大四后半学期了,学校需要体检、写论文、毕业答辩等等,反正能不回去的我都没回校,都是我的室友们,这些原本需要我做的事情都几乎让他们包办了:体检时这个室友帮忙测身高,那个帮忙测视力,另一个帮忙测体重,就这样蒙混过关了,也真是都难为他们的了。我记得只有毕业答辩的这一次我回去了,就连毕业证、学位证都是让小刚帮忙给我领的。 - - -*第四间房子-丁家桥-两个月* - -此时我已经毕业了,找的第一份工作是软件开发,在西安搞软件的几乎都在高新区,当然我的第一个东家也不例外就在科技二路西安软件园内,所以我也就很自然的就在科技二路附近找房子,很快就找到了位于科技二路西段的丁家桥村子里的一间二楼的房间,一个月230块钱,这房子临近小巷子,光线还凑合,妈妈也给我做了一套新的被褥,从老家扛了过来。我本来计划一直住下去的,但是随后出现了两件事情迫使我不得不又搬家。第一件事情是在我刚住了一个月的时候,政府就入住了动员拆迁,到处贴标语,高音喇叭,一队一队的穿着笔挺制服的保安每天来来往往,房东也通知了我再住二十天后腾房子。第二个事情是我一起培训的两个朋友在工作了两个月后公司有机会就选择去了北京发展,他们房子的家居齐全、环境也还不错,他们建议我住过去,我犹豫了几天最后就搬了过去。 - - -*第五间房子-胡家庄-六个月* - -胡家庄这边的房子比较大,家具也齐全,还有一个好处就是这里住了很多从培训班出来的童鞋,平时下班了后大家可以打打台球、聊聊天,特别方便。那段时间也挺爽的,我天天串来串去的,有一堆朋友一起上班,一起下班,一起吃饭,周末白天去软件园一起打篮球、晚上打台球,刚好我那段时间对打台球也特别迷恋,我们几个好朋友几乎一周去好几次。最后我搬离这里的原因一方面是我那个去北京发展的朋友老婆怀孕了他们要回西安,另一方面这里的小巷子天天有人在盖房,过来过去非常危险,而且房东也开始在上面加盖房子了,我总担心这房子会不会地基没打好,在盖的过程中塌了。所以在我这个朋友回来之前,我把房子给人家腾出来了。 - -胡家庄的房租是每月300块,可我那时工资也才2千,虽然住的不错但其实也是心疼了很久的。:) - -胡家庄大概就是这个样子 - -![](http://favorites.ren/assets/images/2017/life/hujiazhuang.jpg) - -*第六间房子-鱼化寨-六个月* - -从胡家庄搬走后我就来到了传说中的鱼化寨,人们眼中的小香港,特大超大的城中村,据说里面住了二十多万的人口。每天上下班时,这里都会形成独特的风景,那个景观简直堪称震撼,到处都是黑压压的一片涌动的人头,就像是火山爆发时的熔岩从村子里流了出来,各种小摩的、小公交车会把路堵得死死的,一般出租车司机是打死也不敢不会进去的。 - -我租住的房子从外观看是一个又细又长的布局,我住在最里面,光线一般,房子的隔音效果奇差,隔壁半夜稍微有个响声我这边就听的到,偶尔半夜会有楼上女生的各种声音。有一次楼上洗衣服,洗完衣服后直接把水倒在了地上,好家伙立刻我的屋顶一片雨,那时候还买了一个小型的电视机,就害怕把电视给我淋坏了。 - -这里的房子我当时租的是一个二房东,要价290块一月,承诺半年内不会给我涨价,他果然讲信用,半年内没有给我涨房租,但半年后立刻涨价,从290涨到340,TMD这简直是敲诈,我岂能从,立刻启动寻找房子,搬家。 - -鱼化寨就是这么繁华 - -![](http://favorites.ren/assets/images/2017/life/yuhuazhan.jpg) - -[鱼化寨历史介绍](http://www.xadqw.cn/pub/xadfz/zjll/dqcs/xacljysj/201609/t20160919_150155.htm) - -*第七间房子-鱼化寨-十六个月* - -找了几天终于找到我在西安住的最后一个房间,这房子在顶层,光线非常好,房间也比较大,有独立的卫生间和厨房,一个月365,很神奇的价格吧,那时候房东要价380我说350,最后老板娘定板365。这个房子伸出头来就可以看到巷子里面的一切,我在这里住了一年多,也发生了很多有趣的事情。楼下有个棋牌室,天天有人打牌,有一次半夜睡觉的时候被吵醒,把一个小伙子直接打趴下来不动了;还记得有一次一个女孩子在外面上网回来的太晚了,被一个混混尾随,又是威胁又是利诱,最后那个女孩实在受不了了,在巷子里面大喊大叫,惊醒了房东,房东才帮忙吓走了小混混,听说还报了案,最后不知道是什么结果。 - -我租住的这家房东的房子被小偷光顾过,所以他特别的谨慎,从进去大门到我的房间要开四道门,先是大门,然后是一楼的楼梯门,每个楼层还有门锁,到最后再开自己的门。即便这样还是被小偷攻陷过一次,偷了好几个笔记本电脑,万幸那次没有撬开我的门。。 - -这就是365的那个房子。 - -![](http://favorites.ren/assets/images/2017/life/365room.jpg) - -那时候,我已经换了第二份工作,好兄弟波仔也搬了过来,就住在我隔壁的隔壁,早上一起上班,中午一起吃饭,晚上下班之后我们一起看电视、谈人生,到夏天的时候搞几个西瓜、几个凉菜、一箱9度,一边吃喝一边聊天。那时候的我和波仔就经常开玩笑说我们是24小时距离都不会超过100米的基友,每天如此。记得每次发工资了,我俩就商量,今天发工资了呀,得吃点好的啊,来个肉夹馍吧,平常我们都吃菜夹馍的。 - -因为我和波仔中间空着一个房子,结果这件空房被一对“神人”租了。有一天波仔像往常一样,下班后在我这边聊天,突然从我隔壁房间传来了一段惊天地泣鬼神的叫床声,这声音吧在城中村租房话应该都会有的,但大都比较含蓄,可是这次这个声音却完全不一样,可以说它响彻整个楼层。当时我俩听到这个声音都愣了,波仔甚至都认为这是不是有人在嫖呢,连走出我房间去他房间的勇气都没有了,过了几天我们终于见到了这声音的制造者,表面上看起来他们也是一对普通的情侣。其实这个事情让我很尴尬,具体说是我和波仔都挺尴尬的,那时我俩都还是单身。偶尔我弟弟会来我这里玩,晚上住这,我就特别害怕那个声音响起,结果还是响了,幸好我弟弟没有问我这是什么声音,我假装睡着了,怎么睡得着?!这对“神人”还养了一条狗经常往我房间跑,幸好他们过了不久就搬走了。 - -鱼化寨的娱乐设施对我们来讲,堪称完美,各种KTV、台球厅、小饭店、小吃街都有,隔三差五的我们就组织朋友、同事聚会,通宵KTV,这真是一段快乐又丰富的经历。 - - - -## 三、帝都 - - -其实每一次的搬家都像是一场战争,战争完了之后房间也是各种凌乱,一般需要两到三周才能彻底整利索了。我在北京,到目前为止换了三个住处两个地点,前两个都在分钟寺,现在在纪家庙,分钟寺那边也是一个超级大的城中村,当然现在也被拆迁了,我已经搬出来三年了也没有一点建设的感觉,北京这边的房子一般分为两种,民房和小区,分钟寺那边是大量的民房也有公寓,但是北京的民房比西安的城中村设施要好很多,基本都能洗澡也有暖气,在西安的城中村,动天那个冷夏天那个热,到现在我都记忆犹新,当然了北京这边租房的价格也比西安翻了几倍。 - -分钟寺拆迁前的片景 - - -![](http://favorites.ren/assets/images/2017/life/fenzhongsi.jpg) - - -*分钟寺* - -网上查了一下分钟寺的人口,是这样介绍的,时间是2012年:丰台区分钟寺城中村,人口众多,地区居住人口高达50万左右,本地常住人口4万人左右,外地居住人口50万左右,居住环境极度的脏乱差。 刚来北京时,Jerry和他的老婆比我早一个月到北京,因为他们住在分钟寺,所以我也就直接投奔他们了。记得那是13年的清明节,刚下完雨,站在分钟寺A口的出站口,我甚至一度怀疑这不是北京,比鱼化寨还要烂,朋友领着我走了进去,特别大,面积是鱼化寨的好多倍,各种小摩的擦着你身子疾驰而过。 - -和朋友们吃过饭后,我就去找房子了,有一家公寓,一楼有房子,950一个月,一走出公寓有一个大的市场卖各种东西,我花了几百元钱买了些日用品就算是住下了。附近有一个卖早餐的店,里面有一种包子我之前从来没有吃过,叫土豆小包子,特别好吃,我特别喜欢。因为我这个房子在一楼,光线不太好,而Jerry住的地方就比较好,一直帮我留意着看房东有没空房出租。终于在我住了三个月的时候,Jerry房东有空房出租而且就在他们隔壁,我没有犹豫立刻就搬了过去,开始了我混吃混喝的幸福生活。我天天在他们小俩口那边蹭吃,王真的手艺真不是盖的,陕西的饭菜做的有模有样,有一段时间我们还商量着要不要在分钟寺里开个餐馆呢。后来他俩有事离开了分钟寺,我又在那里住了半年多,终于迎来了拆迁,贴大标语、喊喇叭、做动员等这些事情我早已在西安就见惯了。 - - -分钟寺 住的房子 - - -![](http://favorites.ren/assets/images/2017/life/fenzhongsiroom.jpg) - - -*纪家庙* - -准备离开分钟寺重新找房子的那个阶段是最麻烦的,整整找了一个月,看了很多很多房子,成寿寺里面有一间单位的套房作为备选,其它的地方实在是不太合适,到了最后我说在试几次吧,终于在纪家庙找到了现在的房子,一住就是三年。 - -这片的小区都是城中村的回迁房,很多房东都有好几套,我们刚来的时候这个小区刚刚交房,很多房东都往外出租,中介还没有占领这里,周边环境也很不成熟,附近都没有几家店,吃饭买菜都是问题;但是这房子的户型很好,标准的一室一厨一卫,没客厅,一月2300,而且房东人很好,这也是我们最终选择这个房子的原因之一。后来果然没看错,房东大姐把新房的所有钥匙都给了我们,并且到现在三年了没涨过价,地地道道的北京善良、大气大姐。因为是新房会有很大的味道,刚搬来的时候,女朋友害怕被甲醛毒死,买了很多花花草草,放满了屋子,这么一个小屋对于我来讲还是挺满足的,所以一直住到了现在。 - - - -![](http://favorites.ren/assets/images/2017/life/jijiamiao.jpg) - - -## 四、最后 - - -在几年前看过的这篇文章[那些年,我在北京租过的房子们](https://www.douban.com/note/366257216/),文章里有这么一段话,我特别喜欢: - -> 我一直相信,有一天每一个人都会有自己的房子,自己的家庭。这大千世界的一隅,总有一天会有一盏等着我们回家的灯。而年轻的时候所有的颠沛流离都将成为日后心中的慰藉,是青春的圣火,是跃动的生命。它们闪着光,透着亮,提醒着我们曾经那么年轻,曾经那么敢闯,曾经天不怕地不怕,曾经什么都可以接受和忍耐。 - -> 所有的年轻,有一天都会长大与成熟,当回忆往事的时候,望着远方,怦然一笑,就是对青春时光里所有的所有,最好的诠释与珍藏。 - -和这篇文章不同的是,作者已经买了房子,而我现在还在租房。当时看完这篇文章后我就也想写写自己的租房经历,没想到这一等就是两年,到在今天才有机会敲下这些文字。 - -租房有租房的乐趣,可以DIY自己的生活,DIY自己的室友。邻居,自由选择居住的地点,我可以随意的毫无顾忌的更换一个城市,可以说走就走。其实发达国家的租房比例是非常高的,只是中国的文化,特别是丈母娘文化根深蒂固,不可能在短期有所改变,但就我个人来讲,租房也是一件很不错的事情,自己也比较幸运,在租房的十几年中几乎没有遇到过特别让我忍受不了的事情。 - -其实,每一个人的租房生活,就是每一个人的生活轨迹,也是每一个人奋斗的经历。从地下室到民房,从民房到小区合租,从小区合租和独自租房,从小房子换到大房子,到最后买房子结束租房,不知道有没有人完整的走过这个链条,很多人飘来飘去终究是还在租房,比如说我,现在还在独自租房的阶段,可能这个阶段还会持续很多年,每次租住一个地方的时候不管是住多久,我都会把它当做家一样,仔仔细细的打扫干净。其实家在哪里呢,家就在我们心中,心在哪里哪里就是我们的家。 - - -> **特别感谢鸽子,帮我修饰文章** - -------------- - -**作者:纯洁的微笑** -**出处:[http://www.ityouknow.com/](http://www.ityouknow.com/life/2017/04/21/house-rented.html)** -**版权归作者所有,转载请注明出处** diff --git a/_posts/2017/2017-04-26-open-wechat.md b/_posts/2017/2017-04-26-open-wechat.md deleted file mode 100644 index 29151840c5..0000000000 --- a/_posts/2017/2017-04-26-open-wechat.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -layout: post -title: 我开通了微信公众号 -category: life -tags: [life] ---- - -今天是2017年4月26日,这是我要推送的第一篇文章。 - -## 写作渊源 - -我从小就一直喜欢读书,从作文到故事会,从故事会到男生女生,从男生女生到读者,从读者到武侠(金庸、古龙)再到文学,《平凡的世界》是截至目前我最喜欢的一部文学。到上大学后我读书就非常杂了,经济、传记、小说、哲学、网络小说、玄幻...这些我读过的书,打开了我的世界。 - -那时候流行QQ空间,最开始的时候我是在QQ空间发发牢骚、写写感悟,虽然都很初级,但也和朋友们热闹了一番。后来有了轻博客点点,用了几次后,感觉特别的适合写字分享,文章就都发布到了点点里面,其实也没有什么读者,就断断续续的写了几篇。突然有一天在我准备登录点点时,输入官网回车后并没有出现那个熟悉的页面而是直接跳转到其他页面了,一点通知都没有,点点就倒闭了,以前写的文章就这样没了。 - -我是10年参加的工作,遇到问题的时候就会到网上查找答案,经常搜出来就是csdn、iteye(那时候还是itjava)和博客园,就都注册了。刚开始我经常在csdn论坛里灌水,iteye里面看Java相关的文章,在博客园里面看IT行业的新闻。后来iteye被csdn给收购了,明显感觉到iteye掉队了,csdn的广告越来越多了,打开的速度越来越慢,慢慢的我用的也就少了,只有博客园坚持每天中午来看看新闻。从一直看新闻到慢慢的开始看博客园首页的一些技术文章,慢慢觉着自己也应该写写文章,但一直都没动笔。 - -清晰的记得那是2016年的6月份,我终于下决心好好写写技术博客了,以前零零碎碎的写过一些,但都没有太在意,提交到博客园首页的时候会直接被打回来,这让我很没面子,我就暗下决心一定要写一篇文章上博客园首页。恰好当时我在研究jvm,查阅了很多资料写了一篇《java类的加载机制》的文章,写完后我迫不及待的点击了发布按钮,不一会就收到了我在博客园的第一条留言,当天阅读量大概800左右。这个结果我已经非常的满足,同时我也基本了解了博客园首页文章的审核标准。 - -我接下来又继续写了一系列的jvm文章,就遇到了spring boot。spring boot是一套全新的快速开发框架,进行了预研之后我决定用作公司平台的第四代架构的技术支撑,于是一边研究一边开始写spring boot系列的文章。到目前为止spring boot系列文章也是我博客里面阅读量最高的。又因为自己所在的行业是互联网金融,我就想着写一篇和程序员有关的但又非技术类的文章,于是我就写了《程序员该用哪种姿势来理财》,发布之后这篇文章得到博客园博友的热烈讨论,这让我很惊喜,于是又打开我写作的另一扇窗,后来这种类型的文章成了我博客的一大部分。 - -后来写着写着,我的某篇文章就上了博客园首页编辑推荐,在我写互联网金融行业实战系列文章的时候连续上榜,再后来就被博客园推荐为"推荐博客",积分排名从我有记录的时候看到10万多名到现在的2千多名。所以在此我深深的感谢博客园,因为我的写作激情完全是由博客园的博友们给带来的,博客不断的推荐和博友不断回复成了对我源源不断的鼓励,博客园的文章推荐机制和评论氛围特别的好,最多推荐和最多评论就是博友自发推荐到首页。ps:这并不是软文~~ - -有一天我发现可以通过github+jekyll可免费在github上面构建自己的博客,再结合markdown来写作,这对我而言简直就是一种以写代码的方式来写文章的感觉,刚好也遇到了[DONGChuan-Yummy](https://github.com/DONGChuan/Yummy-Jekyll)的一个模版我特别喜欢,研究后做了一些改造就有了我现在的这个博客[www.ityouknow.com](http://www.ityouknow.com/) - - -## 微信公众号 - -在写博客一段时间后,老婆就鼓励我开通自己的公众号,但我一直很犹豫,其实在开这个微信公众号之前我已经搞了好几个微信公众号了,均以失败告终。微信官方一个身份证最多可以注册五个,我已经满了。 - -最早在13年我就注册了公众号传你妹(chuannimei),当初计划做一个传话筒类聊天机器人,编码由Jerry搞定,运营了一段时间没有太多关注就放弃了。后来我又注册了一个flyever(weflyever)计划作为我们户外组织的一片天地,也没用几次。最近的一个公号注册时间是在16年,叫一天一部电影(onedayonemovie),我计划这个公号用来做电影推荐的,运营了一段时间也放弃了。我还有两个注册错了的公众号,其中一个注册后从来没有用过,幸好现在公众号有了注销功能,不用的我都已经注销了。 - -以前没有开通公众号,一方面是害怕自己又像以前一样坚持不下去,另一方面觉得自己的文章还没达到能撑起一个公众号。随着自己写的文章越来越多后,不断有网友问起我的公众号是多少;另外,有时候自己很多的文章会在没任何标注连接的情况下就被其它技术类的公众号随便转载走,我当然是欢迎转载的,而且我的要求也很简单,注明原文链接即可,但这么简单的要求很多公号都做不到,他们有直接copy上去的,甚至有更过分的,直接打上他们的原创声明。 - - -最近在Jerry的鼓励下,我开通了此公号。我开通这个公号有两目的,一是防止别人在微信公众号侵权了而我却不知情,二是公号方便我和读者们交流。 - -特别感谢Jerry、鸽子、波仔、虹羽——我的第一批读者,是他们鼓励我一直写作。 - -以后在这里我将开始分享我的技术和生活。 - - -我的公众号是:纯洁的微笑(KeepPureSmile),欢迎大家关注。 - - -![](http://favorites.ren/assets/images/keeppuresmile.jpg) - -------------- - -**作者:纯洁的微笑** -**出处:[http://www.ityouknow.com/](http://www.ityouknow.com/life/2017/04/26/open-wechat.html)** -**版权归作者所有,转载请注明出处** - - diff --git a/_posts/2017/2017-05-01-simple-springcloud.md b/_posts/2017/2017-05-01-simple-springcloud.md deleted file mode 100644 index 3394a4c310..0000000000 --- a/_posts/2017/2017-05-01-simple-springcloud.md +++ /dev/null @@ -1,160 +0,0 @@ ---- -layout: post -title: springcloud(一):大话Spring Cloud -category: springcloud -tags: [springcloud] ---- - - -研究了一段时间Spring Boot了准备向Spring Cloud进发,公司架构和项目也全面拥抱了Spring Cloud。在使用了一段时间后发现Spring Cloud从技术架构上降低了对大型系统构建的要求,使我们以非常低的成本(技术或者硬件)搭建一套高效、分布式、容错的平台,但Spring Cloud也不是没有缺点,小型独立的项目不适合使用。 - - -## Spring Cloud是什么鬼? - -Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。 - -微服务是可以独立部署、水平扩展、独立访问(或者有独立的数据库)的服务单元,springcloud就是这些微服务的大管家,采用了微服务这种架构之后,项目的数量会非常多,springcloud做为大管家需要管理好这些微服务,自然需要很多小弟来帮忙。 - -主要的小弟有:Spring Cloud Config、Spring Cloud Netflix(Eureka、Hystrix、Zuul、Archaius...)、Spring Cloud Bus、Spring Cloud for Cloud Foundry、Spring Cloud Cluster、Spring Cloud Consul、Spring Cloud Security、Spring Cloud Sleuth、Spring Cloud Data Flow、Spring Cloud Stream、Spring Cloud Task、Spring Cloud Zookeeper、Spring Cloud Connectors、Spring Cloud Starters、Spring Cloud CLI,每个小弟身怀独门绝技武功高强下面来做一一介绍。 - - -## 核心成员 - -### Spring Cloud Netflix - -这可是个大boss,地位仅次于老大,老大各项服务依赖与它,与各种Netflix OSS组件集成,组成微服务的核心,它的小弟主要有Eureka, Hystrix, Zuul, Archaius... 太多了 - -**Netflix Eureka** - -服务中心,云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。这个可是springcloud最牛鼻的小弟,服务中心,任何小弟需要其它小弟支持什么都需要从这里来拿,同样的你有什么独门武功的都赶紧过报道,方便以后其它小弟来调用;它的好处是你不需要直接找各种什么小弟支持,只需要到服务中心来领取,也不需要知道提供支持的其它小弟在哪里,还是几个小弟来支持的,反正拿来用就行,服务中心来保证稳定性和质量。 - -**Netflix Hystrix** - -熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。比如突然某个小弟生病了,但是你还需要它的支持,然后调用之后它半天没有响应,你却不知道,一直在等等这个响应;有可能别的小弟也正在调用你的武功绝技,那么当请求多之后,就会发生严重的阻塞影响老大的整体计划。这个时候Hystrix就派上用场了,当Hystrix发现某个小弟不在状态不稳定立马马上让它下线,让其它小弟来顶上来,或者给你说不用等了这个小弟今天肯定不行,该干嘛赶紧干嘛去别在这排队了。 - -**Netflix Zuul** - -Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。当其它门派来找大哥办事的时候一定要先经过zuul,看下有没有带刀子什么的给拦截回去,或者是需要找那个小弟的直接给带过去。 - -**Netflix Archaius** - -配置管理API,包含一系列配置管理API,提供动态类型化属性、线程安全配置操作、轮询框架、回调机制等功能。可以实现动态获取配置, -原理是每隔60s(默认,可配置)从配置源读取一次内容,这样修改了配置文件后不需要重启服务就可以使修改后的内容生效,前提使用archaius的API来读取。 - - -### Spring Cloud Config - -俗称的配置中心,配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git以及Subversion。就是以后大家武器、枪火什么的东西都集中放到一起,别随便自己带,方便以后统一管理、升级装备。 - - -### Spring Cloud Bus - -事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署。相当于水浒传中日行八百里的神行太保戴宗,确保各个小弟之间消息保持畅通。 - - -### Spring Cloud for Cloud Foundry - -Cloud Foundry是VMware推出的业界第一个开源PaaS云平台,它支持多种框架、语言、运行时环境、云平台及应用服务,使开发人员能够在几秒钟内进行应用程序的部署和扩展,无需担心任何基础架构的问题 - -其实就是与CloudFoundry进行集成的一套解决方案,抱了Cloud Foundry的大腿。 - - -### Spring Cloud Cluster - -Spring Cloud Cluster将取代Spring Integration。提供在分布式系统中的集群所需要的基础功能支持,如:选举、集群的状态一致性、全局锁、tokens等常见状态模式的抽象和实现。 - -如果把不同的帮派组织成统一的整体,Spring Cloud Cluster已经帮你提供了很多方便组织成统一的工具。 - -### Spring Cloud Consul - -Consul 是一个支持多数据中心分布式高可用的服务发现和配置共享的服务软件,由 HashiCorp 公司用 Go 语言开发, 基于 Mozilla Public License 2.0 的协议进行开源. Consul 支持健康检查,并允许 HTTP 和 DNS 协议调用 API 存储键值对. - -Spring Cloud Consul 封装了Consul操作,consul是一个服务发现与配置工具,与Docker容器可以无缝集成。 - - -## 其它小弟 - -**Spring Cloud Security** - -基于spring security的安全工具包,为你的应用程序添加安全控制。这个小弟很牛鼻专门负责整个帮派的安全问题,设置不同的门派访问特定的资源,不能把秘籍葵花宝典泄漏了。 - -**Spring Cloud Sleuth** - -日志收集工具包,封装了Dapper和log-based追踪以及Zipkin和HTrace操作,为SpringCloud应用实现了一种分布式追踪解决方案。 - -**Spring Cloud Data Flow** - -- Data flow 是一个用于开发和执行大范围数据处理其模式包括ETL,批量运算和持续运算的统一编程模型和托管服务。 - -- 对于在现代运行环境中可组合的微服务程序来说,Spring Cloud data flow是一个原生云可编配的服务。使用Spring Cloud data flow,开发者可以为像数据抽取,实时分析,和数据导入/导出这种常见用例创建和编配数据通道 (data pipelines)。 - -- Spring Cloud data flow 是基于原生云对 spring XD的重新设计,该项目目标是简化大数据应用的开发。Spring XD 的流处理和批处理模块的重构分别是基于 Spring Boot的stream 和 task/batch 的微服务程序。这些程序现在都是自动部署单元而且他们原生的支持像 Cloud Foundry、Apache YARN、Apache Mesos和Kubernetes 等现代运行环境。 - -- Spring Cloud data flow 为基于微服务的分布式流处理和批处理数据通道提供了一系列模型和最佳实践。 - -**Spring Cloud Stream** - -Spring Cloud Stream是创建消息驱动微服务应用的框架。Spring Cloud Stream是基于Spring Boot创建,用来建立单独的/工业级spring应用,使用spring integration提供与消息代理之间的连接。数据流操作开发包,封装了与Redis,Rabbit、Kafka等发送接收消息。 - -一个业务会牵扯到多个任务,任务之间是通过事件触发的,这就是Spring Cloud stream要干的事了 - -**Spring Cloud Task** - -Spring Cloud Task 主要解决短命微服务的任务管理,任务调度的工作,比如说某些定时任务晚上就跑一次,或者某项数据分析临时就跑几次。 - -**Spring Cloud Zookeeper** - -ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。 - -操作Zookeeper的工具包,用于使用zookeeper方式的服务发现和配置管理,抱了Zookeeper的大腿。 - -**Spring Cloud Connectors** - -Spring Cloud Connectors 简化了连接到服务的过程和从云平台获取操作的过程,有很强的扩展性,可以利用Spring Cloud Connectors来构建你自己的云平台。 - -便于云端应用程序在各种PaaS平台连接到后端,如:数据库和消息代理服务。 - -**Spring Cloud Starters** - -Spring Boot式的启动项目,为Spring Cloud提供开箱即用的依赖管理。 - -**Spring Cloud CLI** - -基于 Spring Boot CLI,可以让你以命令行方式快速建立云组件。 - - -## 和Spring Boot 是什么关系 - -Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务,Spring Cloud是一个基于Spring Boot实现的云应用开发工具;Spring Boot专注于快速、方便集成的单个个体,Spring Cloud是关注全局的服务治理框架;Spring Boot使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现,可以不基于Spring Boot吗?不可以。 - -Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。 - -> spring -> spring boot > Spring Cloud 这样的关系。 - - -## Spring Cloud的优势 - -微服务的框架那么多比如:dubbo、Kubernetes,为什么就要使用Spring Cloud的呢? - -- 产出于spring大家族,spring在企业级开发框架中无人能敌,来头很大,可以保证后续的更新、完善。比如dubbo现在就差不多死了 -- 有Spring Boot 这个独立干将可以省很多事,大大小小的活Spring Boot都搞的挺不错。 -- 作为一个微服务治理的大家伙,考虑的很全面,几乎服务治理的方方面面都考虑到了,方便开发开箱即用。 -- Spring Cloud 活跃度很高,教程很丰富,遇到问题很容易找到解决方案 -- 轻轻松松几行代码就完成了熔断、均衡负载、服务中心的各种平台功能 - -Spring Cloud对于中小型互联网公司来说是一种福音,因为这类公司往往没有实力或者没有足够的资金投入去开发自己的分布式系统基础设施,使用Spring Cloud一站式解决方案能在从容应对业务发展的同时大大减少开发成本。同时,随着近几年微服务架构和Docker容器概念的火爆,也会让Spring Cloud在未来越来越“云”化的软件开发风格中立有一席之地,尤其是在目前五花八门的分布式解决方案中提供了标准化的、全站式的技术方案,意义可能会堪比当前Servlet规范的诞生,有效推进服务端软件系统技术水平的进步。 - - -**[示例代码-github](https://github.com/ityouknow/spring-cloud-examples)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-cloud-examples)** - - -------------- - -**作者:纯洁的微笑** -**出处:[http://www.ityouknow.com/](http://www.ityouknow.com/springcloud/2017/05/01/simple-springcloud.html)** -**版权归作者所有,转载请注明出处** - - - diff --git a/_posts/2017/2017-05-05-1000-and-life.md b/_posts/2017/2017-05-05-1000-and-life.md deleted file mode 100644 index e6d7a5709c..0000000000 --- a/_posts/2017/2017-05-05-1000-and-life.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -layout: post -title: 给你1000万你可以把生活过的更好吗? -copyright: money -category: money -tags: [money] ---- - -我相信绝大数人看到这个问题的第一反应会说:是的,1000万呐,我肯定会把生活过的更好。 - - -## 1000万的价值 - -今天和朋友在外面喝酒的时候聊到了这个话题,给你1000万你可以把生活过的更好吗?最终讨论的结果暂且不表,我们先来想想,1000万究竟的价值是多少?按照年薪50万不吃不喝不花的情况下,你需要赚20年,所以说大多数人可能一辈子都挣不到这么多钱;1000万如果投入理财产品,按照年华10%的利率你一年可以分到100万,如果是p2p这种产品也没有什么手续费也不用交税,100万是纯利润。这样想一想1000万真的很多了,几乎可以说让你少奋斗几十年。 - -如果现实中真的给了你1000万,你会怎么来用它呢?如果你在一线城市飘着,房子对国人太重要了,那么你肯定想的是先买他一套房子,1000万在一线城市刚好可以买个地段一般的100多平的房子,除过装修什么的还可以富裕几十万买个不错的车,然后呢,1000万就花完了,那么你生活最本质的改变就是有了一套不错的房子和一辆还可以的车,除了这个你的生活质量有质的提高吗?你的工作有所改变吗?生活的意义或者幸福感拔升了多少? - -可能你会说,我才不会傻逼的在一线城市买个房子,我会用来创业或者投资,呵呵。你难道会觉得这两样哪一样会简单吗?如果要创业,你的创业项目是什么?创业九死一生其实是高估了,很多九死了一个生不生的了还很难说;投资呢,你需要有火眼金睛的能力,股票一赚二平七亏,你会是那个一吗?投资p2p吧,你需要能找到靠谱的平台,万一踩了雷是0和1的游戏,说不定比股票风险还大;放到银行呢?就吃定期利息一年也一二十万呢,但是如果在算是物价上涨,货币贬值,夸张一点的说有可能20年后的1000万不如现在的100万购买力大。 - -还有这样想的,实在不行我先花光了它,只有花了的钱才是自己的,第一个问题是,你几年后花完了怎么办,你的生活该如何继续;第二个问题,如果你真辞职了专门去旅游,像我们这种一直坐班的人来讲,一个月估计就吐了,什么环游世界,那都是少数人干的,绝大数的人玩个半年就迷失了,你就会非常的想上班,我深有体会。**人终究还是要找事情来做的** - -小时候,我曾不止一次幻想,如果我中了500百万,我该如何,我甚至还计划了后续的安排,如果我中了500万,我要偷偷的去彩票中心兑了奖,然后谁也不告诉甚至家人,然后装作什么事情都没有发生继续去上班,等过了这个风口之后,我改如何如何去挥霍,想着想着就开心的不行。 - -还想起了糗百的一个笑话,现在看起来仍然笑的肚子疼: - -> 上高中的时候,偷偷把同学手机里的我的号码换成了爸爸,然后上课的时候给他发了条短信:孩子,赶紧回来吧,咱家中了1000万,别上学了!同学看了以后东西都没收拾,直接就往外跑,班主任问他干嘛去。他头也不回的地说:去你妈X,要你管那么多! -十分钟后他又回来了... - -有研究显示,70%的巨奖得主没有取得任何成就,通常在几年内就把奖金挥霍一空。英国研究人员发现,44%彩票大奖获得者会在短短5年内花完所有奖金。知乎上有个帖子:[世界上那些彩票中巨奖的人最后都怎样了?](https://www.zhihu.com/question/22163243)大家可以看看,几乎大部分的获奖者都没有特别好的结局,**超出你掌控能力的财富最终都不会是你的。** - -## 另一个角度 - -如果你工作了很多年了,在IT行业至少薪资也翻了很多倍。那么你的生活质量翻倍了吗?你的幸福感又翻倍了吗? - -我敢说几乎没有多少人后面两者会翻倍很多,就说我吧,薪资从2k到2w的时候,我的薪资翻了10倍,我的物质生活好了吗?说时候我的物质生活确实也好了很多,住的环境,一点一点的变好了,吃的我个人基本无要求,但伙食也算好了很多,物质生活感觉提高了一些,咱自己感受并不是特别的明显。精神生活呢或者说幸福感呢,几乎没有变好,甚至没有以前那么好,还倒退了! - -在我30岁之前吧,很长的一段时间我都认为是钱的问题,当我有钱了 怎么怎么样,当我有钱了 生活会怎么怎么样,但其实呢,但我的工资从最初到现在翻了N倍,我的生活质量呢?翻了N倍吗?我的幸福感,翻了多了?以前喜欢 打球、游泳、旅游甚至打麻、扎金花都感觉带劲的很,现在呢兴趣索然。 - -有时候我在想,哪怕现在的公司给我涨薪50%,可能刚开始我会兴奋一下,过了几个月后也就那样了,对我的生活来讲并没有什么本质的改变。 -钱多少才算是够,比如我第一份工作是2k的工资,那时候想6k工资该过的多好呀,当我到了6k工资的时候我在想月薪过万的人太牛逼了,到了现在早已经超出了自己当初的预想,但是我现在觉得牛逼了吗?没有!我会想脉脉匿名区的月薪都是30K起,刚毕业的程序员都是十几K,年薪一百万都被鄙视,对比后,我又会要求更多。 - -物质上你永远都不会满足,它就是一个无底洞。随着物质的不断提高对你生活质量带来的影响会越来越小。像马云、马化腾或者雷军,金钱对他们的意义已经不是在个人生活这个问题上,也许他们在想如何让生命更有意义。 - -目前我的薪资在一线不算高,但真的已经满足了我的日常生活,每个月撑死了能花个5K(除过房租,吃饭公司管了),已经很能满足我的日常需求了,偶尔出去旅游,奢侈一把也没有问题,就是没无房无车而已,这些对我真的很重要吗,对我个人来讲,也挺重要的,但不是重要到必须品这样。 - -所以这段时间我一直在想,我工作赚钱的意义在哪里,我生活的意义在哪里?我做的这些事情是我喜欢的吗? - -## 梦想还在吗? - -随着年纪的增大,我们越来越现实、世俗;随着年纪的增大,真心交往的朋友越来越少;随着年纪的增大,我们的父母在渐渐变老、离去;随着年纪的增大,我们的理想却在慢慢变小。要不我们就要像普通人一样,结婚、生子把生活填满,反正大家都是这样过的,反而你还矫情了。 - -人终究还是要找事情来做的,赚钱是很重要的一方面,但是更重要的是,是你喜欢的,你觉得做的挺有劲的,挺有意义的。你感觉到了自己的价值发挥,你的精神很愉悦,有时虽然累,但也痛快着。这其实就是我想要的,1000万很重要,但是在我讲,在追求这1000万的过程中,让我的生命感觉有点不一样,有价值、有意义更重要。 - - -这里有一首小诗:[梦想](http://liuyue.ren/2016/12/12/%E6%A2%A6%E6%83%B3/) - -> 梦想 -> -> 6岁的那年 谈起梦想 是我新认识的两个汉字 -> 12岁的那时候 梦想 对我来讲是一个很遥远的东西 遥远到只知道它要很精彩 -> 18岁的那年 梦想对我来讲好像很近却又很远 -> 24岁的时候 你若说梦想 身边的朋友会大笑 你傻逼啊。 -> -> 快30了 你再去想梦想 可能你有时候你都会对自己讲 你可能真傻逼了。 -> -> 好像 -> 梦想就是成功人士的专利 -> 梦想就是传说中的东西 -> 梦想就是判断现在的你是否就是傻逼的一个词语 -> -> 梦想是什么? -> 一个词语还是一个梦? - - - -所以到了最后,我也想问问大家? -给你1000万你有信心可以把生活过的更好吗? - - -## 后记 - -这篇文章在博客园引起这么大的争议还是我所料未及,感谢所有人的评论包括反对挖苦的,看来大家都有信心面对突如其来巨额财富,买房、买股票、投资和旅游占了大多数,但是想象中那么美好的事情不知道大家都先行实践过吗,比如爱旅游,先小小的旅游2个月试试;想投资,买个股票两年看能赚不? - -写这篇文章的本意,是讨论一下突然来的金钱能提升你的生活品质吗?对原有生活的冲击,如何用好这笔钱都是风险巨高的事情,**福兮祸所依 祸兮福所伏**。另外随着年纪的增大我们的关注点慢慢都放在了“金钱”的追求上面,梦想都可以实际量化到多少多少万了,这样好吗?其实除了“金钱”还有很多其它的事情更有价值。 - - -这两天刚好看了池老师的文章:[如果你有40亿美元的话](http://mp.weixin.qq.com/s/1hHSWuQgS3fwXuIRG3uXog) - - -------------- - -**作者:纯洁的微笑** -**出处:[http://www.ityouknow.com/](http://www.ityouknow.com/life/2017/05/05/1000-and-life.html)** -**版权归作者所有,转载请注明出处** - - - - - - - - - - - - - - - diff --git a/_posts/2017/2017-05-06-spring-boot-mail.md b/_posts/2017/2017-05-06-spring-boot-mail.md deleted file mode 100644 index d1770d0b6f..0000000000 --- a/_posts/2017/2017-05-06-spring-boot-mail.md +++ /dev/null @@ -1,303 +0,0 @@ ---- -layout: post -title: Spring Boot (十):邮件服务 -category: springboot -tags: [springboot] -copyright: java ---- - -Spring Boot 仍然在狂速发展,才几个多月没有关注,现在看[官网](http://projects.spring.io/spring-boot/)已经到 2.1.0.RELEASE 版本了。准备慢慢在写写 Spring Boot 相关的文章,本篇文章使用 Spring Boot 最新版本 2.1.0 进行开发。 - -发送邮件应该是网站的必备功能之一,什么注册验证,忘记密码或者是给用户发送营销信息。最早期的时候我们会使用 JavaMail 相关 api 来写发送邮件的相关代码,后来 Spring 推出了 JavaMailSender 更加简化了邮件发送的过程,在之后 Spring Boot 对此进行了封装就有了现在的 `spring-boot-starter-mail` ,本章文章的介绍主要来自于此包。 - - -## 简单使用 - -### 1、pom 包配置 - -pom 包里面添加 `spring-boot-starter-mail` 包引用 - -``` xml - - - org.springframework.boot - spring-boot-starter-mail - - -``` - -### 2、在 application.properties 中添加邮箱配置 - -``` properties -spring.mail.host=smtp.qiye.163.com //邮箱服务器地址 -spring.mail.username=xxx@oo.com //用户名 -spring.mail.password=xxyyooo //密码 -spring.mail.default-encoding=UTF-8 - -mail.fromMail.addr=xxx@oo.com //以谁来发送邮件 -``` - -### 3、编写 mailService,这里只提出实现类。 - -``` java -@Component -public class MailServiceImpl implements MailService{ - - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - - @Autowired - private JavaMailSender mailSender; - - @Value("${mail.fromMail.addr}") - private String from; - - @Override - public void sendSimpleMail(String to, String subject, String content) { - SimpleMailMessage message = new SimpleMailMessage(); - message.setFrom(from); - message.setTo(to); - message.setSubject(subject); - message.setText(content); - - try { - mailSender.send(message); - logger.info("简单邮件已经发送。"); - } catch (Exception e) { - logger.error("发送简单邮件时发生异常!", e); - } - - } -} -``` - - -### 4、编写 test 类进行测试 - -``` java -@RunWith(SpringRunner.class) -@SpringBootTest -public class MailServiceTest { - - @Autowired - private MailService MailService; - - @Test - public void testSimpleMail() throws Exception { - MailService.sendSimpleMail("ityouknow@126.com","test simple mail"," hello this is simple mail"); - } -} -``` - -至此一个简单的文本发送就完成了。 - - -## 加点料 - -但是在正常使用的过程中,我们通常在邮件中加入图片或者附件来丰富邮件的内容,下面讲介绍如何使用 Spring Boot 来发送丰富的邮件。 - - -### 发送 html 格式邮件 - -其它都不变在 MailService 添加 sendHtmlMail 方法. - -``` java -public void sendHtmlMail(String to, String subject, String content) { - MimeMessage message = mailSender.createMimeMessage(); - - try { - //true表示需要创建一个multipart message - MimeMessageHelper helper = new MimeMessageHelper(message, true); - helper.setFrom(from); - helper.setTo(to); - helper.setSubject(subject); - helper.setText(content, true); - - mailSender.send(message); - logger.info("html邮件发送成功"); - } catch (MessagingException e) { - logger.error("发送html邮件时发生异常!", e); - } -} -``` - -在测试类中构建 html 内容,测试发送 - -``` java -@Test -public void testHtmlMail() throws Exception { - String content="\n" + - "\n" + - "

    hello world ! 这是一封Html邮件!

    \n" + - "\n" + - ""; - MailService.sendHtmlMail("ityouknow@126.com","test simple mail",content); -} -``` - - -### 发送带附件的邮件 - -在 MailService 添加 sendAttachmentsMail 方法. - -``` java -public void sendAttachmentsMail(String to, String subject, String content, String filePath){ - MimeMessage message = mailSender.createMimeMessage(); - - try { - MimeMessageHelper helper = new MimeMessageHelper(message, true); - helper.setFrom(from); - helper.setTo(to); - helper.setSubject(subject); - helper.setText(content, true); - - FileSystemResource file = new FileSystemResource(new File(filePath)); - String fileName = filePath.substring(filePath.lastIndexOf(File.separator)); - helper.addAttachment(fileName, file); - - mailSender.send(message); - logger.info("带附件的邮件已经发送。"); - } catch (MessagingException e) { - logger.error("发送带附件的邮件时发生异常!", e); - } -} -``` -> 添加多个附件可以使用多条 ```helper.addAttachment(fileName, file)``` - -在测试类中添加测试方法 - -``` java -@Test -public void sendAttachmentsMail() { - String filePath="e:\\tmp\\application.log"; - mailService.sendAttachmentsMail("ityouknow@126.com", "主题:带附件的邮件", "有附件,请查收!", filePath); -} -``` - - -### 发送带静态资源的邮件 - -邮件中的静态资源一般就是指图片,在 MailService 添加 sendAttachmentsMail 方法. - -``` java -public void sendInlineResourceMail(String to, String subject, String content, String rscPath, String rscId){ - MimeMessage message = mailSender.createMimeMessage(); - - try { - MimeMessageHelper helper = new MimeMessageHelper(message, true); - helper.setFrom(from); - helper.setTo(to); - helper.setSubject(subject); - helper.setText(content, true); - - FileSystemResource res = new FileSystemResource(new File(rscPath)); - helper.addInline(rscId, res); - - mailSender.send(message); - logger.info("嵌入静态资源的邮件已经发送。"); - } catch (MessagingException e) { - logger.error("发送嵌入静态资源的邮件时发生异常!", e); - } -} -``` - - -在测试类中添加测试方法 - -``` java -@Test -public void sendInlineResourceMail() { - String rscId = "neo006"; - String content="这是有图片的邮件:"; - String imgPath = "C:\\Users\\summer\\Pictures\\favicon.png"; - - mailService.sendInlineResourceMail("ityouknow@126.com", "主题:这是有图片的邮件", content, imgPath, rscId); -} -``` - -> 添加多个图片可以使用多条 `` 和 `helper.addInline(rscId, res)` 来实现 - - -到此所有的邮件发送服务已经完成了。 - - -## 邮件系统 - -上面发送邮件的基础服务就这些了,但是如果我们要做成一个邮件系统的话还需要考虑以下几个问题: - -### 邮件模板 - -我们会经常收到这样的邮件: - -``` xml -尊敬的neo用户: - - 恭喜您注册成为xxx网的用户,,同时感谢您对xxx的关注与支持并欢迎您使用xx的产品与服务。 - ... - -``` - -其中只有 neo 这个用户名在变化,其它邮件内容均不变,如果每次发送邮件都需要手动拼接的话会不够优雅,并且每次模板的修改都需要改动代码的话也很不方便,因此对于这类邮件需求,都建议做成邮件模板来处理。模板的本质很简单,就是在模板中替换变化的参数,转换为 html 字符串即可,这里以`thymeleaf`为例来演示。 - -**1、pom 中导入 thymeleaf 的包** - -``` xml - - org.springframework.boot - spring-boot-starter-thymeleaf - -``` - -**2、在 resorces/templates 下创建 emailTemplate.html ** - -``` html - - - - - Title - - - 您好,这是验证邮件,请点击下面的链接完成验证,
    -
    激活账号 - - -``` - -**3、解析模板并发送** - -``` java -@Test -public void sendTemplateMail() { - //创建邮件正文 - Context context = new Context(); - context.setVariable("id", "006"); - String emailContent = templateEngine.process("emailTemplate", context); - - mailService.sendHtmlMail("ityouknow@126.com","主题:这是模板邮件",emailContent); -} -``` - -### 发送失败 - -因为各种原因,总会有邮件发送失败的情况,比如:邮件发送过于频繁、网络异常等。在出现这种情况的时候,我们一般会考虑重新重试发送邮件,会分为以下几个步骤来实现: - -- 1、接收到发送邮件请求,首先记录请求并且入库。 -- 2、调用邮件发送接口发送邮件,并且将发送结果记录入库。 -- 3、启动定时系统扫描时间段内,未发送成功并且重试次数小于3次的邮件,进行再次发送 - - -### 异步发送 - -很多时候邮件发送并不是我们主业务必须关注的结果,比如通知类、提醒类的业务可以允许延时或者失败。这个时候可以采用异步的方式来发送邮件,加快主交易执行速度,在实际项目中可以采用MQ发送邮件相关参数,监听到消息队列之后启动发送邮件。 - -可以参考前期文章:[Spring Boot(八):RabbitMQ 详解](http://www.ityouknow.com/springboot/2016/11/30/spring-boot-rabbitMQ.html) 来实现。 - -> 文章内容已经升级到 Spring Boot 2.x - - -**[示例代码-github](https://github.com/ityouknow/spring-boot-examples/tree/master/spring-boot-mail)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-boot-examples/tree/master/spring-boot-mail)** - -**参考:** -[spring boot 发送邮件](http://blog.csdn.net/clementad/article/details/51833416) \ No newline at end of file diff --git a/_posts/2017/2017-05-08-spring-boot-mongodb.md b/_posts/2017/2017-05-08-spring-boot-mongodb.md deleted file mode 100644 index d17e90b7d7..0000000000 --- a/_posts/2017/2017-05-08-spring-boot-mongodb.md +++ /dev/null @@ -1,386 +0,0 @@ ---- -layout: post -title: Spring Boot(十一):Spring Boot 中 MongoDB 的使用 -category: springboot -tags: [springboot] -keywords: mongodb -copyright: java -lock: need ---- - -MongoDB 是最早热门非关系数据库的之一,使用也比较普遍,一般会用做离线数据分析来使用,放到内网的居多。由于很多公司使用了云服务,服务器默认都开放了外网地址,导致前一阵子大批 MongoDB 因配置漏洞被攻击,数据被删,引起了人们的注意,感兴趣的可以看看这篇文章:[场屠戮MongoDB的盛宴反思:超33000个数据库遭遇入侵勒索](http://www.freebuf.com/articles/database/125127.html),同时也说明了很多公司生产中大量使用mongodb。 - - -## MongoDB 简介 - -MongoDB(来自于英文单词“Humongous”,中文含义为“庞大”)是可以应用于各种规模的企业、各个行业以及各类应用程序的开源数据库。基于分布式文件存储的数据库。由C++语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个高性能,开源,无模式的文档型数据库,是当前 NoSql 数据库中比较热门的一种。 - -MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似 json 的 bjson 格式,因此可以存储比较复杂的数据类型。MongoDB 最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。 - -传统的关系数据库一般由数据库(database)、表(table)、记录(record)三个层次概念组成,MongoDB 是由数据库(database)、集合(collection)、文档对象(document)三个层次组成。MongoDB 对于关系型数据库里的表,但是集合中没有列、行和关系概念,这体现了模式自由的特点。 - -MongoDB 中的一条记录就是一个文档,是一个数据结构,由字段和值对组成。MongoDB 文档与 JSON 对象类似。字段的值有可能包括其它文档、数组以及文档数组。MongoDB 支持 OS X、Linux 及 Windows 等操作系统,并提供了 Python,PHP,Ruby,Java及 C++ 语言的驱动程序,社区中也提供了对 Erlang 及 .NET 等平台的驱动程序。 - -MongoDB 的适合对大量或者无固定格式的数据进行存储,比如:日志、缓存等。对事物支持较弱,不适用复杂的多文档(多表)的级联查询。文中演示 Mongodb 版本为 3.5。 - - -## MongoDB 的增删改查 - -Spring Boot 对各种流行的数据源都进行了封装,当然也包括了 Mongodb,下面给大家介绍如何在 Spring Boot 中使用 Mongodb: - -### 1、pom 包配置 - -pom 包里面添加 `spring-boot-starter-data-mongodb` 包引用 - -``` xml - - - org.springframework.boot - spring-boot-starter-data-mongodb - - -``` - -### 2、在 application.properties 中添加配置 - -``` properties -spring.data.mongodb.uri=mongodb://name:pass@localhost:27017/test -``` - -多个 IP 集群可以采用以下配置: - -``` properties -spring.data.mongodb.uri=mongodb://user:pwd@ip1:port1,ip2:port2/database -``` - - -### 2、创建数据实体 - -``` java -public class User implements Serializable { - private static final long serialVersionUID = -3258839839160856613L; - private Long id; - private String userName; - private String passWord; - - //getter、setter省略 -} -``` - -### 3、创建实体的增删改查操作 - -Repository 层实现了 User 对象的增删改查 - -``` java -@Component -public class UserRepositoryImpl implements UserRepository { - - @Autowired - private MongoTemplate mongoTemplate; - - /** - * 创建对象 - * @param user - */ - @Override - public void saveUser(User user) { - mongoTemplate.save(user); - } - - /** - * 根据用户名查询对象 - * @param userName - * @return - */ - @Override - public User findUserByUserName(String userName) { - Query query=new Query(Criteria.where("userName").is(userName)); - User user = mongoTemplate.findOne(query , User.class); - return user; - } - - /** - * 更新对象 - * @param user - */ - @Override - public long updateUser(User user) { - Query query=new Query(Criteria.where("id").is(user.getId())); - Update update= new Update().set("userName", user.getUserName()).set("passWord", user.getPassWord()); - //更新查询返回结果集的第一条 - UpdateResult result =mongoTemplate.updateFirst(query,update,User.class); - //更新查询返回结果集的所有 - // mongoTemplate.updateMulti(query,update,UserEntity.class); - if(result!=null) - return result.getMatchedCount(); - else - return 0; - } - - /** - * 删除对象 - * @param id - */ - @Override - public void deleteUserById(Long id) { - Query query=new Query(Criteria.where("id").is(id)); - mongoTemplate.remove(query,User.class); - } -} -``` - - -### 4、开发对应的测试方法 - - -``` java -@RunWith(SpringRunner.class) -@SpringBootTest -public class UserDaoTest { - - @Autowired - private UserDao userDao; - - @Test - public void testSaveUser() throws Exception { - UserEntity user=new UserEntity(); - user.setId(2l); - user.setUserName("小明"); - user.setPassWord("fffooo123"); - userDao.saveUser(user); - } - - @Test - public void findUserByUserName(){ - UserEntity user= userDao.findUserByUserName("小明"); - System.out.println("user is "+user); - } - - @Test - public void updateUser(){ - UserEntity user=new UserEntity(); - user.setId(2l); - user.setUserName("天空"); - user.setPassWord("fffxxxx"); - userDao.updateUser(user); - } - - @Test - public void deleteUserById(){ - userDao.deleteUserById(1l); - } - -} -``` - -### 5、查看验证结果 - -可以使用工具 MongoVUE 工具来连接后直接图形化展示查看,也可以登录服务器用命令来查看 - -1.登录 mongos -> bin/mongo -host localhost -port 20000 - -2、切换到 test 库 -> use test - -3、查询 user 集合数据 -> db.user.find() - - -根据3查询的结果来观察测试用例的执行是否正确。 - - -到此 Spring Boot 对应 MongoDB 的增删改查功能已经全部实现。 - - -## 多数据源 MongoDB 的使用 - -接下来实现 MongoDB 多数据源的使用 - - -### 1、pom 包配置 - -``` xml - - - org.springframework.boot - spring-boot-starter-data-mongodb - - - org.springframework.boot - spring-boot-starter-test - - -``` - -### 2、配置两条数据源,如下: - -``` properties -mongodb.primary.uri=mongodb://192.168.0.75:20000 -mongodb.primary.database=primary -mongodb.secondary.uri=mongodb://192.168.0.75:20000 -mongodb.secondary.database=secondary -``` - -### 3、配置两个库的数据源 - -封装读取以 Mongodb 开头的两个配置文件 - -``` java -@Data -@ConfigurationProperties(prefix = "mongodb") -public class MultipleMongoProperties { - - private MongoProperties primary = new MongoProperties(); - private MongoProperties secondary = new MongoProperties(); -} -``` - -配置不同包路径下使用不同的数据源 - -第一个库的封装 - -``` java -@Configuration -@EnableMongoRepositories(basePackages = "com.neo.model.repository.primary", - mongoTemplateRef = PrimaryMongoConfig.MONGO_TEMPLATE) -public class PrimaryMongoConfig { - - protected static final String MONGO_TEMPLATE = "primaryMongoTemplate"; -} -``` - -第二个库的封装 - -``` java -@Configuration -@EnableMongoRepositories(basePackages = "com.neo.model.repository.secondary", - mongoTemplateRef = SecondaryMongoConfig.MONGO_TEMPLATE) -public class SecondaryMongoConfig { - - protected static final String MONGO_TEMPLATE = "secondaryMongoTemplate"; -} -``` - -读取对应的配置信息并且构造对应的 MongoTemplate - -``` java -@Configuration -public class MultipleMongoConfig { - - @Autowired - private MultipleMongoProperties mongoProperties; - - @Primary - @Bean(name = PrimaryMongoConfig.MONGO_TEMPLATE) - public MongoTemplate primaryMongoTemplate() throws Exception { - return new MongoTemplate(primaryFactory(this.mongoProperties.getPrimary())); - } - - @Bean - @Qualifier(SecondaryMongoConfig.MONGO_TEMPLATE) - public MongoTemplate secondaryMongoTemplate() throws Exception { - return new MongoTemplate(secondaryFactory(this.mongoProperties.getSecondary())); - } - - @Bean - @Primary - public MongoDbFactory primaryFactory(MongoProperties mongo) throws Exception { - return new SimpleMongoDbFactory(new MongoClient(mongo.getHost(), mongo.getPort()), - mongo.getDatabase()); - } - - @Bean - public MongoDbFactory secondaryFactory(MongoProperties mongo) throws Exception { - return new SimpleMongoDbFactory(new MongoClient(mongo.getHost(), mongo.getPort()), - mongo.getDatabase()); - } -} -``` - -两个库的配置信息已经完成。 - -### 4、创建两个库分别对应的对象和 Repository - -对应可以共用 - -``` java -public class User implements Serializable { - private static final long serialVersionUID = -3258839839160856613L; - private String id; - private String userName; - private String passWord; - - public User(String userName, String passWord) { - this.userName = userName; - this.passWord = passWord; - } -} -``` - -对应的 Repository - - -``` java -public interface PrimaryRepository extends MongoRepository { -} -``` - -继承了 MongoRepository 会默认实现很多基本的增删改查,省了很多自己写 Repository 层的代码 - -Secondary 和上面的代码类似就不贴出来了 - - -## 5、最后测试 - -``` java -@RunWith(SpringRunner.class) -@SpringBootTest -public class MuliDatabaseTest { - - @Autowired - private PrimaryRepository primaryRepository; - - @Autowired - private SecondaryRepository secondaryRepository; - - @Test - public void TestSave() { - - System.out.println("************************************************************"); - System.out.println("测试开始"); - System.out.println("************************************************************"); - - this.primaryRepository - .save(new PrimaryMongoObject(null, "第一个库的对象")); - - this.secondaryRepository - .save(new SecondaryMongoObject(null, "第二个库的对象")); - - List primaries = this.primaryRepository.findAll(); - for (PrimaryMongoObject primary : primaries) { - System.out.println(primary.toString()); - } - - List secondaries = this.secondaryRepository.findAll(); - - for (SecondaryMongoObject secondary : secondaries) { - System.out.println(secondary.toString()); - } - - System.out.println("************************************************************"); - System.out.println("测试完成"); - System.out.println("************************************************************"); - } - -} -``` - -到此,MongoDB 多数据源的使用已经完成。 - -> 文章内容已经升级到 Spring Boot 2.x - -**[示例代码-github](https://github.com/ityouknow/spring-boot-examples/tree/master/spring-boot-mongodb)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-boot-examples/tree/master/spring-boot-mongodb)** - diff --git a/_posts/2017/2017-05-09-spring-boot-deploy.md b/_posts/2017/2017-05-09-spring-boot-deploy.md deleted file mode 100644 index 006466ab5c..0000000000 --- a/_posts/2017/2017-05-09-spring-boot-deploy.md +++ /dev/null @@ -1,359 +0,0 @@ ---- -layout: post -title: Spring Boot(十二):Spring Boot 如何测试打包部署 -category: springboot -tags: [springboot] -copyright: java ---- - -有很多网友会时不时的问我, Spring Boot 项目如何测试,如何部署,在生产中有什么好的部署方案吗?这篇文章就来介绍一下 Spring Boot 如何开发、调试、打包到最后的投产上线。 - - -## 开发阶段 - -### 单元测试 - -在开发阶段的时候最重要的是单元测试了, Spring Boot 对单元测试的支持已经很完善了。 - -1、在 pom 包中添加 `spring-boot-starter-test` 包引用 - -``` xml - - org.springframework.boot - spring-boot-starter-test - test - -``` - -2、开发测试类 - -以最简单的 helloworld 为例,在测试类的类头部需要添加:`@RunWith(SpringRunner.class)`和`@SpringBootTest`注解,在测试方法的顶端添加`@Test`即可,最后在方法上点击右键run就可以运行。 - -``` java -@RunWith(SpringRunner.class) -@SpringBootTest -public class ApplicationTests { - - @Test - public void hello() { - System.out.println("hello world"); - } - -} -``` - -实际使用中,可以按照项目的正常使用去注入数据层代码或者是 Service 层代码进行测试验证,`spring-boot-starter-test` 提供很多基础用法,更难得的是增加了对 Controller 层测试的支持。 - -``` -//简单验证结果集是否正确 -Assert.assertEquals(3, userMapper.getAll().size()); - -//验证结果集,提示 -Assert.assertTrue("错误,正确的返回值为200", status == 200); -Assert.assertFalse("错误,正确的返回值为200", status != 200); - -``` - -引入了`MockMvc`支持了对 Controller 层的测试,简单示例如下: - -``` java -public class HelloControlerTests { - - private MockMvc mvc; - - //初始化执行 - @Before - public void setUp() throws Exception { - mvc = MockMvcBuilders.standaloneSetup(new HelloController()).build(); - } - - //验证controller是否正常响应并打印返回结果 - @Test - public void getHello() throws Exception { - mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON)) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andDo(MockMvcResultHandlers.print()) - .andReturn(); - } - - //验证controller是否正常响应并判断返回结果是否正确 - @Test - public void testHello() throws Exception { - mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()) - .andExpect(content().string(equalTo("Hello World"))); - } - -} -``` - -单元测试是验证你代码第一道屏障,要养成每写一部分代码就进行单元测试的习惯,不要等到全部集成后再进行测试,集成后因为更关注整体运行效果,很容易遗漏掉代码底层的bug. - - - -### 集成测试 - -整体开发完成之后进入集成测试, Spring Boot 项目的启动入口在 Application 类中,直接运行 run 方法就可以启动项目,但是在调试的过程中我们肯定需要不断的去调试代码,如果每修改一次代码就需要手动重启一次服务就很麻烦, Spring Boot 非常贴心的给出了热部署的支持,很方便在 Web 项目中调试使用。 - -pom 需要添加以下的配置: - -``` xml - - - org.springframework.boot - spring-boot-devtools - true - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - true - - - - -``` - -添加以上配置后,项目就支持了热部署,非常方便集成测试。 - - -## 投产上线 - -其实我觉得这个阶段,应该还是比较简单一般分为两种;一种是打包成 jar 包直接执行,另一种是打包成 war 包放到 tomcat 服务器下。 - -### 打成 jar 包 - -如果你使用的是 maven 来管理项目,执行以下命令既可以 - -``` shell -cd 项目跟目录(和pom.xml同级) -mvn clean package -## 或者执行下面的命令 -## 排除测试代码后进行打包 -mvn clean package -Dmaven.test.skip=true -``` - -打包完成后 jar 包会生成到 target 目录下,命名一般是 项目名+版本号.jar - -启动 jar 包命令 - -``` shell -java -jar target/spring-boot-scheduler-1.0.0.jar -``` - -这种方式,只要控制台关闭,服务就不能访问了。下面我们使用在后台运行的方式来启动: - -``` shell -nohup java -jar target/spring-boot-scheduler-1.0.0.jar & -``` - -也可以在启动的时候选择读取不同的配置文件 - -``` shell -java -jar app.jar --spring.profiles.active=dev -``` - -也可以在启动的时候设置 jvm 参数 - -``` shell -java -Xms10m -Xmx80m -jar app.jar & -``` - -**gradle** -如果使用的是 gradle,使用下面命令打包 - -``` shell -gradle build -java -jar build/libs/mymodule-0.0.1-SNAPSHOT.jar -``` - -### 打成 war 包 - -打成 war 包一般可以分两种方式来实现,第一种可以通过 eclipse 这种开发工具来导出 war 包,另外一种是使用命令来完成,这里主要介绍后一种 - - -1、maven 项目,修改 pom 包 - -将 - -``` xml -jar -``` - -改为 - -``` xml -war -``` - -2、打包时排除tomcat. - -``` xml - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-tomcat - provided - -``` - -在这里将 scope 属性设置为 provided,这样在最终形成的 WAR 中不会包含这个 JAR 包,因为 Tomcat 或 Jetty 等服务器在运行时将会提供相关的 API 类。 - - -3、注册启动类 - -创建 ServletInitializer.java,继承 SpringBootServletInitializer ,覆盖 configure(),把启动类 Application 注册进去。外部 Web 应用服务器构建 Web Application Context 的时候,会把启动类添加进去。 - -``` java -public class ServletInitializer extends SpringBootServletInitializer { - @Override - protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { - return application.sources(Application.class); - } -} -``` - -最后执行 - -``` shell -mvn clean package -Dmaven.test.skip=true -``` - -会在 target 目录下生成:项目名+版本号.war文件,拷贝到 tomcat 服务器中启动即可。 - -**gradle** - -如果使用的是 Gradle,基本步奏一样,build.gradle中 添加 war 的支持,排除 spring-boot-starter-tomcat: - -``` shell -... - -apply plugin: 'war' - -... - -dependencies { - compile("org.springframework.boot:spring-boot-starter-web:1.4.2.RELEASE"){ - exclude mymodule:"spring-boot-starter-tomcat" - } -} -... -``` - -再使用构建命令 - -``` shell -gradle build -``` - -war 会生成在 build\libs 目录下。 - - -## 生产运维 - -### 查看 JVM 参数的值 - -可以根据 Java 自带的 jinfo 命令: - -``` shell -jinfo -flags pid -``` - -来查看 jar 启动后使用的是什么 gc、新生代、老年代分批的内存都是多少,示例如下: - -``` shell --XX:CICompilerCount=3 -XX:InitialHeapSize=234881024 -XX:MaxHeapSize=3743416320 -XX:MaxNewSize=1247805440 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=78118912 -XX:OldSize=156762112 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC -``` - -- `-XX:CICompilerCount ` :最大的并行编译数 -- `-XX:InitialHeapSize` 和 `-XX:MaxHeapSize` :指定 JVM 的初始和最大堆内存大小 -- `-XX:MaxNewSize` : JVM 堆区域新生代内存的最大可分配大小 -- ... -- `-XX:+UseParallelGC` :垃圾回收使用 Parallel 收集器 - - -### 如何重启 - -**简单粗暴** - -直接 kill 掉进程再次启动 jar 包 - -``` shell -ps -ef|grep java -##拿到对于Java程序的pid -kill -9 pid -## 再次重启 -Java -jar xxxx.jar -``` - -当然这种方式比较传统和暴力,所以建议大家使用下面的方式来管理 - - -**脚本执行** - -如果使用的是maven,需要包含以下的配置 - -``` xml - - org.springframework.boot - spring-boot-maven-plugin - - true - - -``` - -如果使用是 gradle,需要包含下面配置 - -``` shell -springBoot { - executable = true -} -``` - -启动方式: - -1、 可以直接`./yourapp.jar` 来启动 - -2、注册为服务 - -也可以做一个软链接指向你的jar包并加入到`init.d`中,然后用命令来启动。 - -init.d 例子: - -``` shell -ln -s /var/yourapp/yourapp.jar /etc/init.d/yourapp -chmod +x /etc/init.d/yourapp -``` - -这样就可以使用`stop`或者是`restart`命令去管理你的应用。 - -``` shell -/etc/init.d/yourapp start|stop|restart -``` - -或者 - -``` shell -service yourapp start|stop|restart -``` - -到此 Spring Boot 项目如何测试、联调和打包投产均已经介绍完,以后可以找时间研究一下 Spring Boot 的自动化运维,以及 Spring Boot 和 Docker 相结合的使用。 - -> 文章内容已经升级到 Spring Boot 2.x - -**[示例代码-github](https://github.com/ityouknow/spring-boot-examples/tree/master/spring-boot-package)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-boot-examples/tree/master/spring-boot-package)** - -**参考:** -[Installing Spring Boot applications](http://docs.spring.io/spring-boot/docs/current/reference/html/deployment-install.html) - diff --git a/_posts/2017/2017-05-10-springcloud-eureka.md b/_posts/2017/2017-05-10-springcloud-eureka.md deleted file mode 100644 index a0ac673318..0000000000 --- a/_posts/2017/2017-05-10-springcloud-eureka.md +++ /dev/null @@ -1,296 +0,0 @@ ---- -layout: post -title: springcloud(二):注册中心Eureka -category: springcloud -tags: [springcloud] -lock: need ---- - -Eureka是Netflix开源的一款提供服务注册和发现的产品,它提供了完整的Service Registry和Service Discovery实现。也是springcloud体系中最重要最核心的组件之一。 - - -## 背景介绍 - -### 服务中心 - -服务中心又称注册中心,管理各种服务功能包括服务的注册、发现、熔断、负载、降级等,比如dubbo admin后台的各种功能。 - -有了服务中心调用关系会有什么变化,画几个简图来帮忙理解 - -项目A调用项目B - -正常调用项目A请求项目B - - -![](http://favorites.ren/assets/images/2017/springcloud/ab.jpg) - - -有了服务中心之后,任何一个服务都不能直接去掉用,都需要通过服务中心来调用 - - -![](http://favorites.ren/assets/images/2017/springcloud/a2b.jpg) - -项目A调用项目B,项目B在调用项目C - - -![](http://favorites.ren/assets/images/2017/springcloud/abc.jpg) - -这时候调用的步骤就会为两步:第一步,项目A首先从服务中心请求项目B服务器,然后项目B在从服务中心请求项目C服务。 - - -![](http://favorites.ren/assets/images/2017/springcloud/a2b2c.jpg) - -上面的项目只是两三个相互之间的简单调用,但是如果项目超过20个30个呢,在15年底的时候我司分布式的项目就达到了二十几个,画一张图来描述几十个项目之间的相互调用关系全是线条,任何其中的一个项目改动,就会牵连好几个项目跟着重启,巨麻烦而且容易出错。通过服务中心来获取服务你不需要关注你调用的项目IP地址,由几台服务器组成,每次直接去服务中心获取可以使用的服务去调用既可。 - -由于各种服务都注册到了服务中心,就有了去做很多高级功能条件。比如几台服务提供相同服务来做均衡负载;监控服务器调用成功率来做熔断,移除服务列表中的故障点;监控服务调用时间来对不同的服务器设置不同的权重等等。 - -说Eureka之前我先八卦一下Netflix - -### Netflix - -以下介绍来自于百度百科: - -> Netflix是一家美国公司,在美国、加拿大提供互联网随选流媒体播放,定制DVD、蓝光光碟在线出租业务。该公司成立于1997年,总部位于加利福尼亚州洛斯盖图,1999年开始订阅服务。2009年,该公司可提供多达10万部DVD电影,并有1千万的订户。2007年2月25日,Netflix宣布已经售出第10亿份DVD。HIS一份报告中表示,2011年Netflix网络电影销量占据美国用户在线电影总销量的45%。 - -我第一次看到这个单词的时候,是在各种美剧或者电影的开头,Netflix拍摄的代表性的美剧有《纸牌屋》、《毒枭》、《怪奇物语》。后来研究springcloud的时候发现了Netflix公司,就在想它们是不是同一家公司,经过核对github上面邮件后缀判定确实是同一家公司,其实springcloud的微服务就基于Netflix公司的开源产品来做的。 - -Netflix的开源框架组件已经在Netflix的大规模分布式微服务环境中经过多年的生产实战验证,正逐步被社区接受为构造微服务框架的标准组件。Spring Cloud开源产品,主要是基于对Netflix开源组件的进一步封装,方便Spring开发人员构建微服务基础框架。对于一些打算构建微服务框架体系的公司来说,充分利用或参考借鉴Netflix的开源微服务组件(或Spring Cloud),在此基础上进行必要的企业定制,无疑是通向微服务架构的捷径。 - -### Eureka - -按照官方介绍: - -> Eureka is a REST (Representational State Transfer) based service that is primarily used in the AWS cloud for locating services for the purpose of load balancing and failover of middle-tier servers. -> -> Eureka 是一个基于 REST 的服务,主要在 AWS 云中使用, 定位服务来进行中间层服务器的负载均衡和故障转移。 - - -Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务注册和发现。Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server,并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。Spring Cloud 的一些其他模块(比如Zuul)就可以通过 Eureka Server 来发现系统中的其他微服务,并执行相关的逻辑。 - - -Eureka由两个组件组成:Eureka服务器和Eureka客户端。Eureka服务器用作服务注册服务器。Eureka客户端是一个java客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持。Netflix在其生产环境中使用的是另外的客户端,它提供基于流量、资源利用率以及出错状态的加权负载均衡。 - -用一张图来认识以下: - - -![](http://favorites.ren/assets/images/2017/springcloud/eureka-architecture-overview.png) - -上图简要描述了Eureka的基本架构,由3个角色组成: - -1、Eureka Server - - - 提供服务注册和发现 - -2、Service Provider - -- 服务提供方 -- 将自身服务注册到Eureka,从而使服务消费方能够找到 - -3、Service Consumer - -- 服务消费方 -- 从Eureka获取注册服务列表,从而能够消费服务 - - - -## 案例实践 - - -### Eureka Server - -spring cloud已经帮我实现了服务注册中心,我们只需要很简单的几个步骤就可以完成。 - -1、pom中添加依赖 - -``` xml - - - org.springframework.cloud - spring-cloud-starter - - - org.springframework.cloud - spring-cloud-starter-eureka-server - - - org.springframework.boot - spring-boot-starter-test - test - - -``` - -2、添加启动代码中添加```@EnableEurekaServer```注解 - -``` java -@SpringBootApplication -@EnableEurekaServer -public class SpringCloudEurekaApplication { - - public static void main(String[] args) { - SpringApplication.run(SpringCloudEurekaApplication.class, args); - } -} -``` - - -3、配置文件 - -在默认设置下,该服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为,在```application.properties```添加以下配置: - -``` properties -spring.application.name=spring-cloud-eureka - -server.port=8000 -eureka.client.register-with-eureka=false -eureka.client.fetch-registry=false - -eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/ -``` - -- ```eureka.client.register-with-eureka``` :表示是否将自己注册到Eureka Server,默认为true。 -- ```eureka.client.fetch-registry``` :表示是否从Eureka Server获取注册信息,默认为true。 -- ```eureka.client.serviceUrl.defaultZone``` :设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址。默认是http://localhost:8761/eureka ;多个地址可使用 , 分隔。 - - - -启动工程后,访问:http://localhost:8000/,可以看到下面的页面,其中还没有发现任何服务 - - -![](http://favorites.ren/assets/images/2017/springcloud/eureka_start.jpg) - - -## 集群 - -注册中心这么关键的服务,如果是单点话,遇到故障就是毁灭性的。在一个分布式系统中,服务注册中心是最重要的基础部分,理应随时处于可以提供服务的状态。为了维持其可用性,使用集群是很好的解决方案。Eureka通过互相注册的方式来实现高可用的部署,所以我们只需要将Eureke Server配置其他可用的serviceUrl就能实现高可用部署。 - -### 双节点注册中心 - -首次我们尝试一下双节点的注册中心的搭建。 - -1、创建application-peer1.properties,作为peer1服务中心的配置,并将serviceUrl指向peer2 - -``` properties -spring.application.name=spring-cloud-eureka -server.port=8000 -eureka.instance.hostname=peer1 - -eureka.client.serviceUrl.defaultZone=http://peer2:8001/eureka/ - -``` - -2、创建application-peer2.properties,作为peer2服务中心的配置,并将serviceUrl指向peer1 - -``` properties -spring.application.name=spring-cloud-eureka -server.port=8001 -eureka.instance.hostname=peer2 - -eureka.client.serviceUrl.defaultZone=http://peer1:8000/eureka/ -``` - -3、host转换 - -在hosts文件中加入如下配置 - -``` properties -127.0.0.1 peer1 -127.0.0.1 peer2 -``` - -4、打包启动 - -依次执行下面命令 - -``` shell -#打包 -mvn clean package -# 分别以peer1和peeer2 配置信息启动eureka -java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1 -java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2 -``` - -依次启动完成后,浏览器输入:```http://localhost:8000/``` 效果图如下: - - -![](http://favorites.ren/assets/images/2017/springcloud/eureka-two.jpg) - - -根据图可以看出peer1的注册中心DS Replicas已经有了peer2的相关配置信息,并且出现在available-replicas中。我们手动停止peer2来观察,发现peer2就会移动到unavailable-replicas一栏中,表示peer2不可用。 - -到此双节点的配置已经完成。 - - -### eureka集群使用 - -在生产中我们可能需要三台或者大于三台的注册中心来保证服务的稳定性,配置的原理其实都一样,将注册中心分别指向其它的注册中心。这里只介绍三台集群的配置情况,其实和双节点的注册中心类似,每台注册中心分别又指向其它两个节点即可,使用application.yml来配置。 - -application.yml配置详情如下: - -``` xml ---- -spring: - application: - name: spring-cloud-eureka - profiles: peer1 -server: - port: 8000 -eureka: - instance: - hostname: peer1 - client: - serviceUrl: - defaultZone: http://peer2:8001/eureka/,http://peer3:8002/eureka/ ---- -spring: - application: - name: spring-cloud-eureka - profiles: peer2 -server: - port: 8001 -eureka: - instance: - hostname: peer2 - client: - serviceUrl: - defaultZone: http://peer1:8000/eureka/,http://peer3:8002/eureka/ ---- -spring: - application: - name: spring-cloud-eureka - profiles: peer3 -server: - port: 8002 -eureka: - instance: - hostname: peer3 - client: - serviceUrl: - defaultZone: http://peer1:8000/eureka/,http://peer2:8001/eureka/ - -``` - -分别以peer1、peer2、peer3的配置参数启动eureka注册中心。 - -``` shell -java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1 -java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2 -java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer3 -``` - -依次启动完成后,浏览器输入:```http://localhost:8000/``` 效果图如下: - - -![](http://favorites.ren/assets/images/2017/springcloud/eureka-cluster.jpg) - -可以在peer1中看到了peer2、peer3的相关信息。至此eureka集群也已经完成了 - - -**[示例代码-github](https://github.com/ityouknow/spring-cloud-examples)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-cloud-examples)** - - -参考: - -[Peer Awareness](http://cloud.spring.io/spring-cloud-static/spring-cloud.html#_peer_awareness) diff --git a/_posts/2017/2017-05-12-eureka-provider-constomer.md b/_posts/2017/2017-05-12-eureka-provider-constomer.md deleted file mode 100644 index 1c22794248..0000000000 --- a/_posts/2017/2017-05-12-eureka-provider-constomer.md +++ /dev/null @@ -1,237 +0,0 @@ ---- -layout: post -title: springcloud(三):服务提供与调用 -category: springcloud -tags: [springcloud] -lock: need ---- - -上一篇文章我们介绍了eureka服务注册中心的搭建,这篇文章介绍一下如何使用eureka服务注册中心,搭建一个简单的服务端注册服务,客户端去调用服务使用的案例。 - -案例中有三个角色:服务注册中心、服务提供者、服务消费者,其中服务注册中心就是我们上一篇的eureka单机版启动既可,流程是首先启动注册中心,服务提供者生产服务并注册到服务中心中,消费者从服务中心中获取服务并执行。 - -## 服务提供 - -我们假设服务提供者有一个hello方法,可以根据传入的参数,提供输出“hello xxx,this is first messge”的服务 - -### 1、pom包配置 - -创建一个springboot项目,pom.xml中添加如下配置: - -``` xml - - - org.springframework.cloud - spring-cloud-starter-eureka - - - org.springframework.boot - spring-boot-starter-test - test - - -``` - -### 2、配置文件 - -application.properties配置如下: - -``` properties -spring.application.name=spring-cloud-producer -server.port=9000 -eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ -``` - -参数在上一篇都已经解释过,这里不多说。 - -### 3、启动类 - -启动类中添加```@EnableDiscoveryClient```注解 - -``` java -@SpringBootApplication -@EnableDiscoveryClient -public class ProducerApplication { - - public static void main(String[] args) { - SpringApplication.run(ProducerApplication.class, args); - } -} -``` - -### 4、controller - -提供hello服务 - -``` java -@RestController -public class HelloController { - - @RequestMapping("/hello") - public String index(@RequestParam String name) { - return "hello "+name+",this is first messge"; - } -} -``` - -添加```@EnableDiscoveryClient```注解后,项目就具有了服务注册的功能。启动工程后,就可以在注册中心的页面看到SPRING-CLOUD-PRODUCER服务。 - - -![](http://favorites.ren/assets/images/2017/springcloud/eureka_server.png) - -到此服务提供者配置就完成了。 - -## 服务调用 - -### 1、pom包配置 - -和服务提供者一致 - -``` xml - - - org.springframework.cloud - spring-cloud-starter-eureka - - - org.springframework.boot - spring-boot-starter-test - test - - -``` - -### 2、配置文件 - -application.properties配置如下: - -``` properties -spring.application.name=spring-cloud-consumer -server.port=9001 -eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ -``` - -### 3、启动类 - -启动类添加```@EnableDiscoveryClient```和```@EnableFeignClients```注解。 - -``` java -@SpringBootApplication -@EnableDiscoveryClient -@EnableFeignClients -public class ConsumerApplication { - - public static void main(String[] args) { - SpringApplication.run(ConsumerApplication.class, args); - } - -} -``` - -- ```@EnableDiscoveryClient``` :启用服务注册与发现 -- ```@EnableFeignClients```:启用feign进行远程调用 - ->Feign是一个声明式Web Service客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。 - -### 4、feign调用实现 - -``` java -@FeignClient(name= "spring-cloud-producer") -public interface HelloRemote { - @RequestMapping(value = "/hello") - public String hello(@RequestParam(value = "name") String name); -} -``` - -- name:远程服务名,及spring.application.name配置的名称 - -此类中的方法和远程服务中contoller中的方法名和参数需保持一致。 - - -### 5、web层调用远程服务 - -将HelloRemote注入到controller层,像普通方法一样去调用即可。 - -``` java -@RestController -public class ConsumerController { - - @Autowired - HelloRemote HelloRemote; - - @RequestMapping("/hello/{name}") - public String index(@PathVariable("name") String name) { - return HelloRemote.hello(name); - } - -} -``` - -到此,最简单的一个服务注册与调用的例子就完成了。 - - -## 测试 - -### 简单调用 -依次启动spring-cloud-eureka、spring-cloud-producer、spring-cloud-consumer三个项目 - -先输入:```http://localhost:9000/hello?name=neo``` 检查spring-cloud-producer服务是否正常 - -返回:```hello neo,this is first messge``` - -说明spring-cloud-producer正常启动,提供的服务也正常。 - -浏览器中输入:```http://localhost:9001/hello/neo``` - -返回:```hello neo,this is first messge``` - -说明客户端已经成功的通过feign调用了远程服务hello,并且将结果返回到了浏览器。 - -### 负载均衡 - -以上面spring-cloud-producer为例子修改,将其中的controller改动如下: - -``` java -@RestController -public class HelloController { - - @RequestMapping("/hello") - public String index(@RequestParam String name) { - return "hello "+name+",this is producer 2 send first messge"; - } -} -``` - -在配置文件中改动端口: - -``` properties -spring.application.name=spring-cloud-producer -server.port=9003 -eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ -``` - -打包启动后,在eureka就会发现两个服务提供者,如下图: - - -![](http://favorites.ren/assets/images/2017/springcloud/eureka_server2.png) - -然后在浏览器再次输入:```http://localhost:9001/hello/neo``` 进行测试: - -第一次返回结果:```hello neo,this is first messge``` - -第二次返回结果:```hello neo,this is producer 2 send first messge``` - -不断的进行测试下去会发现两种结果交替出现,说明两个服务中心自动提供了服务均衡负载的功能。如果我们将服务提供者的数量在提高为N个,测试结果一样,请求会自动轮询到每个服务端来处理。 - -**[示例代码-github](https://github.com/ityouknow/spring-cloud-examples)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-cloud-examples)** - -------------- - -**作者:纯洁的微笑** -**出处:[http://www.ityouknow.com/](http://www.ityouknow.com/springcloud/2017/05/12/eureka-provider-constomer.html)** -**版权归作者所有,转载请注明出处** - - - diff --git a/_posts/2017/2017-05-15-wechat-markdown.md b/_posts/2017/2017-05-15-wechat-markdown.md deleted file mode 100644 index a898183b4b..0000000000 --- a/_posts/2017/2017-05-15-wechat-markdown.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -layout: post -title: 如何在微信公众号优雅的展示代码 -category: other -tags: [other] ---- - -我在2017-04-25日开通了微信公众号,尝试着去分享一些技术文章,不可避免的文章里面有很多的代码,尝试了很多的方法,现在算是找到了一个还不错的解决方案,因此想把这个分享出来。 - -刚开始前自然是在网上找了一番有什么好的工具可以支持,看了很多解决方案大概分为下面几种: - -- 手动复制粘贴进去调一调格式 -- 代码制作成图片 -- 购买专业版工具导出为微信公众号格式 -- 很多在线的编辑软件 -- markdown here -- 其它 - -为什么会这样呢,最根本的原因就是微信的公众号不支持markdown的格式,好吧知乎也是。反正不管怎么的大家都还的继续用不是,就出来了很多的解决方案。而且微信的编辑器对代码这块支持也不够,幸好支持网页格式直接复制大家就都利用这个机制去做工具。 - -## 排除的方案 - -先说说手动复制粘贴吧,本来的代码是这个格式的: - -``` java -@SpringBootApplication -@EnableDiscoveryClient -public class ProducerApplication { - - public static void main(String[] args) { - SpringApplication.run(ProducerApplication.class, args); - } -} -``` - -在微信中就会变成下面这样,需要手动去敲回车,代码量大了苦不堪言。 - -``` java -@SpringBootApplication@EnableDiscoveryClientpublic class ProducerApplication { -public static void main(String[] args) { - SpringApplication.run(ProducerApplication.class, args); - } -} -``` - -或者是代码没有挤在一起,但是因为代码比较宽,只显示了半截,半截还在屏幕外面呢。 - -代码转化成图片,最原始的就是用截屏工具一段一段的去截屏,想想就痛苦,业内也有人写了工具来支持,具体可以参考这篇文章:[html2canvas 将代码转为图片](https://www.h5jun.com/post/convert-code-to-image-via-html2canvas.html),但是图片多了很多会影响页面的打开速度,而且编辑的时候需要一段一段的上传图片也很复杂。 - -购买专业版工具导出为微信公众号格式这个方式我不喜欢,第一要花钱,第二每次需要在这个软件中去处理,再导出也挺麻烦的;很多在线的编辑软件,也是一样进去都是花花碌碌的页面,广告贼多,有些还必须先注册,体验很差。 - -所以以上的几种方式在一开始的时候就被我放弃了。 - -## 选择方案 - -刚开始的时候就看到了markdown here这个款工具,感觉算是体验也不错,也用了有一阵子了。我使用的是chrome浏览器,其它浏览器也有对应的插件,使用步骤如下: - -- 1、在Google Chrome中安装Markdown-here插件 -- 2、在sublime中用Markdwon格式书写 -- 3、拷贝粘贴到微信公共帐号的编辑器中 -- 4、上传文章中使用的图片 -- 5、点击浏览器上的插件按钮,使用Markdown-here渲染 - -也可以自定义CSS,自定义代码高亮的格式等等,但是它也有两个致命的缺点: - -- 以markdown格式粘贴进去之后,使用快捷键```CTRL+Alt+M```生成html后代码格式也没有问题,但是点击保存之后很多代码就会黏在一起,什么原因呢?Markdown解释器在转换代码片段时,没有在换行的时候添加```
    ```标签,而是直接输出一个换行符``` \n```,微信编辑页在保存或者预览时,将部分换行符给过滤了。 -- 就算代码格式正常,使用苹果微信查看代码的时候会被自动折行,效果很差。 - -第一个问题也有解决方案,网上有开源精神的朋友写了插件来支持,具体可以参考这篇文章:[微信公众号代码区域换行问题(解决) -](http://www.jianshu.com/p/ea588ec043ab),但是第二个问题还是不能解决,然后我只能每次贴心的给推送的文章下面加这么一句话: - -> 苹果手机代码会折行,建议苹果用户点击阅读原文查看,效果会更好一些。 - -每次在公众号下面去粘贴这一句,感觉也挺傻X的。 - -我在网上查找解决方案的时候,偶然看到小胡子哥作者写了一个开源软件:online-markdown,界面如下; - - -![](http://favorites.ren/assets/images/2017/online-markdown.png) - -使用方式很简单,将写好的markdown格式的代码直接复制粘贴到这个页面里面,点击预览就可以看到渲染后的效果了,根据自己的需要也可以在上面选择不同的样式和代码高亮的格式,选完之后点击复制,直接粘贴到微信公号的编辑器中既可,我试着用了一下效果不错。 - -大家可以使用这个地址来测试[http://md.ityouknow.com/](http://md.ityouknow.com/) - -但是还是有一些小瑕疵,作者也会去完善,感兴趣的可以去github上面star一下。小瑕疵有三个: - -- 1、可以选择的样式不是很多,只有三种,但如果你感兴趣的话可以自己去加 -- 2、“- ”的格式转换不是很好,会换行。我看有人已经提出来了,作者应该也会很快修复这个问题。 -- 3、建议使用chrome浏览器,其它浏览器兼容性较差。 - -目前这个就是我选择使用的方案了,也希望这个工具可以帮助到大家。 - -> 如果你有更好的解决方案,也请一定告诉我。 - - diff --git a/_posts/2017/2017-05-16-springcloud-hystrix.md b/_posts/2017/2017-05-16-springcloud-hystrix.md deleted file mode 100644 index 69b9628bd6..0000000000 --- a/_posts/2017/2017-05-16-springcloud-hystrix.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -layout: post -title: springcloud(四):熔断器Hystrix -category: springcloud -tags: [springcloud] ---- - -说起springcloud熔断让我想起了去年股市中的熔断,多次痛的领悟,随意实施的熔断对整个系统的影响是灾难性的,好了接下来我们还是说正事。 - - -## 熔断器 - - -### 雪崩效应 - -在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。 - -如果下图所示:A作为服务提供者,B为A的服务消费者,C和D是B的服务消费者。A不可用引起了B的不可用,并将不可用像滚雪球一样放大到C和D时,雪崩效应就形成了。 - - -![](http://favorites.ren/assets/images/2017/springcloud/hystrix-1.png) - - -### 熔断器(CircuitBreaker) - - -熔断器的原理很简单,如同电力过载保护器。它可以实现快速失败,如果它在一段时间内侦测到许多类似的错误,会强迫其以后的多个调用快速失败,不再访问远程服务器,从而防止应用程序不断地尝试执行可能会失败的操作,使得应用程序继续执行而不用等待修正错误,或者浪费CPU时间去等到长时间的超时产生。熔断器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。 - -熔断器模式就像是那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生错误的次数,然后决定使用允许操作继续,或者立即返回错误。 -熔断器开关相互转换的逻辑如下图: - - -![](http://favorites.ren/assets/images/2017/springcloud/hystrix-2.png) - -熔断器就是保护服务高可用的最后一道防线。 - -### Hystrix特性 - -**1.断路器机制** - -断路器很好理解, 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN). 这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力. - -**2.Fallback** - -Fallback相当于是降级操作. 对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存. - -**3.资源隔离** - -在Hystrix中, 主要通过线程池来实现资源隔离. 通常在使用的时候我们会根据调用的远程服务划分出多个线程池. 例如调用产品服务的Command放入A线程池, 调用账户服务的Command放入B线程池. 这样做的主要优点是运行环境被隔离开了. 这样就算调用服务的代码存在bug或者由于其他原因导致自己所在线程池被耗尽时, 不会对系统的其他服务造成影响. 但是带来的代价就是维护多个线程池会对系统带来额外的性能开销. 如果是对性能有严格要求而且确信自己调用服务的客户端代码不会出问题的话, 可以使用Hystrix的信号模式(Semaphores)来隔离资源. - - -## Feign Hystrix - -因为熔断只是作用在服务调用这一端,因此我们根据上一篇的示例代码只需要改动spring-cloud-consumer项目相关代码就可以。因为,Feign中已经依赖了Hystrix所以在maven配置上不用做任何改动。 - - -### 1、配置文件 - -application.properties添加这一条: - -``` properties -feign.hystrix.enabled=true -``` - -### 2、创建回调类 - -创建HelloRemoteHystrix类继承与HelloRemote实现回调的方法 - -``` java -@Component -public class HelloRemoteHystrix implements HelloRemote{ - - @Override - public String hello(@RequestParam(value = "name") String name) { - return "hello" +name+", this messge send failed "; - } -} -``` - - -### 3、添加fallback属性 - -在```HelloRemote```类添加指定fallback类,在服务熔断的时候返回fallback类中的内容。 - -``` java -@FeignClient(name= "spring-cloud-producer",fallback = HelloRemoteHystrix.class) -public interface HelloRemote { - - @RequestMapping(value = "/hello") - public String hello(@RequestParam(value = "name") String name); - -} -``` - -改动点就这点,很简单吧。 - -### 4、测试 - -那我们就来测试一下看看效果吧。 - -依次启动spring-cloud-eureka、spring-cloud-producer、spring-cloud-consumer三个项目。 - -浏览器中输入:```http://localhost:9001/hello/neo``` - -返回:```hello neo,this is first messge``` - -说明加入熔断相关信息后,不影响正常的访问。接下来我们手动停止spring-cloud-producer项目再次测试: - -浏览器中输入:```http://localhost:9001/hello/neo``` - -返回:```hello neo, this messge send failed``` - -根据返回结果说明熔断成功。 - - - -**[示例代码-github](https://github.com/ityouknow/spring-cloud-examples)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-cloud-examples)** - -参考: - -[使用Spring Cloud与Docker实战微服务](https://www.gitbook.com/book/eacdy/spring-cloud-book/details) - -[微服务框架Spring Cloud介绍 Part5: 在微服务系统中使用Hystrix, Hystrix Dashboard与Turbine](http://skaka.me/blog/2016/09/04/springcloud5/) - -------------- -**作者:纯洁的微笑** -**出处:[http://www.ityouknow.com/](http://www.ityouknow.com/springcloud/2017/05/16/springcloud-hystrix.html)** -**版权归作者所有,转载请注明出处** - diff --git a/_posts/2017/2017-05-18-hystrix-dashboard-turbine.md b/_posts/2017/2017-05-18-hystrix-dashboard-turbine.md deleted file mode 100644 index ec406da4fb..0000000000 --- a/_posts/2017/2017-05-18-hystrix-dashboard-turbine.md +++ /dev/null @@ -1,238 +0,0 @@ ---- -layout: post -title: springcloud(五):熔断监控Hystrix Dashboard和Turbine -category: springcloud -tags: [springcloud] ---- - -Hystrix-dashboard是一款针对Hystrix进行实时监控的工具,通过Hystrix Dashboard我们可以在直观地看到各Hystrix Command的请求响应时间, 请求成功率等数据。但是只使用Hystrix Dashboard的话, 你只能看到单个应用内的服务信息, 这明显不够. 我们需要一个工具能让我们汇总系统内多个服务的数据并显示到Hystrix Dashboard上, 这个工具就是Turbine. - - -## Hystrix Dashboard - - -我们在熔断示例项目spring-cloud-consumer-hystrix的基础上更改,重新命名为:spring-cloud-consumer-hystrix-dashboard。 - -### 1、添加依赖 - -``` xml - - org.springframework.cloud - spring-cloud-starter-hystrix - - - org.springframework.cloud - spring-cloud-starter-hystrix-dashboard - - - org.springframework.boot - spring-boot-starter-actuator - -``` - -这三个包必须添加 - - -### 2、启动类 - -启动类添加启用Hystrix Dashboard和熔断器 - -``` java -@SpringBootApplication -@EnableDiscoveryClient -@EnableFeignClients -@EnableHystrixDashboard -@EnableCircuitBreaker -public class ConsumerApplication { - - public static void main(String[] args) { - SpringApplication.run(ConsumerApplication.class, args); - } -} -``` - -### 3、测试 - -启动工程后访问 http://localhost:9001/hystrix,将会看到如下界面: - - -![](http://favorites.ren/assets/images/2017/springcloud/hystrix-dashboard-1.jpg) - -图中会有一些提示: - -> Cluster via Turbine (default cluster): http://turbine-hostname:port/turbine.stream -> Cluster via Turbine (custom cluster): http://turbine-hostname:port/turbine.stream?cluster=[clusterName] -> Single Hystrix App: http://hystrix-app:port/hystrix.stream - -大概意思就是如果查看默认集群使用第一个url,查看指定集群使用第二个url,单个应用的监控使用最后一个,我们暂时只演示单个应用的所以在输入框中输入: -http://localhost:9001/hystrix.stream ,输入之后点击 monitor,进入页面。 - -如果没有请求会先显示```Loading ...```,访问http://localhost:9001/hystrix.stream 也会不断的显示ping。 - -请求服务http://localhost:9001/hello/neo,就可以看到监控的效果了,首先访问http://localhost:9001/hystrix.stream,显示如下: - - -``` -ping: - -data: {"type":...} - -data: {"type":...} -``` - -说明已经返回了监控的各项结果 - -到监控页面就会显示如下图: - - -![](http://favorites.ren/assets/images/2017/springcloud/hystrix-dashboard-2.jpg) - -其实就是http://localhost:9001/hystrix.stream返回结果的图形化显示,Hystrix Dashboard Wiki上详细说明了图上每个指标的含义,如下图: - - -![](http://favorites.ren/assets/images/2017/springcloud/hystrix-dashboard-3.png) - -到此单个应用的熔断监控已经完成。 - - -## Turbine - -在复杂的分布式系统中,相同服务的节点经常需要部署上百甚至上千个,很多时候,运维人员希望能够把相同服务的节点状态以一个整体集群的形式展现出来,这样可以更好的把握整个系统的状态。 为此,Netflix提供了一个开源项目(Turbine)来提供把多个hystrix.stream的内容聚合为一个数据源供Dashboard展示。 - - -### 1、添加依赖 - -``` xml - - - org.springframework.cloud - spring-cloud-starter-turbine - - - org.springframework.cloud - spring-cloud-netflix-turbine - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.cloud - spring-cloud-starter-hystrix-dashboard - - -``` - -### 2、配置文件 - -``` properties -spring.application.name=hystrix-dashboard-turbine -server.port=8001 -turbine.appConfig=node01,node02 -turbine.aggregator.clusterConfig= default -turbine.clusterNameExpression= new String("default") - -eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ -``` - -- ```turbine.appConfig``` :配置Eureka中的serviceId列表,表明监控哪些服务 -- ```turbine.aggregator.clusterConfig``` :指定聚合哪些集群,多个使用","分割,默认为default。可使用```http://.../turbine.stream?cluster={clusterConfig之一}```访问 -- ```turbine.clusterNameExpression``` : 1. clusterNameExpression指定集群名称,默认表达式appName;此时:```turbine.aggregator.clusterConfig```需要配置想要监控的应用名称;2. 当clusterNameExpression: default时,```turbine.aggregator.clusterConfig```可以不写,因为默认就是default;3. 当clusterNameExpression: metadata['cluster']时,假设想要监控的应用配置了```eureka.instance.metadata-map.cluster: ABC```,则需要配置,同时```turbine.aggregator.clusterConfig: ABC``` - -### 3、启动类 - -启动类添加```@EnableTurbine```,激活对Turbine的支持 - -``` java -@SpringBootApplication -@EnableHystrixDashboard -@EnableTurbine -public class DashboardApplication { - - public static void main(String[] args) { - SpringApplication.run(DashboardApplication.class, args); - } - -} -``` - -到此Turbine(hystrix-dashboard-turbine)配置完成 - - -### 4、测试 - -在示例项目spring-cloud-consumer-hystrix基础上修改为两个服务的调用者spring-cloud-consumer-node1和spring-cloud-consumer-node2 - -spring-cloud-consumer-node1项目改动如下: -application.properties文件内容 - -``` properties -spring.application.name=node01 -server.port=9001 -feign.hystrix.enabled=true - -eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ -``` - -spring-cloud-consumer-node2项目改动如下: -application.properties文件内容 - -``` properties -spring.application.name=node02 -server.port=9002 -feign.hystrix.enabled=true - -eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ - -``` - -HelloRemote类修改: - -``` java -@FeignClient(name= "spring-cloud-producer2", fallback = HelloRemoteHystrix.class) -public interface HelloRemote { - - @RequestMapping(value = "/hello") - public String hello2(@RequestParam(value = "name") String name); - -} -``` - -对应的```HelloRemoteHystrix```和```ConsumerController```类跟随修改,具体查看代码 - -修改完毕后,依次启动spring-cloud-eureka、spring-cloud-consumer-node1、spring-cloud-consumer-node1、hystrix-dashboard-turbine(Turbine) - -打开eureka后台可以看到注册了三个服务: - - -![](http://favorites.ren/assets/images/2017/springcloud/turbine-01.jpg) - - -访问 http://localhost:8001/turbine.stream - -返回: - -``` -: ping -data: {"reportingHostsLast10Seconds":1,"name":"meta","type":"meta","timestamp":1494921985839} -``` - -并且会不断刷新以获取实时的监控数据,说明和单个的监控类似,返回监控项目的信息。进行图形化监控查看,输入:http://localhost:8001/hystrix,返回酷酷的小熊界面,输入: http://localhost:8001/turbine.stream,然后点击 Monitor Stream ,可以看到出现了俩个监控列表 - - -![](http://favorites.ren/assets/images/2017/springcloud/turbine-02.jpg) - - -**[示例代码-github](https://github.com/ityouknow/spring-cloud-examples)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-cloud-examples)** - -参考: - -[使用Spring Cloud与Docker实战微服务](https://www.gitbook.com/book/eacdy/spring-cloud-book/details) - - -------------- -**作者:纯洁的微笑** -**出处:[http://www.ityouknow.com/](http://www.ityouknow.com/springcloud/2017/05/18/hystrix-dashboard-turbine.html)** -**版权归作者所有,转载请注明出处** \ No newline at end of file diff --git a/_posts/2017/2017-05-19-Limitations-of-thinking.md b/_posts/2017/2017-05-19-Limitations-of-thinking.md deleted file mode 100644 index cb7db03a12..0000000000 --- a/_posts/2017/2017-05-19-Limitations-of-thinking.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -layout: post -title: 思维的局限 -category: life -tags: [life] -lock: need ---- - - -我相信大家都可能听到过这样一个故事,一名记者看见农村的一个正在放羊的小孩 - -问他“你的理想是什么?” -小孩回答说:“放羊!” -“羊儿喂大了干什么?” -“卖钱!” -“有了卖羊儿的钱干什么?” -“娶老婆!” -“娶老婆干什么?” -“生小孩!” -“生小孩来干什么?” -“放羊!” - ...... - -这段流传甚广的段子,但是我们想想这个故事背后的原因是什么。放羊的小孩对世界的理解也就是:放羊挣钱娶老婆生小孩再放羊。对外面世界的了解可能趋向于了零,在他的世界里,这就是美好的一切,幸福的生活。对世界认识的局限性会导致思维的局限性,思维的局限性会导致目标的局限性,目标的局限性最终导致行为的局限性,从而影响你人生中的各种选择。 - - -## 关于贫穷 - -**故事一** - -我在知乎看到的这个故事[你在哪一刻体验到了真正的贫富差距?](https://www.zhihu.com/question/56322619/answer/156776118),故事情节大概是这个样子的。 - -13年的夏天,作者偶然遇到了一个小伙子,一副农民工的打扮,问去从这里到xx城市应该怎么走?作者有点诧异,因为这里距离XX城市要有100百多公里,给他指了指方向,告诉他往西大约一百多公里。小伙子点点头说了声谢谢大哥就往前走了,以为会是个乞讨者或者流浪汉,讨点钱或要点吃的。可他只是问问路,而且看上去是要徒步过去的意思。小伙子走出去几十米,在人行道边坐下,拿出一瓶水喝了几口,然后低着头摆弄着地下的什么玩意,像是在休息。 - -好奇心驱使作者去了解情况,才知道他是要去xx市找自己的老乡,老乡在xx市的一个厂里面工作,工资比现在的高一些,老乡给他拖了一个口信,他现在工作的地方老板不给他发工资并且扣了他的身份证。于是他就准备徒步两百多公里去投靠老乡。小伙子没受过教育,家在云南山区,没有电话,打工是为了补贴家用,同村出来的几个人如今都分散了,没有朋友,没有依靠。小伙子在讲这些的时候,在他的眼睛里没看到赶去见同乡的盼望,没看到被工头欺负后的愤怒,在他看来好像一切都很正常,像是在叙述别人的故事,低垂的眼里空洞无意,就像个老人在等待明天。 - -**故事二** - -有次看中央电视台的一个节目,是采访居住在交通闭塞的大山里村民的生活。有一个画面我记忆特别深刻,中央电视台采访一个小伙子,记者问他:你为什么不出去打工来挣钱呢,那个小伙子说了一句,我听不懂你说啥。这句话深深的震撼了我,那个小伙子和我应该是同龄吧,大概也是三十岁左右,几十年没有走出去过小山村,竟然都听不懂普通话,更不敢出去看看外面的世界。那一刻我感觉这个世界在他的眼里充满了迷茫。 - -后来村长说,这个村子常年不通公路,村里面的人要出去需要先坐牛车,再坐拖拉机两天后才能到达小县城,村里面就一个小学,一个教室里面坐了六个年级的学生,很多孩子上到一半就回家干活了。这两年有一些年轻人走出了村子去深圳、东莞打工,慢慢的也带动了一些人,但还是有一些年轻人因为不会说普通话,不能直接和人交流,就一直都没有出去,在村里面种地生活。这是21世纪呀,非常有可能他们一辈子就走不去了。 - - -![](http://favorites.ren/assets/images/2017/life/life-limit.jpg) - -贫穷在这个年代仍然是存在的,只是可能你不了解。但贫穷其实不是最可怕的,最可怕的是贫穷的人不知道贫穷的可怕,不了解外面的世界,所以对他们来讲没有走出去的动力和勇气,只有让他们了解外面的世界,认识外面的世界,从思想上有了改变的动力和目标,才能真正的走出贫穷。 - -教育有时候就是开智,明事理 ,认识世界,可以有一个和外界交流的窗口。到现在为止,高考仍然是很多人走出老家,跨越阶级的最公平,最重要的一条道路,虽然它仍然有很多的弊端。 - - -## 思维误区 - -我上初中的时候,大家都羡慕不上学去外面世界闯荡的人们。去南方打工、当兵、学一门技术的都觉得挺时髦,可以不用上无用的学,勇敢的走出小县城,自己赚钱自己花,再也没有了上学的约束。我初中的五个好兄弟,两个当了兵,一个去学了理发,一个去学了厨师,只有我一个考上高中,继续求学,每年他们回来都会聊聊他们在外面的故事,一番啤酒下肚之后,我是既羡慕又被吸引,对外面的世界充满了憧憬,多年以后我才知道他们在外面受了多少的苦,但是回老家了,必须要搞的在外面很精彩。 - -其实上过大学的就比没有上过大学的能力好很多吗?有可能只是在开始的时候选择对了而已。我们都处在一个大的洪流之中,不是说选择了不读书以后就混不好,选择了读书就肯定会有不错的生活。但是整体的机遇来讲,读过大学的比没有读过大学的会更多一点,没有读过大学的人大多从事了体力劳动或者技能类的一些工作,读过大学不管怎么样,大都是白领类的工作。 - -我们这里不讨论极个别厉害人的情况,研究一下为什么会出现这种情况。上大学让你在那些方面有了提升,很多人大学四年几乎没有好好学习过,玩了四年照样出来找了不错的工作,所以说大学对人能力的提升不一定会比没有上学的强,在外面对能力的提升反而是更快。我个人认为最本质的是认知的提升会不一样,那怕你的四年什么都没有做,但是你至少看到很多人做的事情,别人做事情的经验也会成为你的的经验,毕业后大家都去找了工作,你肯定也会跟着找了工作,大不了差了一点。都是在认知上,对世界的认识上有了很大的不同,才最终影响了你未来的选择和目标。 - -说到这里想起了波仔的那条理论,你的生活水平就是你身边最亲近的五个人的平均值。我就不信你和奥巴马是哥们,还和我一起在敲代码。其实仔细观察你身边的高管,高层,起步就没在一个层级里面。 - -程序员呢,其实也是一样。大家有没有一种感觉,刚毕业一年左右的时候是最自信的,觉得自己已经掌握了程序开发的基本规律,应付工作没有问题,简历上写的各种精通,精通前端,精通数据库,精通core java等等。反而工作多年以后越发觉得,研究不够深入涉猎不够广泛,很多的新老技术还都停留在使用阶段。为了解释这个问题我画了两个图。 - - -![](http://favorites.ren/assets/images/2017/it-limit-01.jpg) - -把整个的IT知识领域用一个圆圈来表示,黑色实体部分就是我们刚开始认知的区域,因为我们刚开始知道的少,了解的少,里面的圆的周长就会小很多,我们感觉到自己需要了解的东西也相对小。 - - -![](http://favorites.ren/assets/images/2017/it-limit-02.jpg) - -当我们通过不断的学习、扩宽自己知识的边界,就像里面的小圆不断的长大一些,我们感受到的未知也越来越多,认识到自己了解的内容占整个体系只是很小的一部分。 - -很多人,不是不想学习,是不知道如何去学习,很多时候遇到问题,不知道外界有很多解决方案。另外,不进步不学习不可怕,最可怕的是,比我们厉害的人比我们更努力。 - - -## 如何打破 - -1、要跳出你的圈子,我从西安到北京,IT行业工作氛围,技术特征完全没有在一个水平线上。要时常的跳出自己的圈子,站到外面去看原来的自己。 - -2、读书,不断的读书,不断的读好书,其实读书是了解作者怎么看这个世界,读好书更是了解很多大师人物对世界的理解。通过读书打开自己对世界认识的另一扇窗,技术人除了看技术书籍,更应该看一些其它领域的书,来扩宽自己的视野。 - -3、对于技术,多逛逛技术性网站,了解一下最新的前沿知识,比如vue活跃的时候,博客园就出了很多关于vue的教程,微服务火的时候,博客园首页经常有关于微服务的文章,时常关注让你了解有这么个东西,在解决问题的时候或者定方案的时候知道会有这么一个选项。 - -4、多走出自己的视角,做为程序员总是以程序的思维去思考问题。比如遇到了问题,这个问题对产品、客户意味着什么,领导是如何考虑的,当领导让你干某件事的时候他关心的什么。刚好我这里有一个职场真实的小故事,分享给大家。 - -> 某一线互联网电商平台,空降了一名技术高管,要求紧急上线一个项目,必须三个月上线,安排下去,技术总监认真的分析了需求和现有公司的资源,告知这个项目最少需要四个月加班才能完成,高管说实在没有办法吗,总监说,已经是极限了。高管直接就把这位总监给开了,然后问下一个总监周期需要多久,这位总监考虑了一下说:2个月,高管说,项目你来负责,最后这个项目整整做了六个月,但是大家也都相安无事。 - -5、培养一个行业之外的爱好,最好是运动类的,只有身体好了,精神才更好,才能体会挥汗之后的畅快之感。具体的,比如坚持每周去户外,在北京的话像绿野这种就可以,参加几次就可以融入组织。程序员的话,建议打羽毛球,感觉这类运动对程序员是最有益处,避免各种颈椎病,游泳也是很好的一个选择。经常保持运动的人,整个人的精神状态是不一样的,我们大多数办公室工作者都处于亚健康状态,锻炼真的很重要。 - -6、github对程序员来讲真是一个宝库,提交分享代码都是最基础的,建博客、建网站、建工具github都可以免费让你搞定,可以作为你创意或者实践的一个绝佳场所,也可以好好看看其他牛人的杰作,也有很多github第三方的网站可以帮你找到优秀的开源项目。 - - -## 最后 - -很多时候的改变,不是我们不想改变,不愿意改变,是不知道如何改变,不知道什么才是好的选择,或者在深刻一点,很多人到现在都不知道什么是好的什么是坏的。 -所以说很多时候的改变,最先是思维的改变,思维的改变转换成行动,然后达到真正的改变。 - -我们是不是这个版本 - -“你做程序员干什么?” -“找个体面又高薪的工作。” -“找到理想的工作后呢?” -“再寻觅一个温柔漂亮的好老婆。” -“找到好老婆后呢?” -“生一个聪明智慧的孩子!” -“生了孩子后呢?” -“从小就好好教育孩子努力学习,考个好大学,最好再做一名程序员!” - ...... - - -------------- -**作者:纯洁的微笑** -**出处:[http://www.ityouknow.com/](http://www.ityouknow.com/life/2017/05/19/Limitations-of-thinking.html)** -**版权归作者所有,转载请注明出处** \ No newline at end of file diff --git a/_posts/2017/2017-05-22-springcloud-config-git.md b/_posts/2017/2017-05-22-springcloud-config-git.md deleted file mode 100644 index d329d8be56..0000000000 --- a/_posts/2017/2017-05-22-springcloud-config-git.md +++ /dev/null @@ -1,241 +0,0 @@ ---- -layout: post -title: springcloud(六):配置中心git示例 -category: springcloud -tags: [springcloud] -lock: need ---- - -随着线上项目变的日益庞大,每个项目都散落着各种配置文件,如果采用分布式的开发模式,需要的配置文件随着服务增加而不断增多。某一个基础服务信息变更,都会引起一系列的更新和重启,运维苦不堪言也容易出错。配置中心便是解决此类问题的灵丹妙药。 - -市面上开源的配置中心有很多,BAT每家都出过,360的QConf、淘宝的diamond、百度的disconf都是解决这类问题。国外也有很多开源的配置中心Apache Commons Configuration、owner、cfg4j等等。这些开源的软件以及解决方案都很优秀,但是我最钟爱的却是Spring Cloud Config,因为它功能全面强大,可以无缝的和spring体系相结合,够方便够简单颜值高我喜欢。 - - -## Spring Cloud Config - -在我们了解spring cloud config之前,我可以想想一个配置中心提供的核心功能应该有什么 - -- 提供服务端和客户端支持 -- 集中管理各环境的配置文件 -- 配置文件修改之后,可以快速的生效 -- 可以进行版本管理 -- 支持大的并发查询 -- 支持各种语言 - -Spring Cloud Config可以完美的支持以上所有的需求。 - -Spring Cloud Config项目是一个解决分布式系统的配置管理方案。它包含了Client和Server两个部分,server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client通过接口获取数据、并依据此数据初始化自己的应用。Spring cloud使用git或svn存放配置文件,默认情况下使用git,我们先以git为例做一套示例。 - - -首先在github上面创建了一个文件夹config-repo用来存放配置文件,为了模拟生产环境,我们创建以下三个配置文件: - - -``` properties -// 开发环境 -neo-config-dev.properties -// 测试环境 -neo-config-test.properties -// 生产环境 -neo-config-pro.properties -``` - -每个配置文件中都写一个属性neo.hello,属性值分别是 hello im dev/test/pro 。下面我们开始配置server端 - - -## server 端 - -### 1、添加依赖 - -``` xml - - - org.springframework.cloud - spring-cloud-config-server - - -``` - -只需要加入spring-cloud-config-server包引用既可。 - -### 2、配置文件 - -``` properties -server: - port: 8001 -spring: - application: - name: spring-cloud-config-server - cloud: - config: - server: - git: - uri: https://github.com/ityouknow/spring-cloud-starter/ # 配置git仓库的地址 - search-paths: config-repo # git仓库地址下的相对地址,可以配置多个,用,分割。 - username: # git仓库的账号 - password: # git仓库的密码 -``` - - -Spring Cloud Config也提供本地存储配置的方式。我们只需要设置属性```spring.profiles.active=native```,Config Server会默认从应用的```src/main/resource```目录下检索配置文件。也可以通过```spring.cloud.config.server.native.searchLocations=file:E:/properties/```属性来指定配置文件的位置。虽然Spring Cloud Config提供了这样的功能,但是为了支持更好的管理内容和版本控制的功能,还是推荐使用git的方式。 - - -### 3、启动类 - -启动类添加```@EnableConfigServer```,激活对配置中心的支持 - -``` java -@EnableConfigServer -@SpringBootApplication -public class ConfigServerApplication { - - public static void main(String[] args) { - SpringApplication.run(ConfigServerApplication.class, args); - } -} -``` - -到此server端相关配置已经完成 - -### 4、测试 - -首先我们先要测试server端是否可以读取到github上面的配置信息,直接访问:```http://localhost:8001/neo-config/dev``` - -返回信息如下: - -``` -{ - "name": "neo-config", - "profiles": [ - "dev" - ], - "label": null, - "version": null, - "state": null, - "propertySources": [ - { - "name": "https://github.com/ityouknow/spring-cloud-starter/config-repo/neo-config-dev.properties", - "source": { - "neo.hello": "hello im dev" - } - } - ] -} -``` - -上述的返回的信息包含了配置文件的位置、版本、配置文件的名称以及配置文件中的具体内容,说明server端已经成功获取了git仓库的配置信息。 - -如果直接查看配置文件中的配置信息可访问:```http://localhost:8001/neo-config-dev.properties```,返回:```neo.hello: hello im dev``` - -修改配置文件```neo-config-dev.properties```中配置信息为:```neo.hello=hello im dev update```,再次在浏览器访问```http://localhost:8001/neo-config-dev.properties```,返回:```neo.hello: hello im dev update```。说明server端会自动读取最新提交的内容 - -仓库中的配置文件会被转换成web接口,访问可以参照以下的规则: - -- /{application}/{profile}[/{label}] -- /{application}-{profile}.yml -- /{label}/{application}-{profile}.yml -- /{application}-{profile}.properties -- /{label}/{application}-{profile}.properties - -以neo-config-dev.properties为例子,它的application是neo-config,profile是dev。client会根据填写的参数来选择读取对应的配置。 - -## client 端 - -主要展示如何在业务项目中去获取server端的配置信息 - - -### 1、添加依赖 - -``` xml - - - org.springframework.cloud - spring-cloud-starter-config - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-test - test - - -``` - -引入spring-boot-starter-web包方便web测试 - -### 2、配置文件 - -需要配置两个配置文件,application.properties和bootstrap.properties - -application.properties如下: - -``` properties -spring.application.name=spring-cloud-config-client -server.port=8002 -``` -bootstrap.properties如下: - -``` properties -spring.cloud.config.name=neo-config -spring.cloud.config.profile=dev -spring.cloud.config.uri=http://localhost:8001/ -spring.cloud.config.label=master -``` - -- spring.application.name:对应{application}部分 -- spring.cloud.config.profile:对应{profile}部分 -- spring.cloud.config.label:对应git的分支。如果配置中心使用的是本地存储,则该参数无用 -- spring.cloud.config.uri:配置中心的具体地址 -- spring.cloud.config.discovery.service-id:指定配置中心的service-id,便于扩展为高可用配置集群。 - -> 特别注意:上面这些与spring-cloud相关的属性必须配置在bootstrap.properties中,config部分内容才能被正确加载。因为config的相关配置会先于application.properties,而bootstrap.properties的加载也是先于application.properties。 - - -### 3、启动类 - -启动类添加```@EnableConfigServer```,激活对配置中心的支持 - -``` java -@SpringBootApplication -public class ConfigClientApplication { - - public static void main(String[] args) { - SpringApplication.run(ConfigClientApplication.class, args); - } -} -``` - -启动类只需要```@SpringBootApplication```注解就可以 - -### 4、web测试 - -使用```@Value```注解来获取server端参数的值 - -``` java -@RestController -class HelloController { - @Value("${neo.hello}") - private String hello; - - @RequestMapping("/hello") - public String from() { - return this.hello; - } -} -``` - -启动项目后访问:```http://localhost:8002/hello```,返回:```hello im dev update```说明已经正确的从server端获取到了参数。到此一个完整的服务端提供配置服务,客户端获取配置参数的例子就完成了。 - -我们在进行一些小实验,手动修改```neo-config-dev.properties```中配置信息为:```neo.hello=hello im dev update1```提交到github,再次在浏览器访问```http://localhost:8002/hello```,返回:```neo.hello: hello im dev update```,说明获取的信息还是旧的参数,这是为什么呢?因为springboot项目只有在启动的时候才会获取配置文件的值,修改github信息后,client端并没有在次去获取,所以导致这个问题。如何去解决这个问题呢?留到下一章我们在介绍。 - -**[示例代码-github](https://github.com/ityouknow/spring-cloud-examples)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-cloud-examples)** - -------------- -**作者:纯洁的微笑** -**出处:[http://www.ityouknow.com/](http://www.ityouknow.com/springcloud/2017/05/22/springcloud-config-git.html)** -**版权归作者所有,转载请注明出处** - diff --git a/_posts/2017/2017-05-23-springcloud-config-svn-refresh.md b/_posts/2017/2017-05-23-springcloud-config-svn-refresh.md deleted file mode 100644 index cea099d0aa..0000000000 --- a/_posts/2017/2017-05-23-springcloud-config-svn-refresh.md +++ /dev/null @@ -1,169 +0,0 @@ ---- -layout: post -title: springcloud(七):配置中心svn示例和refresh -category: springcloud -tags: [springcloud] ---- - - -上一篇[springcloud(六):配置中心git示例](http://www.ityouknow.com/springcloud/2017/05/22/springcloud-config-git.html)留了一个小问题,当重新修改配置文件提交后,客户端获取的仍然是修改前的信息,这个问题我们先放下,待会再讲。国内很多公司都使用的svn来做代码的版本控制,我们先介绍以下如何使用svn+Spring Cloud Config来做配置中心。 - - -## svn版本 - -同样先示例server端的代码,基本步骤一样。 - -### 1、添加依赖 - -``` xml - - - org.springframework.cloud - spring-cloud-config-server - - - org.tmatesoft.svnkit - svnkit - - -``` - -需要多引入svnkitr包 - -### 2、配置文件 - -``` properties -server: - port: 8001 - -spring: - cloud: - config: - server: - svn: - uri: http://192.168.0.6/svn/repo/config-repo - username: username - password: password - default-label: trunk - profiles: - active: subversion - application: - name: spring-cloud-config-server -``` - -和git版本稍有区别,需要显示声明subversion. - -### 3、启动类 - -启动类没有变化,添加```@EnableConfigServer```激活对配置中心的支持 - -``` java -@EnableConfigServer -@SpringBootApplication -public class ConfigServerApplication { - - public static void main(String[] args) { - SpringApplication.run(ConfigServerApplication.class, args); - } -} -``` - -### 4、测试 - -**服务端测试** - -访问:```http://localhost:8001/neo-config-dev.properties```,返回:```neo.hello: hello im dev```,说明服务端可以正常读取到svn代码库中的配置信息。修改配置文件```neo-config-dev.properties```中配置信息为:```neo.hello=hello im dev update```,再次在浏览器访问```http://localhost:8001/neo-config-dev.properties```,返回:```neo.hello: hello im dev update```。说明server端会自动读取最新提交的内容 - - -**客户端测试** - -客户端直接使用上一篇示例项目```spring-cloud-config-client```来测试,配置基本不用变动。启动项目后访问:```http://localhost:8002/hello,返回:```hello im dev update``说明已经正确的从server端获取到了参数。同样修改svn配置并提交,再次访问```http://localhost:8002/hello```依然获取的是旧的信息,和git版本的问题一样。 - - -## refresh - -现在来解决上一篇的遗留问题,这个问题在svn版本中依然存在。Spring Cloud Config分服务端和客户端,服务端负责将git(svn)中存储的配置文件发布成REST接口,客户端可以从服务端REST接口获取配置。但客户端并不能主动感知到配置的变化,从而主动去获取新的配置。客户端如何去主动获取新的配置信息呢,springcloud已经给我们提供了解决方案,每个客户端通过POST方法触发各自的```/refresh```。 - -修改```spring-cloud-config-client```项目已到达可以refresh的功能。 - -### 1、添加依赖 - -``` xml - - org.springframework.boot - spring-boot-starter-actuator - -``` - -增加了```spring-boot-starter-actuator```包,```spring-boot-starter-actuator```是一套监控的功能,可以监控程序在运行时状态,其中就包括```/refresh```的功能。 - - -### 2、 开启更新机制 - -需要给加载变量的类上面加载```@RefreshScope```,在客户端执行```/refresh```的时候就会更新此类下面的变量值。 - -``` java -@RestController -@RefreshScope // 使用该注解的类,会在接到SpringCloud配置中心配置刷新的时候,自动将新的配置更新到该类对应的字段中。 -class HelloController { - - @Value("${neo.hello}") - private String hello; - - @RequestMapping("/hello") - public String from() { - return this.hello; - } -} -``` - - -### 3、测试 - -*springboot 1.5.X 以上默认开通了安全认证,所以需要在配置文件```application.properties```添加以下配置* - -``` properties -management.security.enabled=false -``` - -OK 这样就改造完了,以post请求的方式来访问```http://localhost:8002/refresh``` 就会更新修改后的配置文件。 - -我们再次来测试,首先访问```http://localhost:8002/hello```,返回:```hello im dev```,我将库中的值修改为```hello im dev update```。在win上面打开cmd执行```curl -X POST http://localhost:8002/refresh```,返回```["neo.hello"]```说明已经更新了```neo.hello```的值。我们再次访问```http://localhost:8002/hello```,返回:```hello im dev update```,客户端已经得到了最新的值。 - -每次手动刷新客户端也很麻烦,有没有什么办法只要提交代码就自动调用客户端来更新呢,github的webhook是一个好的办法。 - - -### 4、webhook - -WebHook是当某个事件发生时,通过发送http post请求的方式来通知信息接收方。Webhook来监测你在Github.com上的各种事件,最常见的莫过于push事件。如果你设置了一个监测push事件的Webhook,那么每当你的这个项目有了任何提交,这个Webhook都会被触发,这时Github就会发送一个HTTP POST请求到你配置好的地址。 - -如此一来,你就可以通过这种方式去自动完成一些重复性工作,比如,你可以用Webhook来自动触发一些持续集成(CI)工具的运作,比如Travis CI;又或者是通过 Webhook 去部署你的线上服务器。下图就是github上面的webhook配置。 - - -![](http://favorites.ren/assets/images/2017/springcloud/webhook.jpg) - - -- ```Payload URL``` :触发后回调的URL -- ```Content type``` :数据格式,两种一般使用json -- ```Secret``` :用作给POST的body加密的字符串。采用HMAC算法 -- ```events``` :触发的事件列表。 - -events事件类型 | 描述| ---- |--- -push | 仓库有push时触发。默认事件 -create | 当有分支或标签被创建时触发 -delete | 当有分支或标签被删除时触发 - - -> svn也有类似的hook机制,每次提交后会触发post-commit脚本,我们可以在这里写一些post请求 - -这样我们就可以利用hook的机制去触发客户端的更新,但是当客户端越来越多的时候hook支持的已经不够优雅,另外每次增加客户端都需要改动hook也是不现实的。其实Spring Cloud给了我们更好解决方案,后面文章来介绍。 - -**[示例代码-github](https://github.com/ityouknow/spring-cloud-examples)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-cloud-examples)** - -------------- -**作者:纯洁的微笑** -**出处:[http://www.ityouknow.com/](http://www.ityouknow.com/springcloud/2017/05/23/springcloud-config-svn-refresh.html)** -**版权归作者所有,转载请注明出处** diff --git a/_posts/2017/2017-05-25-springcloud-config-eureka.md b/_posts/2017/2017-05-25-springcloud-config-eureka.md deleted file mode 100644 index dbe9095b1c..0000000000 --- a/_posts/2017/2017-05-25-springcloud-config-eureka.md +++ /dev/null @@ -1,194 +0,0 @@ ---- -layout: post -title: springcloud(八):配置中心服务化和高可用 -category: springcloud -tags: [springcloud] ---- - - -在前两篇的介绍中,客户端都是直接调用配置中心的server端来获取配置文件信息。这样就存在了一个问题,客户端和服务端的耦合性太高,如果server端要做集群,客户端只能通过原始的方式来路由,server端改变IP地址的时候,客户端也需要修改配置,不符合springcloud服务治理的理念。springcloud提供了这样的解决方案,我们只需要将server端当做一个服务注册到eureka中,client端去eureka中去获取配置中心server端的服务既可。 - -这篇文章我们基于配置中心git版本的内容来改造 - - -## server端改造 - -### 1、添加依赖 - -``` xml - - - org.springframework.cloud - spring-cloud-config-server - - - org.springframework.cloud - spring-cloud-starter-eureka - - -``` - -需要多引入`spring-cloud-starter-eureka`包,来添加对eureka的支持。 - -### 2、配置文件 - -``` properties -server: -server: - port: 8001 -spring: - application: - name: spring-cloud-config-server - cloud: - config: - server: - git: - uri: https://github.com/ityouknow/spring-cloud-starter/ # 配置git仓库的地址 - search-paths: config-repo # git仓库地址下的相对地址,可以配置多个,用,分割。 - username: username # git仓库的账号 - password: password # git仓库的密码 -eureka: - client: - serviceUrl: - defaultZone: http://localhost:8000/eureka/ ## 注册中心eurka地址 -``` - -增加了eureka注册中心的配置 - -### 3、启动类 - -启动类添加`@EnableDiscoveryClient`激活对注册中心的支持 - -``` java -@EnableDiscoveryClient -@EnableConfigServer -@SpringBootApplication -public class ConfigServerApplication { - - public static void main(String[] args) { - SpringApplication.run(ConfigServerApplication.class, args); - } -} -``` - -这样server端的改造就完成了。先启动eureka注册中心,在启动server端,在浏览器中访问:`http://localhost:8000/` 就会看到server端已经注册了到注册中心了。 - - -![](http://favorites.ren/assets/images/2017/springcloud/eureka-config01.jpg) - - -按照上篇的测试步骤对server端进行测试服务正常。 - - -## 客户端改造 - - -### 1、添加依赖 - -``` xml - - - org.springframework.cloud - spring-cloud-starter-config - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.cloud - spring-cloud-starter-eureka - - - org.springframework.boot - spring-boot-starter-test - test - - -``` - -需要多引入`spring-cloud-starter-eureka`包,来添加对eureka的支持。 - -### 2、配置文件 - -``` properties -spring.application.name=spring-cloud-config-client -server.port=8002 - -spring.cloud.config.name=neo-config -spring.cloud.config.profile=dev -spring.cloud.config.label=master -spring.cloud.config.discovery.enabled=true -spring.cloud.config.discovery.serviceId=spring-cloud-config-server - -eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ -``` - -主要是去掉了`spring.cloud.config.uri`直接指向server端地址的配置,增加了最后的三个配置: - -- `spring.cloud.config.discovery.enabled` :开启Config服务发现支持 -- `spring.cloud.config.discovery.serviceId` :指定server端的name,也就是server端`spring.application.name`的值 -- `eureka.client.serviceUrl.defaultZone` :指向注册中心的地址 - -这三个配置文件都需要放到`bootstrap.properties`的配置中 - -### 3、启动类 - -启动类添加`@EnableDiscoveryClient`激活对配置中心的支持 - -``` java -@EnableDiscoveryClient -@SpringBootApplication -public class ConfigClientApplication { - - public static void main(String[] args) { - SpringApplication.run(ConfigClientApplication.class, args); - } -} -``` - -启动client端,在浏览器中访问:`http://localhost:8000/` 就会看到server端和client端都已经注册了到注册中心了。 - - -![](http://favorites.ren/assets/images/2017/springcloud/eureka-config02.jpg) - - -## 高可用 - -为了模拟生产集群环境,我们改动server端的端口为8003,再启动一个server端来做服务的负载,提供高可用的server端支持。 - - -![](http://favorites.ren/assets/images/2017/springcloud/eureka-config03.jpg) - -如上图就可发现会有两个server端同时提供配置中心的服务,防止某一台down掉之后影响整个系统的使用。 - -我们先单独测试服务端,分别访问:`http://localhost:8001/neo-config/dev`、`http://localhost:8003/neo-config/dev`返回信息: - -``` -{ - "name": "neo-config", - "profiles": [ - "dev" - ], - "label": null, - "version": null, - "state": null, - "propertySources": [ - { - "name": "https://github.com/ityouknow/spring-cloud-starter/config-repo/neo-config-dev.properties", - "source": { - "neo.hello": "hello im dev" - } - } - ] -} -``` - -说明两个server端都正常读取到了配置信息。 - -再次访问:`http://localhost:8002/hello`,返回:`hello im dev update`。说明客户端已经读取到了server端的内容,我们随机停掉一台server端的服务,再次访问`http://localhost:8002/hello`,返回:`hello im dev update`,说明达到了高可用的目的。 - - -**[示例代码-github](https://github.com/ityouknow/spring-cloud-examples)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-cloud-examples)** diff --git a/_posts/2017/2017-05-26-springcloud-config-eureka-bus.md b/_posts/2017/2017-05-26-springcloud-config-eureka-bus.md deleted file mode 100644 index ec009d60d5..0000000000 --- a/_posts/2017/2017-05-26-springcloud-config-eureka-bus.md +++ /dev/null @@ -1,306 +0,0 @@ ---- -layout: post -title: springcloud(九):配置中心和消息总线(配置中心终结版) -category: springcloud -tags: [springcloud] ---- - - -我们在[springcloud(七):配置中心svn示例和refresh](http://www.ityouknow.com/springcloud/2017/05/23/springcloud-config-svn-refresh.html)中讲到,如果需要客户端获取到最新的配置信息需要执行```refresh```,我们可以利用webhook的机制每次提交代码发送请求来刷新客户端,当客户端越来越多的时候,需要每个客户端都执行一遍,这种方案就不太适合了。使用Spring Cloud Bus可以完美解决这一问题。 - - -## Spring Cloud Bus - -Spring cloud bus通过轻量消息代理连接各个分布的节点。这会用在广播状态的变化(例如配置变化)或者其他的消息指令。Spring bus的一个核心思想是通过分布式的启动器对spring boot应用进行扩展,也可以用来建立一个多个应用之间的通信频道。目前唯一实现的方式是用AMQP消息代理作为通道,同样特性的设置(有些取决于通道的设置)在更多通道的文档中。 - -Spring cloud bus被国内很多都翻译为消息总线,也挺形象的。大家可以将它理解为管理和传播所有分布式项目中的消息既可,其实本质是利用了MQ的广播机制在分布式的系统中传播消息,目前常用的有Kafka和RabbitMQ。利用bus的机制可以做很多的事情,其中配置中心客户端刷新就是典型的应用场景之一,我们用一张图来描述bus在配置中心使用的机制。 - - -![](http://favorites.ren/assets/images/2017/springcloud/configbus1.jpg) - -根据此图我们可以看出利用Spring Cloud Bus做配置更新的步骤: - -- 1、提交代码触发post给客户端A发送bus/refresh -- 2、客户端A接收到请求从Server端更新配置并且发送给Spring Cloud Bus -- 3、Spring Cloud bus接到消息并通知给其它客户端 -- 4、其它客户端接收到通知,请求Server端获取最新配置 -- 5、全部客户端均获取到最新的配置 - - -## 项目示例 - - -我们选择上一篇文章[springcloud(八):配置中心服务化和高可用](http://www.ityouknow.com/springcloud/2017/05/25/springcloud-config-eureka.html)版本的[示例代码](https://github.com/ityouknow/spring-cloud-starter/tree/master/spring-cloud-config-eureka)来改造,MQ我们使用RabbitMQ来做示例。 - -**客户端spring-cloud-config-client改造** - -### 1、添加依赖 - -``` xml - - org.springframework.cloud - spring-cloud-starter-bus-amqp - -``` - -需要多引入```spring-cloud-starter-bus-amqp```包,增加对消息总线的支持 - -### 2、配置文件 - -``` properties -## 刷新时,关闭安全验证 -management.security.enabled=false -## 开启消息跟踪 -spring.cloud.bus.trace.enabled=true - -spring.rabbitmq.host=192.168.9.89 -spring.rabbitmq.port=5672 -spring.rabbitmq.username=admin -spring.rabbitmq.password=123456 -``` - -配置文件需要增加RebbitMq的相关配置,这样客户端代码就改造完成了。 - -### 3、测试 - -依次启动spring-cloud-eureka、spring-cloud-config-server、spring-cloud-config-client项目,在启动spring-cloud-config-client项目的时候我们会发现启动日志会输出这样的一条记录。 - -``` -2017-05-26 17:05:38.568 INFO 21924 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/bus/refresh],methods=[POST]}" onto public void org.springframework.cloud.bus.endpoint.RefreshBusEndpoint.refresh(java.lang.String) -``` - -说明客户端已经具备了消息总线通知的能力了,为了更好的模拟消息总线的效果,我们更改客户端spring-cloud-config-client项目的端口为8003、8004依次启动,这样测试环境就准备好了。启动后eureka后台效果图如下: - - -![](http://favorites.ren/assets/images/2017/springcloud/configbus3.jpg) - -我们先分别测试一下服务端和客户端是否正确运行,访问:```http://localhost:8001/neo-config/dev```,返回信息: - -``` -{ - "name": "neo-config", - "profiles": [ - "dev" - ], - "label": null, - "version": null, - "state": null, - "propertySources": [ - { - "name": "https://github.com/ityouknow/spring-cloud-starter/config-repo/neo-config-dev.properties", - "source": { - "neo.hello": "hello im dev" - } - } - ] -} -``` - -说明server端都正常读取到了配置信息。 - -依次访问:```http://localhost:8002/hello```、```http://localhost:8003/hello```、```http://localhost:8004/hello```,返回:```hello im dev```。说明客户端都已经读取到了server端的内容。 - -现在我们更新```neo-config-dev.properties``` 中```neo.hello```的值为```hello im dev update```并提交到代码库中,访问:```http://localhost:8002/hello``` 依然返回```hello im dev```。我们对端口为8002的客户端发送一个```/bus/refresh```的post请求。在win下使用下面命令来模拟webhook. - -``` -curl -X POST http://localhost:8002/bus/refresh -``` - -执行完成后,依次访问:```http://localhost:8002/hello```、```http://localhost:8003/hello```、```http://localhost:8004/hello```,返回:```hello im dev update```。说明三个客户端均已经拿到了最新配置文件的信息,这样我们就实现了图一中的示例。 - - -## 改进版本 - -在上面的流程中,我们已经到达了利用消息总线触发一个客户端```bus/refresh```,而刷新所有客户端的配置的目的。但这种方式并不优雅。原因如下: - -- 打破了微服务的职责单一性。微服务本身是业务模块,它本不应该承担配置刷新的职责。 -- 破坏了微服务各节点的对等性。 -- 有一定的局限性。例如,微服务在迁移时,它的网络地址常常会发生变化,此时如果想要做到自动刷新,那就不得不修改WebHook的配置。 - - -因此我们将上面的架构模式稍微改变一下 - - - -![](http://favorites.ren/assets/images/2017/springcloud/configbus2.jpg) - -这时Spring Cloud Bus做配置更新步骤如下: - -- 1、提交代码触发post请求给bus/refresh -- 2、server端接收到请求并发送给Spring Cloud Bus -- 3、Spring Cloud bus接到消息并通知给其它客户端 -- 4、其它客户端接收到通知,请求Server端获取最新配置 -- 5、全部客户端均获取到最新的配置 - - -这样的话我们在server端的代码做一些改动,来支持```bus/refresh``` - - -### 1、添加依赖 - -``` xml - - - org.springframework.cloud - spring-cloud-config-server - - - org.springframework.cloud - spring-cloud-starter-bus-amqp - - - org.springframework.cloud - spring-cloud-starter-eureka - - -``` - -需要多引入```spring-cloud-starter-bus-amqp```包,增加对消息总线的支持 - -### 2、配置文件 - -``` properties -server: - port: 8001 -spring: - application: - name: spring-cloud-config-server - cloud: - config: - server: - git: - uri: https://github.com/ityouknow/spring-cloud-starter/ # 配置git仓库的地址 - search-paths: config-repo # git仓库地址下的相对地址,可以配置多个,用,分割。 - username: username # git仓库的账号 - password: password # git仓库的密码 - rabbitmq: - host: 192.168.0.6 - port: 5672 - username: admin - password: 123456 - -eureka: - client: - serviceUrl: - defaultZone: http://localhost:8000/eureka/ ## 注册中心eurka地址 - - -management: - security: - enabled: false -``` - -配置文件增加RebbitMq的相关配置,关闭安全验证。这样server端代码就改造完成了。 - -### 3、测试 - -依次启动spring-cloud-eureka、spring-cloud-config-server、spring-cloud-config-client项目,改动spring-cloud-config-client项目端口为8003、8004依次启动。测试环境准备完成。 - - -按照上面的测试方式,访问server端和三个客户端测试均可以正确返回信息。同样修改```neo-config-dev.properties``` 中```neo.hello```的值为```hello im dev update```并提交到代码库中。在win下使用下面命令来模拟webhook触发server端```bus/refresh```. - -``` -curl -X POST http://localhost:8001/bus/refresh -``` - -执行完成后,依次访问:```http://localhost:8002/hello```、```http://localhost:8003/hello```、```http://localhost:8004/hello```,返回:```hello im dev update```。说明三个客户端均已经拿到了最新配置文件的信息,这样我们就实现了上图中的示例。 - - - -## 其它 - - -### 局部刷新 - -某些场景下(例如灰度发布),我们可能只想刷新部分微服务的配置,此时可通过```/bus/refresh```端点的destination参数来定位要刷新的应用程序。 - -例如:```/bus/refresh?destination=customers:8000```,这样消息总线上的微服务实例就会根据destination参数的值来判断是否需要要刷新。其中,```customers:8000```指的是各个微服务的ApplicationContext ID。 - -destination参数也可以用来定位特定的微服务。例如:```/bus/refresh?destination=customers:**```,这样就可以触发customers微服务所有实例的配置刷新。 - - - -### 跟踪总线事件 - -一些场景下,我们可能希望知道Spring Cloud Bus事件传播的细节。此时,我们可以跟踪总线事件(RemoteApplicationEvent的子类都是总线事件)。 - -跟踪总线事件非常简单,只需设置```spring.cloud.bus.trace.enabled=true```,这样在```/bus/refresh```端点被请求后,访问```/trace```端点就可获得类似如下的结果: - - -``` -{ - "timestamp": 1495851419032, - "info": { - "signal": "spring.cloud.bus.ack", - "type": "RefreshRemoteApplicationEvent", - "id": "c4d374b7-58ea-4928-a312-31984def293b", - "origin": "stores:8002", - "destination": "*:**" - } - }, - { - "timestamp": 1495851419033, - "info": { - "signal": "spring.cloud.bus.sent", - "type": "RefreshRemoteApplicationEvent", - "id": "c4d374b7-58ea-4928-a312-31984def293b", - "origin": "spring-cloud-config-client:8001", - "destination": "*:**" - } - }, - { - "timestamp": 1495851422175, - "info": { - "signal": "spring.cloud.bus.ack", - "type": "RefreshRemoteApplicationEvent", - "id": "c4d374b7-58ea-4928-a312-31984def293b", - "origin": "customers:8001", - "destination": "*:**" - } -} -``` - -这个日志显示了```customers:8001```发出了RefreshRemoteApplicationEvent事件,广播给所有的服务,被```customers:9000```和```stores:8081```接受到了。想要对接受到的消息自定义自己的处理方式的话,可以添加```@EventListener```注解的AckRemoteApplicationEvent和SentApplicationEvent类型到你自己的应用中。或者到TraceRepository类中,直接处理数据。 - -这样,我们就可清晰地知道事件的传播细节。 - - -## ```/bus/refresh``` BUG - -```/bus/refresh``` 有一个很严重的BUG,一直没有解决:对客户端执行```/bus/refresh```之后,挂到总线的上的客户端都会从Eureka注册中心撤销登记;如果对server端执行```/bus/refresh```,server端也会从Eureka注册中心撤销登记。再用白话解释一下,就是本来人家在Eureka注册中心注册的好好的,只要你对着它执行一次```/bus/refresh```,立刻就会从Euraka中挂掉。 - - -其实这个问题挺严重的,本来你利用```/bus/refresh```给所有的节点来更新配置信息呢,结果把服务从Euraka中给搞掉了,那么如果别人需要调用客户端的服务的时候就直接歇菜了。不知道国内有童鞋公司在生产中用到这个功能没有,用了不就很惨烈。在网上搜索了一下,国内网友和国外网友都遇到过很多次,但是一直没有解决,很幸运就是我在写这篇文章的**前三天**,Netflix修复了这个问题,使用Spring Cloud最新版本的包就可以解决这个问题。由此也可以发现Spring Cloud还在快速的发展中,最新的版本可能也会有一些不稳定性,可见路漫漫而修远兮。 - - -在pom中使用Spring Cloud的版本,解决这个bug. - -``` xml - - UTF-8 - UTF-8 - 1.8 - Dalston.SR1 - -``` - -主要是这句:``` Dalston.SR1``` ,详情可以参考本文示例中的代码 - - -BUG的讨论和解决过程可以看github上面这两个issue: - -- [/bus/refresh causes instances registered in Eureka Server disappeared #692](https://github.com/spring-cloud/spring-cloud-config/issues/692) -- [Making POST on 'refresh' permamently deregisters the service from Eureka #1857](https://github.com/spring-cloud/spring-cloud-netflix/issues/1857) - - - -参考: - - -[Config Server——使用Spring Cloud Bus自动刷新配置](http://www.itmuch.com/spring-cloud/spring-cloud-bus-auto-refresh-configuration/) - -[Spring Cloud构建微服务架构(七)消息总线](http://blog.didispace.com/springcloud7/) - -**[示例代码-github](https://github.com/ityouknow/spring-cloud-examples)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-cloud-examples)** diff --git a/_posts/2017/2017-06-01-gateway-service-zuul.md b/_posts/2017/2017-06-01-gateway-service-zuul.md deleted file mode 100644 index d05efc174b..0000000000 --- a/_posts/2017/2017-06-01-gateway-service-zuul.md +++ /dev/null @@ -1,210 +0,0 @@ ---- -layout: post -title: springcloud(十):服务网关zuul初级篇 -category: springcloud -tags: [springcloud] ---- - -前面的文章我们介绍了,Eureka用于服务的注册于发现,Feign支持服务的调用以及均衡负载,Hystrix处理服务的熔断防止故障扩散,Spring Cloud Config服务集群配置中心,似乎一个微服务框架已经完成了。 - -我们还是少考虑了一个问题,外部的应用如何来访问内部各种各样的微服务呢?在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务。当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分发给后台服务端。 - - -## 为什么需要API Gateway - - -1、简化客户端调用复杂度 - - -在微服务架构模式下后端服务的实例数一般是动态的,对于客户端而言很难发现动态改变的服务实例的访问地址信息。因此在基于微服务的项目中为了简化前端的调用逻辑,通常会引入API Gateway作为轻量级网关,同时API Gateway中也会实现相关的认证逻辑从而简化内部服务之间相互调用的复杂度。 - - -![](http://favorites.ren/assets/images/2017/springcloud/api_gateway.png) - - -2、数据裁剪以及聚合 - -通常而言不同的客户端对于显示时对于数据的需求是不一致的,比如手机端或者Web端又或者在低延迟的网络环境或者高延迟的网络环境。 - -因此为了优化客户端的使用体验,API Gateway可以对通用性的响应数据进行裁剪以适应不同客户端的使用需求。同时还可以将多个API调用逻辑进行聚合,从而减少客户端的请求数,优化客户端用户体验 - - -3、多渠道支持 - -当然我们还可以针对不同的渠道和客户端提供不同的API Gateway,对于该模式的使用由另外一个大家熟知的方式叫Backend for front-end, 在Backend for front-end模式当中,我们可以针对不同的客户端分别创建其BFF,进一步了解BFF可以参考这篇文章:[Pattern: Backends For Frontends](http://samnewman.io/patterns/architectural/bff/) - - -![](http://favorites.ren/assets/images/2017/springcloud/bff.png) - - -4、遗留系统的微服务化改造 - -对于系统而言进行微服务改造通常是由于原有的系统存在或多或少的问题,比如技术债务,代码质量,可维护性,可扩展性等等。API Gateway的模式同样适用于这一类遗留系统的改造,通过微服务化的改造逐步实现对原有系统中的问题的修复,从而提升对于原有业务响应力的提升。通过引入抽象层,逐步使用新的实现替换旧的实现。 - - -![](http://favorites.ren/assets/images/2017/springcloud/bff-process.png) - -> 在Spring Cloud体系中, Spring Cloud Zuul就是提供负载均衡、反向代理、权限认证的一个API gateway。 - -## Spring Cloud Zuul - -Spring Cloud Zuul路由是微服务架构的不可或缺的一部分,提供动态路由,监控,弹性,安全等的边缘服务。Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器。 - - -下面我们通过代码来了解Zuul是如何工作的 - -### 简单使用 - -1、添加依赖 - -``` xml - - org.springframework.cloud - spring-cloud-starter-zuul - -``` - -引入```spring-cloud-starter-zuul```包 - -2、配置文件 - -``` properties -spring.application.name=gateway-service-zuul -server.port=8888 - -#这里的配置表示,访问/it/** 直接重定向到http://www.ityouknow.com/** -zuul.routes.baidu.path=/it/** -zuul.routes.baidu.url=http://www.ityouknow.com/ -``` - -3、启动类 - -``` java -@SpringBootApplication -@EnableZuulProxy -public class GatewayServiceZuulApplication { - - public static void main(String[] args) { - SpringApplication.run(GatewayServiceZuulApplication.class, args); - } -} -``` - -启动类添加```@EnableZuulProxy```,支持网关路由。 - -史上最简单的zuul案例就配置完了 - - -4、测试 - -启动```gateway-service-zuul-simple```项目,在浏览器中访问:```http://localhost:8888/it/spring-cloud```,看到页面返回了:```http://www.ityouknow.com/spring-cloud ``` 页面的信息,如下: - - -![](http://favorites.ren/assets/images/2017/springcloud/zuul-01.jpg) - - -我们以前面文章的示例代码```spring-cloud-producer```为例来测试请求的重定向,在配置文件中添加: - -``` properties -zuul.routes.hello.path=/hello/** -zuul.routes.hello.url=http://localhost:9000/ -``` - -启动```spring-cloud-producer```,重新启动```gateway-service-zuul-simple```,访问:```http://localhost:8888/hello/hello?name=%E5%B0%8F%E6%98%8E```,返回:```hello 小明,this is first messge``` - -说明访问```gateway-service-zuul-simple```的请求自动转发到了```spring-cloud-producer```,并且将结果返回。 - - -### 服务化 - -通过url映射的方式来实现zull的转发有局限性,比如每增加一个服务就需要配置一条内容,另外后端的服务如果是动态来提供,就不能采用这种方案来配置了。实际上在实现微服务架构时,服务名与服务实例地址的关系在eureka server中已经存在了,所以只需要将Zuul注册到eureka server上去发现其他服务,就可以实现对serviceId的映射。 - -我们结合示例来说明,在上面示例项目```gateway-service-zuul-simple```的基础上来改造。 - - -1、添加依赖 - -``` xml - - org.springframework.cloud - spring-cloud-starter-eureka - -``` - -增加```spring-cloud-starter-eureka```包,添加对eureka的支持。 - -2、配置文件 - -配置修改为: - -``` properties -spring.application.name=gateway-service-zuul -server.port=8888 - -zuul.routes.api-a.path=/producer/** -zuul.routes.api-a.serviceId=spring-cloud-producer - -eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ -``` - -3、测试 - -依次启动 ```spring-cloud-eureka```、 ```spring-cloud-producer```、```gateway-service-zuul-eureka```,访问:```http://localhost:8888/producer/hello?name=%E5%B0%8F%E6%98%8E```,返回:```hello 小明,this is first messge``` - -说明访问```gateway-service-zuul-eureka```的请求自动转发到了```spring-cloud-producer```,并且将结果返回。 - - -为了更好的模拟服务集群,我们复制```spring-cloud-producer```项目改为```spring-cloud-producer-2```,修改```spring-cloud-producer-2```项目端口为9001,controller代码修改如下: - -``` java -@RestController -public class HelloController { - - @RequestMapping("/hello") - public String index(@RequestParam String name) { - return "hello "+name+",this is two messge"; - } -} -``` - -修改完成后启动```spring-cloud-producer-2```,重启```gateway-service-zuul-eureka```。测试多次访问```http://localhost:8888/producer/hello?name=%E5%B0%8F%E6%98%8E```,依次返回: - -``` -hello 小明,this is first messge -hello 小明,this is two messge -hello 小明,this is first messge -hello 小明,this is two messge -... -``` - -说明通过zuul成功调用了producer服务并且做了均衡负载。 - - -**网关的默认路由规则** - -但是如果后端服务多达十几个的时候,每一个都这样配置也挺麻烦的,spring cloud zuul已经帮我们做了默认配置。默认情况下,Zuul会代理所有注册到Eureka Server的微服务,并且Zuul的路由规则如下:```http://ZUUL_HOST:ZUUL_PORT/微服务在Eureka上的serviceId/**```会被转发到serviceId对应的微服务。 - - -我们注销掉```gateway-service-zuul-eureka```项目中关于路由的配置: - -``` properties -#zuul.routes.api-a.path=/producer/** -#zuul.routes.api-a.serviceId=spring-cloud-producer -``` - -重新启动后,访问```http://localhost:8888/spring-cloud-producer/hello?name=%E5%B0%8F%E6%98%8E```,测试返回结果和上述示例相同,说明Spring cloud zuul默认已经提供了转发功能。 - -到此zuul的基本使用我们就介绍完了。关于zuul更高级使用,我们下篇再来介绍。 - -参考: - -[API网关那些儿](http://yunlzheng.github.io/2017/03/14/the-things-about-api-gateway/) - -**[示例代码-github](https://github.com/ityouknow/spring-cloud-examples)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-cloud-examples)** - -------------- -**作者:纯洁的微笑** -**出处:[http://www.ityouknow.com/](http://www.ityouknow.com/springcloud/2017/06/01/gateway-service-zuul.html)** -**版权归作者所有,转载请注明出处** diff --git a/_posts/2017/2017-06-06-book-list.md b/_posts/2017/2017-06-06-book-list.md deleted file mode 100644 index 268ff9e8ed..0000000000 --- a/_posts/2017/2017-06-06-book-list.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -layout: post -title: 编程科普书籍推荐 -category: book -tags: [book] -lock: need ---- - -古人云:“书中自有黄金屋,书中自有颜如玉。” 说明我们的老祖先在很久之前就认识到读书的价值。 - -在古代读书还真是挺奢侈的一件事情,第一,书太少了,古代其实总共就没有多少书籍;第二书籍不容保存,全部使用竹卷来书写,如果要读现在的一本书,相当于古代读了一牛车的车,不知道是不是这个原因古代人喜欢用文言文和诗词,可以让几个词语代表很多意思,所以在古代能读上书的基本都是富人了。 - -到了近代,也就是在互联网没有出现的时候,是读书最好的年代,书籍是很多人了解外面世界的唯一途径,那时书籍也不是特别丰富,所以大家也不挑,拿到一本就专心致志的读起来,也不管对自己有用还是无用,没有太多的功利性。当时读书要不是为了增长见识、就是为了消磨时间而已。人们之间经常的借书还书,慢慢的发展成为书友的一种特殊人际关系,有的还会给陌生的人写信成为笔友。 - -到了互联网发展起来的时候,特别是自媒体发展火爆之后,读书已经成为了人们的二等选择,甚至很多的技术人员会说,我不需要买任何的一本书,因为互联网上有我需要的任何东西,有一段时间我也是这样认为的。在现在生活压力越来越大的情况下,人们的心态会发生一些变化,很难平静下来或者沉下心来好好的去看一本书,另外人们的时间越来越碎片化,很难有整段的时间用来读书,甚至很多大V会推荐碎片化阅读,碎片化学习、碎片化时间管理等。 - -那么当今社会读书的意义是什么?因为我是搞技术的,首先从技术的角度来看为什么要读书,如果需要系统全面的了解一门知识的话,需要去读书。比如Mongodb的使用,基本的增删改查网上的例子一大堆,但是当我们需要深入或者全面去了解MongoDB的时候,网上的知识就比较凌乱或者太碎片了,我们只需要看一本《MongoDB权威指南》就立刻在脑海中对MongoDB有一个全方位的了解,如:MongoDB设计的原理是什么,如何监控,MapReduce可以解决什么样的问题等等。 - -对于搞技术的同学,特别建议大家不要只看技术书籍,更多应该关注其它方面的内容。读科普类的书籍可以让你了解世界的广阔、时间的永恒,代表作:人类简史、智能时代;读文学类的书籍可以认识人类思想艺术的精华,代表作:三国演义、平凡的世界;读网络小说可以让你了解人的想象力居然可以如此的丰富,代表作:盗墓笔记、鬼吹灯。太多了,不再一一举例了,作为技术人员跳出自己的视角去看待问题,可能就会有不同的收获,最重要的是我们的生活不只是代码。 - -读书多了久了,读过的书会改变你的容颜,改变你的气质和修养,你的举止和谈吐也会受到读过书籍的影响,很可能你都没有意识到,这是一种潜移默化的行为。读书也是打破自己局限性最廉价,最有效的途径之一,可以参考我前期文章[思维的局限](http://www.ityouknow.com/life/2017/05/19/Limitations-of-thinking.html)。但读书也不是盲目的,一定要找适合自己的,经典的书籍,经典书籍永不过时。适合是第一,经典是第二,再怎么经典不适合自己也没有用,看了也没有感悟,如果书读了一段时间不感冒可以立刻放弃,那么多书总有适合自己的,不需要勉强自己同时又浪费了时间。 - -下面推荐一些,我个人喜好的几本书籍 - - -## 技术类 - -作为技术的同学,总会感觉有一段时间内,技术遇到了瓶颈,总是不能提高,这个时候就需要多看技术书籍了。因为我本身是搞Java的,所以看的技术书籍大都是关于Java类的。 - -### [深入理解Java虚拟机](https://book.douban.com/subject/6522893/) - - -![](http://favorites.ren/assets/images/2017/book/jvm.jpg) - -是关于JVM一本好书,基本上也是了解JVM必看的一本书籍,文章写的很详细和专业,但是也有一些章节比较难懂,大家可以根据自己的需求选择感兴趣的章节来阅读。还有一本[《Java性能权威指南》](https://book.douban.com/subject/26740520/)也是一本不错的选择,但是还么来得及细看。 - -核心内容:Java类的加载机制、JVM内存结构、垃圾回收原理、垃圾回收算法、内存溢出和调优、程序编译与代码优化等。 - - -### [Java并发编程实战](https://book.douban.com/subject/10484692/) - - -![](http://favorites.ren/assets/images/2017/book/JavaConcurrency.jpg) - -被大家评论为Java并发的圣经。但是这本书并不是人人都适合读,书中对于并发的理论性知识描述的特别多,也会从最根本的底层来解释并发的原理。相比于这本书我更推荐一个人的博客,[《深入浅出 Java Concurrency》](http://www.blogjava.net/xylz/archive/2010/07/08/325587.html) 作者重点介绍了JUC(ava.util.concurrent)中各类以及常用的使用方法,特别偏重实践。因此本书+此博客结合起来学习Java并发是最好的方案了。 - -### [Effective java 中文版](https://book.douban.com/subject/3360807/) - - -![](http://favorites.ren/assets/images/2017/book/Effective.jpg) - -本书介绍了在Java编程中78条极具实用价值的经验规则,这些经验规则涵盖了大多数开发人员每天所面临的问题的解决方案。通过对Java平台设计专家所使用的技术的全面描述,揭示了应该做什么,不应该做什么才能产生清晰、健壮和高效的代码。本书中的每条规则都以简短、独立的小文章形式出现,并通过例子代码加以进一步说明。java经典书籍之一。 - -### [Spring揭秘](https://book.douban.com/subject/3897837/) - - -![](http://favorites.ren/assets/images/2017/book/springSecret.jpg) - -搞Java的如果不了解Spring,那么你基本不用再Java这个圈子里面混了,Spring发展到现在已经成Java程序员谁都无法绕过的框架了,关键Spring做的确实太牛了。这本书主要描述了Spring框架原理和使用,先介绍了Spring最核心的两块内容Sping IOC和Spring AOP概述及其实现机制,接下来介绍了Spring ORM框架的支持,Spring MVC的使用以及原理,最后介绍了Spring对j2ee的支持。基本上Spring最核心的部分都有介绍,如果你想了解Spring这绝对是一本好书。 - - -### [重构](https://book.douban.com/subject/4262627/) - - -![](http://favorites.ren/assets/images/2017/book/Refactoring.jpg) - -这应该是代码重构中,最优秀的一本书籍了。重构,一言以蔽之,就是在不改变外部行为的前提下,有条不紊地改善代码。本书通过详细介绍了几十中实战的重构方式,书中以Java代码为例来举例,但不仅仅于Java,其它编程语言也都可以参考。其实我一直认为:好的代码不是写出来的,而是重构出来的。重构是提高个人编程能力的重要手段之一,不断的去回顾自己写过的代码,看还有没有重构提升的空间。 - - -### [研磨设计模式](https://book.douban.com/subject/5343318/) - - -![](http://favorites.ren/assets/images/2017/book/pattern.jpg) - -设计模式是什么呢?Patterns,顾名思义,具有某种重复性规律的方案。Design Patterns,就是设计过程中可以反复使用的、可以解决特定问题的设计方法。可以这样比喻:设计模式就是古代武林中精妙的招式(武林秘籍),其实也是一种解决问题的思想,前辈根据经验总结了23种武林招式,在某种场景中使用某种设计模式必定会事半功倍,少趟很多坑。另外特别推荐csdn的一个博客[Java之美[从菜鸟到高手演变]之设计模式](http://blog.csdn.net/zhangerqing/article/details/8194653),例子浅显易懂,非常适合快速入门型的学习。 - -《研磨设计模式》详细介绍23设计模式中的一本书,书本比较厚,代码示例也比较多,可以作为了解设计模式的一本读物。 - - -## 科普类 - -### [智能时代](https://book.douban.com/subject/26838557/) - - -![](http://favorites.ren/assets/images/2017/book/Intelligence.jpg) - -吴军老师新作,我刚刚看完的一本书,吴军老师知识特别的渊博,从各个角度阐述了对人工智能的理解,开始讲了人工智能的60年来主要的发展历程,以及为什么现在是人工智能时代的爆发点。从思维的角度来分析人工智能给人民会带来的影响,很多事情的改变最先是思维的改变,人工智能也是如此。文中指出人工智能会像蒸汽机、电、信息革命一样会给人类带来大的历史变革,在商业上也会带来非常大的影响,未来只有2%的人会参与其中,我们多数人只是会变成使用大数据的人。 - -这本书会让我们对人工智能有一个总体的了解,在思维上对人工智能会有一个不同的认识,好书,强烈推荐。 - - -### [浪潮之巅](https://book.douban.com/subject/6709783/) - - -![](http://favorites.ren/assets/images/2017/book/wave.jpg) - -从一百年前算起,AT&T 公司、IBM 公司、苹果公司、英特尔公司、微软公司、思科公司、雅虎公司和Google公司都先后被幸运地推到了浪尖。虽然,它们来自不同的领域,中间有些已经衰落或正在衰落,但是它们都极度辉煌过。本书系统地介绍了这些公司成功的本质原因及科技工业一百多年的发展。 - -浪潮之巅,其实就一本IT行业巨头发展史,最初写于Google的黑板报,后来流行后整理成书,是了解IT行业发展的一本科普类书籍。 - - -### [大数据时代](https://book.douban.com/subject/20429677/) - - -![](http://favorites.ren/assets/images/2017/book/bigdata.jpg) - -大数据带来的信息风暴正在变革我们的生活、工作和思维,大数据开启了一次重大的时代转型,并用三个部分讲述了大数据时代的思维变革、商业变革和管理变革。 -大数据时代最大的转变就是,放弃对因果关系的渴求,而取而代之关注相关关系。也就是说只要知道“是什么”,而不需要知道“为什么”。这就颠覆了千百年来人类的思维惯例,对人类的认知和与世界交流的方式提出了全新的挑战。 - -本书主要介绍,大数据对我们生活、工作、思维的影响 - - -### [科技之巅](https://book.douban.com/subject/26891160/) - - -![](http://favorites.ren/assets/images/2017/book/science.jpg) - -《麻省理工科技评论》从2001年开始,每年都会公布“10大突破技术”,即TR10(Technology Review 10),并预测其大规模商业化的潜力,以及对人类生活和社会的重大影响。 -这些技术代表了当前世界科技的发展前沿和未来发展方向,集中反映了近年来世界科技发展的新特点和新趋势,将引领面向未来的研究方向。其中许多技术已经走向市场,主导着产业技术的发展,极大地推动了经济社会发展和科技创新。 - -### [人类简史](https://book.douban.com/subject/25985021/) - - -![](http://favorites.ren/assets/images/2017/book/Human.jpg) - -作者,尤瓦尔·赫拉利,1976年生,牛津大学历史学博士,现为耶路撒冷希伯来大学的历史系教授,青年怪才,全球瞩目的新锐历史学家。 - -这本书从人类的十万年前讲到现在,从兽欲,到物欲,从兽性、人性,到神性。是一本观点新颖、思维宏观、融会贯通的人类历史书籍,可以说它颠覆了所有人类简史类的文章。作者的视野太开阔了,从最初的人类——一种也没什么特别的动物到发展为智人,靠虚构的故事形成组织规模性合作,站上食物链的顶端,经历认知革命、农业革命、科技革命最后智人物种或将升级为神,书中内容包罗万象,涉猎面极广,融合金钱、帝国、宗教,以及作者独特的视角剖析。 - -### [未来简史](https://book.douban.com/subject/26943161/) - - -![](http://favorites.ren/assets/images/2017/book/future.jpg) - -当以大数据、人工智能为代表的科学技术发展的日益成熟,人类将面临着从进化到智人以来最大的一次改变,绝大部分人将沦为“无价值的群体”,只有少部分人能进化成特质发生改变的 “神人”。未来,人类将面临着三大问题:生物本身就是算法,生命是不断处理数据的过程;意识与智能的分离;拥有大数据积累的外部环境将比我们自己更了解自己。如何看待这三大问题,以及如何采取应对措施,将直接影响着人类未来的发展。尤瓦尔又一力作,还在拜读中。 - -*推荐大家阅读纸质书籍,为了便于大家试读我这里都有电子版本(大部分为非PDF格式),需要的话请在公众号里面回复:“书籍”* - -> 你最喜欢的一本书籍是什么?请推荐给我,给我留言。 - diff --git a/_posts/2017/2017-06-10-intelligent-age.md b/_posts/2017/2017-06-10-intelligent-age.md deleted file mode 100644 index e10bc60f26..0000000000 --- a/_posts/2017/2017-06-10-intelligent-age.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -layout: post -title: 人工智能发展史 -category: book -tags: [book] ---- - -我一直很好奇人工智能是如何提出来的,它背后有什么样的故事,在人工智能发展的这60年的时间中,又经历了什么?为什么现在才是人工智能的爆发点,未来人工智能又将走向何处?带着这样的问题我读了吴军博士的《智能时代》这本书,打开了我对人工智能的了解,这篇文章主要内容也来自于这本书。 - -我们这代人对人工智能的关注,来自于2016年AlphaGo大战世界著名围棋选手李世石,在比赛之前各方关注度非常高,国内各方媒体争相报道,预测这场比赛的结果,人们好奇人工智能现在智能到什么程度以及计算机如何和人下围棋,最终AlphaGo以4:1胜了李世明,大家都在感慨人工智能时代即将来临。仅仅过了一年,2017年5月27日AlphaGo的2.0版本3:0战胜围棋世界排名第一的柯洁九段,从此在AlphaGo面前已无人类对手。 - -计算机之所以能够战胜人类,是因为机器获得智能的方式和人类不同,它不是靠逻辑推理,而是靠大数据和算法。Google使用了几十万盘围棋高手之间的对弈的数据来训练AlphaGo,这是它获得所谓“智能”的原因。在计算方面,Google使用了几十万台服务器来训练AlphaGo下棋模型,并让不同的AlphaGo相互对弈上千万盘。第二个关键技术是启发式搜索算法-蒙特卡洛树搜索算法(英语:Monte Carlo tree search;简称:MCTS),它能将搜索的空间限制在非常有限的范围内,保证计算机能够快速找到好的下法。由此可见,下围棋这个看似智能型的问题,从本质上讲,是一个大数据和算法的问题。 - -说到人工智能,就不得不提计算机届的一个传奇人物:阿兰.图灵博士。1950年,图灵在《思想》(mind)杂志上发表了一篇《计算的机器和智能》的论文。在论文中,图灵既没有讲计算机怎样才能获得智能,也没有提出如何解决复杂问题的智能方法,知识提出了一个验证机器有无智能的的判别方法。 - - -![](http://favorites.ren/assets/images/2017/reading/TuringTest.png) - -让一台机器和一个人坐在幕后,让一个裁判同时与幕后的人和机器进行交流,如果这个裁判无法判断自己交流的对象是人还是机器,就说明这台机器有了和人同等的智能。就是大名鼎鼎的图灵测试。后来,计算机科学家对此进行了补充,如果计算机实现了下面几件事情中的一件,就可以认为它有图灵所说的那种智能: - -- 1、语音识别 -- 2、机器翻译 -- 3、文本的自动摘要或者写作 -- 4、战胜人类的国际象棋冠军 -- 5、自动回答问题 - -今天,计算机已经做到了上述的这几件事情,甚至还超额完成了任务,比如现在的围棋比国际象棋要高出6-8个数量级,当然,人类走到这一步并非一帆风顺,而是走了几十年的弯路。 - -## 人工智能的诞生:1943 - 1956 - -在20世纪40年代和50年代,来自不同领域(数学,心理学,工程学,经济学和政治学)的一批科学家开始探讨制造人工大脑的可能性。1956年,人工智能被确立为一门学科。 - -1956年的夏天,香农和一群年轻的学者在达特茅斯学院召开了一次头脑风暴式研讨会。会议的组织者是马文·闵斯基,约翰·麦卡锡和另两位资深科学家Claude Shannon以及Nathan Rochester,后者来自IBM。与会者包括Ray Solomonoff,Oliver Selfridge,Trenchard More,Arthur Samuel,Newell和Simon,他们中的每一位都将在AI研究的第一个十年中作出重要贡献。 - -会议虽然叫做“达特茅斯夏季人工智能研究会议”,其实它不同于今天我们召开几天的学术会议,因为一来没有什么可以报告的科研成果,二来这个会议持续了一个暑假。事实上,这是一次头脑风暴式的讨论会,这10位年轻的学者讨论的是当时计算机尚未解决,甚至尚未开展研究的问题,包括人工智能、自然语言处理和神经网络等。 - -会上纽厄尔和西蒙讨论了“逻辑理论家”,而麦卡锡则说服与会者接受“人工智能”一词作为本领域的名称。1956年达特矛斯会议上人工智能的名称和任务得以确定,同时出现了最初的成就和最早的一批研究者,因此这一事件被广泛承认为人工智能诞生的标志。 - - -![](http://favorites.ren/assets/images/2017/reading/Dartmouth_College_campus_60.jpeg) -60年前的达特茅斯大学 - - -## 黄金年代:1956 - 1974 - -达特茅斯会议之后的数年是大发现的时代。对许多人而言,这一阶段开发出的程序堪称神奇:计算机可以解决代数应用题,证明几何定理,学习和使用英语。当时大多数人几乎无法相信机器能够如此“智能”。研究者们在私下的交流和公开发表的论文中表达出相当乐观的情绪,认为具有完全智能的机器将在二十年内出现。ARPA(国防高等研究计划署)等政府机构向这一新兴领域投入了大笔资金。 - -第一代AI研究者们非常乐观,曾作出了如下预言: - -- 1958年,H. A. Simon,Allen Newell:“十年之内,数字计算机将成为国际象棋世界冠军。” “十年之内,数字计算机将发现并证明一个重要的数学定理。” -- 1965年,H. A. Simon:“二十年内,机器将能完成人能做到的一切工作。” -- 1967年,Marvin Minsky:“一代之内……创造‘人工智能’的问题将获得实质上的解决。” -- 1970年,Marvin Minsky:“在三到八年的时间里我们将得到一台具有人类平均智能的机器。” - -早期,人工智能使用传统的人工智能方法进行研究,什么是传统的人工智能研究呢?简单的讲,就是首先了解人类是如何产生智能的,然后让计算机按照人的思路去做。因此在语音识别、机器翻译等领域迟迟不能突破,人工智能研究陷入低谷。 - -## 第一次AI低谷:1974 - 1980 - -由于人工智能研究者们对项目难度评估不足,这除了导致承诺无法兑现外,还让人们当初的乐观期望遭到严重打击。到了70年代,人工智能开始遭遇批评,研究经费也被转移到那些目标明确的特定项目上。 - -1972年康奈尔大学的教授弗雷德.贾里尼克(Fred Jelinek)被要求到IBM做语音识别。在之前各个大学和研究这个问题已经花了20多年的时间,主流的研究方法有两个特点,一个是让计算机尽可能地模拟人的发音特点和听觉特征,一个是让计算机尽可能的方法理解人所讲的完整的语句。对于前一项研究,有被称为特征提取,后一项的研究大都使用传统人工智能的方法,它基于规则和语义。 - -贾里尼克任务,人的大脑是一个信息源,从思考到找到合适的语句,再通过发音说出来,是一个编码的过程,经过媒介传播到耳朵,是一个解码的过程。既然是一个典型的通讯问题,那就可以用解决通讯方法来解决问题,为此贾里尼克用两个数据模型(马尔科夫模型)分别描述信源和信道。然后使用大量的语音数据来训练。最后,贾里尼克团队花了4年团队,将语音识别从过去的70%提高到90%。后来人们尝试使用此方法来解决其他智能问题,但因为缺少数据,结果不太理想。 - -在当时,由于计算机性能的瓶颈、计算复杂性的指数级增长、数据量缺失等问题,一些难题看上去好像完全找不到答案。比如像今天已经比较常见的机器视觉功能在当时就不可能找到一个足够大的数据库来支撑程序去学习,机器无法吸收足够的数据量自然也就谈不上视觉方面的智能化。 - -项目的停滞不但让批评者有机可乘——1973年Lighthill针对英国人工智能研究状况的报告批评了人工智能在实现其“宏伟目标”上的完全失败,也影响到了项目资金的流向。人工智能遭遇了6年左右的低谷。 - -## 繁荣:1980 - 1987 - -在80年代,一类名为“专家系统”的AI程序开始为全世界的公司所采纳,而“知识处理”成为了主流AI研究的焦点。1981年,日本经济产业省拨款八亿五千万美元支持第五代计算机项目。其目标是造出能够与人对话,翻译语言,解释图像,并且像人一样推理的机器。 - -受到日本刺激,其他国家纷纷作出响应。英国开始了耗资三亿五千万英镑的Alvey工程。美国一个企业协会组织了MCC(Microelectronics and Computer Technology Corporation,微电子与计算机技术集团),向AI和信息技术的大规模项目提供资助。DARPA也行动起来,组织了战略计算促进会(Strategic Computing Initiative),其1988年向AI的投资是1984年的三倍。人工智能又迎来了大发展。 - - -![](http://favorites.ren/assets/images/2017/reading/Symbolics3640_Modified.jpg) -早期的专家系统Symbolics 3640 - -专家系统是一种程序,能够依据一组从专门知识中推演出的逻辑规则在某一特定领域回答或解决问题。最早的示例由Edward Feigenbaum和他的学生们开发。1965年起设计的Dendral能够根据分光计读数分辨混合物。1972年设计的MYCIN能够诊断血液传染病。它们展示了这一方法的威力。专家系统仅限于一个很小的知识领域,从而避免了常识问题;其简单的设计又使它能够较为容易地编程实现或修改。总之,实践证明了这类程序的实用性。直到现在AI才开始变得实用起来。 - -专家系统的能力来自于它们存储的专业知识。这是70年代以来AI研究的一个新方向。Pamela McCorduck在书中写道,“不情愿的AI研究者们开始怀疑,因为它违背了科学研究中对最简化的追求。智能可能需要建立在对分门别类的大量知识的多种处理方法之上。” “70年代的教训是智能行为与知识处理关系非常密切。有时还需要在特定任务领域非常细致的知识。”知识库系统和知识工程成为了80年代AI研究的主要方向。 - -1982年,物理学家John Hopfield证明一种新型的神经网络(现被称为“Hopfield网络”)能够用一种全新的方式学习和处理信息。大约在同时(早于Paul Werbos),David Rumelhart推广了反向传播算法,一种神经网络训练方法。这些发现使1970年以来一直遭人遗弃的联结主义重获新生。 - -## 第二次AI低谷:1987 - 1993 - -“AI之冬”一词由经历过1974年经费削减的研究者们创造出来。他们注意到了对专家系统的狂热追捧,预计不久后人们将转向失望。事实被他们不幸言中:从80年代末到90年代初,AI遭遇了一系列财政问题。 - -变天的最早征兆是1987年AI硬件市场需求的突然下跌。Apple和IBM生产的台式机性能不断提升,到1987年时其性能已经超过了Symbolics和其他厂家生产的昂贵的Lisp机。老产品失去了存在的理由:一夜之间这个价值五亿美元的产业土崩瓦解。 - -XCON等最初大获成功的专家系统维护费用居高不下。它们难以升级,难以使用,脆弱(当输入异常时会出现莫名其妙的错误),成了以前已经暴露的各种各样的问题的牺牲品。专家系统的实用性仅仅局限于某些特定情景。到了80年代晚期,战略计算促进会大幅削减对AI的资助。DARPA的新任领导认为AI并非“下一个浪潮”,拨款将倾向于那些看起来更容易出成果的项目。 - -1991年人们发现十年前日本人宏伟的“第五代工程”并没有实现。事实上其中一些目标,比如“与人展开交谈”,直到2010年也没有实现。与其他AI项目一样,期望比真正可能实现的要高得多。 - -## 走在正确的路上:1993 - 2005 - -现已年过半百的AI终于实现了它最初的一些目标。它已被成功地用在技术产业中,不过有时是在幕后。这些成就有的归功于计算机性能的提升,有的则是在高尚的科学责任感驱使下对特定的课题不断追求而获得的。不过,至少在商业领域里AI的声誉已经不如往昔了。 - -“实现人类水平的智能”这一最初的梦想曾在60年代令全世界的想象力为之着迷,其失败的原因至今仍众说纷纭。各种因素的合力将AI拆分为各自为战的几个子领域,有时候它们甚至会用新名词来掩饰“人工智能”这块被玷污的金字招牌。AI比以往的任何时候都更加谨慎,却也更加成功。 - -第一次让全世界感到计算机智能水平有了质的飞跃实在1966年,IBM的超级计算机深蓝大战人类国际象棋冠军卡斯伯罗夫,卡斯伯罗夫是世界上最富传奇色彩的国际象棋世界冠军,这次比赛最后以4:2比分战胜了深蓝。对于这次比赛媒体认为深蓝虽然输了比赛,但这毕竟是国际象棋上计算机第一次战胜世界冠军两局。时隔一年后,改进后的深蓝卷土重来,以3.5:2.5的比分战胜了斯伯罗夫。自从1997年以后,计算机下棋的本领越来越高,进步超过人的想象。到了现在,棋类游戏中计算机已经可以完败任何人类。 - -深蓝实际上收集了世界上百位国际大师的对弈棋谱,供计算机学习。这样一来,深蓝其实看到了名家们在各种局面下的走法。当然深蓝也会考虑卡斯伯罗夫可能采用的走法,对不同的状态给出可能性评估,然后根据对方下一步走法对盘面的影响,核实这些可能性的估计,找到一个最有利自己的状态,并走出这步棋。因此深蓝团队其实把一个机器智能问题变成了一个大数据和大量计算的问题。 - - -![](http://favorites.ren/assets/images/2017/reading/Deep_Blue.jpg) -IBM“深蓝”战胜国际象棋世界冠军 - -越来越多的AI研究者们开始开发和使用复杂的数学工具。人们广泛地认识到,许多AI需要解决的问题已经成为数学,经济学和运筹学领域的研究课题。数学语言的共享不仅使AI可以与其他学科展开更高层次的合作,而且使研究结果更易于评估和证明。AI已成为一门更严格的科学分支。 - -Judea Pearl发表于1988年的名著将概率论和决策理论引入AI。现已投入应用的新工具包括贝叶斯网络,隐马尔可夫模型,信息论,随机模型和经典优化理论。针对神经网络和进化算法等“计算智能”范式的精确数学描述也被发展出来。 - -## 大数据:2005 - 现在 - -从某种意义上讲,2005年是大数据元年,虽然大部分人感受不到数据带来的变化,但是一项科研成果却让全世界从事机器翻译的人感到震惊,那就是之前在机器翻译领域从来没有技术积累、不为人所知的Google,以巨大的优势打败了全世界所有机器翻译研究团队,一跃成为这个领域的领头羊。 - -就是Google花重金请到了当时世界上水平最高的机器翻译专家弗朗兹·奥科 (Franz Och)博士。奥科用了上万倍的数据来训练系统。量变的积累就导致了质变的发生。奥科能训练出一个六元模型,而当时大部分研究团队的数据量只够训练三元模型。简单地讲,一个 好的三元模型可以准确地构造英语句子中的短语和简单的句子成分之间的搭配,而六元模型则可以构造整个从句和复杂的句子成分之间的搭配,相当于将这些片段从一种语言到另一种语言直接对译过去了。不难想象,如果一个系统对大部分句子在很长的片段上直译,那么其准确性相比那些在词组单元做翻译的系统要准确得多。 - -如今在很多与“智能”有关的研究领域,比如图像识别和自然语言理解,如果所采用的方法无法利用数据量的优势,会被认为是落伍的。 - -数据驱动方法从20世纪70年代开始起步,在八九十年代得到缓慢但稳步的发展。进入21世纪后,由于互联网的出现,使得可用的数据量剧增,数据驱动方法的优势越来越明显,最终完成了从量变到质变的飞跃。如今很多需要类似人类智能才能做的事情,计算机已经可以胜任了,这得益于数据量的增加。 - -全世界各个领域数据不断向外扩展,渐渐形成了另外一个特点,那就是很多数据开始出现交叉,各个维度的数据从点和线渐渐连成了网,或者说,数据之间的关联性极大地增强,在这样的背景下,就出现了大数据。 - -大数据是一种思维方式的改变。现在的相比过去大了很多,量变带来了质变,思维方式、做事情的方法就应该和以往有所不同。这其实是帮助我们理解大数据概念的一把钥匙。在有大数据之前,计算机并不擅长解决需要人类智能来解决的问题,但是今天这些问题换个思路就可以解决了,其核心就是变智能问题为数据问题。由此,全世界开始了新的一轮技术革命——智能革命。 - - -![](http://favorites.ren/assets/images/2017/reading/Artificial_intelligence.jpg) - -> 建议购买正版书籍,如需试读电子版本,请在公众号回复:”智能时代“ - -参考: - -[人工智能史](https://zh.wikipedia.org/wiki/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%E5%8F%B2) - -[智能时代](https://book.douban.com/subject/26838557/) - - - - diff --git a/_posts/2017/2017-06-13-reconciliation-system.md b/_posts/2017/2017-06-13-reconciliation-system.md deleted file mode 100644 index 9ebc9343b3..0000000000 --- a/_posts/2017/2017-06-13-reconciliation-system.md +++ /dev/null @@ -1,192 +0,0 @@ ---- -layout: post -title: 如何做一个对账系统 -category: pay -tags: [pay] ---- - -在互联网行业中只要涉及到支付,必然就会有对账的需求,几乎所有互联网公司的业务中多多少少的都会涉及到支付,大一点的公司甚至都标配有了自己的第三方支付公司,因此对账具有普遍性。对账系统是支付体系中最重要的一环,也是保证交易、资金安全的最后一道防线。在大多数的互联网公司中,一般都会有独立的对账系统来处理,比如:电商平台、互联网金融、第三方支付公司等。 - - -![](http://favorites.ren/assets/images/2017/pay/reconciliation.jpg) - -对账是支付系统中的一环,因此在对账前我们先了解一下相关的业务知识 - -## 业务知识 - -### 什么是对账 - -传统的对账就是核对账目,是指在会计核算中,为保证账簿记录正确可靠,对账簿中的有关数据进行检查和核对的工作。在银行或者第三方支付中,对账其实是对一定周期内的交易进行双方确认的过程,一般都是在第二天银行或者第三方支付公司对前一日交易进行清分,生成对账单供平台商户下载,并将应结算款结算给平台商户。在往下一层,在互联网金融行业或者电商行业中,对账其实就是确认在固定周期内和支付提供方(银行和第反方支付)的交易、资金的正确性,保证双方的交易、资金一致正确。 - -广义的对账,所有跨应用的数据交互,理论上都应该进行对账。所以对账也可以分为信息流对账,资金流对账。信息流对账也一般用在自己内部系统的对账,比如支付系统的支付数据和业务系统的业务数据进行对账,保证资金交易和业务交易的一致性。资金流对账也就是支付系统和银行或者第三方支付系统之间的资金交易对账。 - -**对账方式** - -- 单向对账:一般拿第三方支付机构或银行流水,与自己系统进行对账,防止出现掉单问题; -- 双向对账:两个应用间的流水进行双向核对,如订单与财务系统,既要保证财务系统支付成功的记录,订单系统也是成功的;也要确保订单系统记录成功的记录,财务系统也成功。 - -我们一般采用双向对账的方式进行对账 - -**对账相关的问题** - -不同系统日切点不一致问题:滚动对账 -差错处理:补账,补偿(退款) - - -### 相关概念 - - -**轧帐和平帐** - -每一笔交易,都要做到各参与者的记录能够吻合,没有偏差。对账系统的工作,是发现有差异的记录,即轧帐;然后通过人工或者自动的方式,解决这些差异,即平帐。 - - -**长款和漏单** - -在以平台交易为基准的情况下和银行对账,发现周期内的交易,平台有此订单而第三方支付中没有订单,成为平台长款。平台长款一般是由于用户在支付的时候跨天的情况,比如用户在23:58分创建了订单,在第二天的凌晨00:03分进行了支付。在以银行交易为基准的情况下对账,银行有此订单而平台无此订单,即为平台漏单。平台漏单很少见,一般直接转人工处理。 - - -**账户体系** - -在一般的支付体系中会分为登录账户和支付账户,支付账户指用户在支付系统中用于交易的资金所有者权益的凭证;登录账号指用户在系统中登录的凭证和个人信息。一个用户可以有多个登录账户,一个登录账户可以有多个支付账户,比如零钱账户、储值卡账户等。一般来说,支付账户不会在多个登录账户之间共用。对账的交易一般都是支付账户参与交易。 - - -**交易与账户** - -账户设置,一般是从交易开始的。 交易的实现必须有账户的支持,账户是交易的基本构成元素。 从支付系统的角度,交易中涉及到的资金流是资金从一个账户流向另一个账户。 发起交易的一方,被称之为交易主体,他可以是一个人,也可以是一个机构。 - -**清算和结算** - -清算主要是指不同银行间的货币收付,可以认为是结算进行之前,发起行和接收行对支付指令的发送、接收、核对确认,其结果是全面交换结算工具和支付信息,并建立最终结算头寸。 - -结算是指将清算过程产生的待结算头寸分别在发起行、接收行进行相应的会计处理,完成资金转移,并通知收付双方的过程。当前,大多数银行结算业务的完成主要通过两类账户:一是银行间互相开立的代理账户,二是开立在央行、独立金融机构如银联、或者第三方支付机构的账户。 - -清算:计算各方应收应付钱款的时间与金额。结算:根据清算的结果在指定的时间对各方进行实际的资金转移操作 - - -**资金池** - -用户备付资金(如充值)统一放在企业的银行账户中,企业可以随意支配这些资金,即为资金池。与之对应的是第三方托管,用户备付资金是放在企业在第三方支付机构为用户开设的虚拟账户中,企业无法随意取出这些资金。现在互联网金融全面要求接入银行存管,就是银行会为每个用户创建一个资金账户来保护用户的资金,互联金融公司不能随意划拨这些资金账户中的金额。 - - -## 对账系统 - - -### 对账设计 - - -![](http://favorites.ren/assets/images/2017/pay/reconciliation_design.jpg) - - -对账系统的设计阶段,将对账系统分为四个模块,每个模块的负责自己的职能。 - -- 文件获取模块:下载或者读取各渠道对账文件 -- 文件解析模块:创建不同的解析模板,根据渠道和文件类型获取对应的解析模板进行解析 -- 对账处理模块:对账的业务逻辑处理 -- 差错处理模块:处理差错池中的订单 - -一般会设计一个定时任务,每天固定的时间点触发,定时驱动调度类分别调用四个模块来处理对账。也有的银行会主动的推送对账单,再通过http回调来触发对账流程。 - - - -**对账算法** - -一、流程: - -1、从上游渠道(银行、银联等金融机构)获取对账文件,程序逐行解析入库; -2、在程序处理中,以上游对账文件的表为基准,程序逐行读取并与我们系统的交易记录对比账务记录(有账务系统情况下,合理方案应该是于账务记录)对比,查找出差异记录; -3、以我们系统的交易记录对比账务记录为基准,程序逐行读取与上游对账文件对比,查找出差异记录。 - - -二、缺陷: - -1、对账过程中查询相关数据,如果数据量巨大,对数据库性能影响较大,而且对账逻辑扩展极为麻烦; -2、逐行比对算法效率较低,但算法上并无好的优化余地。如果采用数据库INTERSECT、MINUS对数据库压力也高; -3、在业务量大的情况下(例如有上百家上游渠道需要对,每一家都有几十万条交易记录),对账服务器及数据库服务器负荷较高。即便采用读写分离,对账时候使用读库,压力一样很大; -4、导入批量文件,逐行入库效率较为低下(每一次都需要建立网络连接、关闭连接)。 - -三、改进: - -1、涉及网络传输的,尽量采用批量方式操作,减少网络消耗及时间消耗; -2、使用Redis等NOSQL数据库,降低数据库服务器的压力。同时在扩展上也容易,一台Redis服务器不够,可以无限制增加用于对账用的服务器; -3、使用Redis的set集合的sdiff功能,利用Redis sdiff算法的高性能,比对上游记录和我方记录的差异。 - -### 对账流程 - - -![](http://favorites.ren/assets/images/2017/pay/reconciliation_process.jpg) - - -**1、下载对账单** - -大多数银行都要求接入方提供ftp服务,银行定时将对账单推送到接入方提供的ftp服务器上面;还有一部分银行会提供对账单的下载服务,通过ftp/http的都有,ftp方式居多;另外网银的对账单比较特殊,一般都需要结算登录网银的后台管理系统中,手动下载,结算下载完对账单后在导入到对账系统。 - - -技术实现上可以做成工厂模式,不同的支付渠道有不同的下载类,如果是http接口将文件写入到对账单,如果是ftp服务器,将服务器中的对账单下载到本地带解析的目录中。主要涉及的代码ftp工具类、http(s)工具类,相关IO读写。 - -技术选型上,HTTP(S)用apache httpclient即可实现链接池和断点续传, FTP也可以使用Apache Commons Net API。 但不管是哪一个,都需要设置重试次数和链接超时间。重试次数和间隔的设置需要小心,重试太频繁,容易把服务器打死.;时间间隔太大,又会阻塞后续处理步骤。5~10分钟是一个合适的重试间隔区间。 - - -**2、创建批次** - -创建批次一方面是为了防止重复对账,另一方面需要在对账结束的时候将对账的结果信息存储到批次中。 - - -**3、解析文件** - -解析文件主要是将下载的对账文件解析成我们可以对账的数据类型并且入库。解析的文件不同渠道有不同的类型,因此也可以设计成不同的解析模板,使用工厂模式将不同格式的文件解析成可以对账的统一数据类型。解析的文件类型一般包括:json、text、cvs、excle等,另外部分银行会对账单做加密或者提供zip打包的格式,这里就需要额外开发zip工具类和加解密工具类进行处理。 - -对账文件中包含的主要信息有:商户订单号、交易流水号、交易时间、支付时间、付款方、交易金额、交易类型、交易状态这些字段。 - -**4、对账处理** - -对账处理也是对账的核心逻辑,具体分为以下的几个步骤来实现: - -- 查询平台所有交易成功的订单 -- 查询平台所有的交易订单 -- 查询平台缓存池中的数据 -- 查询银行交易成功的订单 -- 开始以平台的数据为准对账,平台长款记入缓冲池 -- 开始以银行通道的数据为准对账 - - -以平台订单为基准对账逻辑:以平台所有交易成功的订单为基准,遍历银行订单的所有数据,找出订单号相同的订单,对比订单的金额、手续费是否一致。如果一致跳过;如果不一致,平台订单进入差错池;如果在银行订单中没有找到此笔交易,订单进入缓存池,记录平台长款。同时统计对账相关金额和订单数。 - -以银行订单为基准对账逻辑:以银行的交易数据为基准,遍历所有平台的交易(包括未成功的订单),找出订单号相同但支付状态不一致的订单,在进行对比金额存入差错池。如果没有在平台的交易中找到此订单,再从缓存池中遍历查找,找到对应的平台订单验证金额是否一致,不一致进入差错池。如果在缓存池汇中依然没有找到对应的订单,直接进入差错池,记录平台漏单。同时统计对账相关金额和订单数。 - -**5、对账统计** - -根据对账处理中,统计的相关信息包括:对账完成时间、对账是否成功、平账的金额和订单数、差错的金额和订单数、缓存池金额和订单数等。 - - -**6、差错处理** - -在一般系统中,差错处理分为两种,一种人工来处理,一种系统自动来处理。 - -主要有如下情况: - -- 本地未支付,支付渠道已支付。这主要是本地未正确接收到渠道下发的异步通知导致。 一般处理是将本地状态修改为已支付,并做响应的后续处理,比如通知业务方等。 - -- 本地已支付,支付渠道已支付,但是金额不同,这个需要人工核查。 - -- 本地已支付,但是支付渠道中无记录;或者本地无记录,支付渠道有记录。在排除跨日因素外,这种情况非常少见,需要了解具体原因后做处理。 - - -**示例代码** - -网上的对账系统的开源代码不是很多,但有一家还不错:龙果支付。龙果支付中有比较完整的对账流程,代码中以微信、支付宝的对账单为例写了对账的流程,大家可以参考。 - -[roncoo-pay](https://github.com/roncoo/roncoo-pay) - - -> 最后,你们公司的对账系统是怎么实现的,遇到了那些坑?欢迎和我讨论。 - -参考: - -[支付系统的对账处理](http://blog.lixf.cn/essay/2016/10/10/account-2-reconciliation/) - -[账户体系架构设计相关思路记录](http://dinguangx.iteye.com/blog/2271566) - -[对账系统–项目总览](http://www.changsheng.pub/archives/120) - - diff --git a/_posts/2017/2017-06-19-cloudfavorites-build.md b/_posts/2017/2017-06-19-cloudfavorites-build.md deleted file mode 100644 index dce935b1fb..0000000000 --- a/_posts/2017/2017-06-19-cloudfavorites-build.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -layout: post -title: 如何构建云收藏项目 -no-post-nav: true -category: springboot -tags: [springboot] ---- - -很多人github上面反馈我们的springboot开源项目无法构建,这里简单写一下构建的流程希望可以帮助到大家。 - -问题列表如下,Issues有好几个都是因为构建的问题: - -[问题列表](https://github.com/cloudfavorites/favorites-web/issues) - - -我这里以idea为例,演示一下如果导入构建项目: - -1、手动下载[favorites-web](https://github.com/cloudfavorites/favorites-web)代码到本地,或者通过idea的VCS功能下载到本地。 - - -![](http://favorites.ren/assets/images/2017/cloudfavorites/gradle1.png) - - -2、导入项目 - - -![](http://favorites.ren/assets/images/2017/cloudfavorites/gradle2.png) - - - -![](http://favorites.ren/assets/images/2017/cloudfavorites/gradle3.png) - - -3、选择gradle的方式导入 - - -![](http://favorites.ren/assets/images/2017/cloudfavorites/gradle4.png) - - - -![](http://favorites.ren/assets/images/2017/cloudfavorites/gradle5.png) - - -4、导入完成之后,选择Project Stucture查看项目依赖(最新版本已经修复,使用最新版本构建可以跳过第4、5步) - - - -![](http://favorites.ren/assets/images/2017/cloudfavorites/gradle6.png) - - -发现有部分的jar包级别是Provided - - -![](http://favorites.ren/assets/images/2017/cloudfavorites/gradle7.png) - - -5、将标红色部分的jar级别由Provided改为Compile(最新版本已经修复,使用最新版本构建可以跳过第4、5步) - - -![](http://favorites.ren/assets/images/2017/cloudfavorites/gradle8.png) - -修改对应的配置文件为本地配置信息,在运行Application项目便可成功启动 - - -6、mvn导入方式 - -已经有网友提供了项目的mvn支持,导入方式和上面的流程相同,只是在第三步的时候选择maven既可。 - - - -![](http://favorites.ren/assets/images/2017/cloudfavorites/mvn3.png) - - -感谢@souvc提供mvn的支持! -感谢@shatangege提供本地化支持! \ No newline at end of file diff --git a/_posts/2017/2017-06-22-spring-boot-tips.md b/_posts/2017/2017-06-22-spring-boot-tips.md deleted file mode 100644 index 9e6902c2d3..0000000000 --- a/_posts/2017/2017-06-22-spring-boot-tips.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -layout: post -title: Spring Boot (十三): Spring Boot 小技巧 -category: springboot -tags: [springboot] -copyright: java ---- - -一些 Spring Boot 小技巧、小知识点 - -## 初始化数据 - -我们在做测试的时候经常需要初始化导入一些数据,如何来处理呢?会有两种选择,一种是使用 Jpa,另外一种是 Spring JDBC 。两种方式各有区别下面来详细介绍。 - -**使用 Jpa** - -在使用`spring boot jpa`的情况下设置`spring.jpa.hibernate.ddl-auto`的属性设置为 `create` or `create-drop`的时候,Spring Boot 启动时默认会扫描 classpath 下面(项目中一般是 resources 目录)是否有`import.sql`,如果有机会执行`import.sql`脚本。 - - -**使用 Spring JDBC** - -使用 Spring JDBC 需要在配置文件中添加以下配置 - -``` xml -spring: - datasource: - schema: classpath:db/schema.sql - data: classpath:db/data.sql - sql-script-encoding: utf-8 - jpa: - hibernate: - ddl-auto: none -``` - -- schema :脚本中创建表的语句 -- data :脚本中初始化数据的预计 -- sql-script-encoding:设置脚本的编码 - -Spring Boot 项目启动的时候会自动执行脚本。 - -**ddl-auto 四个值的解释** - -> 1. create: 每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。 -> 2. create-drop :每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。 -> 3. update:最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据 model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等 应用第一次运行起来后才会。 -> 4. validate :每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。 -> 5、 none : 什么都不做。 - -**不同点** - -第一种方式启动的时候 Jpa 会自动创建表,import.sql 只负责创建表单后的初始化数据。第二种方式启动的时候不会创建表,需要在初始化脚本中判断表是否存在,再初始化脚本的步骤。 - -> 在生产中,这两种模式都建议慎用! - -## Thymeleaf 设置不校验 html 标签 - -默认配置下,Thymeleaf 对 .html 的内容要求很严格,比如``,如果少封闭符号`/`,就会报错而转到错误页。也比如你在使用 Vue.js 这样的库,然后有`
    `这样的 html 代码,也会被 Thymeleaf 认为不符合要求而抛出错误。 - -通过设置 Thymeleaf 模板可以解决这个问题,下面是具体的配置: - -``` properties -spring.thymeleaf.cache=false -spring.thymeleaf.mode=LEGACYHTML5 -``` - -LEGACYHTML5 需要搭配一个额外的库 NekoHTML 才可用,项目中使用的构建工具是 Maven 添加如下的依赖即可完成: - -``` xml - - net.sourceforge.nekohtml - nekohtml - 1.9.22 - -``` - -## 自定义 Favicon - -Spring Boot 提供了一个默认的 Favicon,也就是 Spring 的 logo ,我们可以根据自己企业的需要来定制它。 - -首先需要在 application.propertie 中关闭原有的logo - -``` properties -spring.mvc.favicon.enable=false -``` - -在将自己的 favicon.ico 放到`src/main/resources/static`下 ,然后再重新启动项目就可以了。 - - -> 文章内容已经升级到 Spring Boot 2.x - -**[示例代码-github](https://github.com/ityouknow/spring-boot-examples)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-boot-examples)** - -参考: - -- [thymeleaf模板对没有结束符的HTML5标签解析出错的解决办法](http://blog.csdn.net/yalishadaa/article/details/60768811) -- [howto-database-initialization](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html) - - - diff --git a/_posts/2017/2017-06-25-save-a-life.md b/_posts/2017/2017-06-25-save-a-life.md deleted file mode 100644 index cefa5a89d3..0000000000 --- a/_posts/2017/2017-06-25-save-a-life.md +++ /dev/null @@ -1,84 +0,0 @@ ---- -layout: post -title: 救人一命是怎样的体验? -category: life -tags: [life] ---- - -说一下我的这个故事吧,太久了,久的我都快忘了,却时时会突然想起它。 - - -![](http://favorites.ren/assets/images/2017/life/river.jpeg) - -小时候老家每隔两年,都会有小孩溺死池塘,河流。父母都很担心自己的孩子,我们邻居一个孩子,只要去池塘里面游泳,父母就过去直接把衣服拿了,再找一根刺条,一路在光溜溜的身上抽着,让走回去。 - -我在上四五年纪的时候,和表哥在小水潭里面噗噗腾腾的学会了一点狗刨。从此就特别爱玩水,学游泳,就差点淹了两次。一次是在砖厂的小泥水潭中,有大点的小孩拿了充气的轮胎,兴奋的骑在了一边,直接就翻了,扑腾了几下就么记忆了,醒来之后就在岸边了。 - -又过了两年,感觉自己游泳水平应该不错了。我们那附近没有河,池塘又都干了,夏天的时候大家就在一个古老的井里面游泳,井大概有四五米深,大家就站在上面往下一跳,因为水很冷,稍微游两下,再爬上来。大家会玩很多花样,抱着石头跳下去沉到底,再上来。或者翻着跟头跳下去,或者两个人一起做动作跳下去。 - -我那时候虽然学会了游泳,一直都是在水浅一点的水库,河流里面玩,没敢在古井里面玩过。有一次我又站到了古井旁边看大家玩,发小就鼓励让我试一下,看着热闹和碍于面子,我就脱掉衣服穿着拖鞋,跳了下去,很快沉到了水下面,就准备游上来,结果扑腾了两下上到了水面,又沉了下去,旁边人都傻了。大概扑腾了四五秒吧,喝了几口水终于把拖鞋给蹬掉了,这样才踩了水游上来。后来才知道,穿着拖鞋踩水游泳很危险。 - -经历过这些事情之后,我就发誓我一定要把游泳学好,经常去各种水库和洛河,往往为了游一次泳要跑十多里路程,那时候不觉得累,游泳多了,慢慢的在游泳这方面家里面担心就小了一些。 - -村里面有个小伙子比我大五六岁,那时候关系还不错,他也想学游泳,有时候我会约他一起去十公里外的洛河游泳。洛河有一个很有名的地方叫做鸽子崖,从西往东的河流被一块大大的山坡给挡住了,河流转了一个90度的直角。常年累月的冲击,这个90度弯的地方就聚起了一个大潭。这个大山坡上有很多野鸽子在上面安家,因此就被叫做鸽子崖。人们大都喜欢在这块来游泳。 - -初二的暑假,那天天照样很热,家里还晒了一院子的麦子,在农村夏天依然很忙,晒的麦子隔30分钟就要翻一次。想游泳的毛病又犯了,就瞒着我妈说我们出去一会,过两三个小时就回来了,还骑了我爸的二八大挂自行车。 - - -![](http://favorites.ren/assets/images/2017/life/bicycle.jpg) - -和往常一样,我俩就在鸽子崖的下游游泳,给他教了一下,他就在水比较浅的地方扑腾着学习游泳,我就在河里面游来游去。刚好玩了半小时的时候,我从岸的这边往鸽子崖对面游,快游到对岸的时候,听见他喊了我一声我的名字,我加速游了两下扒着河对岸的石头上回过头来看咋回事。就看到他头淹在水里面,使劲的游着,想从深水区游出来,游不动而且方向也不对。可能不小心从浅水区扑腾到了深水的地方,由于他还不会换气,所以胡乱游着,越扑腾越往河里面走。 - -那会也没多想,就从对岸游过去准备把他从深水区拉出来,到了他的身边,就拖着他的胳膊往岸边游,他那时候体重140,我那时候100多一点,他一直在扑腾,我拽着他游了两下也么游多远。估计那会喝水喝的也不少了,就突然下意识的用胳膊拽着我抱着我,没两下,两个人就都被拖到了水下。我试着挣脱了一下,手抓的太死,根本没用。 - -我抬头往上一看,阳光经过水面的折射很灿烂,也有一种眩晕的幻觉。我们大概距离水面有二三十厘米,但就是这二三十里厘米就马上快要了我们的命。我现在想想是不是人快死的时候,虽然只有那几秒,但真的脑子跟过电一样会想很多事情。抬头看着水面,脑子里面想,我靠,我不会这样就挂了吧。我妈还在院子里面晒着麦子,听到这个消息会不会哭死。还想了很多其它的事情,不太记得了,但突然我就清醒了一下,我还有很多事情没有去做呢,这样死了太不甘心了。 - -在我继续挣扎的过程中,可能因为他喝了太多的水,人已经进入昏迷的状态了,手松了劲,我就挣脱了。迅速的浮上水面,吸了一口气,然后慌张的游到了岸上,站到岸上之后,又是庆幸又是害怕。往水里一看,他还在水里面,两个胳膊向着前面张开着,好像是在寻找着什么。我的大脑里面又开始高速运转,我实在是害怕再下水去救他了,但是又一想,他是我带来的,而且他们家就他一个男孩,他家里诸多事情也很不顺,回村后估计村子会闹翻了天。 - -就想着另一个办法就是呼救,但是这个地方距离大路边还有几百米,来回也需要十分钟左右,如果等我把找大人找来,估计就挂了,另外河水一直再流,我也能看见他的身体往河水的下游飘,过一会还不一定找得到。虽然想了这么多,但当时在脑子里面估计就一两秒。正在我着急的时候,我看到附近100米左右有两个当地的小孩在石头上晒太阳,估计也看到了我们这边的情况,头也看着这边。 - -我用几乎快哭的声音对他俩说,大哥这里有人溺水了,快来帮忙救救。听了这话,其中一个男孩跳进了水中游了过来,游了两下之后发现另一个男孩没动,他回过头来和那个男孩交流了一下,然后就不动了,我看那个意思他们是不准备帮忙了。这会我几乎给他两叫爷的心思都有了,甚至当时哪怕让我马上吃一口屎,只要他们能救人我那时候都愿意。 - -但是我看他俩的意思,我估计吃屎也叫不动他们了,一方面,因为河水一直在流动,我朋友的身体一直不断的往下流飘,在往下水会更深,甚至水下会有漩涡。我喊了他们还有另外的一层意思,那怕最后我俩都死在了这里,至少家里面的人都知道是什么情况。我看不能再等了,心里面又犹豫了一下,一咬牙跳进了水里,我潜到水下,从他的后背往岸边推一下,然后在浮到水面透一口气,然后在沉下去推一把,如此反复了大概十几次,终于把他推到了岸边。 - -站在岸边,把他往岸上抱,结果抱不动,一方面有点慌和虚,一方面他确实比较我沉。这时候,我对那两个男孩喊,大哥帮忙过来抬一下,这会他俩就往这边走了。我就连拉再拖把我发小扯到了岸边,这时候他的脸上已经没有人的气色了,两眼瞪着,脸色发青。但我那时候稍微镇定了一下,想着应该不会死,溺水的总共时间不超过五分钟。 - -正当我正想着要不要学着电视里面的急救给他来做人工呼吸的时候,他猛地吸了一口气,剧烈的咳嗽了一下,吐出来了一些黏水,水里面有一些血。但人是清醒了过来,喘了几下就缓过来了,这时候那俩个男孩才走到身边,我才给说,谢谢了,不用帮忙了。估计这两个男孩也吓得够呛,第一次遇到这种情况,也不能怪他们,任何人首先想到的是保证自己的生命安全。 - -我俩惊魂未定的在岸边躺了两三个小时,才缓了过来,商量了一下,谁也不敢让家里人知道,直到现在村子里面的人都不知道这事。回家之后,朋友发了高烧,整整打了三天的吊瓶,从此放弃了学习游泳。我也从此再也没有在那个地方游过泳,大学毕业之后越来越惜命,再也没有去外面野游过。 - - -![](http://favorites.ren/assets/images/2017/life/luohe.png) - -在我经历的那个年代中,我们的农村,每年的夏天都有一两个年轻的生命因为溺水而死亡。有时候往往都是一家的孩子,小的溺水了,大的去救小的然后兄弟两个都溺水了,这样的事情不少! - -可怜我的母亲,到每年的夏天不知道操了多少的心。后来我才知道,那次走的时候给母亲说两个小时就回来了,结果过了五个多小时才回来,母亲在案板做着我喜欢的饺子,一边看我怎么还不回来,那时候又没有手机,母亲就每隔个十分钟,到村头看一下。但其实我很多个夏天的日子都是这样来骗母亲,就出去一会,这一会往往都是半天。 - -可怜天下父母心。 - -救人一命的体验是什么?就是人的生命真的很脆弱,不经意间,一个人的生命就去了!只有经历了,才知道生命的脆弱,我的生命不只属于我,还有爱我的亲人和朋友。 - -以下是世界卫生组织的数据: - -**重要事实** - -- 溺水是世界各地非故意伤害死亡的第三大原因,占所有与伤害有关死亡的7%。 -- 世界各地每年溺水死亡数估计为36万例。 -- 全球估计数可能大大低估了与溺水相关的实际公共卫生问题。 -- 儿童、男性以及接触水的机会增多的人,溺水的危险最大。 - -**问题的范围** - -2015年,估计有36万人死于溺水,使溺水成为全球一个主要公共卫生问题。同年,溺水造成的死亡占全球总死亡率的超过9%。溺水是非故意伤害死亡的第三大原因,占所有与伤害有关的死亡的7%。 - -溺水造成的全球性负担和死亡遍及所有经济体和地区,但是: - -- 中、低收入国家占非故意溺水死亡的逾90%; -- 世界上一半以上的溺水事件发生在世卫组织西太平洋区域和世卫组织东南亚区域; -- 世卫组织非洲区域的溺水死亡率最高,比德国或英国分别高出15-20倍; - -**在中国:溺水是1-14岁儿童伤害死亡的主要原因。** - -夏天又来了,珍惜生命,远离野泳! - - diff --git a/_posts/2017/2017-06-26-spring-boot-shiro.md b/_posts/2017/2017-06-26-spring-boot-shiro.md deleted file mode 100644 index 849e918772..0000000000 --- a/_posts/2017/2017-06-26-spring-boot-shiro.md +++ /dev/null @@ -1,470 +0,0 @@ ---- -layout: post -title: Spring Boot (十四): Spring Boot 整合 Shiro-登录认证和权限管理 -category: springboot -tags: [springboot] -copyright: java ---- - -这篇文章我们来学习如何使用 Spring Boot 集成 Apache Shiro 。安全应该是互联网公司的一道生命线,几乎任何的公司都会涉及到这方面的需求。在 Java 领域一般有 Spring Security、 Apache Shiro 等安全框架,但是由于 Spring Security 过于庞大和复杂,大多数公司会选择 Apache Shiro 来使用,这篇文章会先介绍一下 Apache Shiro ,在结合 Spring Boot 给出使用案例。 - - -## Apache Shiro - -### What is Apache Shiro? - -Apache Shiro 是一个功能强大、灵活的,开源的安全框架。它可以干净利落地处理身份验证、授权、企业会话管理和加密。 - -Apache Shiro 的首要目标是易于使用和理解。安全通常很复杂,甚至让人感到很痛苦,但是 Shiro 却不是这样子的。一个好的安全框架应该屏蔽复杂性,向外暴露简单、直观的 API,来简化开发人员实现应用程序安全所花费的时间和精力。 - -Shiro 能做什么呢? - -- 验证用户身份 -- 用户访问权限控制,比如:1、判断用户是否分配了一定的安全角色。2、判断用户是否被授予完成某个操作的权限 -- 在非 Web 或 EJB 容器的环境下可以任意使用 Session API -- 可以响应认证、访问控制,或者 Session 生命周期中发生的事件 -- 可将一个或以上用户安全数据源数据组合成一个复合的用户 "view"(视图) -- 支持单点登录(SSO)功能 -- 支持提供“Remember Me”服务,获取用户关联信息而无需登录 -… - -等等——都集成到一个有凝聚力的易于使用的 API。 - -Shiro 致力在所有应用环境下实现上述功能,小到命令行应用程序,大到企业应用中,而且不需要借助第三方框架、容器、应用服务器等。当然 Shiro 的目的是尽量的融入到这样的应用环境中去,但也可以在它们之外的任何环境下开箱即用。 - -### Apache Shiro Features 特性 - -Apache Shiro 是一个全面的、蕴含丰富功能的安全框架。下图为描述 Shiro 功能的框架图: - -![](http://favorites.ren/assets/images/2017/springboot/ShiroFeatures.png) - -Authentication(认证), Authorization(授权), Session Management(会话管理), Cryptography(加密)被 Shiro 框架的开发团队称之为应用安全的四大基石。那么就让我们来看看它们吧: - -- **Authentication(认证):**用户身份识别,通常被称为用户“登录” -- **Authorization(授权):**访问控制。比如某个用户是否具有某个操作的使用权限。 -- **Session Management(会话管理):**特定于用户的会话管理,甚至在非web 或 EJB 应用程序。 -- **Cryptography(加密):**在对数据源使用加密算法加密的同时,保证易于使用。 - -还有其他的功能来支持和加强这些不同应用环境下安全领域的关注点。特别是对以下的功能支持: - -- Web支持:Shiro 提供的 Web 支持 api ,可以很轻松的保护 Web 应用程序的安全。 -- 缓存:缓存是 Apache Shiro 保证安全操作快速、高效的重要手段。 -- 并发:Apache Shiro 支持多线程应用程序的并发特性。 -- 测试:支持单元测试和集成测试,确保代码和预想的一样安全。 -- "Run As":这个功能允许用户假设另一个用户的身份(在许可的前提下)。 -- "Remember Me":跨 session 记录用户的身份,只有在强制需要时才需要登录。 - - -> 注意: Shiro 不会去维护用户、维护权限,这些需要我们自己去设计/提供,然后通过相应的接口注入给 Shiro - -### High-Level Overview 高级概述 - -在概念层,Shiro 架构包含三个主要的理念:Subject,SecurityManager和 Realm。下面的图展示了这些组件如何相互作用,我们将在下面依次对其进行描述。 - -![](http://favorites.ren/assets/images/2017/springboot/ShiroBasicArchitecture.png) - -- Subject:当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它--当前和软件交互的任何事件。 -- SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。 -- Realms:用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。 - -我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。 - -## 快速上手 - -### 基础信息 - -**pom包依赖** - -``` xml - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-thymeleaf - - - net.sourceforge.nekohtml - nekohtml - 1.9.22 - - - org.springframework.boot - spring-boot-starter-web - - - org.apache.shiro - shiro-spring - 1.4.0 - - - mysql - mysql-connector-java - runtime - - -``` - -重点是 shiro-spring 包 - -**配置文件** - -``` xml -spring: - datasource: - url: jdbc:mysql://localhost:3306/test - username: root - password: root - driver-class-name: com.mysql.jdbc.Driver - - jpa: - database: mysql - show-sql: true - hibernate: - ddl-auto: update - naming: - strategy: org.hibernate.cfg.DefaultComponentSafeNamingStrategy - properties: - hibernate: - dialect: org.hibernate.dialect.MySQL5Dialect - - thymeleaf: - cache: false - mode: LEGACYHTML5 -``` - -thymeleaf的配置是为了去掉html的校验 - - -**页面** - -我们新建了六个页面用来测试: - -- index.html :首页 -- login.html :登录页 -- userInfo.html : 用户信息页面 -- userInfoAdd.html :添加用户页面 -- userInfoDel.html :删除用户页面 -- 403.html : 没有权限的页面 - -除过登录页面其它都很简单,大概如下: - -``` html - - - - - Title - - -

    index

    - - -``` - -### RBAC - -RBAC 是基于角色的访问控制(Role-Based Access Control )在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。 - -采用 Jpa 技术来自动生成基础表格,对应的实体如下: - -用户信息 - -``` java -@Entity -public class UserInfo implements Serializable { - @Id - @GeneratedValue - private Integer uid; - @Column(unique =true) - private String username;//帐号 - private String name;//名称(昵称或者真实姓名,不同系统不同定义) - private String password; //密码; - private String salt;//加密密码的盐 - private byte state;//用户状态,0:创建未认证(比如没有激活,没有输入验证码等等)--等待验证的用户 , 1:正常状态,2:用户被锁定. - @ManyToMany(fetch= FetchType.EAGER)//立即从数据库中进行加载数据; - @JoinTable(name = "SysUserRole", joinColumns = { @JoinColumn(name = "uid") }, inverseJoinColumns ={@JoinColumn(name = "roleId") }) - private List roleList;// 一个用户具有多个角色 - - // 省略 get set 方法 - } -``` - - -角色信息 - -``` java -@Entity -public class SysRole { - @Id@GeneratedValue - private Integer id; // 编号 - private String role; // 角色标识程序中判断使用,如"admin",这个是唯一的: - private String description; // 角色描述,UI界面显示使用 - private Boolean available = Boolean.FALSE; // 是否可用,如果不可用将不会添加给用户 - - //角色 -- 权限关系:多对多关系; - @ManyToMany(fetch= FetchType.EAGER) - @JoinTable(name="SysRolePermission",joinColumns={@JoinColumn(name="roleId")},inverseJoinColumns={@JoinColumn(name="permissionId")}) - private List permissions; - - // 用户 - 角色关系定义; - @ManyToMany - @JoinTable(name="SysUserRole",joinColumns={@JoinColumn(name="roleId")},inverseJoinColumns={@JoinColumn(name="uid")}) - private List userInfos;// 一个角色对应多个用户 - - // 省略 get set 方法 - } -``` - - -权限信息 - -``` java -@Entity -public class SysPermission implements Serializable { - @Id@GeneratedValue - private Integer id;//主键. - private String name;//名称. - @Column(columnDefinition="enum('menu','button')") - private String resourceType;//资源类型,[menu|button] - private String url;//资源路径. - private String permission; //权限字符串,menu例子:role:*,button例子:role:create,role:update,role:delete,role:view - private Long parentId; //父编号 - private String parentIds; //父编号列表 - private Boolean available = Boolean.FALSE; - @ManyToMany - @JoinTable(name="SysRolePermission",joinColumns={@JoinColumn(name="permissionId")},inverseJoinColumns={@JoinColumn(name="roleId")}) - private List roles; - - // 省略 get set 方法 - } -``` - -根据以上的代码会自动生成 user_info(用户信息表)、sys_role(角色表)、sys_permission(权限表)、sys_user_role(用户角色表)、sys_role_permission(角色权限表)这五张表,为了方便测试我们给这五张表插入一些初始化数据: - -``` sql -INSERT INTO `user_info` (`uid`,`username`,`name`,`password`,`salt`,`state`) VALUES ('1', 'admin', '管理员', 'd3c59d25033dbf980d29554025c23a75', '8d78869f470951332959580424d4bf4f', 0); -INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (1,0,'用户管理',0,'0/','userInfo:view','menu','userInfo/userList'); -INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (2,0,'用户添加',1,'0/1','userInfo:add','button','userInfo/userAdd'); -INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (3,0,'用户删除',1,'0/1','userInfo:del','button','userInfo/userDel'); -INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (1,0,'管理员','admin'); -INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (2,0,'VIP会员','vip'); -INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (3,1,'test','test'); -INSERT INTO `sys_role_permission` VALUES ('1', '1'); -INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (1,1); -INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (2,1); -INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (3,2); -INSERT INTO `sys_user_role` (`role_id`,`uid`) VALUES (1,1); -``` - -### Shiro 配置 - -首先要配置的是 ShiroConfig 类,Apache Shiro 核心通过 Filter 来实现,就好像 SpringMvc 通过 DispachServlet 来主控制一样。 -既然是使用 Filter 一般也就能猜到,是通过 URL 规则来进行过滤和权限校验,所以我们需要定义一系列关于 URL 的规则和访问权限。 - -**ShiroConfig** - -``` java -@Configuration -public class ShiroConfig { - @Bean - public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { - System.out.println("ShiroConfiguration.shirFilter()"); - ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); - shiroFilterFactoryBean.setSecurityManager(securityManager); - //拦截器. - Map filterChainDefinitionMap = new LinkedHashMap(); - // 配置不会被拦截的链接 顺序判断 - filterChainDefinitionMap.put("/static/**", "anon"); - //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了 - filterChainDefinitionMap.put("/logout", "logout"); - //:这是一个坑呢,一不小心代码就不好使了; - // - filterChainDefinitionMap.put("/**", "authc"); - // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 - shiroFilterFactoryBean.setLoginUrl("/login"); - // 登录成功后要跳转的链接 - shiroFilterFactoryBean.setSuccessUrl("/index"); - - //未授权界面; - shiroFilterFactoryBean.setUnauthorizedUrl("/403"); - shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); - return shiroFilterFactoryBean; - } - - @Bean - public MyShiroRealm myShiroRealm(){ - MyShiroRealm myShiroRealm = new MyShiroRealm(); - return myShiroRealm; - } - - - @Bean - public SecurityManager securityManager(){ - DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); - securityManager.setRealm(myShiroRealm()); - return securityManager; - } -} -``` - -Filter Chain 定义说明: - -- 1、一个URL可以配置多个 Filter,使用逗号分隔 -- 2、当设置多个过滤器时,全部验证通过,才视为通过 -- 3、部分过滤器可指定参数,如 perms,roles - - -Shiro 内置的 FilterChain - -Filter Name | Class ---- | --- -anon |org.apache.shiro.web.filter.authc.AnonymousFilter -authc |org.apache.shiro.web.filter.authc.FormAuthenticationFilter -authcBasic |org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter -perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter -port |org.apache.shiro.web.filter.authz.PortFilter -rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter -roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter -ssl | org.apache.shiro.web.filter.authz.SslFilter -user | org.apache.shiro.web.filter.authc.UserFilter - -- anon:所有 url 都都可以匿名访问 -- authc: 需要认证才能进行访问 -- user:配置记住我或认证通过可以访问 - - -**登录认证实现** - -在认证、授权内部实现机制中都有提到,最终处理都将交给Real进行处理。因为在 Shiro 中,最终是通过 Realm 来获取应用程序中的用户、角色及权限信息的。通常情况下,在 Realm 中会直接从我们的数据源中获取 Shiro 需要的验证信息。可以说,Realm 是专用于安全框架的 DAO. -Shiro 的认证过程最终会交由 Realm 执行,这时会调用 Realm 的```getAuthenticationInfo(token)```方法。 - -该方法主要执行以下操作: - -- 1、检查提交的进行认证的令牌信息 -- 2、根据令牌信息从数据源(通常为数据库)中获取用户信息 -- 3、对用户信息进行匹配验证。 -- 4、验证通过将返回一个封装了用户信息的```AuthenticationInfo```实例。 -- 5、验证失败则抛出```AuthenticationException```异常信息。 - -而在我们的应用程序中要做的就是自定义一个 Realm 类,继承AuthorizingRealm 抽象类,重载 doGetAuthenticationInfo(),重写获取用户信息的方法。 - -doGetAuthenticationInfo 的重写 - -``` java -@Override -protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) - throws AuthenticationException { - System.out.println("MyShiroRealm.doGetAuthenticationInfo()"); - //获取用户的输入的账号. - String username = (String)token.getPrincipal(); - System.out.println(token.getCredentials()); - //通过username从数据库中查找 User对象,如果找到,没找到. - //实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法 - UserInfo userInfo = userInfoService.findByUsername(username); - System.out.println("----->>userInfo="+userInfo); - if(userInfo == null){ - return null; - } - SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( - userInfo, //用户名 - userInfo.getPassword(), //密码 - ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//salt=username+salt - getName() //realm name - ); - return authenticationInfo; -} -``` - -**链接权限的实现** - -Shiro 的权限授权是通过继承```AuthorizingRealm```抽象类,重载```doGetAuthorizationInfo();```当访问到页面的时候,链接配置了相应的权限或者 Shiro 标签才会执行此方法否则不会执行,所以如果只是简单的身份认证没有权限的控制的话,那么这个方法可以不进行实现,直接返回 null 即可。在这个方法中主要是使用类:```SimpleAuthorizationInfo```进行角色的添加和权限的添加。 - -``` java -@Override -protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { - System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()"); - SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); - UserInfo userInfo = (UserInfo)principals.getPrimaryPrincipal(); - for(SysRole role:userInfo.getRoleList()){ - authorizationInfo.addRole(role.getRole()); - for(SysPermission p:role.getPermissions()){ - authorizationInfo.addStringPermission(p.getPermission()); - } - } - return authorizationInfo; -} -``` - -当然也可以添加 set 集合:roles 是从数据库查询的当前用户的角色,stringPermissions 是从数据库查询的当前用户对应的权限 - -``` java -authorizationInfo.setRoles(roles); -authorizationInfo.setStringPermissions(stringPermissions); -``` - -就是说如果在shiro配置文件中添加了```filterChainDefinitionMap.put(“/add”, “perms[权限添加]”);```就说明访问/add这个链接必须要有“权限添加”这个权限才可以访问,如果在shiro配置文件中添加了```filterChainDefinitionMap.put(“/add”, “roles[100002],perms[权限添加]”);```就说明访问```/add```这个链接必须要有“权限添加”这个权限和具有“100002”这个角色才可以访问。 - - -**登录实现** - -登录过程其实只是处理异常的相关信息,具体的登录验证交给 Shiro 来处理 - -``` java -@RequestMapping("/login") -public String login(HttpServletRequest request, Map map) throws Exception{ - System.out.println("HomeController.login()"); - // 登录失败从request中获取shiro处理的异常信息。 - // shiroLoginFailure:就是shiro异常类的全类名. - String exception = (String) request.getAttribute("shiroLoginFailure"); - System.out.println("exception=" + exception); - String msg = ""; - if (exception != null) { - if (UnknownAccountException.class.getName().equals(exception)) { - System.out.println("UnknownAccountException -- > 账号不存在:"); - msg = "UnknownAccountException -- > 账号不存在:"; - } else if (IncorrectCredentialsException.class.getName().equals(exception)) { - System.out.println("IncorrectCredentialsException -- > 密码不正确:"); - msg = "IncorrectCredentialsException -- > 密码不正确:"; - } else if ("kaptchaValidateFailed".equals(exception)) { - System.out.println("kaptchaValidateFailed -- > 验证码错误"); - msg = "kaptchaValidateFailed -- > 验证码错误"; - } else { - msg = "else >> "+exception; - System.out.println("else -- >" + exception); - } - } - map.put("msg", msg); - // 此方法不处理登录成功,由shiro进行处理 - return "/login"; -} -``` - -其它 Dao 层和 Service 的代码就不贴出来了大家直接看代码。 - - -### 测试 - -1、编写好后就可以启动程序,访问`http://localhost:8080/userInfo/userList`页面,由于没有登录就会跳转到`http://localhost:8080/login`页面。登录之后就会跳转到 index 页面,登录后,直接在浏览器中输入`http://localhost:8080/userInfo/userList`访问就会看到用户信息。上面这些操作时候触发```MyShiroRealm.doGetAuthenticationInfo()```这个方法,也就是登录认证的方法。 - -2、登录admin账户,访问:```http://127.0.0.1:8080/userInfo/userAdd```显示```用户添加界面```,访问```http://127.0.0.1:8080/userInfo/userDel```显示```403没有权限```。上面这些操作时候触发```MyShiroRealm.doGetAuthorizationInfo()```这个方面,也就是权限校验的方法。 - -3、修改 admin不 同的权限进行测试 - -Shiro 很强大,这仅仅是完成了登录认证和权限管理这两个功能,更多内容以后有时间再做探讨。 - -> 文章内容已经升级到 Spring Boot 2.x - - -**[示例代码-github](https://github.com/ityouknow/spring-boot-examples/tree/master/spring-boot-shiro)** - -**[示例代码-码云](https://gitee.com/ityouknow/spring-boot-examples/tree/master/spring-boot-shiro)** - - -参考: - -[Apache Shiro中文手册](https://waylau.gitbooks.io/apache-shiro-1-2-x-reference/content/) -[Spring Boot Shiro权限管理【从零开始学Spring Boot】](http://412887952-qq-com.iteye.com/blog/2299777) -[SpringBoot+shiro整合学习之登录认证和权限控制](http://z77z.oschina.io/2017/02/13/SpringBoot+shiro%E6%95%B4%E5%90%88%E5%AD%A6%E4%B9%A0%E4%B9%8B%E7%99%BB%E5%BD%95%E8%AE%A4%E8%AF%81%E5%92%8C%E6%9D%83%E9%99%90%E6%8E%A7%E5%88%B6/) diff --git a/_posts/2017/2017-06-28-timer-task-develop-1.md b/_posts/2017/2017-06-28-timer-task-develop-1.md deleted file mode 100644 index e8478a2760..0000000000 --- a/_posts/2017/2017-06-28-timer-task-develop-1.md +++ /dev/null @@ -1,287 +0,0 @@ ---- -layout: post -title: 定时任务发展史(一) -category: java -tags: [java] ---- - -定时任务是互联网行业里最常用的服务之一,本文给大家介绍定时任务在我司的发展历程。 - -linux系统中一般使用crontab命令来实现,在Java世界里,使用最广泛的就是quartz了。我司使用quartz就已经升级了三代,每一代在上一代系统之上有所优化,写这篇文章一方面介绍一下quartz的使用,另一方面可以根据此项目的变迁反应出我司平台架构升级的一个缩影。 - -定时任务的使用场景很多,以我们平台来讲:计息,派息、对账等等。 - -## quartz 介绍 - -Quartz是个开源的作业调度框架,为在Java应用程序中进行作业调度提供了简单却强大的机制。Quartz允许开发人员根据时间间隔(或天)来调度作业。它实现了作业和触发器的多对多关系,还能把多个作业与不同的触发器关联。Quartz可以集成几乎任何的java应用程序—从小的单片机系统到大型的电子商务系统。Quartz可以执行上千上万的任务调度。 - -Quartz核心的概念:scheduler任务调度、Job任务、JobDetail任务细节、Trigger触发器 - -- Scheduler:调度器,调度器接受一组JobDetail+Trigger即可安排一个任务,其中一个JobDetail可以关联多个Trigger -- Job:Job是任务执行的流程,是一个类 -- JobDetail:JobDetail是Job是实例,是一个对象,包含了该实例的执行计划和所需要的数据 -- Trigger:Trigger是定时器,决定任务何时执行 - -使用Quartz调度系统的思路就是,首先写一个具体的任务(job),配置任务的触发时间(Trigger),Scheduler很根据JobDetail+Trigger安排去执行此任务。 - -Quartz 定时器的时间设置 - -时间的配置如下:0 30 16 * * ? - -时间大小由小到大排列,从秒开始,顺序为 秒,分,时,天,月,年 *为任意 ?为无限制。由此上面所配置的内容就是,在每天的16点30分启动buildSendHtml() 方法 - -具体时间设定可参考 : - -"0/10 * * * * ?" 每10秒触发 -"0 0 12 * * ?" 每天中午12点触发 -"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发 -"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发 -"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发 -"0 0 06,18 * * ?" 在每天上午6点和下午6点触发 - - -## 第一代定时任务系统 - -第一代定时任务系统使用的很简单,全部按照当时spring推荐的配置方式来进行,开发于2014年初。 - -首先在配置线程池 - -``` xml - - - - - -``` - -配置定时任务工厂和任务基类 - -``` xml - - - - - - -``` - -- machineId:机器编码 -- recordErrorDetail:是否记录详细日志 - - -通过timerFactory 来获取具体的任务和触发器 - -``` java -public class TimerFactory implements BeanFactoryAware { - - private BeanFactory beanFactory; - - public Object getTask(String taskCode) { - return beanFactory.getBean(taskCode+"Task"); - } - - public Object getTrigger(String taskCode) { - return beanFactory.getBean(taskCode+"Trigger"); - } - - public void setBeanFactory(BeanFactory beanFactory) { - this.beanFactory = beanFactory; - } - - public BeanFactory getBeanFactory() { - return beanFactory; - } -} -``` - -baseTask集成了task,在里面做了一些基础的业务,比如定时任务开始执行的时候记录定时任务的开始执行时间,定时任务结束的时候记录执行的结果等。 - -``` java -public interface Task { - public void executeTask(); -} -``` - -配置具体的定时任务。以重发短信邮件的定时任务为例 - -``` xml - - - - - - - - - - - - - 0 0 0 * * ? - - -``` - -- resendSmsAndEmailTask:具体的定时任务类 -- resendSmsAndEmailJob:包装成具体的Job -- resendSmsAndEmailTrigger:设置具体执行的时间,包装成Trigger - -具体的task类,删掉了部分业务代码: - -``` java -public class ResendSmsAndEmailTask extends BaseTask{ - private static final String TASK_CODE = "resendSmsAndEmail"; - AtomicInteger ai = new AtomicInteger(0); - - public void execute(){ - try { - ai = new AtomicInteger(0); - // todo - }catch (Exception e) { - String exception = ExceptionUtils.getStackTrace(e); - logger.error("stat error with exception[{}].", exception); - this.recordTaskErrorDetail(this.taskRecordId, "ResendSmsAndEmailTask-" + e.getMessage(), exception); - }finally{ - this.modifyTaskRecord(ai.get(), taskRecordId); - } - } - - public String getTaskNo() { - return TASK_CODE; - } -} -``` - -最后配置scheduler任务调度 - -``` xml - - - - - - - - - -``` - -DynamicJobAssembler类代码: - - -``` java -public class DynamicJobAssembler { - - private static Logger logger = LoggerFactory.getLogger(DynamicJobAssembler.class); - - @Resource - Scheduler scheduler; - - @Resource - TimerFactory timerFactory; - - @Resource - TaskDao taskDao; - - public void init() { - logger.info("start to assemble task from db."); - List tasks = this.taskDao.getAllTask(); - if (tasks == null || tasks.size() <= 0) { - return; - } - - Map jobNameMap = this.getAllJobNames(); - for (TaskEntity task : tasks) { - logger.debug(task.toString()); - CronTriggerBean taskTrigger = (CronTriggerBean) timerFactory.getTrigger(task.getTaskNo()); - if (taskTrigger != null) { - if (!task.getSchedulerRule().equals(taskTrigger.getCronExpression())) { - try { - taskTrigger.setCronExpression(task.getSchedulerRule()); - } catch (ParseException e) { - logger.error("db task's cronExpression parse error:{}", e.getMessage()); - } - try { - logger.info("rescheduleJob jobName:{}",task.getTaskNo()); - scheduler.rescheduleJob(task.getTaskNo() + "Trigger", Scheduler.DEFAULT_GROUP, taskTrigger); - } catch (SchedulerException e) { - logger.error("revieved task[{},{}] reschedule error:{}", task.getTaskNo(), task.getSchedulerRule(), e.getMessage()); - } - } - jobNameMap.remove(task.getTaskNo() + "Job"); - } - } - - if (jobNameMap != null) { - logger.info("====================================="); - logger.info("Jobs need to be removed:" + Arrays.toString(jobNameMap.keySet().toArray())); - logger.info("====================================="); - for (String jobName : jobNameMap.keySet()) { - try { - scheduler.deleteJob(jobName, jobNameMap.get(jobName)); - } catch (SchedulerException e) { - logger.error("Error occured when deleting Job[{}] with Exception:{}", jobName, e.getMessage()); - } - } - } - logger.info("end to assemble task from db."); - } - - private Map getAllJobNames() { - Map jobNameMap = new HashMap(); - try { - String[] groups = scheduler.getJobGroupNames(); - for (String group : groups) { - String[] jobs = scheduler.getJobNames(group); - if (jobs != null) { - for (String job : jobs) { - jobNameMap.put(job, group); - } - } - } - } catch (SchedulerException e1) { - logger.error("Failed in geting all job names with exception:{}", e1.getMessage()); - } - return jobNameMap; - } - -} -``` - -定时任务表,执行的时候以表里面的数据为准,方便编辑。 - -``` sql -SET FOREIGN_KEY_CHECKS=0; - --- ---------------------------- --- Table structure for `zx_task_informations` --- ---------------------------- -DROP TABLE IF EXISTS `zx_task_informations`; -CREATE TABLE `zx_task_informations` ( - `id` bigint(20) NOT NULL AUTO_INCREMENT, - `version` int(11) NOT NULL COMMENT '版本号:需要乐观锁控制', - `taskNo` varchar(64) NOT NULL COMMENT '任务编号', - `taskName` varchar(64) NOT NULL COMMENT '任务名称', - `schedulerRule` varchar(64) NOT NULL COMMENT '定时规则表达式', - `frozenStatus` varchar(16) NOT NULL COMMENT '冻结状态', - `executorNo` varchar(128) NOT NULL COMMENT '执行方', - `timeKey` varchar(32) NOT NULL COMMENT '执行时间格式', - `frozenTime` bigint(13) DEFAULT NULL COMMENT '冻结时间', - `unfrozenTime` bigint(13) DEFAULT NULL COMMENT '解冻时间', - `createTime` bigint(13) NOT NULL COMMENT '创建时间', - `lastModifyTime` bigint(13) DEFAULT NULL COMMENT '最近修改时间', - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8 COMMENT='定时任务信息表'; - --- ---------------------------- --- Records of zx_task_informations --- ---------------------------- -INSERT INTO `zx_task_informations` VALUES ('1', '0', 'resendSmsAndEmail', '重发短信和邮件', '10 */10 * * * ?', 'FROZEN', '0', 'yyyy-MM-dd HH:mm', '0', '0', '0', '1486807296009'); -``` - -这就是我们第一代定时任务系统,达到了定期执行定时任务的效果,但是同样有两个缺点: - -- 1、定时调度和业务代码耦合在一起 -- 2、每次调整定时任务的时间需要重启服务 - diff --git a/_posts/2017/2017-06-29-timer-task-develop-2.md b/_posts/2017/2017-06-29-timer-task-develop-2.md deleted file mode 100644 index 1c12dd8424..0000000000 --- a/_posts/2017/2017-06-29-timer-task-develop-2.md +++ /dev/null @@ -1,241 +0,0 @@ ---- -layout: post -title: 定时任务发展史(二) -category: java -tags: [java] ---- - -第一代定时任务系统上线用了大概半年之后,就被我们厌倦了。于是就规划了第二代定时任务系统。 - - -## 第二代定时任务系统 - -第二代调度系统主要解决的是,避免每次修改定时任务的执行时间都需要重新启动整个项目。另外也可支持单独重新调度单个定时任务。 - -我们做了一个请求入口,当更新了库表里面的数据之后,重新请求一下特定的url就会自动重新加载定时任务。 - -使用scheduler删除定时任务 - -``` java -public void reScheduler() throws Exception { - // 取消现有的任务 - String[] jobNames = quartzUtil.getJobNames(); - if (null != jobNames && jobNames.length > 0) { - for (String jobName : jobNames) { - logger.info("----开始移除任务:" + jobName); - quartzUtil.cancelJob(jobName); - logger.info("----成功移除任务:" + jobName); - } - } - logger.info("现有任务已全部取消"); - this.initScheduler(); -} -``` - -``` java -public void cancelJob(String jobName) throws Exception { - scheduler.pauseTrigger(jobName, Scheduler.DEFAULT_GROUP); - scheduler.unscheduleJob(jobName, Scheduler.DEFAULT_GROUP); - scheduler.deleteJob(jobName, Scheduler.DEFAULT_GROUP); -} -``` - - -使用scheduler重新加载所有的定时任务。 - -``` java -job.setCronExpression(taskInfo.getSchedulerRule()); -String jobName = taskInfo.getTaskNo() + "Job"; -job.getJobDataMap().put(QuartzJob.OBJECT_ID,objectMethod); -job.setJobName(jobName); -logger.info("----开始部署任务:" + jobName); -quartzUtil.scheduleCronJob(job); -logger.info("----成功部署任务:" + jobName); -``` - -``` java -public void scheduleCronJob(QuartzJobEntity jobEntity) throws Exception { - JobDetailBean jobDetail = createJobDetail(jobEntity); - scheduler.addJob(jobDetail, true); - CronTriggerBean trigger = new CronTriggerBean(); - trigger.setCronExpression(jobEntity.getCronExpression()); - trigger.setJobDetail(jobDetail); - trigger.setName(jobEntity.getJobName()); - trigger.setJobName(jobDetail.getName()); - scheduler.scheduleJob(trigger); -} -``` - -如果只是重新调度某一个定时任务可以触发单独的调用 - -``` java -// 初始化某个加载定时任务 -public void initScheduler(TaskEntity taskInfo) throws Exception { - // 设置任务信息到quartz,并调度任务 - QuartzJobEntity job = new QuartzJobEntity(); - String objectName = taskInfo.getTaskNo()+"Task"; - String objectMethod = "executeTask"; - job.getJobDataMap().put(QuartzJob.OBJECT_NAME,objectName); - job.getJobDataMap().put(QuartzJob.OBJECT_METHOD,objectMethod); - // 单线程方式执行任务 - job.setJobClass(QuartzJob.class); - job.setCronExpression(taskInfo.getSchedulerRule()); - String jobName = taskInfo.getTaskNo() + "Job"; - job.getJobDataMap().put(QuartzJob.OBJECT_ID,objectMethod); - job.setJobName(jobName); - logger.info("----开始部署任务:" + jobName); - quartzUtil.scheduleCronJob(job); - logger.info("----成功部署任务:" + jobName); -} -``` - - -这样我们的第二代定时任务系统就完成了,第二代定时任务是在第一代定时任务的基础上改造的,增加了重新调度所有定时任务和单个定时任务。 - -> 第二代定时任务系统的缺点是:定时调度和业务代码耦合 - - -## 第三代定时任务系统 - -第二代定时任务上线没有多久,我们就意识到有很多的子系统也需要定时任务,比如订单系统需要45分钟不支付的订单失效,监控系统需要定时扫描是否有业务报警,统计系统需要定时去统计一些数据,但是如果我们给每一个子系统都做一个定时任务的话,就不太合理,很分散。 - -于是计划开发一个统一的定时任务调度中心,负责整个平台中所有的定时任务的调度,另外规划了监控系统,来监控和分析每次定时任务的执行结果和执行时间等信息。为了更好的管理定时任务开发了简单的管理界面。如下: - - -![](http://favorites.ren/assets/images/2017/quartz-01.png) - -根据上图可以看出,通过这个管理界面我们可以非常方便的去修改、启动、暂停定时任务。别的系统如果需要定时任务,可以随时在页面去添加,全部界面化操作,不需要重新启动项目等。 - -点击详情可以清晰的查看定时任务的上次执行情况 - - -![](http://favorites.ren/assets/images/2017/quartz-02.png) - - -定时任务的支持的调度方式分有两种:http和mq,我们一般建议使用mq。 - -- http :使用http一般适用于用时特别少的定时任务。或者接收请求之后立刻返回结果,重新启动另外一个线程去执行具体的业务,业务执行完成之后在通过http回调返回执行结果。 - -- mq :使用mq的话,调度系统和业务系统的交互就完全异步来执行,调度系统定时触发后,发送MQ消息给业务系统,业务系统接收到消息开始执行业务,执行完毕之后,再发送MQ系统通知调度系统的执行结果。 - - -**主要核心代码** - -初始化加载 - -``` java -public void initScheduler(){ - List taskList = taskInformationsDao.getTaskList(); - Scheduler scheduler = schedulerBean.getScheduler(); - for(TaskInformationsEntity task : taskList){ - try { - this.scheduler(task, scheduler); - } catch (Exception e) { - logger.error("定时:" + task.getTaskNo() + "启动失败"); - } - } -} -``` - -遍历调度 - -``` java -public void scheduler(TaskInformationsEntity task,Scheduler scheduler){ - TriggerKey triggerKey = TriggerKey.triggerKey(task.getTaskNo(), Scheduler.DEFAULT_GROUP); - JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class).withDescription(task.getTaskName()).withIdentity(task.getTaskNo(), Scheduler.DEFAULT_GROUP).build(); - jobDetail.getJobDataMap().put("targetObjectId", task.getTaskNo()); - jobDetail.getJobDataMap().put("executorNo", task.getExecutorNo()); - jobDetail.getJobDataMap().put("sendType", task.getSendType()); - jobDetail.getJobDataMap().put("url", task.getUrl()); - jobDetail.getJobDataMap().put("executeParamter", task.getExecuteParamter()); - CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(task.getSchedulerRule()); - CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); - try { - scheduler.scheduleJob(jobDetail, trigger); - logger.info("task "+task.getTaskNo()+" schedulerRule :"+task.getSchedulerRule()+" reload succeed"); - } catch (Exception e) { - logger.error("scheduler--异常:",e); - throw new RuntimeException(); - } -} -``` - -添加定时任务 - -``` java -public String addScheduler(String key){ - TaskInformationsEntity entity = taskInformationsDao.getTaskByTaskNo(key); - if(null != entity){ - Scheduler scheduler = schedulerBean.getScheduler(); - try { - scheduler.deleteJob(new JobKey(key)); - this.scheduler(entity, scheduler); - entity.setFrozenStatus(TaskStatusEnum.UNFROZEN); - entity.setUnfrozenTime(DateUtil.getLastModifyTime()); - entity.setLastModifyTime(DateUtil.getLastModifyTime()); - taskInformationsDao.updateById(entity); - return "任务启动成功"; - } catch (Exception e) { - logger.info("异常:",e); - return "任务启动失败"; - } - }else{ - return "该任务编号不存在"; - } -} -``` - - -删除定时任务 - -``` java -public String delScheduler(String key){ - TaskInformationsEntity entity = taskInformationsDao.getTaskByTaskNo(key); - if(null != entity && TaskStatusEnum.UNFROZEN == entity.getFrozenStatus()){ - Scheduler scheduler = schedulerBean.getScheduler(); - try { - scheduler.deleteJob(new JobKey(key)); - entity.setFrozenStatus(TaskStatusEnum.FROZEN); - entity.setFrozenTime(DateUtil.getLastModifyTime()); - entity.setLastModifyTime(DateUtil.getLastModifyTime()); - taskInformationsDao.updateById(entity); - return "暂停任务成功"; - } catch (Exception e) { - logger.error("异常:",e); - return "暂停任务异常"; - } - }else{ - return "该任务编号不存在"; - } -} -``` - - - -重新加载定时任务 - -``` java -public String resumeScheduler(String key){ - TaskInformationsEntity entity = taskInformationsDao.getTaskByTaskNo(key); - if(null != entity){ - Scheduler scheduler = schedulerBean.getScheduler(); - try { - scheduler.deleteJob(new JobKey(key)); - this.scheduler(entity, scheduler); - return "重启成功"; - } catch (SchedulerException e) { - logger.info("异常:",e); - return "重启异常"; - } - }else{ - return "该任务编号不存在"; - } -} -``` - -项目已经开源,详细的代码请在github上面查看。 - -[zx-quartz](https://github.com/justdojava/zx-quartz) - - -其实最后这版定时调度系统,还是有很多的缺陷,http模式没有进行完善,开源的代码中有部分内部依赖的jar还没有去掉。开放出来仅仅做为交流使用,后期有时间的话再去慢慢完善。也欢迎各网友多提提建议,一起加入完善。 \ No newline at end of file diff --git a/_posts/2017/2017-06-30-technology-stack.md b/_posts/2017/2017-06-30-technology-stack.md deleted file mode 100644 index 52d71ea139..0000000000 --- a/_posts/2017/2017-06-30-technology-stack.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -layout: post -title: 百亿互金平台技术栈大起底 -category: arch -tags: [arch] ---- - -技术栈(technology stack)就是一个公司的透视镜,从某些程度上可以展示出公司的技术实力。从技术桟也可以看出整个平台的技术要素,平台大小规模等,今天来给大家分享我司的技术全家桶。 - - -![](http://favorites.ren/assets/images/2017/architecture/programming-languages.jpg) - -## 题外话 - -今天是一个特殊的日子,我就多说两句,2017年过半了,大家的年终计划都执行的怎么样?而对于我还有另一层的意思,就是我终于要离职了。 - -今天是我在这家公司的最后一个工作日。以前每次和朋友聚会都会问,最近发展的怎么样,在那家公司?我说还在xx,他们就开玩笑说,强哥你准备把公司干倒呀。从写下一行代码到成为公司技术的负责人我用了三年的时间,期间经历很多事情,但总体还是收获了很多。看着公司三年多的变迁,也有很多感慨,但是目前的这个阶段也不方便多说。 - -公司在14、15年的时候发展势头很好,一度有冲击一线互金平台的趋势,互联网行业就是这样,不进则退,短短两年就脱离了互金的一线大本营。这两年发生了什么会导致公司迅速掉队呢?公司文化绝对是第一原因。公司文化就像一个人的气质一样,从开始就决定了往后的发展,公司文化也是一种风气,干劲十足、人浮于事、混日子都会传染,特别如果老板都是打工心态的话,公司肯定做不好。 - -两个大家肯定感兴趣的问题:为什么离开公司,原因只能回答:一声叹息,以后有机会可以给大家慢慢道来。我将去那家公司?已经有下家,暂时也不方便告知,望见谅。 - -## 总览 - -回到主题,这两天已经提了离职,闲来无事就想着还可以总结总结什么,平台架构、事故解决方案等以前都写过了,这次就主要写写我们都用过那些技术吧。我分了五块内容来介绍我们的技术栈:前端、后端、中间件、运维和工具。画了一个思维导图方便大家整体预览。 - - -![](http://favorites.ren/assets/images/2017/architecture/technology-stack.png) -[点击这里看大图](http://favorites.ren/assets/images/2017/architecture/technology-stack.png) - -接下来就展开来说 - - -## 前端 - - -![](http://favorites.ren/assets/images/2017/architecture/frontend.png) - - -我司的前端比较简单主要分为了三大块:PC前端、移动端、模板引擎。 - -- PC前端 主要使用了H5、JS,还有很多其它的组件,但以前两者为主。少量的使用过angularjs,最后效果不是特别好,放弃了 -- 移动端 分了三块:安卓、IOS、WAP。安卓前期主要以Java语言为主,现在慢慢在考虑kotlin;IOS以Objective-c为主,少量使用Swift;WAP又称H5,用于微信或者手机浏览器,也是使用Html5、js、少量使用了VUE,H5端的一些JS组件和PC会有不同,一般都有对应的替代品比如:使用zepto替代jquery。 -- 模板引擎 前期一直使用的是Beetle,大量使用springboot后替换为Thymeleaf,Thymeleaf使用体验很不错。 - - -## 后端 - -后端以开发语言的角度给大家介绍 - - -![](http://favorites.ren/assets/images/2017/architecture/backend.png) - - -后端使用的开发语言有:php、golang、python和Java。 - -- php 我们公司的前端的网站都是使用php开发,框架主要使用了thinkphp,小项目试验性的用了laravel。 -- golang 主要用于大数据,使用gin框架,用beego做过一个后台。 -- python 没有在公司用过,自己写小爬虫玩。 -- java 公司最主要的开发语言,核心系统、支撑系统、服务组件均使用Java开发,下面详细介绍一下。 - -Java技术栈比较多,这里挑选了几个具有代表性的来讲: - -- spring 做Java开发的,几乎离不开spring全家桶了,不需要多介绍。 -- alibaba 阿里这两年非常牛逼,也开源了不少的东西,主要使用过dubbo和druid,都很优秀。 -- apache 如果说搞Java的离不开spring,那么搞开发的就离不开apache,我们主要使用了commons、cxf、zookepper等。 -- orm框架 基本以mybatis为主,hibernate和jpa为辅的模式。 -- quartz 定时任务使用的quartz - - -## 中间件 - -这里面是比较泛的中间件集合,把相关的组件也都包含进来,主要分为:数据库、web容器、消息、缓存、文件服务器和安全。 - - -![](http://favorites.ren/assets/images/2017/architecture/middleware.png) - -- 数据库 业务主要使用mysql,需要跑批统计的离线数据由tungsten replicator同步到mongodb。 -- web容器 php使用的apache,Java使用的tomcat,静态资源代理使用的是nginx -- 消息 最开始使用activemq,后来架构升级全面替换为rabbitmq -- 缓存 满标控制使用memcached,后端业务缓存使用redis -- 文件服务器 最开始使用nginx做图片服务器,后来上线合同就全面使用了fastdfs -- 安全 https证书保证传输安全,shiro做权限控制,oauth做登录认证。 - - -## 运维 - -运维是平台的生命线,主要分为六部分:监控、负载均衡、CI(持续集成)、服务器、自动化部署和网络 - - -![](http://favorites.ren/assets/images/2017/architecture/ops.png) - -- 监控 主要使用了zabbix来监控服务器的各项指标,少量使用shell脚本和crontab -- 负载 使用VIP来做均衡负载,也就是LVS。 -- CI 持续集成工具主要使用了jenkins。Java依赖使用maven为主,gradle少量使用,版本控制svn为主,少量使用git -- 服务器 线上服务器大多使用的是centos 6.5。少量使用7.0。测试环境使用vsphere来虚拟化 -- 自动化部署 这块还在研究,备选有:puppet、ansible、saltstack。 -- 网络 使用Wireshark做网络分析 - - -## 工具 - -优秀的工具可以让工作事半功倍,节省很多时间。这里分开发、测试、数据库、画图和运维五个维度来介绍 - - -![](http://favorites.ren/assets/images/2017/architecture/tools.png) - -- 开发 Java常用的开发工具:eclipse和idea。前两年一直使用的是eclipse,但eclipse对spring boot支持的不够友好,后来就全面使用了idea;php开发工具比较多,我司开发人员主要使用phpstorm和zend,集成环境使用upupw;前端使用WebStorm和sublime3;golang开发工具liteide,IOS使用xcode。 -- 测试 自动化测试工具selenjum,性能测试使用jmeter或者loadrunner,开发人员一般使用jmeter。接口测试使用postman;移动端测试使用 appiumforandroid和appiumforIOS;抓包工具使用 firebug、MIniSniffer、Fiddler。 -- 数据库 mysql数据库可视化工具常用navicat,生产使用Workbench,少部分开发人员使用sqlyog和phpMyAdmin。mongodb使用MongoVUE,表设计用PowerDesigner。 -- 画图 架构图设计使用Visio,也尝试过processon;思维导图使用Xmind。 -- 运维 运维工具使用xftp或者SecureCRT - - - -> 本文所有示例图均使用xmind,需要原图的同学请在公众号回复:xmind。 \ No newline at end of file diff --git a/_posts/2017/2017-07-01-leave-to-talk.md b/_posts/2017/2017-07-01-leave-to-talk.md deleted file mode 100644 index 714c7a8cba..0000000000 --- a/_posts/2017/2017-07-01-leave-to-talk.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -layout: post -title: 离职闲谈 -category: life -tags: [life] -no-post-nav: true ---- - -我毕业后第一次准备辞职换工作的时候,一个前辈就给我说,换工作人不空,也是说要在跳槽之前把工作找好了,然后再辞职,这样在这家离职后,马上就可以去一下家公司了,中间不会有空档。 - -就这样接下来换工作基本都是按照这个节奏来走,当然除过刚来北京的那次。这样做的好处是,一方面不用冒着辞职后一段时间内没有收入,一方面如果外面工作机会不是那么好,也可以选择再等等。也有一点点不好的地方,就是感觉自己一直在工作,没有留下一段思考的时间。我就是如此,这个公司今天辞职,下个公司第二天就上班,忙忙碌碌七年就已经过去。这次我想给自己留一段时间,那怕就只有两周,可以让自己好好放松一下,看看书写写文章什么的。 - -昨天下午办完离职手续后,就想着要不要给我们部门的小伙伴发一个告别的邮件,在以前的公司有同事发过,但自己一直没有写过。后来想了想好歹给大家说一声我要走了,最后简短写了几句: - -> 主题:最后一个工作日 -> 小伙伴们: -> -> 非常遗憾的对大家说,这是我在xx的最后一个工作日了。三年亲历了xx的从无到有,从小到大,真到走的这一天还是感触良多。能和大家在一起共事几年是我莫大的荣幸,xx技术团队,也是我呆过最有凝聚力、最有活力的团队,这一切都是大家共同努力的结果。 -> -> 14年、15年平台刚上线的时候,研发测试任务繁重天天加班,也搞过几个通宵,现在想起来,和大家一起拼搏的时光还是很棒。16年到现在众信算是慢慢慢了下来,我也因为家庭等各方面的变故有一阵非常消沉,工作基本上都让大家扛了,在此感谢大家。 -> -> 做为一个纯粹的技术人,我们的追求是什么?这是我最近一直在思考的一个问题,就是安心的去干活,在工作中可以找到自己的成就感。但是在xx慢慢的我已经很难找到这种感觉了,其它的干扰因素太多,因此我选择了离开。 -> -> 最后,感谢这么多年,大家对我工作的支持,希望大家继续支持 xx、oo的工作。 - -在公司工作的三年中,我们技术部门的同事每走一个人,我都请他们一起吃个饭道个别,到我离职的这天,当然也要请部门的兄弟姐妹一起吃个饭,最后一聚。吃饭喝酒自不用说,在大家热热闹闹的时候,有点恍惚,眼前的同事竟都是如此可爱。让我惊喜的一件事情是,大家一起送给了我一件全员签名的T恤,搞的我像退役的球员一样,我很喜欢。像波仔说的那样,有这样一群兄弟,有这样一段经历,值了! - - -![](http://favorites.ren/assets/images/2017/life/we.png) - - -![](http://favorites.ren/assets/images/2017/life/t-front.png) - - -![](http://favorites.ren/assets/images/2017/life/t-back.png) - -今天香港回归祖国二十周年,当时有一个画面记忆深刻,就是驻港部队和英国部队做交接的时候,我方一个威风凛凛军官高声说到:“我代表中国人民解放军驻香港部队接管军营。你们可以下岗,我们上岗。祝你们一路平安。“看到这个场景的时候,瞬间爱国情怀高涨。1997年7月0点0分香港正式回归祖国,全国一片欢腾。 - -二十年前我才十岁,二十年后我已三十岁,岁月如烟。 - - - - - - - - - - - - - - - diff --git a/_posts/2017/2017-07-03-pingjing-life.md b/_posts/2017/2017-07-03-pingjing-life.md deleted file mode 100644 index 66c2b83b9e..0000000000 --- a/_posts/2017/2017-07-03-pingjing-life.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -layout: post -title: 高中毕业,我想出去看看 -category: life -tags: [life] -no-post-nav: true ---- - -> 这是我身边一个朋友的真实经历 - -“擀面的”大家一阵哄笑,我有点懵,大家都在笑什么? -“擀面的”又有人高声学了一句,大家又是一阵哄笑。 -为安静满脸通红的对我们说,”你们不行!” -我还是很懵逼! - -后来我才知道,为安静在培训班的过道,想装x的说一句“on my god”,结果发音走调的飞上了天,就成了"擀面的"。我到现在也没想出两者发音有什么联系,那时候刚认识为安静没两天,就一直在想能把“on my god”发音成 "擀面的",是一个什么样的男人?"擀面的"后来就成我们开玩笑的一个梗。 - -## 1 - -为安静高三的时候有一个女同桌,快高考的时候有一天他突发感慨,就对着女同桌说:我想去南方,看看外面的世界。女同桌说:哎呀妈呀,老娘等你这句话已经很久了,走走走! - -为安静和女同桌在南方辗转六年去过很多地方,深圳东莞广州福建…他们在工厂里打过工,高档小区当过保安,酒店做过服务生。为安静曾经给我们吹牛,在酒店的那些日子里,什么牌子的避孕套他都用过。 - -六年的时间把女同桌变成了女朋友,她跟着他五湖四海的跑,干的事情也和为安静差不多,超市管理员、酒店服务生、糕点店的服务员。 - -终于有一天,他们觉得这样打一辈子工,未来也没有希望,就决定先回老家西安,然后再好好想想以后的人生之路。 - -## 2 - -我刚去培训班的时候,为安静坐在我的前面,当我不会用eclipse调试debug的时候,为安静大着嗓门说,这还不简单,上手给我演示了一遍。瞬间对他有了好感,同时觉得班里大家技术都很牛逼。 - -培训那年在夏天,天很热空调很不给力,那时候我胃不好,一吃东西就打嗝,需要喝大量的水把它压下去。由于喝水多,闲频繁的去打水比较麻烦,于是,我就去超市买了一个很大的水瓶,大概5、6升。拿到班里没几天,被为安静发现了,见人就说,强子准备把学费喝回来,每天能喝半桶纯净水。后来大家跟风,都买了很大的水瓶来打水,看来大家都想把学费喝回去。 - -两个多月后,情况发生了逆转,为安静天天开始拉着我给他讲程序,所以读大学根底还是挺重要的。但这家伙问题太多,讲多了有点烦躁,于是为安静每次就以下载了最新日本爱情动作片为由,吸引我去拷贝。就在为安静的那个小出租屋子里,两个大男人光着膀子,研究着技术与生活,也就在那个夏天,让我对苍老师有了最深刻的认识,当然了,为安静在这方面永远是我的老师。 - -为安静经常在教室里面吹牛,说他女朋友多么漂亮、温柔,他在家的时候怎么怎么样,只要他吼上一声,让女友干嘛就干嘛,我们一直保持着高度的怀疑。有一天我和虎成又去了为安静那里搞程序,他女友下班回来了,简单介绍了之后,她女友在那边玩手机,为安静大嗓门说了一句:给我做xx去,她女友没动,为安静又说了一句:听见了么,给我做XX去,她女友还是没有动,回了一句:爱吃不吃。我和虎成大笑,为安静给媳妇说,我本来还想在他俩面装一把,结果搞的我很没有面子。 - -为安静只有高中学历,到后期学习的时候比较吃力,但是他一直有一股子不服气的劲,不管是拉着别人去讲,还是自己搞到晚上两三点,总是激情满满。到了毕业答辩的日子,我返校了两周,回来之后我顺延到了下一个班,他跳到了下下一个班重新去学习一遍基础。有时候我从他们班门口经过的时候,还会听到他在讲,我们以前的班里面有一个娃,天天要喝半桶矿泉水,准备把学费喝回去,我一脸黑线。 - -## 3 - -为安静和媳妇刚回西安的时候也不知道要干啥,很迷茫,但是这个家伙爱上网,天天泡在网上思考未来。终于有一天,有大大的四个字飘在他眼前,为安静激动不已,仿佛看到了人生即将走向辉煌,轻松就业大把挣钱马上就能实现,那四个字就是:北大青鸟。 - -激动归激动,但毕竟要花好几万,为安静还是冷静了下来,仔细的在网上看了大量的资料,确定搞软件绝对是一个辉煌的事业,方向没错。那时候北大青鸟正是火爆的时候,到处都是青鸟的广告,为安静想这么大的阵势,应该不是骗人的吧,但他还是不放心,和培训机构老师做了深度的交流。 - - -> 为安静说,高中毕业可以学吗? -> 机构老师,我们这里都有初中生、中专生都有。 -> 我看网上说,公司招聘只要大专以上学历? -> 只要从北大青鸟出来的,学历就是大专,三年学的东西比上大学还多。 -> 学不会咋办? -> 包会,包分配,包学历 -> …… - -培训机构老师打消了为安静所有的疑虑,接下来为安静又挣扎了几天,还是去报了西安北大青鸟。天下第一大骗子培训机构不是盖的,除了培训费收费国际一流外,其它均是打了忽悠,并且不断的忽悠学员继续缴费学习下阶段的课程。为安静在里面呆了一年多,除了基本入门外,其它都很委婉,但是随着对IT行业的了解加深,更加坚定了要在这个行业里面混下去的想法。于是就在西安继续找其它培训,我也由于一些原因,刚好没去另外一家培训机构,于是我俩就在这里相遇了。 - -为安静的女友回西安后,也对未来有所思考,因为在南方搞过糕点,所以想自己再继续学学糕点看以后是否自己能开一家糕点店。但为安静报名培训机构的钱也不少,两个人也需要维持生活,开糕点店的事情一下子也不成熟,于是为安静的女友就去超市干了老本行,因此他们家总会有一些超市的赠品,我也非常够意思的不时从他们那里顺走一些洗漱用品,当然我也付出了代价,从此成了他们家的首席搬家工。 - - -## 4 - -培训机构毕业后,我们在一起的时间少了,但也会时不时的联系一下。我先毕业,去了一家关于电讯方面的公司,为安静毕业晚我一个月,应聘上了一家做电信相关的公司,需要出差,有什么问题的时候,我们也会在网上讨论一下。由于经常出差和老婆异地,工作也有一部分实施的部分,搞的他也很烦躁,一年后也跳槽了,去了西安很有名的一家外包公司做开发,薪资不错。 - -在这家公司工作没多久,就和她的女友在老家举办了婚礼,来到西安后,请我们这些同学、同事、朋友一起吃个饭。在给为安静红包的时候,我给为安静说,我今天要把红包钱吃回来,我俩大笑。酒席后为安静喝大了,我们坐着小蹦子三轮车前往鱼化寨的时候,为安静吐了一车,为安静媳妇使劲的给他擦着,脸上一脸心疼状一边说,不能喝,让你喝这么多。 - -再后来我就来到了北京,我们之间的联系就慢慢的少了,听一些朋友说,他在那家公司混的不错。偶尔还会打几个电话,问问长短,得知他在那家公司先是做着开发,后来因为沟通能力突出,慢慢的被提拔为项目经理,手底下也带有二十几号人,薪资也涨了几波,就是加班比较多。我说知足常乐吧,IT行业那有不加班的,那小子嘿嘿一笑说,你不行。 - -前两年突然有一天,为安静给我打电话说要聊聊,我说,啥情况,为安静说,我迷茫了,我说都tm工作4、5年的人了,还迷茫个毛线。他说,自从当了项目经理之后,沟通协调的工作太多了,写代码的机会就越来越少了,干了两年项目经理之后,突然有点小迷茫,到底要不要把技术重新捡起来,也了解了一下北京的IT行情,探讨一下来北京的可能性。 - -闲聊中,我得知这两年又做了一些售前的工作,专门给政府部门做汇报,并且PPT技术已经非常牛逼,那时候我刚开始做管理,ppt还向他请教了一番,现在就只记得传授的四大要点: - -> 做这项目的背景是什么? -> 为什么要做这个项目? -> 如何去做个项目? -> 项目做完后会带来那些收益? - - -## 5 - -为安静最终没有来北京,也没有换工作。我知道他也就是突然有一阵子思想开了火车,想趁着年轻挣扎一把,但是老婆孩子热炕头不是那么容易就舍弃的,并且他在那个公司也呆了那么久,有了一些积累,哪有那么容易说走就走。偶尔我也会这样,自己瞎想一阵子,到了真做决定的时候,还是要结合现实情况。 - -为安静最终在那家公司找到了平衡,又申请带起了项目,时不时的也开始写一些代码,同时也带着一帮子兄弟赶项目,从朋友圈发布的动态来看,工作干的风风火火很带劲。 - -为安静的媳妇在西安学了两年多的糕点,终于在家乡的县城开了一家糕点店,刚开业的时候为安静天天在朋友圈打广告,运营了一段时间后,生意很不错,经常有人开着车一定就是五六个,一到了周末,为安静就火急火燎的赶回县城帮助媳妇做生意。为安静的媳妇终于如了愿,开了自己的糕点店,成了老板娘。 - -为安静,他父母给他起名的时候,是希望他这一辈子,平平安安、干干净净。但他却天生一副好嗓门,说话高声高气,人也爱折腾,喜欢交朋友够义气,性格完全和他的名字相反。现在我慢慢理解了这个名字另外的一层含义,那就是:为何要安静? - -每每我想到很多年前,有一对高中毕业生,买了两张南下的火车票,去探索这个未知的大千世界,我的眼前就会升起一副画,画里面有两个年轻的学生,一个叫无畏,一个叫青春。 \ No newline at end of file diff --git a/_posts/2017/2017-07-06-big-case.md b/_posts/2017/2017-07-06-big-case.md deleted file mode 100644 index 33c4797f1f..0000000000 --- a/_posts/2017/2017-07-06-big-case.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -layout: post -title: 大案 -category: life -tags: [life] -no-post-nav: true ---- - -> 小时候身边的杀人案 - -我们总会觉得电视里面的剧情太狗血,恐怖电影太离奇,长大后才发现,我们真实的生活狗血程度远远大于剧情。 - -我的家乡在陕西的南部,算是在秦岭的山脉之中的一个小县城,在我很小的时候我们那里发生过很多大案,其中有三起大案就发生在我的身边,这三起大案发生的时间在1997年到2003年左右。有时我也会对发生的这些案子进行思考,为什么会出现这样的情况呢?和民风、社会习气、文化程度都有关系吗?我们看完这些事迹在说。 - -## 一 - -我们那里有一个特别大的村子叫郭村,是我们原上的第一大村,里面又分了很多的组,我的很多同学都是来自郭村。这个故事就发生在我的小学三年级同学郭某的家里,去年过年的时候和亲戚聊天,又聊到了这个事情,让我对这个案子又有了更详细的了解。 - -记忆中郭某是一个身材高大的小子,因为学习不好留级到了三年级,所以我们就到了一个班,在班里也很调皮总是欺负我们班里的同学。郭某的父亲其实不是我们本地人,老家在山西,因为郭某的爷爷辈家里面没有儿子,只有一个女儿,按照农村的习俗,就寻思着找一个上门女婿,当时郭某的父亲家家庭条件也不是很好,经过媒人的介绍郭某的父亲xx就来到郭村做了上门女婿。 - -农村不是家庭条件特别差的,一般不会让自己孩子去做上门女婿,因为一个男人入赘到女方家里生活,本身就意味着可能就低女方一等,在家庭出现矛盾的时候,一般家庭也会偏向女方。但这个也不是绝对,我的干爸也是上门女婿,家庭就非常的和睦,主要还是看人。 - -郭某的父亲自从入赘到了女方家里面,也算是勤勤恳恳干活卖力,挣钱养家都搞的不错,但是郭某的家庭太偏向他们的女儿,他们两口子一担出现什么矛盾,家里都是将郭某的父亲骂一顿,郭某父亲一边很痛苦,一边却又忍受了下来,想着以后有了孩子可能会好点。就这样郭某的父亲在郭村生活了十几年,生下了一儿二女,其中就有我的同学郭某。 - -这十几年郭某的母亲却仗着家里面人都帮衬着她,就更加变笨加利了,郭某的母亲也是农民,几乎不下地干活,最后两年倒学会了打麻将,刚开始是在村里面打一打,后来甚至搬到了家里打,郭某的父亲下地干活回来还要给她做饭。为这个他们两口子不但骂仗,也打了好几次,但每次一起冲突,家庭一致对他,甚至一起打他。郭某家的三个孩子,也被母亲和爷爷奶奶教唆着,说着父亲的种种不是,对父亲的尊重是一点都没有,当父母起冲突的时候,他们甚至一起帮忙打骂父亲。 - -郭某的父亲慢慢的绝望了,这些年他一直忍受着屈辱,村子里面人的指指点点,觉得自己活的不像一个男人,现在孩子也骂他不是东西,各种矛盾到了一个临界点。郭某的父亲从忍受到含有希望,从含有希望到绝望,从绝望到愤怒,从愤怒到想要报复,心里暗暗的筹划着,如何去报复这一家子的亲人! - -**郭某的父亲想了很久,终于决定要动手了!** - -在二十年前的那个冬天,凌晨两点郭某的父亲等一家人都沉睡的时候,拿起准备好的钢棍(农村架子车的中轴管)和斧头,先去老丈人的屋子,用钢棍将老丈母娘和老丈人在睡梦中打死,然后在回到自己的屋子用斧头在媳妇的头上一阵猛砍,两个孩子惊醒后吓得已经不会说话,他一不做二不休将两个孩子也相续杀掉。完了之后稍微收拾了一下屋子,将家人的尸体一个个拖到院子里面的旱井(我们那时候家家有旱井)中,然后锁上门逃之夭夭。 - -细心的朋友可能发现上面杀害的是两个孩子,而不是三个,不知道是郭某的父亲想给自己留个后,还是可怜他最小的儿子郭某,出事的那一天让郭某去他亲戚家玩了好几天,躲过了一劫。 - -事情发生了很多天后人们才知道,邻居看着这家人怎么几天都不见,起初邻居以为他们家去走亲戚过了,过了两天感觉不对,家里面养的猪饿的嗷嗷叫,走亲戚也不会不管猪呀。砸开门后也么发现人,但是有血迹,于是报了警。警察来了之后找到了尸体,在水里泡了几天都浮肿了,在院子里面摆了一排,很多人去看热闹。 - -这在我们县好多年没有出过这么大的案子,县里市里高度重视,派了好多警察来办案,那时候的社会没有摄像头啥的,警察也很难查人到底跑到了哪里,因为我们在郭村的隔壁,村里面也是高度的重视,每天晚上7点以后不允许出门,并且派了村民在门口站岗,以防他返村,警察到了晚上也是在我们原上到处巡逻。 - -大概过了十几天,警察在他的一个远房妹子家里面抓到了他,他也没做太多的反抗,回来之后对自己犯过的罪行也供认不讳,指认现场的时候很多村子里面的人都去看热闹,我太小家里么让去。没过多久就被枪毙了。郭某后来就被村里的叔叔家里养了,学也没有好好上,成了社会的混混,上初中的时候还找过我们,不是借钱就是想住租的小房子,后来听说因为偷窃也进去了好多次。 - - -## 二 - -这个案子有点临时性或者说是即发性的犯案。 - -老李家和老王家因为土地的事情有点小矛盾,那个时候这个情况在农村很普遍,谁在村子里面没有和别人拌过嘴的。有一天老王准备把自己院子里面的树砍掉,就坐在路边磨斧头,磨了大概半个小时,磨得光亮光亮的。这时候老李恰好在村子里面闲逛,就看到老王在磨斧头,就笑骂说,你x你妈的磨斧头干嘛!老王可能有点生气就说,我磨斧头准备杀你狗日的。 - -本来两人之前就过节,这下子也激怒了老李,老李挑衅说你狗日的有本事今天就把我砍了,还故意把头低了下来给老王让老王砍,老王者心中火烧呀,砍就砍,拿起斧头直接对着老李的头就砍了下去,一下子就把头砍成了两半,老李直接就挂了。 - -老王砍死老李之后,血冲大脑,想着一不做二不休,杀一个也是杀,不如多杀几个人。就拿起斧头,往村子里面走,看到了村子里面的一个妇女,直接上去就是一个一斧头砍到,然后继续往村子里面走,看到了学校隔壁药店的小孙,上去又是一斧头,小孙躲了一下,耳朵被砍掉了。这个时候村子里面其它人也看到了,大喊杀人了,赶紧来帮忙。 - -村民听到声音出来后,老王这时候已经杀红了眼,举着血淋淋斧头到处追着人砍,村里几个小伙子到家里面拿了农具(䦆头、铲子)开始追老王,把老王逼到了我们学校外面的操场,老王一边跑一边喊,我还没有杀够呢!村民打掉了老王的斧头,用绳子把老王结结实实的绑了,才开始打110。 - -警察来了之后处理了一下现场就把老王带走了,听说家里面的人想让警察查一查老王精神上是否有问题,不知道最后查了没,审查了几个月后,把老王枪毙了。 - - -## 三 - -杨某是我的小学同学,他家在一个大沟里面离学校稍微远一些,到了4年级之后才来到我们这里的中心小学,从4年级到6年级我们都在一个班级里面,我对他的印象不是很深,他本人比较内向,几乎三年也没有说过太多的话,人长的又黑又廋,家里条件也不是很好,小学六年级毕业后就不上学了。 - -我上初中的时候,杨某就去县城的工地上干活,他们那里的人要去县城的时候必须要从我家的门口经过,因此我有时候还会碰到几次,见面也会打个招呼,身体更黑更瓷实了,人还是少言寡语。 - -那时候大家都处于青春期,对性都很懵懂,青春期很躁动,得不到合理的宣泄就很容易干出一些冲动的事情。杨某本人性格内向,又处于青春荷尔蒙爆发期,又没有合理的宣泄或者发泄的渠道,对性的冲动、好奇一直压抑着,在这样的背景下悲剧就发生了。 - -有一天的早上,杨某去了村子里面的xx家,他和xx家都姓杨还有很远的一些亲属关系,经常会去串门,xx家有一个十几岁的女儿那天正好在炕上睡觉。杨某去的时候可能因为女孩还没有睡醒,大夏天的穿的少,父母下地去了,屋子里面就女孩一个人。杨某看到睡觉的女孩后,心里面就起了邪心,用手去摸女孩的身体,猥琐女孩。 - -女孩被惊醒后,大骂杨某,看我大回来不打死你狗日的,一边挣扎一边大喊,杨某这个时候非常害怕,拿起旁边的枕头就捂住了女孩的头部,女孩挣扎,杨某捂住不放,一会时间女孩就缺氧晕过去了。杨某放开女孩后,简单收拾了一下炕上的东西,就往出走,快走到门口的时候,女孩咳嗽了两声又醒了过来,又大骂杨某,你狗x的死定了,看我大回来不弄死你。 - -杨某心一沉,一不做二不休,在女孩家里面找了一根铁丝又扑了上去,用铁丝勒住女孩的脖子,女孩不断的挣扎,但力气毕竟太小,不一会就没气了,完了杨某用被子将女孩身体盖好,逃离屋子回到家。女孩家里面人从地里回来之后,喊女孩没人答应,揭开被子发现女孩已死,于是打110报了警。 - -强奸杀人案,在我们县城都算是惊爆大新闻了,来的都是老警察,侦查了一遍屋子的情况,了解了村民的情况后,要求把村子里面所有的小伙子(年龄从12岁到30岁)都喊到一起谈话,问在这段时间都干了什么?有没有人可以证明。问了完十几个小伙子后,将其中的两个带到了警局,其中就有我的小学同学杨某。 - -大家都议论纷纷,说谁tm这么缺德干出了这种事情,警察带走的两个小伙子不知道是不是凶手,第二天的时候警察把另外的一个小伙子放了回来,大家都纷纷猜测杨某就是凶手。其实当晚杨某回警局之后就全招了,对如何犯案以及细节都能对上。这个案子最后也上了我们当地的电视台,画面上播出了几秒杨某的画面,看到他的样子很平静,给警察演示着如何用铁丝勒死女孩。 - -没过多久,杨某就被枪毙了。 - - - -## 四 - -时隔多年,当我现在想起这些案子时候,还是心有余悸。他们都是非常普通的人、没有什么犯罪前科、平时就在你的身边,感觉人也挺好的没有什么不对,但突然有一天他们就成了十恶不赦的杀人犯!这就究竟是为什么呢? - -就像辱母杀人案,富士康十连跳有没有什么共性?是社会发展把人逼到这个份上了吗?我想了一下也不完全是,但也不能排除这一点,比如我所说的上面这几起大案: - -郭某的父亲因为遭受排挤,村民笑话最后杀了全家,放到现在大家会想大不了离婚远走高飞,其实当时的社会出去打工的人很少,大家的生机都是绑到了那一亩二分地上,根本就没有太多的选择。 - -老王一时激起,竟然杀害了村民老李,甚至杀红了眼继续砍杀村民,其实根本也是因为土地,大家都在那个贫瘠的社会中挣扎着,时间久了,人性是否会发生某些变化? - -我的小学同学杨某,地地道道农民的儿子,家里父母本本分分,他给人的印象也是老实巴交的,但是却成了一个强奸杀人犯。在青春期的时候,甚至不知道可以打手枪来解决青春的骚动,震惊的同时引人深思。 - -在落后的社会里,人就会野蛮,人性就会扭曲!任何事件的发展都离不开社会的大环境,对世俗的理解,伦理道德认同,这些个人也许就是这个时代的牺牲品。 - - - - - - - - - - - diff --git a/_posts/2017/2017-07-07-bigdata-collect.md b/_posts/2017/2017-07-07-bigdata-collect.md deleted file mode 100644 index f6253504ab..0000000000 --- a/_posts/2017/2017-07-07-bigdata-collect.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -layout: post -title: 大数据学习资料汇总 -category: bigdata -tags: [bigdata] -no-post-nav: true ---- - - -收集大数据相关的学习资料 - - -## 博客 - -- [纯洁的微笑](http://www.ityouknow.com) - -- [粉丝日志](http://blog.fens.me/series-hadoop-family/) - - -## 网站 - -http://bigdata.evget.com/ - - -## 开源项目 \ No newline at end of file diff --git a/_posts/2017/2017-07-16-operating-technology-blog.md b/_posts/2017/2017-07-16-operating-technology-blog.md deleted file mode 100644 index eb87d93dc8..0000000000 --- a/_posts/2017/2017-07-16-operating-technology-blog.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -layout: post -title: 技术博客那些事儿 -category: other -tags: [other] -lock: need ---- - -写文章是一个短期没有收益,长期收益很大的一件事情,但往往是很多人坚持不下来,特别是写文章的初期,刚写完文章没有人阅读会有一种挫败感,影响了后期写文章的积极性。这篇文章我给大家分享如何提升技术博客的影响力,如何去写技术博客,是否应该建立独立博客,如何去推广自己的文章,建立自己的品牌等。 - -好多人刚开始写文章最主要的目的有两个:第一,记录自己阶段性的学习成果;第二,打开一个让自己和大千世界交流的窗口。不管是以什么样的目的开始的,只要坚持写下去,就会源源不断的感受到写文章带来的好处: - -- 加深自己对技术的理解 -- 可以结交更多的朋友 -- 记录自己的技术轨迹 -- 分享让世界更美好 - -虽然有这么多好处,但对于目前的我来讲:写作是一种享受,它就是我的爱好之一,我喜欢写作、分享、交流的整个过程。 - -## 什么时候开始 - -知乎上有一个问题“技术人技术到什么程度才有资格去开通自己的博客?”上面有很多的答案,我的回答是“种一棵树的最好时机是十年前,其次是现在,开通技术博客同样如此”。 - -万事开头难,写技术文章也是,很多人会在起步这个阶段逗留很长的时间,在考虑要不要写技术文章,写什么样的题材,去哪里写技术文章,要不要搭建自己独立的博客等等,光是考虑这个问题就耽搁了很长的时间,其中我就是一个例子,早在14年的时候,就想好好写写技术文章,结果到了16年才真正的开始。 - -初期写文章的时候可以给自己定一些计划,比如一周一篇,坚持一段时间后,会找到自己的感觉和节奏,再进行调整。刚开始写文章的时候可能会想,写什么呢?有什么素材可以写?写的越多越会发现,其实生活中的一切都是素材,处处皆是文章。 - -## 写什么如何写 - -在技术文章的圈子里面有两类的文章最受欢迎,第一类是实战类的问题,描述在实际工作中的问题,解决问题的思路和技巧;第二类就是通俗易懂入门系列文章,方便初学者快速入门。当然为了更容易的上手,选择自己最熟悉的内容作为开始,可以起到事半功倍的效果。 - -### 实战类 - -实战类的文章写作思路大概是这样子的:背景->排查->解决->防止,以解决某个事故为例来介绍。 - -首先描述问题背景,如何发现的问题?在什么情况下发生的?造成了什么样的影响;排查过程,针对展现出来的异常,怀疑某方面出现了问题,需要做一些测试来验证,大多数情况下,不会一次就找到问题的本质,整个过程就需要不断的测试、研究、推断、验证,直到最后找到问题的根源;解决方案,根据上一个步骤找到的问题本质,在这个步骤中去解决,总结有哪几种解决方式,各种方式的优劣,最后采用了哪种解决方案;防止,解决问题之后,总结经验教训,防止下次此问题再次发生,采用了哪种监控措施和应对措施等。 - -实战类的文章最受大家欢迎,也最有价值,也许会有其他人也会遇到此类问题,搜到文章会有帮助,和网友交流后也会得到更多的解决方案。(真是只要人人都献出一份爱,世界将拥有美好明天呀 :)) - -### 入门系列 - -当你遇到某一个新技术的时候,或者说项目中需要引入一个新技术的时候,肯定需要对技术做调研,在调研的过程中,一般会写一些相关的demo来研究体验,可以将这个过程写下来,方便自己深入理解,也可以作为团队的培训教程。 - -入门类的文章写作的思路,首先描述一下技术背景,对比有那些类似技术,各有那些优势,为什么选择此技术?基础环境介绍,此技术的依赖环境是什么?需要做哪些技术准备,可以写写搭建环境的步骤,搭建完成之后,用一个最简单的hello world验证环境是否搭建成功。 - -到了快速上手的阶段,先写一两篇非常简单但可以突出其巨大优势的demo,让读者用以最低的成本来了解此技术的优势,比如spring boot最大的特别是完全注解快速集成;这些内容写完之后,需要对此技术进行进一步的研究,可以写一些解决特定问题的demo,比如spring boot和thymeleaf集成,写写thymeleaf页面布局或者crud的demo。 - -当这些特定领域的文章都写完之后,就到了综合实践的内容了,描述公司内部是如何整体来使用此技术的,最好可以利用此技术做一款开源软件,在解决实际问题中去使用它,比如学习vue实践,可以利用vue技术模仿开发网易云音乐的app,如果学习的是spring boot,可以利用spring boot开发一个网站。 - -### 小结 - -在写一篇文章之前,最重要的是先理清楚文章的思路和概要,可以先在书本上记录文章的大纲,写作过程中按照大纲的思路去执行;文章结构要清晰,根据大纲或者思路分为不同的段落,每个段落有重点描述内容;文中涉及到代码,尽量使用code的样式来展示,只展示核心代码片段,在文章末尾给出完整代码地址,最好是github地址方便大家关注和跟踪。 - -简洁大方颜值高的文章,会给读者如沐春风一样的感觉;试想一下如果你看到这么一篇文章,有背景介绍、思路清新、文章排版整洁、又提供了完整的示例代码,你会想看吗?写完一篇文章的时候,首先想一想,如果你自己看到这篇文章自己会喜欢吗?如果自己都不喜欢,那肯定还有优化的空间。 - -现在回过头来仔细想想,其实写文章和写代码的过程是一致的: 构思文章概要(设计),根据概要去写文章(编码),检查文章是否有错别字、条例是否清晰(测试)、调整文章结构(重构)、发布文章(投产),这样看来技术人写文章更有优势。 - -## 哪里写 - -现在可以写文章的地方太多了,所以选择会有很多,反而让人迷茫,哪里才是技术人员真正的聚集地。我发现很多爱写文章的技术人,首先会在一个平台去写文章,慢慢的在这个平台积累了一些名气之后,就会被各种编辑邀请或者是出于推广的目的,会在各个热门的平台下去同步自己的文章,然后引流到自己的独立博客,最后开通自己的公众号。 - -作为一个写文章的新手该如何选择呢?是搭建自己的独立博客还是先在某技术平台开始呢?其实这两个选择不冲突,可以两者一起同步,最重要的是开始写了。作为写作新手我建议先重点选择一个平台,了解这个平台的规则和技术氛围,如果各方面都和自己比较贴切,那就选择它写作发表文章,平台积累了一些名气之后,再选择开通自己的博客,因为个人刚开始写文章的时候,如果只是自己独立建站,那么几乎没有什么阅读量,选择平台的话,平台上会有海量的技术人员都聚集在这里,写出来的文章比较容易找到自己的读者,而且平台的交流氛围更好一些。 - -在IT技术平台中写文章首选博客园和csdn,这两个平台上都有海量的技术读者,重点推荐博客园,因为它更纯粹一些,csdn上面也有很多的大牛,但是这些年csdn的广告实在是太多了,特别影响阅读体验。第二阵营有简书、开源中国、知乎、掘金等,简书是这两年才火了起来,写作体验很不错,对代码的支持也很好,但简书技术类只是其中的一部分;开源中国博客频道markdown排版支持的也挺好,但博客只是平台的很小一部分;知乎不用介绍了,markdown不支持;掘金是新起来的一个技术类平台,刚开始专注分享,现在也可以写作发布文章。 - -建议写作的路线如下:在这些平台中,选择一个自己喜欢的圈子,当到很多人将你的文章转载到其它平台的时候,说明你的文章有价值,可以将自己的文章同步到其它的平台。如果文章不错,一段时间后,会培养一部分固定的读者,这时候就可以尝试着去搭建独立博客,技术人有自己的博客是多么酷的一件事情,想怎么改造就怎么改造,而且现在搭建一个独立博客太简单了,特别推荐利用github去搭建自己的博客,但是使用github建博客也有一点点缺点,百度搜索引擎支持的不是很好。 - -本人写文章的路线: - -博客园->同步到不同的平台->独立博客->开通个人公众号 - -这样有一个好处,就是读者会根据问题搜索到不同平台下的文章,根据文章会找到独立博客,根据博客会找到公众号,层层引导直到最后成为你的粉丝。 - -## 如何推广 - -啥?写技术文章还需要推广?可能你会这样想。 - -好酒也怕巷子深,如今写技术文章而且写的不错的人太多了,如果不太注重推广的话,文章很快淹没在知识的海洋中,阅读量很小,继而影响写文章的动力和激情。就我个人而言,写文章最大的乐趣就是和网友互动交流,如果阅读的人少了,交流自然不多,就会想我写的这些文章对别人到底有没有帮助,文章质量是不是很差?不断交流反馈才是持续写作的源动力。 - -**平台规则** - -到底如何推广呢,第一需要研究清楚平台的规则,比如我在博客园写作,如果文章可以发布到首页,阅读量会比没有发布到首页的文章高十倍,如果文章被编辑推荐到了首页“编辑推荐”,那阅读量会在此基础上再翻五倍。同样csdn首页,简书的专题都是类似的规则,在保持文章质量的前提下,持续的将文章推荐到首页是第一步。当你的文章持续保持高质量并且持续被编辑推荐,那么就有可能会成为平台的推荐博客或者博客专家,如果成为了推荐博客或者博客专家,那么平时博客的流量会比以前翻个倍数,文章也更容易被搜索引擎或者其它第三方网站所收录。 - -**第三方网站** - -这些年出了很多第三方技术分享网站,流量很大,典型的代表有技术头条,掘金等,csdn和segmentfault也有类似的头条分享但是流量都很小。具体的操作是,首先在各个平台注册账号,将自己觉得写的不错的文章按照网站录入的要求输入进去,一般有文章标题、文章链接地址、文章简介和分类等,大部分都需要审核,审核通过之后,才会展示到网站的首页。其中技术头条还有技术周报,将一些热门的文章总结到一起发送到订阅者的邮箱中,曾经有一段时间发现我的个人博客好多流量都来自于邮箱,就是因为有文章被推荐到了技术周刊中。 - -**其它** - -搜索引擎,搜索引擎是长期稳定的流量来源,跟踪我在博客园的博客流量会发现,长期最大的流量一直都是百度搜索,如果是自己搭建的独立博客也可以做SEO。 - -文章标题,好的文章标题可以引导读者点击查看,但千万不要做标题党,另外好的标题也是提升搜索引擎的方式之一。 - -统一标识,建议在各个平台、社交网站使用统一的ID、昵称、头像,方便读者记住你,比如我的id“ityouknow”,昵称“纯洁的微笑” ,有比较高的辨识度,方便读者识别记忆。 - -工具,利用一些流量统计工具,方便统计分析各个平台、独立博客的流量,像博客园和csdn都可以自定义js模块,在cnzz申请一个账户,生成对应博客的统计代码,将相关代码添加到博客中,这样就非常方便的查看每天有多少人访问了你的博客,来源是哪里,都看了那些内容这些信息,很棒。 - -版权信息,文章下面添加版权信息,要求别人转载文章的时候添加原文链接,这样别人转载你的文章后也会带来一些流量。我常用的版权声明信息: - -``` -作者:纯洁的微笑 -出处:www.ityouknow.com -版权所有,欢迎保留原文链接进行转载:) -``` - -## 最后 - -我们经常会说,字如其人,其实文章更如其人,个人博客展示了个人对技术、对生活的理解。在IT业内,技术博客是了解一个技术人最好的方式之一,所以经营好一个博客,就是经营好自己对外的形象。 - -写作有时候是痛苦的,辛苦几天写的文章,感觉并没有完全表达出自己对某些技术的理解,很沮丧;写完文章后反复核查语义是否正确、是否有错别字,常常修改好多遍,往往把自己都读吐了。(么办法语文是体育老师教的:)) - -写文章也是快乐,看到自己的文章帮助了很多人会很欣慰,看到自己写的文章引发大家的共鸣很有成就感,当看到很多读者留言的时候,想想可以通过这种方式和五湖四海的技术人在一起交流,so cool。 - -写文章是一种思考,对技术的思考,对生活的思考,对人生的思考。 - - diff --git a/_posts/2017/2017-07-24-hadoop-cluster-setup.md b/_posts/2017/2017-07-24-hadoop-cluster-setup.md deleted file mode 100644 index 8614a061a1..0000000000 --- a/_posts/2017/2017-07-24-hadoop-cluster-setup.md +++ /dev/null @@ -1,440 +0,0 @@ ---- -layout: post -title: hadoop分布式集群搭建 -category: hadoop -tags: [hadoop] ---- - -终于要开始玩大数据了,之前对haoop生态几乎没有太多的了解,现在赶鸭子上架,需要完全使用它来做数据中心,这是我的haoop第一篇文章,以后估计会写很多大数据相关的文章。另外有大数据实践经验的网友请联系我,有问题请教谢谢! - -Hadoop的搭建有三种方式,单机版适合开发调试;伪分布式版,适合模拟集群学习;完全分布式,生产使用的模式。这篇文件介绍如何搭建完全分布式的hadoop集群,一个主节点,三个数据节点为例来讲解。 - -## 基础环境 - -### 环境准备 - -1、软件版本 - -四台服务器配置,系统:centos6.5、内存:1G、硬盘:20G -四台服务器分配的IP地址:192.168.0.71/72/73/74 -规划:71用作主节点用作hadoop-master,其它三台为数据节点72、73、74用作hadoop-salve1~3 -jdk和生成保持一致使用1.7版本 -hadoop使用2.7.3版本,下载地址:http://apache.claz.org/hadoop/common/hadoop-2.7.3/hadoop-2.7.3.tar.gz - -2、host配置和主机名(四台) - -修改四台服务器的hosts文件 - -```vim /etc/hosts``` - -``` shell -192.168.0.71 hadoop-master -192.168.0.72 hadoop-slave1 -192.168.0.73 hadoop-slave2 -192.168.0.74 hadoop-slave3 -``` - -分别修改服务器的主机名:HOSTNAME,master为例说明 - -```vi /etc/sysconfig/network``` - -``` shell -HOSTNAME=hadoop-master -``` - -执行```reboot```后生效,完成之后依次修改其它salve服务器为: hadoop-slave1~3。 - -3、服务器安装jdk(四台) - -建议使用yum安装jdk,也可以自行下载安装 - -``` shell -yum -y install java-1.7.0-openjdk* -``` - -配置环境变量,修改配置文件```vim /etc/profile``` - -``` shell -export JAVA_HOME=/usr/lib/jvm/jre-1.7.0-openjdk.x86_64 -export PATH=$JAVA_HOME/bin:$PATH -export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar -``` - -使用souce命令让立刻生效 - -``` shell -source /etc/profile -``` - -### 免密登陆 - -一、首先关闭四台服务器的防火墙和SELINUX - -查看防火墙状态 - -``` shell -service iptables status -``` - -关闭防火墙 - -``` shell -service iptables stop -chkconfig iptables off -``` - -关闭SELINUX后,需要重启服务器 - -``` shell --- 关闭SELINUX -# vim /etc/selinux/config --- 注释掉 -#SELINUX=enforcing -#SELINUXTYPE=targeted --- 添加 -SELINUX=disabled -``` - -二、免密码登录本机 - -下面以配置hadoop-master本机无密码登录为例进行讲解,用户需参照下面步骤完成h-salve1~3三台子节点机器的本机无密码登录; - -1)生产秘钥 - -``` shell -ssh-keygen -t rsa -``` - -2)将公钥追加到”authorized_keys”文件 - -``` shell -cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys -``` - -3)赋予权限 - -``` shell -chmod 600 .ssh/authorized_keys -``` - -4)验证本机能无密码访问 - -``` shell -ssh hadoop-master -``` - -*最后,依次配置h-salve1~3无密码访问* - -二、hadoop-master本机无密码登录hadoop-slave1、hadoop-slave2、hadoop-slave3,以hadoop-master无密码登录hadoop-slave1为例进行讲解: - -1)登录hadoop-slave1 ,复制hadoop-master服务器的公钥”id_rsa.pub”到hadoop-slave1服务器的”root”目录下。 - -``` shell -scp root@hadoop-master:/root/.ssh/id_rsa.pub /root/ -``` - -2)将hadoop-master的公钥(id_rsa.pub)追加到hadoop-slave1的authorized_keys中 - -``` shell -cat id_rsa.pub >> .ssh/authorized_keys -rm -rf id_rsa.pub -``` - -3)在 hadoop-master上面测试 - -``` shell -ssh hadoop-slave1 -``` - -三、配置hadoop-slave1~hadoop-slave3本机无密码登录hadoop-master - -下面以hadoop-slave1无密码登录hadoop-master为例进行讲解,用户需参照下面步骤完成hadoop-slave2~hadoop-slave3无密码登录hadoop-master。 - -1)登录hadoop-master,复制hadoop-slave1服务器的公钥”id_rsa.pub”到hadoop-master服务器的”/root/”目录下。 - -``` shell -scp root@hadoop-slave1:/root/.ssh/id_rsa.pub /root/ -``` - -2)将hadoop-slave1的公钥(id_rsa.pub)追加到hadoop-master的authorized_keys中。 - -``` shell -cat id_rsa.pub >> .ssh/authorized_keys -rm -rf id_rsa.pub //删除id_rsa.pub -``` - -3)在 hadoop-slave1上面测试 - -``` shell -ssh hadoop-master -``` - -*依次配置 hadoop-slave2、hadoop-slave3* - -到此主从的无密登录已经完成了。 - - -## Hadoop环境搭建 - - -### 配置hadoop-master的hadoop环境 - -1、hadoop-master上 解压缩安装包及创建基本目录 - -``` shell -#下载 -wget http://apache.claz.org/hadoop/common/hadoop-2.7.3/hadoop-2.7.3.tar.gz -#解压 -tar -xzvf hadoop-2.7.3.tar.gz -C /usr/local -#重命名 -mv hadoop-2.7.3 hadoop -``` - -2、 配置hadoop-master的hadoop环境变量 - -1)配置环境变量,修改配置文件``` vi /etc/profile``` - -``` shell -export HADOOP_HOME=/usr/local/hadoop -export PATH=$PATH:$HADOOP_HOME/bin -``` - -使得hadoop命令在当前终端立即生效 - -``` shell -source /etc/profile -``` - -*下面配置,文件都在:```/usr/local/hadoop/etc/hadoop```路径下* - -2、配置core-site.xml - -修改Hadoop核心配置文件```/usr/local/hadoop/etc/hadoop/core-site.xml```,通过```fs.default.name```指定NameNode的IP地址和端口号,通过```hadoop.tmp.dir```指定hadoop数据存储的临时文件夹。 - -``` xml - - - hadoop.tmp.dir - file:/usr/local/hadoop/tmp - Abase for other temporary directories. - - - fs.defaultFS - hdfs://hadoop-master:9000 - - -``` - -**特别注意:如没有配置```hadoop.tmp.dir```参数,此时系统默认的临时目录为:```/tmp/hadoo-hadoop```。而这个目录在每次重启后都会被删除,必须重新执行format才行,否则会出错。** - -3、配置hdfs-site.xml: - -修改HDFS核心配置文件```/usr/local/hadoop/etc/hadoop/hdfs-site.xml```,通过```dfs.replication```指定HDFS的备份因子为3,通过```dfs.name.dir```指定namenode节点的文件存储目录,通过```dfs.data.dir```指定datanode节点的文件存储目录。 - -``` xml - - - dfs.replication - 3 - - - dfs.name.dir - /usr/local/hadoop/hdfs/name - - - dfs.data.dir - /usr/local/hadoop/hdfs/data - - -``` - -4、配置mapred-site.xml - -拷贝mapred-site.xml.template为mapred-site.xml,在进行修改 - -``` shell -cp /usr/local/hadoop/etc/hadoop/mapred-site.xml.template /usr/local/hadoop/etc/hadoop/mapred-site.xml -vim /usr/local/hadoop/etc/hadoop/mapred-site.xml -``` - -``` xml - - - mapreduce.framework.name - yarn - - - mapred.job.tracker - http://hadoop-master:9001 - - -``` - -5、配置yarn-site.xml - -``` xml - - - - yarn.nodemanager.aux-services - mapreduce_shuffle - - - yarn.resourcemanager.hostname - hadoop-master - - -``` - -6、配置masters文件 - -修改```/usr/local/hadoop/etc/hadoop/masters```文件,该文件指定namenode节点所在的服务器机器。删除localhost,添加namenode节点的主机名hadoop-master;不建议使用IP地址,因为IP地址可能会变化,但是主机名一般不会变化。 - -``` shell -vi /usr/local/hadoop/etc/hadoop/masters -## 内容 -hadoop-master -``` - -7、配置slaves文件(Master主机特有) - -修改```/usr/local/hadoop/etc/hadoop/slaves```文件,该文件指定哪些服务器节点是datanode节点。删除locahost,添加所有datanode节点的主机名,如下所示。 - -``` shell -vi /usr/local/hadoop/etc/hadoop/slaves -## 内容 -hadoop-slave1 -hadoop-slave2 -hadoop-slave3 -``` - -### 配置hadoop-slave的hadoop环境 - -下面以配置hadoop-slave1的hadoop为例进行演示,用户需参照以下步骤完成其他hadoop-slave2~3服务器的配置。 - -1)复制hadoop到hadoop-slave1节点 - -``` shell -scp -r /usr/local/hadoop hadoop-slave1:/usr/local/ -``` - -登录hadoop-slave1服务器,删除slaves内容 - -``` shell -rm -rf /usr/local/hadoop/etc/hadoop/slaves -``` - -2)配置环境变量 - -``` shell -vi /etc/profile -## 内容 -export HADOOP_HOME=/usr/local/hadoop -export PATH=$PATH:$HADOOP_HOME/bin -``` - -使得hadoop命令在当前终端立即生效; - -``` shell -source /etc/profile -``` - -依次配置其它slave服务 - -### 启动集群 - -1、格式化HDFS文件系统 - -进入master的~/hadoop目录,执行以下操作 - -``` shell -bin/hadoop namenode -format -``` -**格式化namenode,第一次启动服务前执行的操作,以后不需要执行。** - -2、然后启动hadoop: - -``` shell -sbin/start-all.sh -``` - -3、使用jps命令查看运行情况 - -``` shell -#master 执行 jps查看运行情况 -25928 SecondaryNameNode -25742 NameNode -26387 Jps -26078 ResourceManager -``` - -``` shell -#slave 执行 jps查看运行情况 -24002 NodeManager -23899 DataNode -24179 Jps -``` - -4、命令查看Hadoop集群的状态 - -通过简单的jps命令虽然可以查看HDFS文件管理系统、MapReduce服务是否启动成功,但是无法查看到Hadoop整个集群的运行状态。我们可以通过```hadoop dfsadmin -report```进行查看。用该命令可以快速定位出哪些节点挂掉了,HDFS的容量以及使用了多少,以及每个节点的硬盘使用情况。 - -``` shell -hadoop dfsadmin -report -``` - -输出结果: - -``` shell -Configured Capacity: 50108030976 (46.67 GB) -Present Capacity: 41877471232 (39.00 GB) -DFS Remaining: 41877385216 (39.00 GB) -DFS Used: 86016 (84 KB) -DFS Used%: 0.00% -Under replicated blocks: 0 -Blocks with corrupt replicas: 0 -Missing blocks: 0 -Missing blocks (with replication factor 1): 0 -...... -``` - -5、hadoop 重启 - -``` shell -sbin/stop-all.sh -sbin/start-all.sh -``` - -## 错误 - -在搭建完成启动的时候,发生过两个错误: - -1、 xxx: Error: JAVA_HOME is not set and could not be found - -这个错误意思没有找到jdk的环境变量,需要在hadoop-env.sh配置。 - -``` shell -vi /usr/local/hadoop/etc/hadoop/hadoop-env.sh -## 配置项 -export JAVA_HOME=/usr/lib/jvm/jre-1.7.0-openjdk.x86_64 -``` - -2、The authenticity of host '0.0.0.0 (0.0.0.0)' can't be established. - -解决方案关闭SELINUX - -``` shell --- 关闭SELINUX -# vim /etc/selinux/config --- 注释掉 -#SELINUX=enforcing -#SELINUXTYPE=targeted -— 添加 -SELINUX=disabled -``` - - -参考: -[Hadoop之完全分布式环境搭建](http://www.linuxidc.com/Linux/2015-03/114669p4.htm) - - diff --git a/_posts/2017/2017-07-25-hbase-cluster-setup.md b/_posts/2017/2017-07-25-hbase-cluster-setup.md deleted file mode 100644 index 53742f9c8a..0000000000 --- a/_posts/2017/2017-07-25-hbase-cluster-setup.md +++ /dev/null @@ -1,205 +0,0 @@ ---- -layout: post -title: hbase分布式集群搭建 -category: hbase -tags: [hbase] ---- - -hbase和hadoop一样也分为单机版、伪分布式版和完全分布式集群版本,这篇文件介绍如何搭建完全分布式集群环境搭建。 - -hbase依赖于hadoop环境,搭建habase之前首先需要搭建好hadoop的完全集群环境,因此看这篇文章之前需要先看我的上一篇文章:[hadoop分布式集群搭建](http://www.ityouknow.com/hadoop/2017/07/24/hadoop-cluster-setup.html)。本文中没有按照独立的zookeeper,使用了hbase自带的zookeeper。 - -## 环境准备 - -- hbase软件包: http://mirror.bit.edu.cn/apache/hbase/1.3.1/hbase-1.3.1-bin.tar.gz -- 完成hadoop集群环境搭建 - -## 安装hbase - -> 首先在hadoop-master安装配置好之后,在复制到从节点 - -``` shell -wget http://mirror.bit.edu.cn/apache/hbase/1.3.1/hbase-1.3.1-bin.tar.gz -#解压 -tar -xzvf hbase-1.3.1-bin.tar.gz -C /usr/local/ -#重命名 -mv hbase-1.3.1 hbase -``` - -配置环境变量```vim /etc/profile``` - -``` shell -#内容 -export HBASE_HOME=/usr/local/hbase -export PATH=$HBASE_HOME/bin:$PATH -#使立即生效 -source /etc/profile -``` - -修改系统变量ulimit - -``` shell -ulimit -n 10240 -``` - -## 配置文件 - -hbase 相关的配置主要包括hbase-env.sh、hbase-site.xml、regionservers三个文件,都在 /usr/local/hbase/conf目录下面: - -配置hbase-env.sh - -``` shell -vim hbase-env.sh -#内容 -export JAVA_HOME=/usr/lib/jvm/jre-1.7.0-openjdk.x86_64 -export HBASE_CLASSPATH=/usr/local/hbase/conf -# 此配置信息,设置由hbase自己管理zookeeper,不需要单独的zookeeper。 -export HBASE_MANAGES_ZK=true -export HBASE_HOME=/usr/local/hbase -export HADOOP_HOME=/usr/local/hadoop -#Hbase日志目录 -export HBASE_LOG_DIR=/usr/local/hbase/logs -``` - -配置 hbase-site.xml - -``` xml - - - hbase.rootdir - hdfs://hadoop-master:9000/hbase - - - hbase.cluster.distributed - true - - - hbase.master - hadoop-master:60000 - - - hbase.zookeeper.quorum - hadoop-master,hadoop-slave1,hadoop-slave2,hadoop-slave3 - - -``` - -配置regionservers - -``` shell -vim /usr/local/hbase/conf/regionservers -hadoop-slave1 -hadoop-slave2 -hadoop-slave3 -``` - -复制hbase到从节点中 - -``` shell -scp -r /usr/local/hbase hadoop-slave1:/usr/local/ -scp -r /usr/local/hbase hadoop-slave2:/usr/local/ -scp -r /usr/local/hbase hadoop-slave3:/usr/local/ -``` - -## 启动hbase - -> 启动仅在master节点上执行即可 - -``` shell -~/hbase/bin/start-hbase.sh -``` - -启动后,master上进程和slave进程列表 - -master中的信息 - -``` shell -[hadoop@master ~]$ jps -6225 Jps -2897 SecondaryNameNode # hadoop进程 -2710 NameNode # hadoop master进程 -3035 ResourceManager # hadoop进程 -5471 HMaster # hbase master进程 -2543 HQuorumPeer # zookeeper进程 -``` - -salve中的信息 - -``` shell -[hadoop@slave1 ~]$ jps -4689 Jps -2533 HQuorumPeer # zookeeper进程 -2589 DataNode # hadoop slave进程 -4143 HRegionServer # hbase slave进程 -``` - -因为hbase依赖于hadoop,因此启动和停止都是需要按照顺序进行 - -如果安装了独立的zookeeper - -启动顺序: ```hadoop-> zookeeper-> hbase``` -停止顺序:```hbase-> zookeeper-> hadoop``` - -使用自带的zookeeper - -启动顺序: ```hadoop-> hbase``` -停止顺序:```hbase-> hadoop``` - - -重启hbase - -``` shell -~/hbase/bin/stop-hbase.sh -~/hadoop/sbin/stop-all.sh -~/hadoop/sbin/start-all.sh -~/hbase/bin/start-hbase.sh -``` - -## 错误处理 - -在搭建的过程中,报了这么一个错误,错误信息如下: - -``` shell -Unhandled: org.apache.hadoop.hbase.ClockOutOfSyncException: Server hadoop-slave3,16020,1500526355333 - -Caused by: org.apache.hadoop.hbase.ipc.RemoteWithExtrasException(org.apache.hadoop.hbase.ClockOutOfSyncException): - org.apache.hadoop.hbase.ClockOutOfSyncException: - Server hadoop-slave3,16020,1500526355333 has been rejected; Reported time is too far out of sync with - master. Time difference of 77348ms > max allowed of 30000ms -``` - -看大概的意思是主节点连接从节点超时了。可能有两方面的原因,第一、linux服务器时间不一致导致,第二、由于网络其它原因导致连接的时间超长。 - -解决方案: - -第一个原因,修改各服务器时间保持一致。最终的解决方案是:设置一个定时使用ntp从某个服务器定时同步时间 - -``` shell -查看定时 -crontab -l -编辑 -crontab -e -# 内容 -0 */1 * * * /usr/sbin/ntpdate 192.168.0.12;/sbin/hwclock -w -``` - -手动执行 - -``` shell -#从 0.12同步时间 -/usr/sbin/ntpdate 192.168.0.12 -``` - -第二个原因,可以修改hbase默认的最大链接时间长一些。 - -HBase配置文件hbase-siter.xml中添加连接时长的属性 - -``` xml - - hbase.master.maxclockskew - 120000 - -``` - -参考: -[centos 6.4下hbase 1.0.1 分布式集群搭建](http://www.ixirong.com/2015/05/25/how-to-install-hbase-cluster/) diff --git a/_posts/2017/2017-07-28-hbase-shell.md b/_posts/2017/2017-07-28-hbase-shell.md deleted file mode 100644 index 0afcd5ec4d..0000000000 --- a/_posts/2017/2017-07-28-hbase-shell.md +++ /dev/null @@ -1,277 +0,0 @@ ---- -layout: post -title: HBase shell 命令介绍 -category: hbase -tags: [hbase] ---- - -HBase shell是HBase的一套命令行工具,类似传统数据中的sql概念,可以使用shell命令来查询HBase中数据的详细情况。安装完HBase之后,如果配置了HBase的环境变量,只要在shell中执行```hbase shell```就可以进入命令行界面,HBase的搭建可以参考我的上一篇文章:[hbase分布式集群搭建 -](http://www.ityouknow.com/hbase/2017/07/25/hbase-cluster-setup.html) - -## HBase介绍 - -### HBase简介 - -HBase的名字的来源于Hadoop database,即hadoop数据库,不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库,而且它是基于列的而不是基于行的模式。 - -HBase是一个分布式的、面向列的开源数据库,源于google的一篇论文[《bigtable:一个结构化数据的分布式存储系统》](https://static.googleusercontent.com/media/research.google.com/zh-CN//archive/bigtable-osdi06.pdf)。HBase是Google Bigtable的开源实现,它利用Hadoop HDFS作为其文件存储系统,利用Hadoop MapReduce来处理HBase中的海量数据,利用Zookeeper作为协同服务。 - -hbase提供了一个shell的终端给用户交互。使用命令hbase shell进入命令界面。通过执行 help可以看到命令的帮助信息。 - -### HBase的表结构 - -HBase以表的形式存储数据。表有行和列组成。列划分为若干个列族/列簇(column family)。 - - -![](http://favorites.ren/assets/images/2017/bigdata/hbase-table.png) - -如上图所示,```key1```,```key2```,```key3```是三条记录的唯一的```row key```值,```column-family1```,```column-family2```,```column-family3```是三个列族,每个列族下又包括几列。比如```column-family1```这个列族下包括两列,名字是```column1```和```column2```,```t1:abc```,```t2:gdxdf```是由```row key1```和```column-family1-column1```唯一确定的一个单元```cell```。这个```cell```中有两个数据,abc和gdxdf。两个值的时间戳不一样,分别是t1,t2, hbase会返回最新时间的值给请求者。 - -这些名词的具体含义如下: - -1、Row Key - -与nosql数据库们一样,row key是用来检索记录的主键。访问hbase table中的行,只有三种方式: - -- 通过单个row key访问 -- 通过row key的range -- 全表扫描 - -Row key行键 (Row key)可以是任意字符串(最大长度是 64KB,实际应用中长度一般为 10-100bytes),在hbase内部,row key保存为字节数组。 - -存储时,数据按照Row key的字典序(byte order)排序存储。设计key时,要充分排序存储这个特性,将经常一起读取的行存储放到一起。(位置相关性) - -注意: - -字典序对int排序的结果是1,10,100,11,12,13,14,15,16,17,18,19,2,20,21,…,9,91,92,93,94,95,96,97,98,99。要保持整形的自然序,行键必须用0作左填充。 - -行的一次读写是原子操作 (不论一次读写多少列)。这个设计决策能够使用户很容易的理解程序在对同一个行进行并发更新操作时的行为。 - -2、列族 column family - -hbase表中的每个列,都归属与某个列族。列族是表的chema的一部分(而列不是),必须在使用表之前定义。列名都以列族作为前缀。例如```courses:history```,```courses:math```都属于```courses```这个列族。 - -访问控制、磁盘和内存的使用统计都是在列族层面进行的。实际应用中,列族上的控制权限能帮助我们管理不同类型的应用:我们允许一些应用可以添加新的基本数据、一些应用可以读取基本数据并创建继承的列族、一些应用则只允许浏览数据(甚至可能因为隐私的原因不能浏览所有数据)。 - -3、单元 Cell - -HBase中通过row和columns确定的为一个存贮单元称为cell。由```{row key, column( = +