[{"content":"如何使用Time Machine备份苹果系统到NAS 主要参考的这篇文章\n如何使用 Time Machine 将文件从 Mac 备份到 Synology NAS？ - Synology 知识中心 ","permalink":"https://blog.jimersylee.com/posts/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8time-machine%E5%A4%87%E4%BB%BD%E8%8B%B9%E6%9E%9C%E7%B3%BB%E7%BB%9F%E5%88%B0nas/","summary":"\u003ch1 id=\"如何使用time-machine备份苹果系统到nas\"\u003e如何使用Time Machine备份苹果系统到NAS\u003c/h1\u003e\n\u003cp\u003e主要参考的这篇文章\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://kb.synology.cn/zh-cn/DSM/tutorial/How_to_back_up_files_from_Mac_to_Synology_NAS_with_Time_Machine\"\u003e如何使用 Time Machine 将文件从 Mac 备份到 Synology NAS？ - Synology 知识中心\u003c/a\u003e\n\u003cimg alt=\"Untitled.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527212113311.png\"\u003e\u003c/p\u003e","title":"如何使用Time Machine备份苹果系统到NAS"},{"content":"2024年阅读记录 设计数据密集应用\n一人公司起步的思维\n程序员修炼之道\n刘擎西方现代思想讲义\n创造\n程序员健康指南\n做对产品\nTidy First\n一人企业方法论\n两个月$12000 ARR实践之路\nThe First 20 Hours\nCTO说\n太白金星有点烦\n福格行为模型\n","permalink":"https://blog.jimersylee.com/posts/2024%E5%B9%B4%E9%98%85%E8%AF%BB%E8%AE%B0%E5%BD%95/","summary":"\u003ch1 id=\"2024年阅读记录\"\u003e2024年阅读记录\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"12638D03-46AE-444E-AEAA-4D8D0CE19CCF.jpeg\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527203616246.jpeg\"\u003e\u003c/p\u003e\n\u003cp\u003e设计数据密集应用\u003c/p\u003e\n\u003cp\u003e一人公司起步的思维\u003c/p\u003e\n\u003cp\u003e程序员修炼之道\u003c/p\u003e\n\u003cp\u003e刘擎西方现代思想讲义\u003c/p\u003e\n\u003cp\u003e创造\u003c/p\u003e\n\u003cp\u003e程序员健康指南\u003c/p\u003e\n\u003cp\u003e做对产品\u003c/p\u003e\n\u003cp\u003eTidy First\u003c/p\u003e\n\u003cp\u003e一人企业方法论\u003c/p\u003e\n\u003cp\u003e两个月$12000 ARR实践之路\u003c/p\u003e\n\u003cp\u003eThe First 20 Hours\u003c/p\u003e\n\u003cp\u003eCTO说\u003c/p\u003e\n\u003cp\u003e太白金星有点烦\u003c/p\u003e\n\u003cp\u003e福格行为模型\u003c/p\u003e","title":"2024年阅读记录"},{"content":"Mac的知识索引 MacOS的SIP Mac快捷键 Mac设置记录 20231105\n观察勿扰模式, 还是会唤醒, 16个小时消耗4%的电量 20231104 休眠还是掉电,设置了勿扰模式 20231011 关闭了SIP 删除了下列软件 iTerm: 终端 [using: **Aerial-屏幕保护程序**](https://www.notion.so/using-Aerial-4c0f1b74803347ce9fe775d6d10d332f?pvs=21) Pencil: 原型制作工具 Karabiner: 改键程序 20230930: 使用Time Machine备份Ventura 13.6; 升级到Sonoma 14.0 20231211: 参考https://github.com/paulmillr/encrypted-dns/tree/master, 安装了安全DNS, 使用的cloudflare的1.1.1.1 20231212\n删除软件 AppCode Steam++ OmniFocus: 任务管理工具 Typewriter keyboard: 键盘打字声 FruitJuice: 电池管理软件 hook: 不知道什么软件引入的 安装软件 Surge5.4.3: 网络监控与代理软件 20231225\n删除软件 Visual Studio AI-Chat桌面版 TripleA Klokki 20231227\n删除软件 ‣ 20230108\n安装软件 blackhole: 音频输入输出管理软件 lettura: 基于rust,tauri的开源RSS阅读器 ","permalink":"https://blog.jimersylee.com/posts/macbook%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C/","summary":"\u003ch2 id=\"mac的知识索引\"\u003eMac的知识索引\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"../../MacOS%E7%9A%84SIP.md\"\u003eMacOS的SIP\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"../../Mac%E5%BF%AB%E6%8D%B7%E9%94%AE.md\"\u003eMac快捷键\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"mac设置记录\"\u003eMac设置记录\u003c/h2\u003e\n\u003cp\u003e20231105\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e观察勿扰模式, 还是会唤醒, 16个小时消耗4%的电量\n20231104\u003c/li\u003e\n\u003cli\u003e休眠还是掉电,设置了勿扰模式\n20231011\u003c/li\u003e\n\u003cli\u003e关闭了SIP\u003c/li\u003e\n\u003cli\u003e删除了下列软件\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://www.notion.so/iTerm-fff3c48ce69a40fba1b62313d417b43a?pvs=21\"\u003eiTerm:  终端\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e[using: \u003ca href=\"https://github.com/JohnCoates/Aerial\"\u003e**Aerial\u003c/a\u003e-屏幕保护程序**](\u003ca href=\"https://www.notion.so/using-Aerial-4c0f1b74803347ce9fe775d6d10d332f?pvs=21\"\u003ehttps://www.notion.so/using-Aerial-4c0f1b74803347ce9fe775d6d10d332f?pvs=21\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.notion.so/Pencil-83087b2625f141fe9517a1ea939269b5?pvs=21\"\u003ePencil: 原型制作工具\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.notion.so/Karabiner-fd35b0aa4f6745f5be5bccc3e588c5d8?pvs=21\"\u003eKarabiner: 改键程序\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e20230930: 使用Time Machine备份Ventura 13.6;  升级到Sonoma 14.0\n20231211: 参考\u003ca href=\"https://github.com/paulmillr/encrypted-dns/tree/master\"\u003ehttps://github.com/paulmillr/encrypted-dns/tree/master\u003c/a\u003e, 安装了安全DNS, 使用的cloudflare的1.1.1.1\n20231212\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e删除软件\n\u003cul\u003e\n\u003cli\u003eAppCode\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.notion.so/Steam-9ad35eb9852a4facb44228bae6bd0be6?pvs=21\"\u003eSteam++\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.notion.so/OmniFocus-c6ebbb544c4c4758a60b0c2b7658c1ec?pvs=21\"\u003eOmniFocus: 任务管理工具\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.notion.so/Typewriter-keyboard-72a4f1295a6b480e9a8898ba8eaec2b3?pvs=21\"\u003eTypewriter keyboard: 键盘打字声\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.notion.so/FruitJuice-257d62241a124377974bd844fbc431b0?pvs=21\"\u003eFruitJuice: 电池管理软件\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003ehook: 不知道什么软件引入的\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e安装软件\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://www.notion.so/Surge5-4-3-432af3baef914488b4f8f170aa128fc0?pvs=21\"\u003eSurge5.4.3: 网络监控与代理软件\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e20231225\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e删除软件\n\u003cul\u003e\n\u003cli\u003eVisual Studio\u003c/li\u003e\n\u003cli\u003eAI-Chat桌面版\u003c/li\u003e\n\u003cli\u003eTripleA\u003c/li\u003e\n\u003cli\u003eKlokki\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e20231227\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e删除软件\n\u003cul\u003e\n\u003cli\u003e‣\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e20230108\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e安装软件\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://www.notion.so/blackhole-749f139891ad402fa59b207b97b1788a?pvs=21\"\u003eblackhole: 音频输入输出管理软件\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.notion.so/lettura-rust-tauri-RSS-95fda525cba94f8f90a8a9ddcf7b414d?pvs=21\"\u003elettura: 基于rust,tauri的开源RSS阅读器\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e","title":"MacBook使用手册"},{"content":"\n每个运动做10次，共5回，最重要的是要坚持循环训练(circuit training)的原则，最大限度地减少休息的时间。\n动作要领：选择适合自己的重量，选择\u0026quot;扛杠铃\u0026quot;的方式，背部挺直，保持竖脊肌的中立位和稳定性，双腿由直立慢慢弯曲半蹲，最后再回到直立状态。\n动作过程：用背阔肌的收缩力量将身体往上拉起，直到单杠触及或接近胸部。静止一秒钟，使背阔肌彻底收缩。然后逐渐放松背阔肌，让身体徐徐下降，直到回复完全下垂，重复再做。注意事项：把身体拉高时，尽量保持身体不要摆动，另外你可在腰上钩挂杠铃片来加重。\n双杠臂屈伸主要锻炼胸大肌下部和肱三头肌，一个男人是否经常锻炼，看看他的胸大肌就知道了，所以双杠臂屈伸可以帮你做到这点。\n动作要领：\n握双杠两臂伸直支撑在双杠上，两腿自然弯曲，双脚重叠，身体放松下垂，双臂屈肘使身体降至最低点，呼吸时双臂用力，将身体撑起。如果想练习胸大肌的下部，则需要使胸大肌的下部位垂直于地面。身体保持垂直放松，不要故意挺胸。如练习肱三头肌可以抬头挺胸，通过臀部向后伸展使身体稍向后倾斜。这时力量会转移到肱三头肌上。\n【丹尼尔的健身食谱】\n早餐：两个水煮蛋，两片面包片\n加餐：蛋白条、水果、坚果和葡萄干任选其一作为早餐与午餐间的零食\n午餐：肉或者鱼，配一份糙米饭或者烤土豆\n加餐：依然可以选择蛋白条\n晚餐：肉或者鱼，配有绿叶蔬菜，像沙拉、莴笋、菠菜或者西兰花这样的蔬菜\n","permalink":"https://blog.jimersylee.com/posts/%E5%85%8B%E9%9B%B7%E6%A0%BC%E7%9A%84%E5%81%A5%E8%BA%AB%E6%B3%95/","summary":"\u003cp\u003e\u003cimg alt=\"Untitled.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527205753010.png\"\u003e\u003c/p\u003e\n\u003cp\u003e每个运动做10次，共5回，最重要的是要坚持循环训练(circuit training)的原则，最大限度地减少休息的时间。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Untitled 1.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527205801756.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Untitled 2.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527205808443.png\"\u003e\u003c/p\u003e\n\u003cp\u003e动作要领：选择适合自己的重量，选择\u0026quot;扛杠铃\u0026quot;的方式，背部挺直，保持竖脊肌的中立位和稳定性，双腿由直立慢慢弯曲半蹲，最后再回到直立状态。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Untitled 3.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527205813754.png\"\u003e\u003c/p\u003e\n\u003cp\u003e动作过程：用背阔肌的收缩力量将身体往上拉起，直到单杠触及或接近胸部。静止一秒钟，使背阔肌彻底收缩。然后逐渐放松背阔肌，让身体徐徐下降，直到回复完全下垂，重复再做。注意事项：把身体拉高时，尽量保持身体不要摆动，另外你可在腰上钩挂杠铃片来加重。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Untitled 4.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527205819890.png\"\u003e\u003c/p\u003e\n\u003cp\u003e双杠臂屈伸主要锻炼胸大肌下部和肱三头肌，一个男人是否经常锻炼，看看他的胸大肌就知道了，所以双杠臂屈伸可以帮你做到这点。\u003c/p\u003e\n\u003cp\u003e动作要领：\u003c/p\u003e\n\u003cp\u003e握双杠两臂伸直支撑在双杠上，两腿自然弯曲，双脚重叠，身体放松下垂，双臂屈肘使身体降至最低点，呼吸时双臂用力，将身体撑起。如果想练习胸大肌的下部，则需要使胸大肌的下部位垂直于地面。身体保持垂直放松，不要故意挺胸。如练习肱三头肌可以抬头挺胸，通过臀部向后伸展使身体稍向后倾斜。这时力量会转移到肱三头肌上。\u003c/p\u003e\n\u003cp\u003e【丹尼尔的健身食谱】\u003c/p\u003e\n\u003cp\u003e早餐：两个水煮蛋，两片面包片\u003c/p\u003e\n\u003cp\u003e加餐：蛋白条、水果、坚果和葡萄干任选其一作为早餐与午餐间的零食\u003c/p\u003e\n\u003cp\u003e午餐：肉或者鱼，配一份糙米饭或者烤土豆\u003c/p\u003e\n\u003cp\u003e加餐：依然可以选择蛋白条\u003c/p\u003e\n\u003cp\u003e晚餐：肉或者鱼，配有绿叶蔬菜，像沙拉、莴笋、菠菜或者西兰花这样的蔬菜\u003c/p\u003e","title":"克雷格的健身法"},{"content":"我用过的手机 整理网站书签的时候, 发现一个统计使用过的手机网站, 登录后, 看了一下,之前填写的手机使用数据, 蛮有意思.\n主线就是NOKIA→XIAOMI→APPLE\n我的主页在这https://mowned.com/jimmy-lee/, 你们也可以统计下.\n","permalink":"https://blog.jimersylee.com/posts/%E6%88%91%E7%94%A8%E8%BF%87%E7%9A%84%E6%89%8B%E6%9C%BA/","summary":"\u003ch1 id=\"我用过的手机\"\u003e我用过的手机\u003c/h1\u003e\n\u003cp\u003e整理网站书签的时候, 发现一个统计使用过的手机网站, 登录后, 看了一下,之前填写的手机使用数据, 蛮有意思.\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"76f6ec76e15e4392fd4d6d1a33b45bdc.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527211453440.png\"\u003e\u003c/p\u003e\n\u003cp\u003e主线就是NOKIA→XIAOMI→APPLE\u003c/p\u003e\n\u003cp\u003e我的主页在这\u003ca href=\"https://mowned.com/jimmy-lee/\"\u003ehttps://mowned.com/jimmy-lee/\u003c/a\u003e, 你们也可以统计下.\u003c/p\u003e","title":"我用过的手机"},{"content":"2022孩子生日给孩子的一封信 五周岁了，我觉得跟你们可以沟通交流了, 因此有个想法，以后每年你们生日的时候都给你们写一封信。\n不要因为别人夸你聪明就骄傲自满, 也不能因为别人说你笨而哭泣.\n因为别人夸你, 可能是出于礼貌,也可能是出于客套,也有可能有所目的,需要从你这里获取一些利益. 你们需要认识到, 聪明并不是把事情做好的决定性因素, 坚持与反思才能获得进步. 很多有小聪明的人, 做不成大事, 反而是比较固执的, 在事情上下笨功夫的人, 才能获得成功.\n而且, 大部分人都喜欢和普通人打交道. ⽐如我问你：“你是喜欢和聪明⼈做⽣意，还是和傻⼦做⽣意呢？”你恐怕会告诉我是后者，因为你觉得傻⼦的钱好挣。 如果很多⼈都这么想，那么笨⼈做成⽣意的机会就多了。从那以后，我恪守⼀个原则，不论对⽅挣多少 钱，我只挣⾃⼰那⼀份就好，不要贪图对⽅的任何⼀点⼉利益。这样⼀来，⽣意就能持久。你和⼩朋友 交往时，也不必怕吃亏，和⼩朋友换东⻄时，多⼀点⼉少⼀点⼉也没有关系，因为如果每⼀次都是你占 了便宜，时间⼀⻓，⼩朋友就不愿意再和你打交道了。通常，别⼈觉得你⽼实，就很放⼼地和你做朋 友，觉得你太聪明了，就会害怕和你来往.\n希望你们对世界上所有的事情保持批判与怀疑精神\n不要看到听到别人说对就认为是对的, 需要经过自己的思考.因为这个社会, 很多你看到的听到的, 是有些人故意想让你看到的, 比如诱人的广告, 好看的动画片, 看上去很好吃的食物, 几乎所有东西都有一个包装, 而我们需要有火眼金睛, 让目光穿过这些包装, 看到本质的东西. 比如有的食物很好吃, 其实它加了很多糖, 香料, 让食物更加美味的添加剂, 而这些东西, 没有营养, 对身体有害, 虽然有些食物尝起来并不美味, 但是它是食物本来的味道, 比如说煮鸡蛋, 煮米饭, 炒青菜. 我们不能让自己习惯这些人为添加的味道而忽略了食物的本味.\n希望你们能够更加勇敢, 面对挫折, 百折不挠.\n没有人可以轻易做成功任何事,都需要付出努力, 就算是走路,跑步, 都是需要学习如何走得更稳,跑得更快. 简单的事谁都会做, 那我们跟别人就没有区别. 这个世界上, 各个领域, 都是做的最好的人才能推动进步, 但是要做得最好, 就需要付出比别人多的努力. 说实话, 把事情做一般般是简单的, 大部分人都能做到这样, 而且这也是人的本性, 喜欢做简单的事, 抗拒难的事. 但是事实是, 在克服困难的过程种, 我们才能学到更多知识和技能, 而且能让我们的心更加强壮.\n需要带着问题看世界, 才能解决问题\n大科学家费曼曾经说过 你的脑海中，必须始终记着十几个你最喜欢的问题，平时它们处于休眠状态。每次你听到或读到一个新发现时，就针对这十几个问题中的每一个进行测试，看看对解答问题是否有帮助，很多突破就是这样产生的。\n需要带着问题看世界, 这样子的话, 你会建立世界和你的问题的联系, 就如孕妇效应一样, 因为自己怀孕了, 就会突然觉得生活中有很多怀孕的人. 因为自己喜欢穿恐龙的衣服, 就会发现别人也穿恐龙衣服. 也许灵感就是这么来的.\n真理不是复杂的, 它非常简单.\n比如生活学习的原则, 认真, 坚持, 学习, 总结, 归纳. 这些词看上去都简单, 但是要执行下去, 却又是非常难的, 时常让人忘记.\n关于学习:小时候,我的爸爸, 也就是你们的爷爷就告诉我, 好记性不如烂笔头,什么意思呢,就是说不管自己的记忆力也多好, 也不如把一件事写下来印象深刻, 延伸开来就是, 想把一件事情学会和理解, 最好是总结后写下来, 以及交给别人. 如果你们能把一个跆拳道动作或者舞蹈动作教会一个从没有学过的小朋友或者大人, 我相信这个动作你们一定是真的学会了.\n关于认真: 我的爸爸告诉我, 毛主席说过, 这个世界 怕就怕认真两个字, 我小时候也不理解, 认真有什么怕的. 我在经历了漫长的学习生涯, 以及工作这些年后, 才明白这个道理. 不用怕现在比你厉害的人, 而要怕那些做事情认真的人, 因为往往他们才是最后的赢家.\n关于失败和总结: 美国著名演说家温德尔·菲利普斯说过, 失败是成功之母. 什么意思呢, 就是说失败是成功的妈妈. 那就像你们一样, 没有妈妈会有你们, 不会有的吧. 没有失败在前面, 怎么会有后面的成功呢. 如果失败了只是感觉委屈, 害羞而哭, 不去总结为什么这次是失败, 那就会对下次的尝试感觉到恐惧, 也就放弃这件事了.如果我们改变想法, 在失败后, 想想怎么做可能能成功, 想想怎么做能够离成功更近一点点, 我想只要这样, 再尝试多次之后, 一定会成功的.\n不要忘记做一件事最开始的目标和意义\n比如学习跆拳道, 你觉得难, 是因为你心里觉得, 教练演示的时候看着很简单, 你学起来应该很简单. 这就犯了一个错误, 对自己期望太高. 你们现在才5岁, 属于能够做简单动作的阶段, 复杂的动作需要更好的身体协调性, 你们的身体还没有发育到这个阶段, 所以不必因为自己一下子没有学会而难过, 你的教练现在都是黑带, 他们已经练习了好几年才能达到现在的水平, 你们不用跟他比, 或者跟别的同学比, 你需要跟自己比, 争取今天的自己做得比昨天的自己更好就可以, 爸爸妈妈就会为你们骄傲. 我们要记得自己做一件事的最初的目的, 爸爸妈妈只是希望你们能锻炼身体, 让身体更强壮,能够更勇敢, 更坚韧而已, 而不是要求你们成为跆拳道世界冠军, 跆拳道动作做得不如别人没有关系, 但是哭哭啼啼就不好了, 跟练跆拳道的口号都不一样了.\n在目前的生活上, 多听长辈的建议.\n家人现在会建议你吃什么, 穿什么, 不要做什么, 这些都是家长积累了几十年的经验. 因为你们来到这个世界上的时间只有5年, 见到的事情还不够多, 不知道做一些事情会有什么后果. 其实, 每一个经验, 都是我们学习到的教训. 比如让你们出去玩的时候不要乱跑, 是因为我们看到很多孩子乱跑然后掉到坑里了, 被车撞了, 或者被人拐跑. 告诉你某种食物不能多吃, 是因为我们见过吃多了的孩子拉肚子, 生病. 有的时候, 你们不愿意家人的话, 也许是故意对着干, 也许是不知道事情有多严重, 希望你们多问一下为什么.\n我说生活上多听家长的建议, 如果是知识类的, 我希望你们多探索, 去询问别人, 去自己找书看, 因为知识很多, 而且涉及到很多领域和专业, 需要问专业的人. 比如恐龙问题我们就看恐龙的书, 去恐龙博物馆学习, 或者有机会就去问恐龙专家. 如果是如何把舞跳好, 我们就要问舞蹈老师, 你来问我我并不能给出很好的答案. 当然, 爸爸也有专业的方面. 比如你问我怎么玩电脑, 我就很专业, 我可以教你怎么拆东西和装东西, 我会教你们怎么打球等等. 记住, 专业的知识问专业的人, 以后少走很多弯路. 家庭生活的事, 多问家人就好.\n自己的事情自己做, 享受自己的权利, 承担自己的责任.\n我不会帮你提书包，读书是你的事情，提书包也是你的事情。同理，收拾书包是你的事情，做作业是你的事情，上学迟到也有你的责任。在你小的时候, 很多事情你不会做, 家长可以帮你, 现在你慢慢长大, 会做的事情越来越多, 因此需要自己做的事情也会多起来. 你们不能这么想, 以前爸爸妈妈天天都抱着我走路, 也喂饭给我吃, 帮我拿水瓶, 现在为什么不帮我了, 是不是不爱我了呢. 不是这样的, 我们不会不爱你们, 只是希望你能锻炼你们生活的本领, 这样, 就算爸爸妈妈不在, 你们不会手足无措, 只能坐在地上哭, 而不会在寒冷的日子让自己不要受冻, 在饿了之后不会制作食物. 每个人成长的过程, 也就是锻炼自己独立能力的过程, 就想小鸟小时候在鸟窝里, 不会自己找虫子吃, 这个时候就是鸟妈妈帮忙, 但是等到小鸟羽毛长好以后, 它们就会练习如何飞行, 虽然一开始它们可能飞不起来, 也会摔跤, 但是最后, 它们肯定会学会飞行, 飞向更广阔的世界, 去看到更多以前从来没有见过的风景. 你们也是一样的.\n","permalink":"https://blog.jimersylee.com/posts/2022%E5%AD%A9%E5%AD%90%E7%94%9F%E6%97%A5%E7%BB%99%E5%AD%A9%E5%AD%90%E7%9A%84%E4%B8%80%E5%B0%81%E4%BF%A1/","summary":"\u003ch1 id=\"2022孩子生日给孩子的一封信\"\u003e2022孩子生日给孩子的一封信\u003c/h1\u003e\n\u003cp\u003e五周岁了，我觉得跟你们可以沟通交流了, 因此有个想法，以后每年你们生日的时候都给你们写一封信。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e不要因为别人夸你聪明就骄傲自满, 也不能因为别人说你笨而哭泣.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e因为别人夸你, 可能是出于礼貌,也可能是出于客套,也有可能有所目的,需要从你这里获取一些利益. 你们需要认识到, 聪明并不是把事情做好的决定性因素, 坚持与反思才能获得进步. 很多有小聪明的人, 做不成大事, 反而是比较固执的, 在事情上下笨功夫的人, 才能获得成功.\u003c/p\u003e\n\u003cp\u003e而且, 大部分人都喜欢和普通人打交道.  ⽐如我问你：“你是喜欢和聪明⼈做⽣意，还是和傻⼦做⽣意呢？”你恐怕会告诉我是后者，因为你觉得傻⼦的钱好挣。 如果很多⼈都这么想，那么笨⼈做成⽣意的机会就多了。从那以后，我恪守⼀个原则，不论对⽅挣多少 钱，我只挣⾃⼰那⼀份就好，不要贪图对⽅的任何⼀点⼉利益。这样⼀来，⽣意就能持久。你和⼩朋友 交往时，也不必怕吃亏，和⼩朋友换东⻄时，多⼀点⼉少⼀点⼉也没有关系，因为如果每⼀次都是你占 了便宜，时间⼀⻓，⼩朋友就不愿意再和你打交道了。通常，别⼈觉得你⽼实，就很放⼼地和你做朋 友，觉得你太聪明了，就会害怕和你来往.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e希望你们对世界上所有的事情保持批判与怀疑精神\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e不要看到听到别人说对就认为是对的, 需要经过自己的思考.因为这个社会, 很多你看到的听到的, 是有些人故意想让你看到的, 比如诱人的广告, 好看的动画片,  看上去很好吃的食物, 几乎所有东西都有一个包装, 而我们需要有火眼金睛, 让目光穿过这些包装, 看到本质的东西. 比如有的食物很好吃, 其实它加了很多糖, 香料, 让食物更加美味的添加剂, 而这些东西, 没有营养, 对身体有害, 虽然有些食物尝起来并不美味, 但是它是食物本来的味道, 比如说煮鸡蛋, 煮米饭, 炒青菜. 我们不能让自己习惯这些人为添加的味道而忽略了食物的本味.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e希望你们能够更加勇敢, 面对挫折, 百折不挠.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e没有人可以轻易做成功任何事,都需要付出努力, 就算是走路,跑步, 都是需要学习如何走得更稳,跑得更快. 简单的事谁都会做, 那我们跟别人就没有区别. 这个世界上, 各个领域, 都是做的最好的人才能推动进步, 但是要做得最好, 就需要付出比别人多的努力. 说实话, 把事情做一般般是简单的, 大部分人都能做到这样, 而且这也是人的本性, 喜欢做简单的事, 抗拒难的事. 但是事实是, 在克服困难的过程种, 我们才能学到更多知识和技能, 而且能让我们的心更加强壮.\u003c/p\u003e","title":"2022孩子生日给孩子的一封信"},{"content":"如何通过markdown生成一个在线的电子书 前情提要 不知道大家有没有这样的时刻.\n无聊时拿出手机, 想看书发现手机里现在没有在另一台设备里的书, 因此开始看短视频. 自己写了一些关联的内容, 想归类成册, 却不知怎么办, 只能让它们杂乱的存放在笔记软件中. 互联网上收集的内容, 觉得可以归类成册. 心中有个小小的梦想, 出版一本书. TBD: 更多时刻 如果你有这样的时刻, 我觉得下面的内容你就值得观看, 因为我将在后面介绍如何快速将已有的内容发布成一本在线电子书的教程.\n工具准备 npm: nodejs的包管理软件,用来安装docsify docsify: 本文的主角, 用这个软件就可以使用markdown文件生成电子书 一个文本编辑器, vscode或者其他的 物料准备 一张好看的封面图, 可以自己设计, 也可以网上搜索 自己写好的内容,或者收集的内容 实际操作 安装相关软件 先安装nodejs, 就会自动安装npm了, 参看nodejs官网的安装流程\n使用各个系统的命令行软件安装docsify\n# 全局安装docsify, 这样在任何地方都可以使用这个命令行工具了 npm i docsify-cli -g 安装文本编辑器, 这里不再赘述\n初始化项目 创建一个新的目录, 比如就叫how-to-get-rich\n在命令行中切换到这个目录, 执行初始化操作\ndocsify init 这时候目录下就会出现以下文件或者文件夹\n.nojekyll: 防止 GitHub Pages 忽略以下划线开头的文件 index.html: 入口文件 README.md : 首页内容在这编辑 这个时候,我们就可以测试运行下, 能不能跑起来了.\ndocsify serve 如果运行后显示了,Listening at http://localhost:3000, 那说明成功了, 复制这个网址在浏览器打开, 就能看见效果了\n导入物料 这里的物料指的就是图片, 文字内容等, 我这里使用的是Notion, 因此我从Notion中导出相关的markdown文件,放到目录中\nREADME.md中,编写介绍文字\nindex.html中可以配置的就比较多了.下面示例代码\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;UTF-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;How to get rich\u0026lt;/title\u0026gt; \u0026lt;link rel=\u0026#34;icon\u0026#34; href=\u0026#34;assets/images/swe_at_google.2.cover.jpg\u0026#34;\u0026gt; \u0026lt;meta http-equiv=\u0026#34;X-UA-Compatible\u0026#34; content=\u0026#34;IE=edge,chrome=1\u0026#34;/\u0026gt; \u0026lt;meta name=\u0026#34;description\u0026#34; content=\u0026#34;Description\u0026#34;\u0026gt; \u0026lt;meta name=\u0026#34;viewport\u0026#34; content=\u0026#34;width=device-width, initial-scale=1.0, minimum-scale=1.0\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css\u0026#34;\u0026gt; \u0026lt;!-- gitalk --\u0026gt; \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;//cdn.jsdelivr.net/npm/gitalk/dist/gitalk.css\u0026#34;\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;div id=\u0026#34;app\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;script\u0026gt; window.$docsify = { name: \u0026#39;如何致富\u0026#39; + \u0026#39;\u0026#39;, repo: \u0026#39;jimersylee/how-to-get-rich\u0026#39;, auto2top: true, // 侧边栏 loadSidebar: true, alias: { \u0026#39;/.*/_sidebar.md\u0026#39;: \u0026#39;/_sidebar.md\u0026#39; }, subMaxLevel: 3, // 自动为每个页面增加标题 // autoHeader: true, // 封面 coverpage: true, // 搜索支持 search: { maxAge: 86400000, // 过期时间，单位毫秒，默认一天 paths: \u0026#39;auto\u0026#39;, // or \u0026#39;auto\u0026#39; // 支持本地化 placeholder: { \u0026#39;/zh-cn/\u0026#39;: \u0026#39;搜索\u0026#39;, \u0026#39;/\u0026#39;: \u0026#39;Type to search\u0026#39; }, // 支持本地化 noData: { \u0026#39;/zh-cn/\u0026#39;: \u0026#39;找不到结果\u0026#39;, \u0026#39;/\u0026#39;: \u0026#39;No Results\u0026#39; }, // 避免搜索索引冲突 // 同一域下的多个网站之间 namespace: \u0026#39;how-to-get-rich\u0026#39;, // 使用不同的索引作为路径前缀（namespaces） // 注意：仅适用于 paths: \u0026#39;auto\u0026#39; 模式 // // 初始化索引时，我们从侧边栏查找第一个路径 // 如果它与列表中的前缀匹配，我们将切换到相应的索引 pathNamespaces: [\u0026#39;/zh-cn\u0026#39;], }, plugins: [ function (hook, vm) { hook.beforeEach(function (html) { if (/githubusercontent\\.com/.test(vm.route.file)) { url = vm.route.file .replace(\u0026#39;raw.githubusercontent.com\u0026#39;, \u0026#39;github.com\u0026#39;) .replace(/\\/master/, \u0026#39;/blob/master\u0026#39;); } else if (/jsdelivr\\.net/.test(vm.route.file)) { url = vm.route.file .replace(\u0026#39;cdn.jsdelivr.net/gh\u0026#39;, \u0026#39;github.com\u0026#39;) .replace(\u0026#39;@master\u0026#39;, \u0026#39;/blob/master\u0026#39;); } else { url = \u0026#39;https://github.com/jimersylee/how-to-get-rich/blob/main/\u0026#39; + vm.route.file; } var editHtml = \u0026#39;[:memo: Edit Document](\u0026#39; + url + \u0026#39;)\\n\u0026#39;; return ( editHtml + html ); }) }, function (i) { // 加载 Gitalk 元素 var e = Docsify.dom; i.mounted(function (i) { var n = e.create(\u0026#34;div\u0026#34;); n.id = \u0026#34;gitalk-container\u0026#34;; var t = e.getNode(\u0026#34;#main\u0026#34;); n.style = \u0026#34;width: \u0026#34; + t.clientWidth + \u0026#34;px; margin: 0 auto 20px;\u0026#34;, e.appendTo(e.find(\u0026#34;.content\u0026#34;), n) }), i.doneEach(function (i) { for (var n = document.getElementById(\u0026#34;gitalk-container\u0026#34;); n.hasChildNodes();) n.removeChild(n.firstChild); }) }, function (hook, vm) { hook.doneEach(function () { // remove gitalk-container document.getElementById(\u0026#39;gitalk-container\u0026#39;).innerHTML = \u0026#34;\u0026#34;; // 渲染 NewGitalk().render(\u0026#39;gitalk-container\u0026#39;); }) } ], } \u0026lt;/script\u0026gt; \u0026lt;!-- Docsify v4 --\u0026gt; \u0026lt;script src=\u0026#34;//cdn.jsdelivr.net/npm/docsify@4\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- Pagination --\u0026gt; \u0026lt;script src=\u0026#34;//cdn.jsdelivr.net/npm/docsify-pagination/dist/docsify-pagination.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- gitalk --\u0026gt; \u0026lt;script src=\u0026#34;//cdn.jsdelivr.net/npm/gitalk/dist/gitalk.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- Medium\u0026#39;s 风格的图片缩放插件 --\u0026gt; \u0026lt;script src=\u0026#34;//cdn.jsdelivr.net/npm/docsify/lib/plugins/zoom-image.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;//cdn.jsdelivr.net/npm/crypto-js@4.1.1/crypto-js.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; function NewGitalk() { // 除去param const hashPath = location.hash.indexOf(\u0026#34;?id=\u0026#34;) === -1 ? location.hash : location.hash.substring(0, location.hash.indexOf(\u0026#34;?id=\u0026#34;)); const hrefPath = location.hash.indexOf(\u0026#34;?id=\u0026#34;) === -1 ? location.href : location.href.substring(0, location.href.indexOf(\u0026#34;?id=\u0026#34;)); // id 50字符限制 const md5Id = CryptoJS.MD5(hashPath).toString(); return new Gitalk({ clientID: \u0026#39;860d2cfa5e15a4d8e3f7\u0026#39;, clientSecret: \u0026#39;21e564b7c5a24170fca8385d2d73a620d7db1fde\u0026#39;, repo: \u0026#39;how-to-get-rich\u0026#39;, owner: \u0026#39;jimersylee\u0026#39;, admin: [\u0026#39;jimersylee\u0026#39;], id: md5Id, body: hrefPath, distractionFreeMode: false }); } \u0026lt;/script\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; _coverpage.md 中设置书的封面,关于封面,准备写一篇新的文章介绍快速生成封面\n![logo](generate.jpeg \u0026#39;:size=20%\u0026#39;) # How to get rich \u0026lt;small\u0026gt;中文版\u0026lt;/small\u0026gt; \u0026gt; 经历教会我的那些课 [GitHub](https://github.com/jimersylee/how-to-get-rich) [Get Started](#How-to-get-rich) _sidebar.md中设置目录\n- [**追求财富，而非金钱或身份《如何致富》译文连载1**](docs/追求财富，而非金钱或身份《如何致富》译文连载1%20f5e775df2a6f4f8fa29da6f1cb0b4c35.md) 等等 部署发布 虽然本地可以预览了,但是最终还是希望线上可看,因此,我们会使用到github.\n创建一个仓库,名字就叫how-to-get-rich,然后把目录下所有文件加入版本管理,push到这个仓库中.\n在这个仓库的Settings中找到Pages,Branch选项中选择代码所在的分支,然后等待自动部署.几分钟后,就会出现一个网址,就可以在线访问了.\n总结 我使用这个方式, 构建了2本在线电子书, 分别是\u0026laquo;数字时代的效率手册\u0026raquo;, \u0026laquo;如何致富\u0026raquo;. 开源, 随时更新, 允许自由评论, 感觉很棒, 你们也可以试试.\n","permalink":"https://blog.jimersylee.com/posts/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87markdown%E7%94%9F%E6%88%90%E4%B8%80%E4%B8%AA%E5%9C%A8%E7%BA%BF%E7%9A%84%E7%94%B5%E5%AD%90%E4%B9%A6/","summary":"\u003ch1 id=\"如何通过markdown生成一个在线的电子书\"\u003e如何通过markdown生成一个在线的电子书\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"Untitled.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527212254598.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"前情提要\"\u003e前情提要\u003c/h2\u003e\n\u003cp\u003e不知道大家有没有这样的时刻.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e无聊时拿出手机, 想看书发现手机里现在没有在另一台设备里的书, 因此开始看短视频.\u003c/li\u003e\n\u003cli\u003e自己写了一些关联的内容, 想归类成册, 却不知怎么办, 只能让它们杂乱的存放在笔记软件中.\u003c/li\u003e\n\u003cli\u003e互联网上收集的内容, 觉得可以归类成册.\u003c/li\u003e\n\u003cli\u003e心中有个小小的梦想, 出版一本书.\u003c/li\u003e\n\u003cli\u003eTBD: 更多时刻\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e如果你有这样的时刻, 我觉得下面的内容你就值得观看, 因为我将在后面介绍如何快速将已有的内容发布成一本在线电子书的教程.\u003c/p\u003e\n\u003ch2 id=\"工具准备\"\u003e工具准备\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003enpm: nodejs的包管理软件,用来安装docsify\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://docsify.js.org/#/quickstart\"\u003edocsify\u003c/a\u003e: 本文的主角, 用这个软件就可以使用markdown文件生成电子书\u003c/li\u003e\n\u003cli\u003e一个文本编辑器, vscode或者其他的\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"物料准备\"\u003e物料准备\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e一张好看的封面图, 可以自己设计, 也可以网上搜索\u003c/li\u003e\n\u003cli\u003e自己写好的内容,或者收集的内容\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"实际操作\"\u003e实际操作\u003c/h2\u003e\n\u003ch3 id=\"安装相关软件\"\u003e安装相关软件\u003c/h3\u003e\n\u003cp\u003e先安装nodejs, 就会自动安装npm了, 参看\u003ca href=\"https://nodejs.org/zh-cn/\"\u003enodejs官网\u003c/a\u003e的安装流程\u003c/p\u003e\n\u003cp\u003e使用各个系统的命令行软件安装docsify\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 全局安装docsify, 这样在任何地方都可以使用这个命令行工具了\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enpm i docsify-cli -g\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e安装文本编辑器, 这里不再赘述\u003c/p\u003e\n\u003ch3 id=\"初始化项目\"\u003e初始化项目\u003c/h3\u003e\n\u003cp\u003e创建一个新的目录, 比如就叫how-to-get-rich\u003c/p\u003e\n\u003cp\u003e在命令行中切换到这个目录, 执行初始化操作\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocsify init\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e这时候目录下就会出现以下文件或者文件夹\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e.nojekyll: 防止 GitHub Pages 忽略以下划线开头的文件\u003c/li\u003e\n\u003cli\u003eindex.html:  入口文件\u003c/li\u003e\n\u003cli\u003eREADME.md :  首页内容在这编辑\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e这个时候,我们就可以测试运行下, 能不能跑起来了.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edocsify serve\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e如果运行后显示了,Listening at \u003ca href=\"http://localhost:3000/\"\u003ehttp://localhost:3000\u003c/a\u003e, 那说明成功了, 复制这个网址在浏览器打开, 就能看见效果了\u003c/p\u003e","title":"如何通过markdown生成一个在线的电子书"},{"content":" 西蓝花一小颗 1.5/斤 1元 南瓜一小片 0.8/斤 4元 芹菜少点 6/斤 6.9 姜两块 5.5/斤 5.7 黄瓜 1.5/斤 元 茄子 2/斤 3.5 菠菜 4元/斤 香菜 8.5斤 小油菜 3/斤3元 生菜 3/斤 3 元 香菇 7.5/斤+10/斤*0.5=12 西红柿 2.5/斤 4.9元 大葱 2块/斤 5元 黄豆芽两块钱的 2元 鸡胸肉四块 7/斤 10元 干豆腐 4元 ","permalink":"https://blog.jimersylee.com/posts/20200822-%E6%9D%AD%E5%B7%9E%E6%B0%B8%E8%BE%89%E8%8F%9C%E4%BB%B7/","summary":"这些物价正常吗","title":"20200822-杭州永辉菜价"},{"content":"使用4D工作法规划每日工作 概念 什么是4D工作法 对于我来说,4D工作法这个概念来源于《小强升职记》、《只管去做》的作者邹小强老师.分别是Delay it, Do it now, Don’t do it, Delegate it.\nDo it now: 就是需要立马去做的, 这在公司中对应着生产事故, 老板开会, 访客到访, 或者是那种两分钟内就能做完的事情, 那就直接归到这类, 完成再说.\nDelay it: 指那种需要比较长时间的, 比较重要, 但是不是特别紧急, 可以安排时间做的事.\nDelegate it: 指那种可以尽量授权别人做的事, 尤其是作为管理者, 尽量把自己已经会的东西, 教授给同事去做. 但是需要注意的事, 安排别人做的事情, 需要自己定一个提醒, 到期了去询问进度和成果.\nDon’t do it: 一些无意义的会议, 无效的社交, 就不要去做了. 我觉得这类事情还真的需要好好思考, 不要什么事都想去做, 把自己的时间浪费掉.\n什么是工作四项限 这是在学习到4D工作法之前就看到过的概念 工作四象限这个概念的问题是,没有明确的action,我觉得不如4D工作法, 直接包含4个action, 通俗易懂.\n实践 心理建设和仪式感 不管多忙, 需要养成每日规划计划的习惯, 否则不管是生活还是工作, 都像是随波逐流的浮萍, 飘到哪算哪.\n从心理学的角度讲, 就像条件反射一样, 需要一些”触发器”提醒你的身体, 该开始做什么了, 将规划一天的工作, 做的像每天喝水一样, 自然就能做好. 因此, 可以在每天喝第一口水后, 开始规划今天的4D任务.\n我的每日待办清单演进过程 v1-普通清单-使用notion的todo block\n最初, 我使用的是notion的todo block, 非常简单, 但是实施了后发现, 基本上只能做流水账, 做了什么后记录一下, 虽然能记录开始结束时间, 是否完成, 任务类别, 使用了几个番茄钟, 但是, 当我想进行一些统计分析总结的时候, 发现存在以下需求无法实现.\n我想知道每天完成多少个番茄钟,只能手工计算 我想知道一天的时间主要花在什么类别的任务上, 无法统计 这个todo 任务无法跟我的P.A.R.A 管理模型结合, todo任务不能体现在Tasks数据库中, 总是得重复建立task 不符合4D工作法 于是, 对于每日任务跟踪, 进行了迭代.\nv2-多列清单-使用todo block结合多列排版\n这个模型,解决了之前的”不符合4D工作法”的问题, 没有解决其他问题, 但是, 事情逐渐变好.\nv3-使用notion的table view的board,以及tasks database的properties迭代\n下图为一个任务所包含的属性\nTags: 标签 Projects: 关联哪个项目,这个项目需要有截止日期 Status: 任务状态,常规的那几个状态 Completed Time: 任务实际完成时间 Mood: 做任务的时候心情, 我想分析一下我做什么任务比较开心, 什么任务比较难受, 了解自己真正喜欢做什么 Actual Start: 实际开始做的时间,这个与完成时间可以进行计算花费了多少时间,如果做一天的时间分配计算可以用 Dead line: 任务的截止日期,我一般会设置一个Reminder Plan Day: 计划去做这个任务的时间 Tomatoes: 花费的番茄钟 下图为新的4D看板,基于Table view制作,数据源就是Tasks数据库.\n因为任务实际在数据库中,所以可以方便的根据属性进行一些聚合运算.因为是看板,所以可以方便移动更改状态.我把这些内容做成了模板, 每天只要按一下模板按钮就能生成一天新的4D计划.只要设置了Plan day属性的过滤器, 就能展示出今天的任务或者任意一天的计划的任务.\n思考 目前, 我就用v3版本的每日任务规划, 使用感受良好, 等以后有新的需求再迭代. 归纳一下几点自己的收获.\n可以通过归一数据,减少数据的重复创建整理,以及建立模板内容,方便每日使用快速使用. 既可以节省时间, 又不重复工作. 不要想着一开始就做得很完美. 与其纠结哪里没做好, 不如多想想如何设计得更方便迭代, 我认为好产品都是迭代出来的, 方法论如是. 不能互相关联的任务, 就是流水账, 不好分析与反思, 也无法量化, ,每日任务只要基于4D工作模型, P.A.R.A知识管理模型, 直接使用模型中的Tasks数据库存任务, 不要使用todo block, 否则无法跟踪. 参考 #task#done# 4D工作法 可以做到每日模板中\n","permalink":"https://blog.jimersylee.com/posts/%E4%BD%BF%E7%94%A84d%E5%B7%A5%E4%BD%9C%E6%B3%95%E8%A7%84%E5%88%92%E6%AF%8F%E6%97%A5%E5%B7%A5%E4%BD%9C/","summary":"\u003ch1 id=\"使用4d工作法规划每日工作\"\u003e使用4D工作法规划每日工作\u003c/h1\u003e\n\u003ch2 id=\"概念\"\u003e概念\u003c/h2\u003e\n\u003ch3 id=\"什么是4d工作法\"\u003e什么是4D工作法\u003c/h3\u003e\n\u003cp\u003e对于我来说,4D工作法这个概念来源于《小强升职记》、《只管去做》的作者邹小强老师.分别是Delay it, Do it now, Don’t do it, Delegate it.\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Untitled.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527205610735.png\"\u003e\u003c/p\u003e\n\u003cp\u003eDo it now: 就是需要立马去做的, 这在公司中对应着生产事故, 老板开会, 访客到访, 或者是那种两分钟内就能做完的事情, 那就直接归到这类, 完成再说.\u003c/p\u003e\n\u003cp\u003eDelay it: 指那种需要比较长时间的, 比较重要, 但是不是特别紧急, 可以安排时间做的事.\u003c/p\u003e\n\u003cp\u003eDelegate it: 指那种可以尽量授权别人做的事, 尤其是作为管理者, 尽量把自己已经会的东西, 教授给同事去做. 但是需要注意的事, 安排别人做的事情, 需要自己定一个提醒, 到期了去询问进度和成果.\u003c/p\u003e\n\u003cp\u003eDon’t do it: 一些无意义的会议, 无效的社交, 就不要去做了. 我觉得这类事情还真的需要好好思考, 不要什么事都想去做, 把自己的时间浪费掉.\u003c/p\u003e\n\u003ch3 id=\"什么是工作四项限\"\u003e什么是工作四项限\u003c/h3\u003e\n\u003cp\u003e这是在学习到4D工作法之前就看到过的概念\n\u003cimg alt=\"Untitled 1.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527205622011.png\"\u003e\u003c/p\u003e\n\u003cp\u003e工作四象限这个概念的问题是,没有明确的action,我觉得不如4D工作法, 直接包含4个action, 通俗易懂.\u003c/p\u003e\n\u003ch2 id=\"实践\"\u003e实践\u003c/h2\u003e\n\u003ch3 id=\"心理建设和仪式感\"\u003e心理建设和仪式感\u003c/h3\u003e\n\u003cp\u003e不管多忙, 需要养成每日规划计划的习惯, 否则不管是生活还是工作, 都像是随波逐流的浮萍, 飘到哪算哪.\u003c/p\u003e\n\u003cp\u003e从心理学的角度讲, 就像条件反射一样, 需要一些”触发器”提醒你的身体, 该开始做什么了, 将规划一天的工作, 做的像每天喝水一样, 自然就能做好. 因此, 可以在每天喝第一口水后, 开始规划今天的4D任务.\u003c/p\u003e","title":"使用4D工作法规划每日工作"},{"content":"项目地址:http://github.com/jimersylee/MachineLearningAction\n决策树(Decision Trees) 类似20个问题的游戏 参与游戏的一方在脑海里想某个事物,其他参与者向他提问题,只允许提20个问题,问题的答案只能用对或错回答,问问题的人通过推断分解,逐步缩小带猜测事物的范围.决策树的工作原理与20个问题类似,用户输入一系列数据,然后给出游戏的答案\n决策树 优点:计算复杂度不高,输入结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据 缺点:可能产生过度匹配问题 适用数据类型:数值型与标称型\n决策树的一般流程\n收集数据:可以适用任何方法 准备数据:树构造算法只适用于标称型数据,因此数值型数据必须离散化 分析数据:可以适用任何方法,构造书完成之后,我们应该检查图形是否符合预期 训练算法:构造树的数据结构 测试算法:适用经验树计算错误率 适用算法:此步骤可以适用于任何监督学习算法,而适用决策树可以更好地理解数据的内在含义 # coding:utf-8from mathimport log import operator import matplotlib.pyplotas plt defcalcShannonEnt(dataSet): \u0026#34;\u0026#34;\u0026#34; 计算给定数据集的香农熵,香农熵代表数据的无序程度,香农熵越大,数据集越无序 :param dataSet: 数据集 :return:香农熵的值 \u0026#34;\u0026#34;\u0026#34; numEntries = len(dataSet)# 获得数据中实例的总数 labelCounts = {}# 声明一个字典# 为所有可能分类创建字典,它的键值是最后一列的数值for featVecin dataSet: currentLabel = featVec[-1]# 目前的标签=数据集的每行的最后一个元素if currentLabelnotin labelCounts.keys():# 如果键值不存在,则扩展字典并将当前键值加入字典 labelCounts[currentLabel] = 0# 初始化为0 labelCounts[currentLabel] = labelCounts[currentLabel] + 1# 每个键值都记录了当前类别出现的次数,出现一次加1 shannonEnt = 0.0 for keyin labelCounts: prob = float(labelCounts[key]) / numEntries# 使用所有类标签的发生频率计算类别出现的概率 shannonEnt = shannonEnt - prob * log(prob, 2)# 使用这个概率计算香农熵 以2为底求对数return shannonEnt defcreateDataSet(): \u0026#34;\u0026#34;\u0026#34; 创建简单鱼类鉴定数据集 :return:dataSet(数据集),labels(标签) \u0026#34;\u0026#34;\u0026#34; _dataSet = [ [1, 1, \u0026#39;yes\u0026#39;], [1, 1, \u0026#39;yes\u0026#39;], [1, 0, \u0026#39;no\u0026#39;], [0, 1, \u0026#39;no\u0026#39;], [0, 1, \u0026#39;no\u0026#39;] ] _labels = [\u0026#39;no surfacing\u0026#39;, \u0026#39;flippers\u0026#39;] return _dataSet, _labels myData, labels = createDataSet() print myData print calcShannonEnt(myData) \u0026#34;\u0026#34;\u0026#34; result [[1, 1, \u0026#39;yes\u0026#39;], [1, 1, \u0026#39;yes\u0026#39;], [1, 0, \u0026#39;no\u0026#39;], [0, 1, \u0026#39;no\u0026#39;], [0, 1, \u0026#39;no\u0026#39;]] 0.970950594455 \u0026#34;\u0026#34;\u0026#34; # 熵越高,则混合的数据也越多,我们可以在数据集中添加更多的分类,观察熵是如何变化的,这里我们增加第三个名为maybe的分类,测试熵的变化 myData[0][-1] = \u0026#39;maybe\u0026#39;# 第0行的最后一个元素置为maybeprint myData print calcShannonEnt(myData) \u0026#34;\u0026#34;\u0026#34; [[1, 1, \u0026#39;maybe\u0026#39;], [1, 1, \u0026#39;yes\u0026#39;], [1, 0, \u0026#39;no\u0026#39;], [0, 1, \u0026#39;no\u0026#39;], [0, 1, \u0026#39;no\u0026#39;]] 1.37095059445 \u0026#34;\u0026#34;\u0026#34; # 得到熵以后,我们就可以按照获取最大信息增益的方法划分数据集# 另一个度量集合无需程度的方法是`基尼不纯度`(Gini impurity),简单地说就是从一个数据集中随机选取子项,度量其被错误分类到其他分组里的概率\u0026#34;\u0026#34;\u0026#34; 分类算法除了要测量信息熵,还需要划分数据集,度量花费数据集的熵,以便判断当前是否正确划分了数据集,我们将对每个特征划分数据集的结果计算一次信息熵,然后判断按照那个特征划分数据集是最好的划分方式 \u0026#34;\u0026#34;\u0026#34; defsplitDataSet(dataSet, axis, value): \u0026#34;\u0026#34;\u0026#34; 按照给定特征划分数据集 :param dataSet: 待划分的数据集 :param axis:划分数据集的特征位 :param value:预设特征值 :return: \u0026#34;\u0026#34;\u0026#34; retDataSet = []# 创建新的list对象,因为函数中传递的是列表的引用,在函数内部对列表对象的修改,将会影响该列表对象的整个生存周期,因此需要创建一个新的列表for featVecin dataSet: if featVec[axis] == value:# 如果axis位的值等于预设的特征值 reducedFeatVec = featVec[:axis]# 取行数据的前axis位 reducedFeatVec.extend(featVec[axis + 1:])# 加上axis位后的元素 retDataSet.append(reducedFeatVec)# 就是将除了axis特征位的元素抽取出来return retDataSet # 测试splitDataSetprint \u0026#34;测试splitDataSet\u0026#34; myData, labels = createDataSet() print myData newData = splitDataSet(myData, 0, 1)# 将待测试数据的第0位为1的数据的其他特征和标签筛选出来print newData defchooseBestFeatureToSplit(dataSet): \u0026#34;\u0026#34;\u0026#34; 选择最好的数据集划分方式 :param dataSet: 数据集 :return: \u0026#34;\u0026#34;\u0026#34; numFeatures = len(dataSet[0]) - 1 # 计算整个数据的原始香农熵 baseEntropy = calcShannonEnt(dataSet) bestInfoGain = 0.0 bestFeature = -1 # 遍历数据集中的所有特征for iin range(numFeatures): # 创建唯一的分类标签列表 featList = [example[i]for examplein dataSet] uniqueVals = set(featList)# 将list转换为set,集合类型中每个值不同,从列表中创建集合是Python语言中得到列表中唯一元素值的最快方法 newEntropy = 0.0 # 计算每种划分方式的信息熵for valuein uniqueVals:# 遍历当前特征中的所以唯一属性值,对每个特征划分一次数据集 subDataSet = splitDataSet(dataSet, i, value) prob = len(subDataSet) / float(len(dataSet)) newEntropy += prob * calcShannonEnt(subDataSet)# 并对所有唯一特征值得到的熵求和 infoGian = baseEntropy - newEntropy# 信息增益=熵的减少值或者数据无序度的减少# 计算最好的信息增益if infoGian \u0026gt; bestInfoGain: bestInfoGain = infoGian bestFeature = i return bestFeature# 返回最好特征划分的索引值# 获取最好特征划分索引值print \u0026#34;获取最好特征划分索引值\u0026#34; myData, labels = createDataSet() print \u0026#34;dataSet:\u0026#34; + str(myData) print \u0026#34;best index:\u0026#34; + str(chooseBestFeatureToSplit(myData)) \u0026#34;\u0026#34;\u0026#34; 代码表示,第0个特征是最好的用户划分数据集的特征 \u0026#34;\u0026#34;\u0026#34; \u0026#34;\u0026#34;\u0026#34; 递归构建决策树 \u0026#34;\u0026#34;\u0026#34; defmajorityCnt(classList): \u0026#34;\u0026#34;\u0026#34; 类似classify0部分的投票表决 创建键值为classList中唯一值的数据字典,字典对象存储了classList中每个类标签出现的频率,最后利用operator操作键值排序字典,返回出现次数最多的分类名称 :rtype: str :param classList: 分类名称的列表 :return: 返回出现次数最多的分类名称 \u0026#34;\u0026#34;\u0026#34; classCount = {} for votein classList: if votenotin classCount.keys():# 如果投票的类别不在classCount字典的key中, classCount[vote] = 0# 则新建此key,value设置为0 classCount[vote] += 1 sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0] defcreateTree(dataSet, labels): \u0026#34;\u0026#34;\u0026#34; 创建树,使用了递归 :param dataSet:数据集 :param labels: 分类标签 :return: 树结构 \u0026#34;\u0026#34;\u0026#34; classList = [example[-1]for examplein dataSet]# 将数据集中的每一行的最后一个元素的值取出生成类别列表classListif classList.count(classList[0]) == len(classList):# 递归停止条件1return classList[0]# 类别完全相同时停止继续划分if len(dataSet[0]) == 1:# 递归停止条件2return majorityCnt(classList)# 遍历完所有特征时返回出现次数最多的 bestFeat = chooseBestFeatureToSplit(dataSet)# 选择最好的划分特征位 bestFeatLabel = labels[bestFeat]# 取出最好的标签 myTree = {bestFeatLabel: {}}# 初始化一个tree字典del (labels[bestFeat])# 删除最好的标签,防止树的下一个节点还使用此标签 featValues = [example[bestFeat]for examplein dataSet]# 创建最好的特征位的值所构建的list uniqueVals = set(featValues)# 生成唯一的特征位的值构成的集合setfor valuein uniqueVals: subLabels = labels[:]# 复制所有标签,且树不会和已经存在的标签搞混 myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)# 递归创建树return myTree # 构建树测试print \u0026#34;构建树测试\u0026#34; myData, labels = createDataSet() myTree = createTree(myData, labels) print myTree # coding:utf-8import matplotlib.pyplotas plt# 导入绘制图库\u0026#34;\u0026#34;\u0026#34; 绘制树图 \u0026#34;\u0026#34;\u0026#34; decisionNode = dict(boxstyle=\u0026#34;sawtooth\u0026#34;, fc=\u0026#34;0.8\u0026#34;) leafNode = dict(boxstyle=\u0026#34;round4\u0026#34;, fc=\u0026#34;0.8\u0026#34;) arrow_args = dict(arrowstyle=\u0026#34;\u0026lt;-\u0026#34;) defgetNumLeafs(myTree): \u0026#34;\u0026#34;\u0026#34; 获取叶节点的数目 :param myTree:树结构 :return: 叶节点的数目 \u0026#34;\u0026#34;\u0026#34; numLeafs = 0 firstStr = myTree.keys()[0] secondDict = myTree[firstStr] for keyin secondDict.keys(): if type(secondDict[ key]).__name__ == \u0026#39;dict\u0026#39;:# test to see if the nodes are dictonaires, if not they are leaf nodes numLeafs += getNumLeafs(secondDict[key]) else: numLeafs += 1 return numLeafs defgetTreeDepth(myTree): \u0026#34;\u0026#34;\u0026#34; 获取树的层数(深度) :param myTree: :return: 树的层数(深度) \u0026#34;\u0026#34;\u0026#34; maxDepth = 0 firstStr = myTree.keys()[0] secondDict = myTree[firstStr] for keyin secondDict.keys(): if type(secondDict[ key]).__name__ == \u0026#39;dict\u0026#39;:# 判断此节点是否是字典,如果不是就是叶节点 thisDepth = 1 + getTreeDepth(secondDict[key])# 是字典,层数+1else: thisDepth = 1# 叶节点,层数为1if thisDepth \u0026gt; maxDepth: maxDepth = thisDepth return maxDepth defplotNode(nodeTxt, centerPt, parentPt, nodeType): \u0026#34;\u0026#34;\u0026#34; 绘制节点 :param nodeTxt:节点名称 :param centerPt: 目前的位置 :param parentPt: 父节点的位置 :param nodeType: 节点类型 :return: \u0026#34;\u0026#34;\u0026#34; createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords=\u0026#39;axes fraction\u0026#39;, xytext=centerPt, textcoords=\u0026#39;axes fraction\u0026#39;, va=\u0026#34;center\u0026#34;, ha=\u0026#34;center\u0026#34;, bbox=nodeType, arrowprops=arrow_args) defplotMidText(currentPt, parentPt, txtString): \u0026#34;\u0026#34;\u0026#34; 绘制中间的文本 :param currentPt:目前的位置 :param parentPt: 父节点的位置 :param txtString: 想绘制的文本 :return: \u0026#34;\u0026#34;\u0026#34; xMid = (parentPt[0] - currentPt[0]) / 2.0 + currentPt[0] yMid = (parentPt[1] - currentPt[1]) / 2.0 + currentPt[1] createPlot.ax1.text(xMid, yMid, txtString, va=\u0026#34;center\u0026#34;, ha=\u0026#34;center\u0026#34;, rotation=30) defplotTree(myTree, parentPt, nodeTxt):# if the first key tells you what feat was split on numLeafs = getNumLeafs(myTree)# this determines the x width of this tree depth = getTreeDepth(myTree) firstStr = myTree.keys()[0]# the text label for this node should be this cntrPt = (plotTree.xOff + (1.0 + float(numLeafs)) / 2.0 / plotTree.totalW, plotTree.yOff) plotMidText(cntrPt, parentPt, nodeTxt) plotNode(firstStr, cntrPt, parentPt, decisionNode) secondDict = myTree[firstStr] plotTree.yOff = plotTree.yOff - 1.0 / plotTree.totalD for keyin secondDict.keys(): if type(secondDict[ key]).__name__ == \u0026#39;dict\u0026#39;:# test to see if the nodes are dictonaires, if not they are leaf nodes plotTree(secondDict[key], cntrPt, str(key))# recursionelse:# it\u0026#39;s a leaf node print the leaf node plotTree.xOff = plotTree.xOff + 1.0 / plotTree.totalW plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode) plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key)) plotTree.yOff = plotTree.yOff + 1.0 / plotTree.totalD # if you do get a dictionary you know it\u0026#39;s a tree, and the first element will be another dictdefcreatePlot(inTree): fig = plt.figure(1, facecolor=\u0026#39;white\u0026#39;) fig.clf() axprops = dict(xticks=[], yticks=[]) createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)# no ticks# createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses plotTree.totalW = float(getNumLeafs(inTree)) plotTree.totalD = float(getTreeDepth(inTree)) plotTree.xOff = -0.5 / plotTree.totalW plotTree.yOff = 1.0 plotTree(inTree, (0.5, 1.0), \u0026#39;\u0026#39;) plt.show() # def createPlot():# fig = plt.figure(1, facecolor=\u0026#39;white\u0026#39;)# fig.clf()# createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses# plotNode(\u0026#39;a decision node\u0026#39;, (0.5, 0.1), (0.1, 0.5), decisionNode)# plotNode(\u0026#39;a leaf node\u0026#39;, (0.8, 0.1), (0.3, 0.8), leafNode)# plt.show()defretrieveTree(i): listOfTrees = [{\u0026#39;no surfacing\u0026#39;: {0: \u0026#39;no\u0026#39;, 1: {\u0026#39;flippers\u0026#39;: {0: \u0026#39;no\u0026#39;, 1: \u0026#39;yes\u0026#39;}}}}, {\u0026#39;no surfacing\u0026#39;: {0: \u0026#39;no\u0026#39;, 1: {\u0026#39;flippers\u0026#39;: {0: {\u0026#39;head\u0026#39;: {0: \u0026#39;no\u0026#39;, 1: \u0026#39;yes\u0026#39;}}, 1: \u0026#39;no\u0026#39;}}}} ] return listOfTrees[i] # createPlot(thisTree) myTree = retrieveTree(0) createPlot(myTree) ","permalink":"https://blog.jimersylee.com/posts/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%AE%9E%E6%88%98-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B9%8Bdecision-trees/","summary":"\u003cp\u003e项目地址:\u003ca href=\"http://github.com/jimersylee/MachineLearningAction\"\u003ehttp://github.com/jimersylee/MachineLearningAction\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"决策树decision-trees\"\u003e\u003cstrong\u003e决策树(Decision Trees)\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e类似20个问题的游戏 参与游戏的一方在脑海里想某个事物,其他参与者向他提问题,只允许提20个问题,问题的答案只能用对或错回答,问问题的人通过推断分解,逐步缩小带猜测事物的范围.决策树的工作原理与20个问题类似,用户输入一系列数据,然后给出游戏的答案\u003c/p\u003e\n\u003cp\u003e决策树 优点:计算复杂度不高,输入结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据 缺点:可能产生过度匹配问题 适用数据类型:数值型与标称型\u003c/p\u003e\n\u003cp\u003e决策树的一般流程\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e收集数据:可以适用任何方法\u003c/li\u003e\n\u003cli\u003e准备数据:树构造算法只适用于标称型数据,因此数值型数据必须离散化\u003c/li\u003e\n\u003cli\u003e分析数据:可以适用任何方法,构造书完成之后,我们应该检查图形是否符合预期\u003c/li\u003e\n\u003cli\u003e训练算法:构造树的数据结构\u003c/li\u003e\n\u003cli\u003e测试算法:适用经验树计算错误率\u003c/li\u003e\n\u003cli\u003e适用算法:此步骤可以适用于任何监督学习算法,而适用决策树可以更好地理解数据的内在含义\u003c/li\u003e\n\u003c/ol\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# coding:utf-8from mathimport log\nimport operator\nimport matplotlib.pyplotas plt\n\ndefcalcShannonEnt(dataSet):\n    \u0026#34;\u0026#34;\u0026#34;\n    计算给定数据集的香农熵,香农熵代表数据的无序程度,香农熵越大,数据集越无序\n    :param dataSet: 数据集\n    :return:香农熵的值\n    \u0026#34;\u0026#34;\u0026#34;\n    numEntries = len(dataSet)# 获得数据中实例的总数\n    labelCounts = {}# 声明一个字典# 为所有可能分类创建字典,它的键值是最后一列的数值for featVecin dataSet:\n        currentLabel = featVec[-1]# 目前的标签=数据集的每行的最后一个元素if currentLabelnotin labelCounts.keys():# 如果键值不存在,则扩展字典并将当前键值加入字典\n            labelCounts[currentLabel] = 0# 初始化为0\n\n        labelCounts[currentLabel] = labelCounts[currentLabel] + 1# 每个键值都记录了当前类别出现的次数,出现一次加1\n\n    shannonEnt = 0.0\nfor keyin labelCounts:\n        prob = float(labelCounts[key]) / numEntries# 使用所有类标签的发生频率计算类别出现的概率\n        shannonEnt = shannonEnt - prob * log(prob, 2)# 使用这个概率计算香农熵 以2为底求对数return shannonEnt\n\ndefcreateDataSet():\n    \u0026#34;\u0026#34;\u0026#34;\n    创建简单鱼类鉴定数据集\n    :return:dataSet(数据集),labels(标签)\n    \u0026#34;\u0026#34;\u0026#34;\n    _dataSet = [\n        [1, 1, \u0026#39;yes\u0026#39;],\n        [1, 1, \u0026#39;yes\u0026#39;],\n        [1, 0, \u0026#39;no\u0026#39;],\n        [0, 1, \u0026#39;no\u0026#39;],\n        [0, 1, \u0026#39;no\u0026#39;]\n    ]\n\n    _labels = [\u0026#39;no surfacing\u0026#39;, \u0026#39;flippers\u0026#39;]\nreturn _dataSet, _labels\n\nmyData, labels = createDataSet()\nprint myData\nprint calcShannonEnt(myData)\n\n\u0026#34;\u0026#34;\u0026#34;\nresult\n\n[[1, 1, \u0026#39;yes\u0026#39;], [1, 1, \u0026#39;yes\u0026#39;], [1, 0, \u0026#39;no\u0026#39;], [0, 1, \u0026#39;no\u0026#39;], [0, 1, \u0026#39;no\u0026#39;]]\n0.970950594455\n\u0026#34;\u0026#34;\u0026#34;\n\n# 熵越高,则混合的数据也越多,我们可以在数据集中添加更多的分类,观察熵是如何变化的,这里我们增加第三个名为maybe的分类,测试熵的变化\n\nmyData[0][-1] = \u0026#39;maybe\u0026#39;# 第0行的最后一个元素置为maybeprint myData\nprint calcShannonEnt(myData)\n\u0026#34;\u0026#34;\u0026#34;\n[[1, 1, \u0026#39;maybe\u0026#39;], [1, 1, \u0026#39;yes\u0026#39;], [1, 0, \u0026#39;no\u0026#39;], [0, 1, \u0026#39;no\u0026#39;], [0, 1, \u0026#39;no\u0026#39;]]\n1.37095059445\n\u0026#34;\u0026#34;\u0026#34;\n# 得到熵以后,我们就可以按照获取最大信息增益的方法划分数据集# 另一个度量集合无需程度的方法是`基尼不纯度`(Gini impurity),简单地说就是从一个数据集中随机选取子项,度量其被错误分类到其他分组里的概率\u0026#34;\u0026#34;\u0026#34;\n分类算法除了要测量信息熵,还需要划分数据集,度量花费数据集的熵,以便判断当前是否正确划分了数据集,我们将对每个特征划分数据集的结果计算一次信息熵,然后判断按照那个特征划分数据集是最好的划分方式\n\u0026#34;\u0026#34;\u0026#34;\n\ndefsplitDataSet(dataSet, axis, value):\n    \u0026#34;\u0026#34;\u0026#34;\n    按照给定特征划分数据集\n    :param dataSet: 待划分的数据集\n    :param axis:划分数据集的特征位\n    :param value:预设特征值\n    :return:\n    \u0026#34;\u0026#34;\u0026#34;\n    retDataSet = []# 创建新的list对象,因为函数中传递的是列表的引用,在函数内部对列表对象的修改,将会影响该列表对象的整个生存周期,因此需要创建一个新的列表for featVecin dataSet:\nif featVec[axis] == value:# 如果axis位的值等于预设的特征值\n            reducedFeatVec = featVec[:axis]# 取行数据的前axis位\n            reducedFeatVec.extend(featVec[axis + 1:])# 加上axis位后的元素\n            retDataSet.append(reducedFeatVec)# 就是将除了axis特征位的元素抽取出来return retDataSet\n\n# 测试splitDataSetprint \u0026#34;测试splitDataSet\u0026#34;\nmyData, labels = createDataSet()\nprint myData\n\nnewData = splitDataSet(myData, 0, 1)# 将待测试数据的第0位为1的数据的其他特征和标签筛选出来print newData\n\ndefchooseBestFeatureToSplit(dataSet):\n    \u0026#34;\u0026#34;\u0026#34;\n    选择最好的数据集划分方式\n    :param dataSet: 数据集\n    :return:\n    \u0026#34;\u0026#34;\u0026#34;\n    numFeatures = len(dataSet[0]) - 1\n# 计算整个数据的原始香农熵\n    baseEntropy = calcShannonEnt(dataSet)\n    bestInfoGain = 0.0\n    bestFeature = -1\n# 遍历数据集中的所有特征for iin range(numFeatures):\n# 创建唯一的分类标签列表\n        featList = [example[i]for examplein dataSet]\n        uniqueVals = set(featList)# 将list转换为set,集合类型中每个值不同,从列表中创建集合是Python语言中得到列表中唯一元素值的最快方法\n        newEntropy = 0.0\n# 计算每种划分方式的信息熵for valuein uniqueVals:# 遍历当前特征中的所以唯一属性值,对每个特征划分一次数据集\n            subDataSet = splitDataSet(dataSet, i, value)\n            prob = len(subDataSet) / float(len(dataSet))\n            newEntropy += prob * calcShannonEnt(subDataSet)# 并对所有唯一特征值得到的熵求和\n        infoGian = baseEntropy - newEntropy# 信息增益=熵的减少值或者数据无序度的减少# 计算最好的信息增益if infoGian \u0026gt; bestInfoGain:\n            bestInfoGain = infoGian\n            bestFeature = i\nreturn bestFeature# 返回最好特征划分的索引值# 获取最好特征划分索引值print \u0026#34;获取最好特征划分索引值\u0026#34;\nmyData, labels = createDataSet()\nprint \u0026#34;dataSet:\u0026#34; + str(myData)\nprint \u0026#34;best index:\u0026#34; + str(chooseBestFeatureToSplit(myData))\n\n\u0026#34;\u0026#34;\u0026#34;\n代码表示,第0个特征是最好的用户划分数据集的特征\n\u0026#34;\u0026#34;\u0026#34;\n\n\u0026#34;\u0026#34;\u0026#34;\n递归构建决策树\n\n\u0026#34;\u0026#34;\u0026#34;\n\ndefmajorityCnt(classList):\n    \u0026#34;\u0026#34;\u0026#34;\n    类似classify0部分的投票表决\n    创建键值为classList中唯一值的数据字典,字典对象存储了classList中每个类标签出现的频率,最后利用operator操作键值排序字典,返回出现次数最多的分类名称\n    :rtype: str\n    :param classList: 分类名称的列表\n    :return: 返回出现次数最多的分类名称\n    \u0026#34;\u0026#34;\u0026#34;\n    classCount = {}\nfor votein classList:\nif votenotin classCount.keys():# 如果投票的类别不在classCount字典的key中,\n            classCount[vote] = 0# 则新建此key,value设置为0\n        classCount[vote] += 1\n\n    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)\nreturn sortedClassCount[0][0]\n\ndefcreateTree(dataSet, labels):\n    \u0026#34;\u0026#34;\u0026#34;\n    创建树,使用了递归\n    :param dataSet:数据集\n    :param labels: 分类标签\n    :return: 树结构\n    \u0026#34;\u0026#34;\u0026#34;\n\n    classList = [example[-1]for examplein dataSet]# 将数据集中的每一行的最后一个元素的值取出生成类别列表classListif classList.count(classList[0]) == len(classList):# 递归停止条件1return classList[0]# 类别完全相同时停止继续划分if len(dataSet[0]) == 1:# 递归停止条件2return majorityCnt(classList)# 遍历完所有特征时返回出现次数最多的\n    bestFeat = chooseBestFeatureToSplit(dataSet)# 选择最好的划分特征位\n    bestFeatLabel = labels[bestFeat]# 取出最好的标签\n    myTree = {bestFeatLabel: {}}# 初始化一个tree字典del (labels[bestFeat])# 删除最好的标签,防止树的下一个节点还使用此标签\n    featValues = [example[bestFeat]for examplein dataSet]# 创建最好的特征位的值所构建的list\n    uniqueVals = set(featValues)# 生成唯一的特征位的值构成的集合setfor valuein uniqueVals:\n        subLabels = labels[:]# 复制所有标签,且树不会和已经存在的标签搞混\n        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)# 递归创建树return myTree\n\n#  构建树测试print \u0026#34;构建树测试\u0026#34;\nmyData, labels = createDataSet()\nmyTree = createTree(myData, labels)\nprint myTree\n\u003c/code\u003e\u003c/pre\u003e\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# coding:utf-8import matplotlib.pyplotas plt# 导入绘制图库\u0026#34;\u0026#34;\u0026#34;\n绘制树图\n\u0026#34;\u0026#34;\u0026#34;\ndecisionNode = dict(boxstyle=\u0026#34;sawtooth\u0026#34;, fc=\u0026#34;0.8\u0026#34;)\nleafNode = dict(boxstyle=\u0026#34;round4\u0026#34;, fc=\u0026#34;0.8\u0026#34;)\narrow_args = dict(arrowstyle=\u0026#34;\u0026lt;-\u0026#34;)\n\ndefgetNumLeafs(myTree):\n    \u0026#34;\u0026#34;\u0026#34;\n    获取叶节点的数目\n    :param myTree:树结构\n    :return: 叶节点的数目\n    \u0026#34;\u0026#34;\u0026#34;\n    numLeafs = 0\n    firstStr = myTree.keys()[0]\n    secondDict = myTree[firstStr]\nfor keyin secondDict.keys():\nif type(secondDict[\n                    key]).__name__ == \u0026#39;dict\u0026#39;:# test to see if the nodes are dictonaires, if not they are leaf nodes\n            numLeafs += getNumLeafs(secondDict[key])\nelse:\n            numLeafs += 1\nreturn numLeafs\n\ndefgetTreeDepth(myTree):\n    \u0026#34;\u0026#34;\u0026#34;\n    获取树的层数(深度)\n    :param myTree:\n    :return: 树的层数(深度)\n    \u0026#34;\u0026#34;\u0026#34;\n    maxDepth = 0\n    firstStr = myTree.keys()[0]\n    secondDict = myTree[firstStr]\nfor keyin secondDict.keys():\nif type(secondDict[\n                    key]).__name__ == \u0026#39;dict\u0026#39;:# 判断此节点是否是字典,如果不是就是叶节点\n            thisDepth = 1 + getTreeDepth(secondDict[key])# 是字典,层数+1else:\n            thisDepth = 1# 叶节点,层数为1if thisDepth \u0026gt; maxDepth:\n            maxDepth = thisDepth\nreturn maxDepth\n\ndefplotNode(nodeTxt, centerPt, parentPt, nodeType):\n    \u0026#34;\u0026#34;\u0026#34;\n    绘制节点\n    :param nodeTxt:节点名称\n    :param centerPt: 目前的位置\n    :param parentPt: 父节点的位置\n    :param nodeType: 节点类型\n    :return:\n    \u0026#34;\u0026#34;\u0026#34;\n    createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords=\u0026#39;axes fraction\u0026#39;,\n                            xytext=centerPt, textcoords=\u0026#39;axes fraction\u0026#39;,\n                            va=\u0026#34;center\u0026#34;, ha=\u0026#34;center\u0026#34;, bbox=nodeType, arrowprops=arrow_args)\n\ndefplotMidText(currentPt, parentPt, txtString):\n    \u0026#34;\u0026#34;\u0026#34;\n    绘制中间的文本\n    :param currentPt:目前的位置\n    :param parentPt: 父节点的位置\n    :param txtString: 想绘制的文本\n    :return:\n    \u0026#34;\u0026#34;\u0026#34;\n    xMid = (parentPt[0] - currentPt[0]) / 2.0 + currentPt[0]\n    yMid = (parentPt[1] - currentPt[1]) / 2.0 + currentPt[1]\n    createPlot.ax1.text(xMid, yMid, txtString, va=\u0026#34;center\u0026#34;, ha=\u0026#34;center\u0026#34;, rotation=30)\n\ndefplotTree(myTree, parentPt, nodeTxt):# if the first key tells you what feat was split on\n    numLeafs = getNumLeafs(myTree)# this determines the x width of this tree\n    depth = getTreeDepth(myTree)\n    firstStr = myTree.keys()[0]# the text label for this node should be this\n    cntrPt = (plotTree.xOff + (1.0 + float(numLeafs)) / 2.0 / plotTree.totalW, plotTree.yOff)\n    plotMidText(cntrPt, parentPt, nodeTxt)\n    plotNode(firstStr, cntrPt, parentPt, decisionNode)\n    secondDict = myTree[firstStr]\n    plotTree.yOff = plotTree.yOff - 1.0 / plotTree.totalD\nfor keyin secondDict.keys():\nif type(secondDict[\n                    key]).__name__ == \u0026#39;dict\u0026#39;:# test to see if the nodes are dictonaires, if not they are leaf nodes\n            plotTree(secondDict[key], cntrPt, str(key))# recursionelse:# it\u0026#39;s a leaf node print the leaf node\n            plotTree.xOff = plotTree.xOff + 1.0 / plotTree.totalW\n            plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)\n            plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))\n    plotTree.yOff = plotTree.yOff + 1.0 / plotTree.totalD\n\n# if you do get a dictionary you know it\u0026#39;s a tree, and the first element will be another dictdefcreatePlot(inTree):\n    fig = plt.figure(1, facecolor=\u0026#39;white\u0026#39;)\n    fig.clf()\n    axprops = dict(xticks=[], yticks=[])\n    createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)# no ticks# createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses\n    plotTree.totalW = float(getNumLeafs(inTree))\n    plotTree.totalD = float(getTreeDepth(inTree))\n    plotTree.xOff = -0.5 / plotTree.totalW\n    plotTree.yOff = 1.0\n    plotTree(inTree, (0.5, 1.0), \u0026#39;\u0026#39;)\n    plt.show()\n\n# def createPlot():#    fig = plt.figure(1, facecolor=\u0026#39;white\u0026#39;)#    fig.clf()#    createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses#    plotNode(\u0026#39;a decision node\u0026#39;, (0.5, 0.1), (0.1, 0.5), decisionNode)#    plotNode(\u0026#39;a leaf node\u0026#39;, (0.8, 0.1), (0.3, 0.8), leafNode)#    plt.show()defretrieveTree(i):\n    listOfTrees = [{\u0026#39;no surfacing\u0026#39;: {0: \u0026#39;no\u0026#39;, 1: {\u0026#39;flippers\u0026#39;: {0: \u0026#39;no\u0026#39;, 1: \u0026#39;yes\u0026#39;}}}},\n                   {\u0026#39;no surfacing\u0026#39;: {0: \u0026#39;no\u0026#39;, 1: {\u0026#39;flippers\u0026#39;: {0: {\u0026#39;head\u0026#39;: {0: \u0026#39;no\u0026#39;, 1: \u0026#39;yes\u0026#39;}}, 1: \u0026#39;no\u0026#39;}}}}\n                   ]\nreturn listOfTrees[i]\n\n# createPlot(thisTree)\n\nmyTree = retrieveTree(0)\ncreatePlot(myTree)\n\u003c/code\u003e\u003c/pre\u003e","title":"机器学习实战-学习笔记之Decision Trees"},{"content":"概念 什么是卡片工作法 实践 选用工具-以notion为例 notion synced block支持的功能 写在一个地方,可以同步到任何地方 在使用同步块的任何页面,随时编辑,可以实时同步,不用找到最原始的出处,方便修改 任意block可以随意转换成同步块 在某些场景下,一个同步块的内容在不同的使用场景下,需要进行些许的修改,但是又不想影响到原始的内容,则可以在使用的地方对这个同步块进行unsync操作 支持源同步块一键取消所有同步→unsync Notion中如何实践卡片笔记 参考 https://www.bilibili.com/video/BV1mG411g7Wz\n让AI助力资讯收集 参考 https://www.bilibili.com/video/BV1524y1M73Y， 新建一个带有AI block的模板, 让AI来帮助我们将文章分类, 以及做摘要, 方便分类\n思考 参考 Synced blocks - Notion Help Center\n【社会学】 访谈卢曼：1973年与1989年 （双语）_哔哩哔哩_bilibili\n创意笔记大师：卢曼和达芬奇 - Forte Labs\n卢曼纪录片卡片盒分析笔记\n关联起发散的思维成果，我用 Notion 实践卡片盒笔记法\nhttps://www.bilibili.com/video/BV1524y1M73Y\n","permalink":"https://blog.jimersylee.com/posts/%E5%8D%A1%E7%89%87%E5%B7%A5%E4%BD%9C%E6%B3%95%E5%9C%A8notion%E4%B8%AD%E7%9A%84%E5%AE%9E%E8%B7%B5/","summary":"\u003ch2 id=\"概念\"\u003e概念\u003c/h2\u003e\n\u003ch3 id=\"什么是卡片工作法\"\u003e什么是卡片工作法\u003c/h3\u003e\n\u003cp\u003e\u003cimg alt=\"Untitled.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527212030652.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Untitled 1.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527212039519.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"实践\"\u003e实践\u003c/h2\u003e\n\u003ch3 id=\"选用工具-以notion为例\"\u003e选用工具-以notion为例\u003c/h3\u003e\n\u003ch3 id=\"notion-synced-block支持的功能\"\u003enotion synced block支持的功能\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e写在一个地方,可以同步到任何地方\u003c/li\u003e\n\u003cli\u003e在使用同步块的任何页面,随时编辑,可以实时同步,不用找到最原始的出处,方便修改\u003c/li\u003e\n\u003cli\u003e任意block可以随意转换成同步块\u003c/li\u003e\n\u003cli\u003e在某些场景下,一个同步块的内容在不同的使用场景下,需要进行些许的修改,但是又不想影响到原始的内容,则可以在使用的地方对这个同步块进行unsync操作\u003c/li\u003e\n\u003cli\u003e支持源同步块一键取消所有同步→unsync\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"notion中如何实践卡片笔记\"\u003eNotion中如何实践卡片笔记\u003c/h2\u003e\n\u003cp\u003e参考 \u003ca href=\"https://www.bilibili.com/video/BV1mG411g7Wz\"\u003ehttps://www.bilibili.com/video/BV1mG411g7Wz\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"让ai助力资讯收集\"\u003e让AI助力资讯收集\u003c/h2\u003e\n\u003cp\u003e参考 \u003ca href=\"https://www.bilibili.com/video/BV1524y1M73Y\"\u003ehttps://www.bilibili.com/video/BV1524y1M73Y\u003c/a\u003e， 新建一个带有AI block的模板, 让AI来帮助我们将文章分类, 以及做摘要, 方便分类\u003c/p\u003e\n\u003ch2 id=\"思考\"\u003e思考\u003c/h2\u003e\n\u003ch2 id=\"参考\"\u003e参考\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://www.notion.so/help/synced-blocks\"\u003eSynced blocks - Notion Help Center\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://www.bilibili.com/video/BV1Vb411k7wE\"\u003e【社会学】 访谈卢曼：1973年与1989年 （双语）_哔哩哔哩_bilibili\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://www.notion.so/Forte-Labs-76092829a9fa4bbfb5a177429a824f17?pvs=21\"\u003e创意笔记大师：卢曼和达芬奇 - Forte Labs\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://www.notion.so/d6ec09b1dce84bbba7d8fa15853ad42b?pvs=21\"\u003e卢曼纪录片卡片盒分析笔记\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://www.notion.so/Notion-f95d16236b07412b99fba607ba163933?pvs=21\"\u003e关联起发散的思维成果，我用 Notion 实践卡片盒笔记法\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://www.bilibili.com/video/BV1524y1M73Y\"\u003ehttps://www.bilibili.com/video/BV1524y1M73Y\u003c/a\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e","title":"卡片工作法在notion中的实践"},{"content":"Yii 1.x的管理后台分页导致的性能问题 上午客服反应管理后台在售管理和求购管理打开缓慢,我跟踪了下原因 我猜想原因如下:\n交易相关的,走了交易服务的接口,接口比较缓慢 没加limit或者查询条件比较复杂没走索引 查看代码,发现是php代码直接查询数据库的,排除第一个原因,接下来往直接查数据的方向查,可能原因有\n交易数据负载比较高,查询响应慢 管理后台使用了外网地址连接数据库 查看交易数据的近期慢SQL,是否有管理后台请求的慢SQL,发现端倪 本地开启浏览器显示sql调试信息 发现会select count(*) from trade_product_sell,这个应该是为了分页,但是线上表数据已经5600万了,这个sql得执行9秒,能不慢吗 管理后台的分页插件都会执行这个count操作\npublic function getTotalItemCount($refresh=false) { if($this-\u0026gt;_totalItemCount===null || $refresh) $this-\u0026gt;_totalItemCount=$this-\u0026gt;calculateTotalItemCount(); return $this-\u0026gt;_totalItemCount; } 找到问题后,怎么处理,现状如下\n此处代码为yii1.x的分页逻辑,查询总条数,然后计算分页,此代码在管理后台使用的非常多 此分页逻辑在针对小表查询的时候一点问题也没有,且使用比较方便 原则是不能改框架,且一定要获取到总条数,前端页面才能正常显示分页 解决方案\n既然总条数的获取不可避免,那就优化count(*)的性能\n根治的方法:针对大表进行归档处理,这个是交易系统的表,处理起来需要时间,可能要排到下季度了 治标的方法: 赋值一个假的总条数,这个的问题是体验不好,有的表只有几行,却显示一堆分页 治标的方法: 将查询总数缓存起来,虽然第一次会慢,但是之后都挺快的 代码实现\n不能修改框架代码,防止之后升级框架版本的时候覆盖了\n可以选择修改基础的模型类,增加一个方法,进行总数缓存,且可以按需使用,针对大表及对总数精确度不敏感的场景\nBasicAcitveRecord类中增加方法/** * 缓存一下分页组件使用的数据总条数 * 适用场景: * 对count不要求精确的,表比较大的,select count(*) 比较慢的那种,如果不缓存,每次都得查count(*)比较慢 * 不适用场景: * 对总数要求精确的 * 实现原理,使用表名+查询条件的md5作为key,缓存此条件下的查询总条数 * @param CActiveDataProvider $dataProvider :分页使用的dataProvider */ public function cacheTotalCount(CActiveDataProvider $dataProvider) { /** * @var $redis ARedisCache */ $redis = Yii::app()-\u0026gt;cache; $md5 = md5(json_encode($dataProvider-\u0026gt;getCountCriteria()) . $dataProvider-\u0026gt;model-\u0026gt;tableName()); $count = $redis-\u0026gt;get(RedisKeyService::getCachedTabelCountKey($md5)); if ($count != null) { $dataProvider-\u0026gt;setTotalItemCount($count); } else { $redis-\u0026gt;set(RedisKeyService::getCachedTabelCountKey($md5), $dataProvider-\u0026gt;getTotalItemCount(), 3600); } }\n使用方式\n$model = new TradeTransfer(); $model-\u0026gt;setAttributes([ \u0026#39;id\u0026#39; =\u0026gt; $id, \u0026#39;receiver_steam_id\u0026#39; =\u0026gt; $receiverSteamId, \u0026#39;sender_steam_id\u0026#39; =\u0026gt; $senderSteamId, \u0026#39;status\u0026#39; =\u0026gt; $status, ], false); $dataProvider = $model-\u0026gt;search($orderAssetId); $dataProvider-\u0026gt;pagination-\u0026gt;setPageSize(10); //原有的代码增加下面这行 $model-\u0026gt;cacheTotalCount($dataProvider); $this-\u0026gt;render(\u0026#39;index\u0026#39;, [\u0026#39;dataProvider\u0026#39; =\u0026gt; $dataProvider]); 优化成果,接口响应时间从10秒多到1秒多\n","permalink":"https://blog.jimersylee.com/posts/yii1.x%E7%9A%84%E7%AE%A1%E7%90%86%E5%90%8E%E5%8F%B0%E5%88%86%E9%A1%B5%E5%AF%BC%E8%87%B4%E7%9A%84%E6%80%A7%E8%83%BD%E9%97%AE%E9%A2%98/","summary":"\u003ch1 id=\"yii-1x的管理后台分页导致的性能问题\"\u003eYii 1.x的管理后台分页导致的性能问题\u003c/h1\u003e\n\u003cp\u003e上午客服反应管理后台在售管理和求购管理打开缓慢,我跟踪了下原因 我猜想原因如下:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e交易相关的,走了交易服务的接口,接口比较缓慢\u003c/li\u003e\n\u003cli\u003e没加limit或者查询条件比较复杂没走索引\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e查看代码,发现是php代码直接查询数据库的,排除第一个原因,接下来往直接查数据的方向查,可能原因有\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cdel\u003e交易数据负载比较高,查询响应慢\u003c/del\u003e\u003c/li\u003e\n\u003cli\u003e\u003cdel\u003e管理后台使用了外网地址连接数据库\u003c/del\u003e\u003c/li\u003e\n\u003cli\u003e查看交易数据的近期慢SQL,是否有管理后台请求的慢SQL,发现端倪\u003c/li\u003e\n\u003cli\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cimg alt=\"page1.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527205259288.png\"\u003e\u003c/p\u003e\n\u003cp\u003e本地开启浏览器显示sql调试信息\n\u003cimg alt=\"page2.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527205313102.png\"\u003e\u003c/p\u003e\n\u003cp\u003e发现会select count(*) from trade_product_sell,这个应该是为了分页,但是线上表数据已经5600万了,这个sql得执行9秒,能不慢吗 管理后台的分页插件都会执行这个count操作\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epublic function getTotalItemCount($refresh=false)\n   {\n        if($this-\u0026gt;_totalItemCount===null || $refresh)\n            $this-\u0026gt;_totalItemCount=$this-\u0026gt;calculateTotalItemCount();\n        return $this-\u0026gt;_totalItemCount;\n   }\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e找到问题后,怎么处理,现状如下\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e此处代码为yii1.x的分页逻辑,查询总条数,然后计算分页,此代码在管理后台使用的非常多\u003c/li\u003e\n\u003cli\u003e此分页逻辑在针对小表查询的时候一点问题也没有,且使用比较方便\u003c/li\u003e\n\u003cli\u003e原则是不能改框架,且一定要获取到总条数,前端页面才能正常显示分页\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e解决方案\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e既然总条数的获取不可避免,那就优化count(*)的性能\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cdel\u003e根治的方法:针对大表进行归档处理,这个是交易系统的表,处理起来需要时间,可能要排到下季度了\u003c/del\u003e\u003c/li\u003e\n\u003cli\u003e\u003cdel\u003e治标的方法: 赋值一个假的总条数,这个的问题是体验不好,有的表只有几行,却显示一堆分页\u003c/del\u003e\u003c/li\u003e\n\u003cli\u003e治标的方法: 将查询总数缓存起来,虽然第一次会慢,但是之后都挺快的\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e代码实现\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e不能修改框架代码,防止之后升级框架版本的时候覆盖了\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e可以选择修改基础的模型类,增加一个方法,进行总数缓存,且可以按需使用,针对大表及对总数精确度不敏感的场景\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ccode\u003eBasicAcitveRecord类中增加方法/** * 缓存一下分页组件使用的数据总条数 * 适用场景: * 对count不要求精确的,表比较大的,select count(*) 比较慢的那种,如果不缓存,每次都得查count(*)比较慢 * 不适用场景: * 对总数要求精确的 * 实现原理,使用表名+查询条件的md5作为key,缓存此条件下的查询总条数 * @param CActiveDataProvider $dataProvider :分页使用的dataProvider */ public function cacheTotalCount(CActiveDataProvider $dataProvider) { /** * @var $redis ARedisCache */ $redis = Yii::app()-\u0026gt;cache; $md5 = md5(json_encode($dataProvider-\u0026gt;getCountCriteria()) . $dataProvider-\u0026gt;model-\u0026gt;tableName()); $count = $redis-\u0026gt;get(RedisKeyService::getCachedTabelCountKey($md5)); if ($count != null) { $dataProvider-\u0026gt;setTotalItemCount($count); } else { $redis-\u0026gt;set(RedisKeyService::getCachedTabelCountKey($md5), $dataProvider-\u0026gt;getTotalItemCount(), 3600); } }\u003c/code\u003e\u003c/p\u003e","title":"Yii1.x的管理后台分页导致的性能问题"},{"content":"故障表现 在重新发布交易服务的时候,网站出现访问延迟增长的情况\n故障分析 排查清单 RDS数据库指标是否正常,CPU,内存正常,会话数激增\nES搜索服务是否正常,正常\nREDIS服务是否正常,正常\n确认基本方向,可能数据库连接配置存在问题\n排查druid依赖版本问题 查看pom依赖,发现版本太老了,1.0.16的版本发布于2015年10月\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;com.alibaba\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;druid-spring-boot-starter\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.1.0\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;com.alibaba\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;druid\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.0.16\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; 进行升级测试,保险起见,升级到1.1.x系列的最新版本,发布于2020.9月,还有最新的1.2.x版本可能存在较大更新,没有尝试\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;com.alibaba\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;druid-spring-boot-starter\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.1.24\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;com.alibaba\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;druid\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;1.1.24\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; 本地测试正常,无兼容性修改\n排查druid配置问题 生产环境配置如下\ntrade: datasource: #druid相关配置 druid: #监控统计拦截的filters filters: stat driverClassName: com.mysql.jdbc.Driver #配置基本属性 url: **** username: *** password: **** #配置初始化大小/最小/最大 initialSize: 50 minIdle: 10 maxActive: 300 #获取连接等待超时时间 maxWait: 60000 #间隔多久进行一次检测，检测需要关闭的空闲连接 timeBetweenEvictionRunsMillis: 60000 #一个连接在池中最小生存的时间 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT \u0026#39;x\u0026#39; testWhileIdle: true testOnBorrow: false testOnReturn: false #打开PSCache，并指定每个连接上PSCache的大小。oracle设为true，mysql设为false。分库分表较多推荐设置为false poolPreparedStatements: false maxPoolPreparedStatementPerConnectionSize: 20 查询druid配置最佳实践相关资料 有赞DB连接池性能优化-InfoQ\n数据库连接池配置（案例及排查指南）\ndruid连接池推荐配置 - 晨猫 - OSCHINA - 中文开源技术交流社区\nDruid连接池配置 - 用户指南| 阿里云\nDruidDataSource配置 · alibaba/druid Wiki\n技术经理：求求你，别再乱改数据库连接池的大小了！ - SegmentFault 思否\ndruid mysql 最佳配置_DRUID连接池的实用 配置详解_weixin_39531834的博客-CSDN博客\n得到的核心结论为:最大连接数 = ((核心数 * 2) + 有效磁盘数) 我们容器配置为初始化2核,SSD盘,根据理论公式,2核基本上为4线程,最大连接数为8即可\n根据druid官方仓库的DruidDataSource配置 · alibaba/druid Wiki的说明,最大连接数并没有超过20\n因此,做了一些更改\ntrade: datasource: #druid相关配置 druid: filters: stat driverClassName: com.mysql.jdbc.Driver url: jdbc:**** username: **** password: **** initialSize: 10 #由50改为10 minIdle: 10 maxActive: 20 #由300改为20 #获取连接等待超时时间,由60秒改为6秒 maxWait: 6000 #间隔多久进行一次检测，检测需要关闭的空闲连接 timeBetweenEvictionRunsMillis: 2000 #由60秒改为2秒,目的尽早释放连接 #一个连接在池中最小生存的时间 minEvictableIdleTimeMillis: 600000 validationQuery: SELECT 1 testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: false maxPoolPreparedStatementPerConnectionSize: 20 keepAlive: true # 增加keepAlive参数,保持连接 phyMaxUseCount: 1000 #新版本的druid新增的参数,针对分布式数据库的优化,每个连接使用此次数后就释放,让负载更加均衡 压测性能表现 通过调用一个多次查询数据库的接口进行测试\n原来的配置 ab -n1000 -c40 https://xxxxx This is ApacheBench, Version 2.3 \u0026lt;$Revision: 1879490 $\u0026gt; Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Completed 100 requests Completed 200 requests Completed 300 requests Completed 400 requests Completed 500 requests Completed 600 requests Completed 700 requests Completed 800 requests Completed 900 requests Completed 1000 requests Finished 1000 requests Server Software: Server Port: 443 SSL/TLS Protocol: TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256 Server Temp Key: X25519 253 bits Document Path: Document Length: 194 bytes Concurrency Level: 40 Time taken for tests: 6.032 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 670000 bytes HTML transferred: 194000 bytes Requests per second: 165.78 [#/sec] (mean) Time per request: 241.288 [ms] (mean) Time per request: 6.032 [ms] (mean, across all concurrent requests) Transfer rate: 108.47 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 28 129 91.2 114 793 Processing: 22 84 69.3 65 537 Waiting: 22 84 69.2 64 537 Total: 55 214 139.1 181 1042 Percentage of the requests served within a certain time (ms) 50% 181 66% 214 75% 246 80% 266 90% 357 95% 522 98% 706 99% 769 100% 1042 (longest request) 升级druid版本和修改配置后的压测结果 调整配置后 ab -n1000 -c40 https://xxxx This is ApacheBench, Version 2.3 \u0026lt;$Revision: 1879490 $\u0026gt; Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Completed 100 requests Completed 200 requests Completed 300 requests Completed 400 requests Completed 500 requests Completed 600 requests Completed 700 requests Completed 800 requests Completed 900 requests Completed 1000 requests Finished 1000 requests Server Software: Server Hostname: xxxx Server Port: 443 SSL/TLS Protocol: TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256 Server Temp Key: X25519 253 bits TLS Server Name: xxxx Document Path: 114\u0026amp;platformId=2 Document Length: 194 bytes Concurrency Level: 40 Time taken for tests: 3.611 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 670000 bytes HTML transferred: 194000 bytes Requests per second: 276.95 [#/sec] (mean) Time per request: 144.430 [ms] (mean) Time per request: 3.611 [ms] (mean, across all concurrent requests) Transfer rate: 181.21 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 27 85 34.1 95 196 Processing: 22 52 15.2 51 112 Waiting: 21 51 15.1 50 111 Total: 56 137 45.4 143 251 Percentage of the requests served within a certain time (ms) 50% 143 66% 168 75% 175 80% 179 90% 193 95% 204 98% 221 99% 228 100% 251 (longest request) 核心指标,并发请求性能提升67%,延迟降低100ms\nRequests per second: 165.78 [#/sec] (mean) Time per request: 241.288 [ms] (mean)\nRequests per second: 276.95 [#/sec] (mean) Time per request: 144.430 [ms] (mean)\n连接池配置修改之前,从13:34-13:53出现比较大的波动\n修改配置之后10:35-10:38 波动较少\n结论 连接池不宜设置过大的最大连接数,还需要配置连接尽快释放,可以提升并发能力,减轻应用更新时对数据库的影响\n额外需要注意的点 实际上，连接池的大小的设置还是要结合实际的业务场景来说事。\n比如说，你的系统同时混合了长事务和短事务，这时，根据上面的公式来计算就很难办了。正确的做法应该是创建两个连接池，一个服务于长事务，一个服务于\u0026quot;实时\u0026quot;查询，也就是短事务。\n还有一种情况，比方说一个系统执行一个任务队列，业务上要求同一时间内只允许执行一定数量的任务，这时，我们就应该让并发任务数去适配连接池连接数，而不是连接数大小去适配并发任务数。\n在更新了连接池配置后,发现发布时访问延迟波动还是比较明显 其中10:35分之后的波动,为更新了连接池配置的波动,还是比较大\n10:34开始更新交易服务 10:40全部更新完毕\n想起之前针对更新部署的优化,询问小马之前的说的eureka配置优化是否已经发布,小马表示已经上线\n然而我去查看git历史,并没有合并入master的代码,查看构建系统,docker镜像,都没有更新过,上次更新还在2020年\n小马表示不敢相信,最后确认代码还在本地,没有提交,他表示可能梦中发布了 :broken_heart:\n于是提交代码,我进行测试发布\n相关资料\neureka一级二级缓存\n在重新发布服务的时候,我们会先请求/pause端点,节点状态会变成DOWN,但是因为存在30秒的缓存,各个服务获取到的可用服务IP还存在延迟,导致ribbon把流量导到不可用的ip,然后进行多次重试,造成响应时间增加\n更新eureka后,11:19分开始测试更新交易服务,响应延迟波动相比之前降低60%\n结论 一级缓存不存在自动失效期和手动清除 二级缓存存在默认180s自动清除以及当注册服务下线，过期，注册，状态变更，都会来清除里面的数据 另外当二级缓存数据被清除以后以后，只能依靠定时任务刷新一级缓存里面的数据，也就是说最快也要等默认的30s才能更新一级缓存\n一级缓存是默认开启的，如果不能忍受这30秒的响应缓存变更延迟，可以手动禁止使用一级缓存，然后把二级缓存改为你能忍受的时间 后续 1.如何添加 druid 数据库连接池的监控指标\n获取容器中所有类型为 DruidDataSource 的bean，然后遍历他们，map的key是这个 bean的id，也就是bean的名称，value 就是 DruidDataSource d对象，他身上可以获取到 活跃连接数\n另外这是一个定时任务，即每个实例都会每三秒收集一次指标\n@Component @Slf4j public class DruidDataSourceMetricsTask { private final static String ACTIVE_CONNECTIONS = \u0026#34;druid.pool.active_connections\u0026#34;; private final static String IDLE_CONNECTIONS = \u0026#34;druid.pool.pool_connections\u0026#34;; private final static String WAIT_CONNECTION_THREAD = \u0026#34;druid.pool.wait_connection_thread\u0026#34;; private final static String ACTIVE_PEEK = \u0026#34;druid.pool.active_peek\u0026#34;; @Resource private ApplicationContext applicationContext; @Resource private MeterRegistry meterRegistry; /** * 五秒收集一次连接池的指标 */ @Scheduled(fixedDelay = 3000) public void collectMetrics(){ Map\u0026lt;String, DruidDataSource\u0026gt; dataSourceMap = applicationContext.getBeansOfType(DruidDataSource.class); if (dataSourceMap.isEmpty()) { return; } for (String datasourceName : dataSourceMap.keySet()) { DruidDataSource druidDataSource = dataSourceMap.get(datasourceName); // 当前活跃连接数 Gauge.builder(ACTIVE_CONNECTIONS, druidDataSource::getActiveCount).tags(\u0026#34;datasource\u0026#34;, datasourceName).description(\u0026#34;druid pool active count\u0026#34;) .register(meterRegistry); // 当前连接池中的连接数 Gauge.builder(IDLE_CONNECTIONS, druidDataSource::getPoolingCount).tags(\u0026#34;datasource\u0026#34;, datasourceName).description(\u0026#34;druid pool count\u0026#34;) .register(meterRegistry); // 等待连接的线程数量，这个一旦大于0并且增多的话，就会导致对数据库的增删改查变慢，因为他需要等待一段时间才可以获取到连接才可以去操作数据库 Gauge.builder(WAIT_CONNECTION_THREAD, druidDataSource::getWaitThreadCount).tags(\u0026#34;datasource\u0026#34;, datasourceName).description(\u0026#34;druid pool wait thread count\u0026#34;) .register(meterRegistry); // 活跃峰值 Gauge.builder(ACTIVE_PEEK, druidDataSource::getActivePeak).tags(\u0026#34;datasource\u0026#34;, datasourceName).description(\u0026#34;druid active peek\u0026#34;) .register(meterRegistry); } } } 之后启动项目访问 /actuator/prometheus 端点，可以看到相应的指标数据。\n2. 如何展示上述指标数据\n例如，下图是 steamTrade 服务 以及 steamTrade 数据库中各实例的 活跃连接数的统计情况\n如何根据指标来对数据库链接池进行优化 这是我在胖子的基础上增大了最大连接数的修改，并且是以该数据发布的。 后面一张图是我调整后的配置\n首先是初始连接数10个，在此基础上的发布，发布期间发现存在较多的线程存在需要等待才能获取连接的情况。所以初始连接数肯定是不够的，我后面把他改成了40个，可以发现后面发布的时候等待获取连接的线程数大大减少了。\n另外比较有问题的是 ，连接池中的连接数量，即空闲连接数量。（活跃的连接不在这之内）。\n之前的策略是每2秒清理一次，最小空闲连接数量是10.但是可以看数据，即使2秒清理一次，我也从来没有看到空闲连接数量小于过20，这就表示，最小空闲连接数量10是肯定不够的。（因为被清理了后面程序需要又被新创建了。而这种不断清理创建连接资源的行为是非常的损耗性能的。）所以我觉得20这个数字是比较合适的最小空闲连接数量。可以看到20点之后这个数据非常的平稳维持在20左右。\n另外还修改的一个参数是 minEvictableIdleTimeMillis 这个是一个连接最少的存活时间，相应其实还有一个最大的存活时间。原来的参数是 10分钟，我改成了1分钟。即当一开始应用起来会较多的创建连接，但是1分钟后这些多余的连接就会被清理回收掉。\n总结：\n像连接池线程池这种对象池化的思想其实非常常见。即最大程度的对对象（或者说是资源，像mysql的会话连接就是属于资源）进行复用，不用频繁的去创建和销毁对象，带来不必要的开销。\n我所理解高性能的程序是：cpu将更多的时间用在程序本身逻辑上，而不是去做一些额外的工作。例如创建销毁对象，例如GC。\n所以应该是去观察现有的业务情况下现有的指标是怎么样的，根据线上运行的程序再来合理的调整参数。这也是度量驱动开发的思想。\n参考资料 有赞DB连接池性能优化-InfoQ\n数据库连接池配置（案例及排查指南）\ndruid连接池推荐配置 - 晨猫 - OSCHINA - 中文开源技术交流社区\nDruid连接池配置 - 用户指南| 阿里云\nDruidDataSource配置 · alibaba/druid Wiki\n技术经理：求求你，别再乱改数据库连接池的大小了！ - SegmentFault 思否\ndruid mysql 最佳配置_DRUID连接池的实用 配置详解_weixin_39531834的博客-CSDN博客\nspring-cloud/pause 平滑升级踩坑记录\n致谢：\n","permalink":"https://blog.jimersylee.com/posts/%E6%9C%8D%E5%8A%A1%E9%87%8D%E6%96%B0%E5%8F%91%E5%B8%83%E5%AF%BC%E8%87%B4%E7%9A%84mysql%E4%BC%9A%E8%AF%9D%E9%A3%99%E5%8D%87%E4%BB%A5%E5%8F%8A%E7%BD%91%E7%AB%99%E8%AE%BF%E9%97%AE%E5%BB%B6%E8%BF%9F%E7%9A%84%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5%E5%A4%84%E7%90%86/","summary":"\u003ch2 id=\"故障表现\"\u003e故障表现\u003c/h2\u003e\n\u003cp\u003e在重新发布交易服务的时候,网站出现访问延迟增长的情况\u003c/p\u003e\n\u003ch2 id=\"故障分析\"\u003e故障分析\u003c/h2\u003e\n\u003ch3 id=\"排查清单\"\u003e\u003cstrong\u003e排查清单\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e RDS数据库指标是否正常,CPU,内存正常,会话数激增\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"https://img.c5game.com/docimg/%E8%BF%9E%E6%8E%A5%E6%B1%A0%E8%B0%83%E6%95%B4%E4%B9%8B%E5%89%8Drds%E4%BC%9A%E8%AF%9D%E6%95%B0.png\"\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e ES搜索服务是否正常,正常\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e REDIS服务是否正常,正常\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e确认基本方向,可能数据库连接配置存在问题\u003c/p\u003e\n\u003ch3 id=\"排查druid依赖版本问题\"\u003e\u003cstrong\u003e排查druid依赖版本问题\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e查看pom依赖,发现版本太老了,1.0.16的版本发布于2015年10月\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\u0026lt;dependency\u0026gt;\n                \u0026lt;groupId\u0026gt;com.alibaba\u0026lt;/groupId\u0026gt;\n                \u0026lt;artifactId\u0026gt;druid-spring-boot-starter\u0026lt;/artifactId\u0026gt;\n                \u0026lt;version\u0026gt;1.1.0\u0026lt;/version\u0026gt;\n\u0026lt;/dependency\u0026gt;\n\u0026lt;dependency\u0026gt;\n                \u0026lt;groupId\u0026gt;com.alibaba\u0026lt;/groupId\u0026gt;\n                \u0026lt;artifactId\u0026gt;druid\u0026lt;/artifactId\u0026gt;\n                \u0026lt;version\u0026gt;1.0.16\u0026lt;/version\u0026gt;\n\u0026lt;/dependency\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e进行升级测试,保险起见,升级到1.1.x系列的最新版本,发布于2020.9月,还有最新的1.2.x版本可能存在较大更新,没有尝试\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e \u0026lt;dependency\u0026gt;\n                \u0026lt;groupId\u0026gt;com.alibaba\u0026lt;/groupId\u0026gt;\n                \u0026lt;artifactId\u0026gt;druid-spring-boot-starter\u0026lt;/artifactId\u0026gt;\n                \u0026lt;version\u0026gt;1.1.24\u0026lt;/version\u0026gt;\n\u0026lt;/dependency\u0026gt;\n\u0026lt;dependency\u0026gt;\n                \u0026lt;groupId\u0026gt;com.alibaba\u0026lt;/groupId\u0026gt;\n                \u0026lt;artifactId\u0026gt;druid\u0026lt;/artifactId\u0026gt;\n                \u0026lt;version\u0026gt;1.1.24\u0026lt;/version\u0026gt;\n \u0026lt;/dependency\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e本地测试正常,无兼容性修改\u003c/p\u003e\n\u003ch3 id=\"排查druid配置问题\"\u003e\u003cstrong\u003e排查druid配置问题\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e生产环境配置如下\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003etrade:\n  datasource:\n    #druid相关配置\n    druid:\n      #监控统计拦截的filters\n      filters: stat\n      driverClassName: com.mysql.jdbc.Driver\n      #配置基本属性\n      url: ****\n      username: ***\n      password: ****\n      #配置初始化大小/最小/最大\n      initialSize: 50\n      minIdle: 10\n      maxActive: 300\n      #获取连接等待超时时间\n      maxWait: 60000\n      #间隔多久进行一次检测，检测需要关闭的空闲连接\n      timeBetweenEvictionRunsMillis: 60000\n      #一个连接在池中最小生存的时间\n      minEvictableIdleTimeMillis: 300000\n      validationQuery: SELECT \u0026#39;x\u0026#39;\n      testWhileIdle: true\n      testOnBorrow: false\n      testOnReturn: false\n      #打开PSCache，并指定每个连接上PSCache的大小。oracle设为true，mysql设为false。分库分表较多推荐设置为false\n      poolPreparedStatements: false\n      maxPoolPreparedStatementPerConnectionSize: 20\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"查询druid配置最佳实践相关资料\"\u003e\u003cstrong\u003e查询druid配置最佳实践相关资料\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e\u003ca href=\"https://www.infoq.cn/article/njxtjbzpnlnfp6d56adr\"\u003e有赞DB连接池性能优化-InfoQ\u003c/a\u003e\u003c/p\u003e","title":"服务重新发布导致的mysql会话飙升以及网站访问延迟的问题排查处理"},{"content":"archlinux,manjaro 使用nvm安装管理node\n# 安装nvm sudo pacman -S nvm #配置nvm echo \u0026#34;source /usr/share/nvm/init-nvm.sh\u0026#34;\u0026gt;\u0026gt;~/.profile #立即生效 source /usr/share/nvm/init-nvm.sh # 安装稳定版 nvm install stable # 或者安装指定版本 nvm install 10.15.0 # 指定使用某个版本 nvm use 10.15.0 # 检查版本是否正确 node -v https://github.com/creationix/nvm\n","permalink":"https://blog.jimersylee.com/posts/%E4%BD%BF%E7%94%A8nvm%E7%AE%A1%E7%90%86node%E7%8E%AF%E5%A2%83/","summary":"\u003cp\u003earchlinux,manjaro 使用nvm安装管理node\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 安装nvm\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo pacman -S nvm\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#配置nvm\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;source /usr/share/nvm/init-nvm.sh\u0026#34;\u003c/span\u003e\u0026gt;\u0026gt;~/.profile\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#立即生效\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esource /usr/share/nvm/init-nvm.sh\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 安装稳定版\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003envm install stable\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 或者安装指定版本\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003envm install 10.15.0\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 指定使用某个版本\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003envm use 10.15.0\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 检查版本是否正确\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enode -v\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/creationix/nvm\"\u003ehttps://github.com/creationix/nvm\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e","title":"使用nvm管理node环境"},{"content":"背景 线上存在数据表主键重复的错误\n通过阿里云日志搜索以下关键词可以查询到\n\u0026ldquo;Duplicate\u0026rdquo; and \u0026ldquo;primary\u0026rdquo;\n日志例子\nlog:2021-04-22 13:19:31,896 ERROR [http-nio2-8088-exec-100] [ac140db2-knscphtx-437669] [steam-trade-boot] c.x.s.t.s.b.i.TradeOrderLogServiceImpl - 插入日志冲突 org.springframework.dao.DuplicateKeyException: ### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry \u0026#39;859225024379092992\u0026#39; for key \u0026#39;PRIMARY\u0026#39; ### The error may exist in com/xingchao/steam/trade/dal/mapper/TradeOrderLogMapper.java (best guess) ### The error may involve com.xingchao.steam.trade.dal.mapper.TradeOrderLogMapper.insertSelective-Inline ### The error occurred while setting parameters ### SQL: INSERT INTO trade_order_log ( id,order_asset_id,type,before_status,after_status,event,content,create_time,update_time ) VALUES( ?,?,?,?,?,?,?,?,? ) ### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry \u0026#39;859225024379092992\u0026#39; for key \u0026#39;PRIMARY\u0026#39; 代码例子 //项目中所有主键使用的SnowFlakeUtil这个组件生成id logDO.setId(SnowFlakeUtil.getId()); tradeOrderLogMapper.insertSelective(logDO); 表现 针对这种频繁插入的情况,容易出现id重复的问题,与当初预想的不一样\n分析 分布式id采用的twitter开源的snowflake算法,其本身是作为一个id服务集群运行的,其中关键的配置dataCenterId,workerId,需要在不同数据中心,不同的机器设置成不一样的\n但是这种方案,一是需要一些额外的资源来运行此服务,我们不想耗费这个资源,二是需要远程调用,存在性能问题,于是采用的本机生成的方案\n本地生成的方案关键在于如何设置这两个参数,我们看之前的实现\n// 生成worker_id long workId; try { StringBuilder sbInternet = new StringBuilder(); Enumeration\u0026lt;NetworkInterface\u0026gt; enumInter = NetworkInterface.getNetworkInterfaces(); while (enumInter.hasMoreElements()) { sbInternet.append(enumInter.nextElement().toString()); } int machinePiece = sbInternet.toString().hashCode() \u0026lt;\u0026lt; 16; workId = (long) (machinePiece % 32); } catch (Exception e) { // 如果获取失败，则使用随机数备用 workId = RandomUtils.nextLong(0, 31); } // 生成data_centerId int processPiece = ManagementFactory.getRuntimeMXBean().getName().hashCode() \u0026amp; 0xFFFF; long dataCenterId = (long) (processPiece % 32); 看代码意思是其中workId想用网卡信息来计算,datacenterId想用jvm管理器的对象来计算\nbug1:\nString str=\u0026#34;wejwjeuwewewje\u0026#34;; int hashCode=str.hashCode(); //hashCode: -418012329 int xx=hashCode\u0026lt;\u0026lt;16; //xx:-1554579456 因为左移了16位,等于乘以2^16, int mod = xx % 32; //mod:0 //必然可以被32整除,余数永远为0 bug2:\nStringBuilder sbInternet = new StringBuilder(); Enumeration\u0026lt;NetworkInterface\u0026gt; enumInter = NetworkInterface.getNetworkInterfaces(); while (enumInter.hasMoreElements()) { sbInternet.append(enumInter.nextElement().toString()); } 容器1 / # ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02 inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:1348 errors:0 dropped:0 overruns:0 frame:0 TX packets:1686 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:218719 (213.5 KiB) TX bytes:589400 (575.5 KiB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) 容器2 /# ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03 inet addr:172.17.0.3 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:776 errors:0 dropped:0 overruns:0 frame:0 TX packets:959 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:128968 (125.9 KiB) TX bytes:322602 (315.0 KiB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) 因为我们的应用在docker中运行,因此以上代码取到的网卡名永远一样,所以hashcode也永远一样,就算修复了第一个问题,取模的结果也一样\n以上问题导致workId永远为0,造成重复的可能性大大加大\n修改成如下代码\n// 生成worker_id long workId; try { StringBuilder sbInternet = new StringBuilder(); Enumeration\u0026lt;NetworkInterface\u0026gt; enumInter = NetworkInterface.getNetworkInterfaces(); //改成取硬件地址,也就是mac,得到的字符串肯定不一样 while (enumInter.hasMoreElements()) { sbInternet.append(Arrays.toString(enumInter.nextElement().getHardwareAddress())); } //hashcode可能为负数,与运算去除符号 int machinePiece = sbInternet.toString().hashCode() \u0026amp; Integer.MAX_VALUE; workId = machinePiece % 32; } catch (Exception e) { // 如果获取失败，则使用随机数备用 workId = RandomUtils.nextLong(0, 31); } ","permalink":"https://blog.jimersylee.com/posts/%E5%88%86%E5%B8%83%E5%BC%8Fid%E9%87%8D%E5%A4%8D%E9%97%AE%E9%A2%98/","summary":"\u003ch2 id=\"背景\"\u003e背景\u003c/h2\u003e\n\u003cp\u003e线上存在数据表主键重复的错误\u003c/p\u003e\n\u003cp\u003e通过\u003ca href=\"https://sls.console.aliyun.com/lognext/project/trade-api/logsearch/steam-trade-boot\"\u003e阿里云日志\u003c/a\u003e搜索以下关键词可以查询到\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u0026ldquo;Duplicate\u0026rdquo; and \u0026ldquo;primary\u0026rdquo;\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e日志例子\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003elog:2021-04-22 13:19:31,896 ERROR [http-nio2-8088-exec-100] [ac140db2-knscphtx-437669] [steam-trade-boot] c.x.s.t.s.b.i.TradeOrderLogServiceImpl - 插入日志冲突\norg.springframework.dao.DuplicateKeyException:\n### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry \u0026#39;859225024379092992\u0026#39; for key \u0026#39;PRIMARY\u0026#39;\n### The error may exist in com/xingchao/steam/trade/dal/mapper/TradeOrderLogMapper.java (best guess)\n### The error may involve com.xingchao.steam.trade.dal.mapper.TradeOrderLogMapper.insertSelective-Inline\n### The error occurred while setting parameters\n### SQL: INSERT INTO trade_order_log ( id,order_asset_id,type,before_status,after_status,event,content,create_time,update_time ) VALUES( ?,?,?,?,?,?,?,?,? )\n### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry \u0026#39;859225024379092992\u0026#39; for key \u0026#39;PRIMARY\u0026#39;\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"代码例子\"\u003e代码例子\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e//项目中所有主键使用的SnowFlakeUtil这个组件生成id\nlogDO.setId(SnowFlakeUtil.getId());\ntradeOrderLogMapper.insertSelective(logDO);\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"表现\"\u003e表现\u003c/h2\u003e\n\u003cp\u003e针对这种频繁插入的情况,容易出现id重复的问题,与当初预想的不一样\u003c/p\u003e","title":"分布式ID重复问题"},{"content":"背景 目前BI需求繁多,且都是些复杂联表查询,很容易产生数据库性能问题,影响线上用户, 目前架构如下,虽然能够满足现在的需求,但面对未来更大的数据量,更加复杂的统计需求,将只能通过添加只读实例和升级配置解决 用户基础信息与交易信息不在同一个实例,数据分析需要联合表查询得到数据,目前先查用户id再去用户信息表取存在诸多不便,需要寻求更方便的形式 针对用户行为漏斗分析等场景,MySQL的支持很差,基本查不动,需要更合适的数据库 需求 支持更复杂的聚合查询,更快的查询速度 不影响用户体验 更少的成本 方案 方案A:metabase,使用matebase构建目前的bi.xxx.com,数据源自RDS,当进行复杂查询的时候存在性能问题,会影响生产环境\n初代版本\n演进版本\n备选方案 B:使用阿里云DLA服务\n阿里云DLA ,介绍 https://help.aliyun.com/document_detail/70378.html\n跨库查询支持 https://help.aliyun.com/document_detail/107698.html\n知乎专栏:https://www.zhihu.com/column/data-lake-analytics\n测试过程\n1：尝试云原生数据湖的联合查询多个MySql实例，通过https://help.aliyun.com/document_detail/107698.html?spm=a2c4g.11186623.6.837.4d7974f4GsP1S0\n可以实现不同数据库的多表联合查询，但是咨询工单反馈，查询过程仍再原数据库中执行，在小数量的数据库查询中影响不大，但是在查询量较大较频繁时，无法\n保证原数据库的性能。于是尝试其他方法。\n2：尝试创建云原生数据湖分析中的T+1多库合并建仓，将数据库导入到DLA数据湖中，通过自己新创建的数据量很小的数据库导入，可以实现，并且不会占用大量性能。\n但针对test库的多库合并建仓时，因为性能原因造成较大的影响。并且询问阿里云工单之后反馈，多库合并建仓再第一次入湖之后，再每一次的更新数据时是全量重新同步，意味着\n每天进行同步都会遭遇很大的性能影响，并且数据存在一天延迟于是尝试其他方法。\n优点\n如果使用数据库联合查询方案,可以直接跨库查询 支持OSS直接上传离线数据文件进行分析 缺点\n虽然可以实现跨库查询,但是占用的还是生产库的计算资源,当需要复杂查询的时候可能影响用户体验 T+1方案存在数据延迟,且每天都是全量同步,在同步时对生产库有性能影响 C:使用clickhouse订阅多个mysql,实现数据聚合\n方案优势\nclickhouse在推出了支持MySQL实时复制后,可以很方便的订阅binlog实现clickhouse的数据库更新同步,实现准实时的数据分析 clickhouse数据库和用户使用的mysql数据库是完全隔离的,不会互相影响 无论是直接购买还是自建,相对其他大数据方案,成本较低 其他公司应用较多,携程,有赞等公司有在线上环境使用,参考资料较多 针对用户行为漏斗,留存等场景,有专有函数支持计算 metabase支持clickhouse数据库,原来的使用习惯不用改变,还是可以通过bi.xxx.com看数据 测试过程\nClickHouse，通过购买建立ClickHouse集群，通过购买DTS数据同步，将RDS数据库中的数据导入到ClickHouse集群中，可以实现不同数据的不同表导入到同一个数据库中，实现快速查询。\nClickHouse ，并且可以实时更新数据。 现ClickHouse配置为4核8G配置 1000G 属于按量付费，约1.5RMB/小时，30RMB/天，如需长期使用建议转为按月付费约600RMB/月， DTS数据同步\n现购买两个每个约0.5RMB/小时，两个共计20RMB/天，如需长期使用建议按月付费每个400RMB/月\n初始成本1400/月,后续可以视业务规模扩容升配置\n线上业务性能对比\n实例配置对比 mysql5.7 16核64G内存 clickhouse 20.3 4核8G\n查询速度对比:\nUntitled\n调研过程 1：尝试云原生数据湖的联合查询多个MySql实例，通过https://help.aliyun.com/document_detail/107698.html?spm=a2c4g.11186623.6.837.4d7974f4GsP1S0\n可以实现不同数据库的多表联合查询，但是咨询工单反馈，查询过程仍再原数据库中执行，在小数量的数据库查询中影响不大，但是在查询量较大较频繁时，无法\n保证原数据库的性能。于是尝试其他方法。\n2：尝试创建云原生数据湖分析中的T+1多库合并建仓，将数据库导入到DLA数据湖中，通过自己新创建的数据量很小的数据库导入，可以实现，并且不会占用大量性能。\n但针对test库的多库合并建仓时，因为性能原因造成较大的影响。并且询问阿里云工单之后反馈，多库合并建仓再第一次入湖之后，再每一次的更新数据时是全量重新同步，意味着\n每天进行同步都会遭遇很大的性能影响，并且数据存在一天延迟于是尝试其他方法。\n3：ClickHouse，通过购买建立ClickHouse集群，通过购买DTS数据同步，将RDS数据库中的数据导入到ClickHouse集群中，可以实现不同数据的不同表导入到同一个数据库中，实现\n快速查询。在单表通过执行 select COUNT( *) FROM trade_order_asset 统计语句ClickHouse中查询完成需要约3-4ms，在原数据库中查询，约3000-4000ms，ClickHouse性能领先。\nClickHouse ，并且可以实时更新数据。 现ClickHouse配置为4核8G配置 1000G 属于按量付费，约1.5RMB/小时，30RMB/天，如需长期使用建议转为按月付费约600RMB/月， DTS数据同步\n现购买两个每个约0.5RMB/小时，两个共计20RMB/天，如需长期使用建议按月付费每个400RMB/月\n参考资料\nClickHouse王炸功能即将来袭?\nClickHouse和他的朋友们（9）MySQL实时复制与实现\nClickhouse中文文档\nclickhouse,mysql性能对比:https://blog.csdn.net/bluetjs/article/details/80539847\nclickhouse,postgresql对比:https://www.codenong.com/cs109208273/\nClickHouse实战留存、路径、漏斗、session\nClickHouse 在有赞的实践之路\n趣头条基于Flink+ClickHouse的实时数据分析平台\n致谢：\n","permalink":"https://blog.jimersylee.com/posts/%E8%B7%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AE%9E%E4%BE%8B%E4%BB%A5%E5%8F%8A%E5%A4%8D%E6%9D%82%E8%81%9A%E5%90%88%E6%9F%A5%E8%AF%A2%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E6%96%B9%E6%A1%88%E8%B0%83%E7%A0%94/","summary":"\u003ch2 id=\"背景\"\u003e背景\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e目前BI需求繁多,且都是些复杂联表查询,很容易产生数据库性能问题,影响线上用户, 目前架构如下,虽然能够满足现在的需求,但面对未来更大的数据量,更加复杂的统计需求,将只能通过添加只读实例和升级配置解决\u003c/li\u003e\n\u003cli\u003e用户基础信息与交易信息不在同一个实例,数据分析需要联合表查询得到数据,目前先查用户id再去用户信息表取存在诸多不便,需要寻求更方便的形式\u003c/li\u003e\n\u003cli\u003e针对用户行为漏斗分析等场景,MySQL的支持很差,基本查不动,需要更合适的数据库\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"需求\"\u003e需求\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e支持更复杂的聚合查询,更快的查询速度\u003c/li\u003e\n\u003cli\u003e不影响用户体验\u003c/li\u003e\n\u003cli\u003e更少的成本\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"方案\"\u003e方案\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e方案A:metabase,使用matebase构建目前的bi.xxx.com,数据源自RDS,当进行复杂查询的时候存在性能问题,会影响生产环境\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e初代版本\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"mysql-architect.svg\" loading=\"lazy\" src=\"2-Areas/blog/public-blog/hugo/content/posts/%E8%B7%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AE%9E%E4%BE%8B%E4%BB%A5%E5%8F%8A%E5%A4%8D%E6%9D%82%E8%81%9A%E5%90%88%E6%9F%A5%E8%AF%A2%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E6%96%B9%E6%A1%88%E8%B0%83%E7%A0%94/mysql-architect.svg\"\u003e\u003c/p\u003e\n\u003cp\u003e演进版本\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"mysql-architect.png\" loading=\"lazy\" src=\"2-Areas/blog/public-blog/hugo/content/posts/%E8%B7%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AE%9E%E4%BE%8B%E4%BB%A5%E5%8F%8A%E5%A4%8D%E6%9D%82%E8%81%9A%E5%90%88%E6%9F%A5%E8%AF%A2%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E6%96%B9%E6%A1%88%E8%B0%83%E7%A0%94/mysql-architect.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"备选方案\"\u003e备选方案\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eB:使用阿里云DLA服务\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e阿里云DLA ,介绍 \u003ca href=\"https://help.aliyun.com/document_detail/70378.html\"\u003ehttps://help.aliyun.com/document_detail/70378.html\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e跨库查询支持 \u003ca href=\"https://help.aliyun.com/document_detail/107698.html\"\u003ehttps://help.aliyun.com/document_detail/107698.html\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e知乎专栏:\u003ca href=\"https://www.zhihu.com/column/data-lake-analytics\"\u003ehttps://www.zhihu.com/column/data-lake-analytics\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e测试过程\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e1：尝试云原生数据湖的联合查询多个MySql实例，通过\u003ca href=\"https://help.aliyun.com/document_detail/107698.html?spm=a2c4g.11186623.6.837.4d7974f4GsP1S0\"\u003ehttps://help.aliyun.com/document_detail/107698.html?spm=a2c4g.11186623.6.837.4d7974f4GsP1S0\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e可以实现不同数据库的多表联合查询，但是咨询工单反馈，查询过程仍再原数据库中执行，在小数量的数据库查询中影响不大，但是在查询量较大较频繁时，无法\u003c/p\u003e\n\u003cp\u003e保证原数据库的性能。于是尝试其他方法。\u003c/p\u003e\n\u003cp\u003e2：尝试创建云原生数据湖分析中的T+1多库合并建仓，将数据库导入到DLA数据湖中，通过自己新创建的数据量很小的数据库导入，可以实现，并且不会占用大量性能。\u003c/p\u003e\n\u003cp\u003e但针对test库的多库合并建仓时，因为性能原因造成较大的影响。并且询问阿里云工单之后反馈，多库合并建仓再第一次入湖之后，再每一次的更新数据时是全量重新同步，意味着\u003c/p\u003e\n\u003cp\u003e每天进行同步都会遭遇很大的性能影响，并且数据存在一天延迟于是尝试其他方法。\u003c/p\u003e\n\u003cp\u003e优点\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e如果使用数据库联合查询方案,可以直接跨库查询\u003c/li\u003e\n\u003cli\u003e支持OSS直接上传离线数据文件进行分析\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e缺点\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e虽然可以实现跨库查询,但是占用的还是生产库的计算资源,当需要复杂查询的时候可能影响用户体验\u003c/li\u003e\n\u003cli\u003eT+1方案存在数据延迟,且每天都是全量同步,在同步时对生产库有性能影响\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cstrong\u003eC:使用clickhouse订阅多个mysql,实现数据聚合\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"page1.png\" loading=\"lazy\" src=\"2-Areas/blog/public-blog/hugo/content/posts/%E8%B7%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AE%9E%E4%BE%8B%E4%BB%A5%E5%8F%8A%E5%A4%8D%E6%9D%82%E8%81%9A%E5%90%88%E6%9F%A5%E8%AF%A2%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E6%96%B9%E6%A1%88%E8%B0%83%E7%A0%94/page1.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e方案优势\u003c/strong\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eclickhouse在推出了支持MySQL实时复制后,可以很方便的订阅binlog实现clickhouse的数据库更新同步,实现准实时的数据分析\u003c/li\u003e\n\u003cli\u003eclickhouse数据库和用户使用的mysql数据库是完全隔离的,不会互相影响\u003c/li\u003e\n\u003cli\u003e无论是直接购买还是自建,相对其他大数据方案,成本较低\u003c/li\u003e\n\u003cli\u003e其他公司应用较多,携程,有赞等公司有在线上环境使用,参考资料较多\u003c/li\u003e\n\u003cli\u003e针对用户行为漏斗,留存等场景,有专有函数支持计算\u003c/li\u003e\n\u003cli\u003emetabase支持clickhouse数据库,原来的使用习惯不用改变,还是可以通过bi.xxx.com看数据\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cstrong\u003e测试过程\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eClickHouse，通过购买建立ClickHouse集群，通过购买DTS数据同步，将RDS数据库中的数据导入到ClickHouse集群中，可以实现不同数据的不同表导入到同一个数据库中，实现快速查询。\u003c/p\u003e\n\u003cp\u003eClickHouse ，并且可以实时更新数据。 现ClickHouse配置为4核8G配置 1000G 属于按量付费，约1.5RMB/小时，30RMB/天，如需长期使用建议转为按月付费约600RMB/月， DTS数据同步\u003c/p\u003e\n\u003cp\u003e现购买两个每个约0.5RMB/小时，两个共计20RMB/天，如需长期使用建议按月付费每个400RMB/月\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e初始成本1400/月,后续可以视业务规模扩容升配置\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e线上业务性能对比\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e实例配置对比  mysql5.7 16核64G内存      clickhouse 20.3  4核8G\u003c/p\u003e\n\u003cp\u003e查询速度对比:\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"%E8%B7%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AE%9E%E4%BE%8B%E4%BB%A5%E5%8F%8A%E5%A4%8D%E6%9D%82%E8%81%9A%E5%90%88%E6%9F%A5%E8%AF%A2%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E6%96%B9%E6%A1%88%E8%B0%83%E7%A0%94/Untitled%20ddf9682b130c44e39aa76f60bf371cd8.csv\"\u003eUntitled\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"调研过程\"\u003e调研过程\u003c/h2\u003e\n\u003cp\u003e1：尝试云原生数据湖的联合查询多个MySql实例，通过\u003ca href=\"https://help.aliyun.com/document_detail/107698.html?spm=a2c4g.11186623.6.837.4d7974f4GsP1S0\"\u003ehttps://help.aliyun.com/document_detail/107698.html?spm=a2c4g.11186623.6.837.4d7974f4GsP1S0\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e可以实现不同数据库的多表联合查询，但是咨询工单反馈，查询过程仍再原数据库中执行，在小数量的数据库查询中影响不大，但是在查询量较大较频繁时，无法\u003c/p\u003e\n\u003cp\u003e保证原数据库的性能。于是尝试其他方法。\u003c/p\u003e\n\u003cp\u003e2：尝试创建云原生数据湖分析中的T+1多库合并建仓，将数据库导入到DLA数据湖中，通过自己新创建的数据量很小的数据库导入，可以实现，并且不会占用大量性能。\u003c/p\u003e\n\u003cp\u003e但针对test库的多库合并建仓时，因为性能原因造成较大的影响。并且询问阿里云工单之后反馈，多库合并建仓再第一次入湖之后，再每一次的更新数据时是全量重新同步，意味着\u003c/p\u003e\n\u003cp\u003e每天进行同步都会遭遇很大的性能影响，并且数据存在一天延迟于是尝试其他方法。\u003c/p\u003e\n\u003cp\u003e3：ClickHouse，通过购买建立ClickHouse集群，通过购买DTS数据同步，将RDS数据库中的数据导入到ClickHouse集群中，可以实现不同数据的不同表导入到同一个数据库中，实现\u003c/p\u003e","title":"跨数据库实例以及复杂聚合查询数据分析方案调研"},{"content":"❗故障表现: 报价队列无法处理\n🗣️故障原因: 初步排查,消费者全部断开,没有在线的消费者,生产者也无法往RocketMQ集群发送消息,导致严重的队列堆积问题\n阿里云控制台截图 ⚠️ 报错日志 4:21\nprod-c51614077061steam-trade-bootlevel:ERRORlocation:com.xingchao.steam.trade.web.interceptor.GlobalExceptionHandler.defaultExceptionHandler(GlobalExceptionHandler.java:35)log:2021-02-23 18:44:21,871 ERROR [http-nio2-8088-exec-124] [ac140d90-klhuklux-191292] [steam-trade-boot] c.x.s.t.w.i.GlobalExceptionHandler - uri=/trade-buy/v1/create, params=access-token=cba0b12acf3b4fbd9675bc460f895700\u0026amp;ios_version_code=300140, method=POST, platform=2, device=3, deviceId=iPhone-CF5EC4BA-DB52-4FE2-BE45-5EED36309D04com.aliyun.openservices.ons.api.exception.ONSClientException: defaultMQProducer send exception at com.aliyun.openservices.ons.api.impl.rocketmq.ProducerImpl.checkProducerException(ProducerImpl.java:238) at com.aliyun.openservices.ons.api.impl.rocketmq.ProducerImpl.send(ProducerImpl.java:132) at com.c5game.mq.send.MQSendClient.sendDelayMessage(MQSendClient.java:45) at com.xingchao.steam.trade.service.mq.producer.RocketProducer.sendOrderDelay(RocketProducer.java:53) at com.xingchao.steam.trade.service.mq.producer.RocketProducer.sendOrderDelayDefault(RocketProducer.java:40) at com.xingchao.steam.trade.service.component.order.TradeBuyComponent.createTradeOrderAsset(TradeBuyComponent.java:709) at com.xingchao.steam.trade.service.component.order.TradeBuyComponent.createOrder(TradeBuyComponent.java:727) at com.xingchao.steam.trade.service.component.order.TradeBuyComponent$FastClassBySpringCGLIB$$1f2afe5.invoke() at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:684) at com.xingchao.steam.trade.service.component.order.TradeBuyComponentEnhancerBySpringCGLIB$$dd8876ee.createOrder() at com.xingchao.steam.trade.service.biz.impl.TradeBuyOrderServiceImpl.createOrder(TradeBuyOrderServiceImpl.java:107)\n🙏临时解决方案 当时重新发布应用,RocketMQ客户端重新连接上RocketMQ服务,消息可以正常生产消费,故障影响的报价逐步正常\n👏彻底解决方案 根据故障表现,推测为阿里云RocketMQ集群出现网络波动,导致客户端断开连接,但是客户端没有重连机制或者存在bug,不会重新连接上服务器\n我们使用的SDK版本为1.7.9.Final,推出时间为2019.1,相对与现在(2021.3)已经很长时间了,查看此SDK版本记录,https://help.aliyun.com/document_detail/114448.html,在1.8.0.Final的版本说明中,出现问题修复\n修复自动重试逻辑（默认重试三次），该逻辑适用于消息从生产端同步发送至实例化后新建实例下的Topic失败的场景。 咨询阿里云工程师求证,得知的确有bug\n接下来就是升级版本,进行c5game-mq-starter的升级与测试\n升级到最新的sdk版本 去除没有使用的事务消息功能 增加顺序消息生产与消费功能 🤔反思 定期升级依赖是必须的,需要落实到制度中去 需要规划出时间进行非功能的迭代升级 ","permalink":"https://blog.jimersylee.com/posts/rocketmq%E6%B6%88%E8%B4%B9%E8%80%85%E5%85%A8%E9%83%A8%E7%A6%BB%E7%BA%BF%E6%95%85%E9%9A%9C/","summary":"\u003ch2 id=\"故障表现\"\u003e❗故障表现:\u003c/h2\u003e\n\u003cp\u003e报价队列无法处理\u003c/p\u003e\n\u003ch2 id=\"故障原因\"\u003e🗣️故障原因:\u003c/h2\u003e\n\u003cp\u003e初步排查,消费者全部断开,没有在线的消费者,生产者也无法往RocketMQ集群发送消息,导致严重的队列堆积问题\u003c/p\u003e\n\u003ch2 id=\"阿里云控制台截图\"\u003e阿里云控制台截图\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"page1.png\" loading=\"lazy\" src=\"https://cdn.jimersylee.com/obsidian/2026/20260527205011523.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"-报错日志\"\u003e⚠️ 报错日志\u003c/h2\u003e\n\u003cp\u003e4:21\u003c/p\u003e\n\u003cp\u003eprod-c51614077061steam-trade-bootlevel:ERRORlocation:com.xingchao.steam.trade.web.interceptor.GlobalExceptionHandler.defaultExceptionHandler(GlobalExceptionHandler.java:35)log:2021-02-23 18:44:21,871 ERROR [http-nio2-8088-exec-124] [ac140d90-klhuklux-191292] [steam-trade-boot] c.x.s.t.w.i.GlobalExceptionHandler - uri=/trade-buy/v1/create, params=access-token=cba0b12acf3b4fbd9675bc460f895700\u0026amp;ios_version_code=300140, method=POST, platform=2, device=3, deviceId=iPhone-CF5EC4BA-DB52-4FE2-BE45-5EED36309D04com.aliyun.openservices.ons.api.exception.ONSClientException: defaultMQProducer send exception        at com.aliyun.openservices.ons.api.impl.rocketmq.ProducerImpl.checkProducerException(ProducerImpl.java:238)        at com.aliyun.openservices.ons.api.impl.rocketmq.ProducerImpl.send(ProducerImpl.java:132)        at \u003ca href=\"http://com.c5game.mq/\"\u003ecom.c5game.mq\u003c/a\u003e.send.MQSendClient.sendDelayMessage(MQSendClient.java:45)        at \u003ca href=\"http://com.xingchao.steam.trade.service.mq/\"\u003ecom.xingchao.steam.trade.service.mq\u003c/a\u003e.producer.RocketProducer.sendOrderDelay(RocketProducer.java:53)        at \u003ca href=\"http://com.xingchao.steam.trade.service.mq/\"\u003ecom.xingchao.steam.trade.service.mq\u003c/a\u003e.producer.RocketProducer.sendOrderDelayDefault(RocketProducer.java:40)        at com.xingchao.steam.trade.service.component.order.TradeBuyComponent.createTradeOrderAsset(TradeBuyComponent.java:709)        at com.xingchao.steam.trade.service.component.order.TradeBuyComponent.createOrder(TradeBuyComponent.java:727)        at com.xingchao.steam.trade.service.component.order.TradeBuyComponent$FastClassBySpringCGLIB$$1f2afe5.invoke(\u003c!-- raw HTML omitted --\u003e)        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:684)        at com.xingchao.steam.trade.service.component.order.TradeBuyComponentEnhancerBySpringCGLIB$$dd8876ee.createOrder(\u003c!-- raw HTML omitted --\u003e)        at \u003ca href=\"http://com.xingchao.steam.trade.service.biz/\"\u003ecom.xingchao.steam.trade.service.biz\u003c/a\u003e.impl.TradeBuyOrderServiceImpl.createOrder(TradeBuyOrderServiceImpl.java:107)\u003c/p\u003e\n\u003ch2 id=\"临时解决方案\"\u003e🙏临时解决方案\u003c/h2\u003e\n\u003cp\u003e当时重新发布应用,RocketMQ客户端重新连接上RocketMQ服务,消息可以正常生产消费,故障影响的报价逐步正常\u003c/p\u003e\n\u003ch2 id=\"彻底解决方案\"\u003e👏彻底解决方案\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e根据故障表现,推测为阿里云RocketMQ集群出现网络波动,导致客户端断开连接,但是客户端没有重连机制或者存在bug,不会重新连接上服务器\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e我们使用的SDK版本为1.7.9.Final,推出时间为2019.1,相对与现在(2021.3)已经很长时间了,查看此SDK版本记录,\u003ca href=\"https://help.aliyun.com/document_detail/114448.html,%E5%9C%A81.8.0.Final%E7%9A%84%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E%E4%B8%AD,%E5%87%BA%E7%8E%B0*%E9%97%AE%E9%A2%98%E4%BF%AE%E5%A4%8D*\"\u003ehttps://help.aliyun.com/document_detail/114448.html,在1.8.0.Final的版本说明中,出现\u003cem\u003e问题修复\u003c/em\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cem\u003e修复自动重试逻辑（默认重试三次），该逻辑适用于消息从生产端同步发送至实例化后新建实例下的Topic失败的场景。\u003c/em\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e咨询阿里云工程师求证,得知的确有bug\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"page1.png\" loading=\"lazy\" src=\"https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3baf70e2-20d3-4236-aaba-477f5f62099f/page1.png\"\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e接下来就是升级版本,进行c5game-mq-starter的升级与测试\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e升级到最新的sdk版本\u003c/li\u003e\n\u003cli\u003e去除没有使用的事务消息功能\u003c/li\u003e\n\u003cli\u003e增加顺序消息生产与消费功能\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"反思\"\u003e🤔反思\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e定期升级依赖是必须的,需要落实到制度中去\u003c/li\u003e\n\u003cli\u003e需要规划出时间进行非功能的迭代升级\u003c/li\u003e\n\u003c/ol\u003e","title":"RocketMQ消费者全部离线故障"},{"content":" 网站时不时卡顿几分钟,自动恢复,搜索服务响应缓慢\n影响范围:web,app都出现502 原因: 阿里云低版本ES存在稳定性问题 解决方案:升级ES内核,阿里云并不会自动升级 香港的serverless 容器 访问国内的网络有connect time out的问题，每天的晚上8点开始会突然多起来\n影响范围:香港报价服务和国内交易服务的通讯 原因:运营商反馈：是运营商海缆故障，请您知悉。 解决方案:等待恢复 服务内调用服务出现超时\n影响范围:一些服务异常,包括库存,短信等 原因:阿里云k8s集群自己改变了策略,导致我们之前的正常的流量策略不可用 解决方案:设置成新的策略 证书出现问题,无法访问 影响范围:人工发货功能不可用,用户无法使用我们的代理地址 原因:阿里云升级了k8s的配置,没有通知我们 解决方案:按新的方式配置证书 阿里云的RDS数据库读写分离地址突然失效\n影响范围:半夜的时候造成网站完全无法访问 原因:阿里云故障 解决方案:我们当时立马申请了新的地址,才恢复访问 香港的serverless集群突然全部实例异常,流量无法进入\n影响范围:部署在香港的服务全部无法问题 原因:阿里云故障 解决方案:等待阿里云修复之后才恢复正常,已经要求阿里云赔偿 香港的serverless集群绑定的弹性公网IP自动消失\n影响范围:报价服务异常 原因:阿里云故障 解决方案:等阿里云修复 RocketMQ消费者全部离线故障\n影响范围: 公司全部报价无法处理 原因: 阿里云旧的SDK存在断线重连重试BUG 解决方案: 升级新的SDK ","permalink":"https://blog.jimersylee.com/posts/%E7%9C%9F%E4%B8%8D%E6%98%AF%E6%88%91%E7%94%A9%E9%94%85%E7%BB%86%E6%95%B0%E6%88%91%E9%81%87%E5%88%B0%E8%BF%87%E7%9A%84%E9%98%BF%E9%87%8C%E4%BA%91%E7%9A%84%E6%95%85%E9%9A%9C/","summary":"\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://workorder.console.aliyun.com/#/ticket/detail/?ticketId=9LCGD1D\"\u003e网站时不时卡顿几分钟,自动恢复,搜索服务响应缓慢\u003c/a\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e影响范围:web,app都出现502\u003c/li\u003e\n\u003cli\u003e原因: 阿里云低版本ES存在稳定性问题\u003c/li\u003e\n\u003cli\u003e解决方案:升级ES内核,阿里云并不会自动升级\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://workorder.console.aliyun.com/#/ticket/detail/?ticketId=CACMXPA\"\u003e香港的serverless 容器 访问国内的网络有connect time out的问题，每天的晚上8点开始会突然多起来\u003c/a\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e影响范围:香港报价服务和国内交易服务的通讯\u003c/li\u003e\n\u003cli\u003e原因:运营商反馈：是运营商海缆故障，请您知悉。\u003c/li\u003e\n\u003cli\u003e解决方案:等待恢复\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://workorder.console.aliyun.com/#/ticket/detail/?ticketId=DGCHHT9\"\u003e服务内调用服务出现超时\u003c/a\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e影响范围:一些服务异常,包括库存,短信等\u003c/li\u003e\n\u003cli\u003e原因:阿里云k8s集群自己改变了策略,导致我们之前的正常的流量策略不可用\u003c/li\u003e\n\u003cli\u003e解决方案:设置成新的策略\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://workorder.console.aliyun.com/#/ticket/detail/?ticketId=ACCF4A2\"\u003e证书出现问题,无法访问\u003c/a\u003e   \u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e影响范围:人工发货功能不可用,用户无法使用我们的代理地址\u003c/li\u003e\n\u003cli\u003e原因:阿里云升级了k8s的配置,没有通知我们\u003c/li\u003e\n\u003cli\u003e解决方案:按新的方式配置证书\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://workorder.console.aliyun.com/#/ticket/detail/?ticketId=G2CD82G\"\u003e阿里云的RDS数据库读写分离地址突然失效\u003c/a\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e影响范围:半夜的时候造成网站完全无法访问\u003c/li\u003e\n\u003cli\u003e原因:阿里云故障\u003c/li\u003e\n\u003cli\u003e解决方案:我们当时立马申请了新的地址,才恢复访问\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://workorder.console.aliyun.com/#/ticket/detail/?ticketId=BSBUTSW\"\u003e香港的serverless集群突然全部实例异常,流量无法进入\u003c/a\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e影响范围:部署在香港的服务全部无法问题\u003c/li\u003e\n\u003cli\u003e原因:阿里云故障\u003c/li\u003e\n\u003cli\u003e解决方案:等待阿里云修复之后才恢复正常,已经要求阿里云赔偿\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"https://workorder.console.aliyun.com/#/ticket/detail/?ticketId=3EC8CU7\"\u003e香港的serverless集群绑定的弹性公网IP自动消失\u003c/a\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e影响范围:报价服务异常\u003c/li\u003e\n\u003cli\u003e原因:阿里云故障\u003c/li\u003e\n\u003cli\u003e解决方案:等阿里云修复\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ca href=\"./RocketMQ%E6%B6%88%E8%B4%B9%E8%80%85%E5%85%A8%E9%83%A8%E7%A6%BB%E7%BA%BF%E6%95%85%E9%9A%9C.md#\"\u003e\u003cstrong\u003eRocketMQ消费者全部离线故障\u003c/strong\u003e\u003c/a\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e影响范围: 公司全部报价无法处理\u003c/li\u003e\n\u003cli\u003e原因: 阿里云旧的SDK存在断线重连重试BUG\u003c/li\u003e\n\u003cli\u003e解决方案: 升级新的SDK\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003c/ol\u003e","title":"真不是我甩锅,细数我遇到过的阿里云的故障"},{"content":"server { listen 80; listen 443; ssl on; ssl_certificate conf.d/jimersylee.com.crt; ssl_certificate_key conf.d/jimersylee.com.key.pem; ssl_session_timeout 5m; ssl_protocols SSLv2 SSLv3 TLSv1; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP; ssl_prefer_server_ciphers on; root /etc/nginx/conf.d; # Add index.php to the list if you are using PHP index index.html index.htm index.nginx-debian.html; server_name *.jimersylee.com; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; } } 另外推荐，一个工具 https://www.digitalocean.com/community/tools/nginx 可视化配置nginx，方便新手了解nginx\n","permalink":"https://blog.jimersylee.com/posts/nginx-https-%E9%85%8D%E7%BD%AE/","summary":"\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-nix\" data-lang=\"nix\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eserver {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tlisten \u003cspan style=\"color:#ae81ff\"\u003e80\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tlisten \u003cspan style=\"color:#ae81ff\"\u003e443\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tssl on;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tssl_certificate \u003cspan style=\"color:#e6db74\"\u003econf.d/jimersylee.com.crt\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tssl_certificate_key \u003cspan style=\"color:#e6db74\"\u003econf.d/jimersylee.com.key.pem\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tssl_session_timeout \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e5\u003c/span\u003em;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tssl_protocols SSLv2 SSLv3 TLSv1;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tssl_ciphers \u003cspan style=\"color:#e6db74\"\u003eALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tssl_prefer_server_ciphers on;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\troot \u003cspan style=\"color:#e6db74\"\u003e/etc/nginx/conf.d\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e# Add index.php to the list if you are using PHP\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tindex index\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehtml index\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehtm index\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003enginx-debian\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ehtml;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tserver_name \u003cspan style=\"color:#f92672\"\u003e*.\u003c/span\u003ejimersylee\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ecom;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\tlocation\u003cspan style=\"color:#f92672\"\u003e / \u003c/span\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e# First attempt to serve request as file, then\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#75715e\"\u003e# as directory, then fall back to displaying a 404.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\ttry_files \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e$\u003c/span\u003euri \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e$\u003c/span\u003euri\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e/\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e404\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e另外推荐，一个工具 \u003ca href=\"https://www.digitalocean.com/community/tools/nginx\"\u003ehttps://www.digitalocean.com/community/tools/nginx\u003c/a\u003e    可视化配置nginx，方便新手了解nginx\u003c/p\u003e","title":"nginx https 配置"},{"content":"原因 公司的php项目之前的日志基本是存数据库,存服务器文件,存在占用数据库大量空间,日志查看存在难以看到完整上下文,服务器上查找日志困难等问题,急需解决方案\n方案 1.购买阿里云ELK:缺点贵\n2.购买阿里云服务器自己搭ELK:虽然便宜些,但是增加运维成本\n3.使用阿里云的日志服务,官方示例项目\n优点:1.便宜 2:方便接入 3:机器人日志已有使用经验 4:在app即将上线的时候能够快速接入 示例项目 依赖引入\n进入项目根目录,执行\ncomposer require \u0026ndash;prefer-dist ranvk/yii2-aliyun-logtarget -vvv\ncomposer.json文件中会增加\n\u0026#34;ranvk/yii2-aliyun-logtarget\u0026#34;: \u0026#34;^18.3\u0026#34; 修改配置,dev和local环境配置阿里云的外网地址,才可以上传日志,在线上的test和prod环境,使用内网地址,加快日志上传速度\napi/config/dev/main.php 修改log的组件 ```php 'components' =\u0026gt; [ 'log' =\u0026gt; [ 'targets' =\u0026gt; [ [ 'class' =\u0026gt; 'yii\\log\\FileTarget', 'levels' =\u0026gt; ['error', 'warning', 'trace', 'info'], ], [ 'levels' =\u0026gt; ['error', 'warning', 'info'], 'class' =\u0026gt; 'Ranvk\\Yii2AliyunLogtarget\\AliyunLogTarget', 'logstore' =\u0026gt; 'www-xxx-com', 'topic' =\u0026gt; YII_ENV, 'project' =\u0026gt; 'c5game-webserver', 'accessKeyId' =\u0026gt; 'xxx', 'accessKeySecret' =\u0026gt; 'xxx', 'endpoint' =\u0026gt; 'cn-shanghai.log.aliyuncs.com', //外网地址,内网地址为cn-shanghai-intranet.log.aliyuncs.com ], ], ], ``` 1. 其他环境的配置文件类似,别忘了修改 2. 具体代码 [http://xxxx/-/merge_requests/49/diffs](http://git.c5game.com/zbt/www.zbt.com/-/merge_requests/49/diffs) 查询示例 网址:https://sls.console.aliyun.com/lognext/project/xxx\n授权才能访问\n比如要查prod环境的异常拦截处理器中的关于库存请求相关的日志\nexceptionHandle and \u0026ldquo;inventory\u0026rdquo; and topic: prod\n更多查询方式看文档\n总结 配置的时候要注意,dev环境使用的阿里云的公网上传日志地址,test和prod环境使用内网传输提高速度,也免收流量费,主要是endpoint配置,topic存环境,区分dev,test,prod 参考文章 https://github.com/ranvk/yii2-aliyun-logtarget ","permalink":"https://blog.jimersylee.com/posts/yii2%E9%A1%B9%E7%9B%AE%E6%95%B4%E5%90%88%E9%98%BF%E9%87%8C%E4%BA%91%E6%97%A5%E5%BF%97%E6%9C%8D%E5%8A%A1/","summary":"\u003ch1 id=\"原因\"\u003e原因\u003c/h1\u003e\n\u003cp\u003e公司的php项目之前的日志基本是存数据库,存服务器文件,存在占用数据库大量空间,日志查看存在难以看到完整上下文,服务器上查找日志困难等问题,急需解决方案\u003c/p\u003e\n\u003ch1 id=\"方案\"\u003e方案\u003c/h1\u003e\n\u003cp\u003e1.购买阿里云ELK:缺点贵\u003c/p\u003e\n\u003cp\u003e2.购买阿里云服务器自己搭ELK:虽然便宜些,但是增加运维成本\u003c/p\u003e\n\u003cp\u003e3.使用\u003ca href=\"https://help.aliyun.com/learn/learningpath/log.html?spm=5176.2020520112.104.7.122834c07Lpxpa\"\u003e阿里云的日志服务\u003c/a\u003e,\u003ca href=\"https://github.com/aliyun/aliyun-log-logback-appender\"\u003e官方示例项目\u003c/a\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e优点:1.便宜 2:方便接入 3:机器人日志已有使用经验 4:在app即将上线的时候能够快速接入\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"示例项目\"\u003e示例项目\u003c/h1\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e依赖引入\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e进入项目根目录,执行\u003c/p\u003e\n\u003cp\u003ecomposer require \u0026ndash;prefer-dist ranvk/yii2-aliyun-logtarget -vvv\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003ecomposer.json文件中会增加\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\u0026#34;ranvk/yii2-aliyun-logtarget\u0026#34;: \u0026#34;^18.3\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e修改配置,dev和local环境配置阿里云的外网地址,才可以上传日志,在线上的test和prod环境,使用内网地址,加快日志上传速度\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eapi/config/dev/main.php\u003c/li\u003e\n\u003cli\u003e修改log的组件\u003c/li\u003e\n\u003cli\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cpre\u003e\u003ccode\u003e```php\n'components' =\u0026gt; [\n    'log' =\u0026gt; [\n        'targets' =\u0026gt; [\n            [\n                'class' =\u0026gt; 'yii\\log\\FileTarget',\n                'levels' =\u0026gt; ['error', 'warning', 'trace', 'info'],\n            ],\n            [\n                'levels' =\u0026gt; ['error', 'warning', 'info'],\n                'class' =\u0026gt; 'Ranvk\\Yii2AliyunLogtarget\\AliyunLogTarget',\n                'logstore' =\u0026gt; 'www-xxx-com',\n                'topic' =\u0026gt; YII_ENV,\n                'project' =\u0026gt; 'c5game-webserver',\n                'accessKeyId' =\u0026gt; 'xxx',\n                'accessKeySecret' =\u0026gt; 'xxx',\n                'endpoint' =\u0026gt; 'cn-shanghai.log.aliyuncs.com', //外网地址,内网地址为cn-shanghai-intranet.log.aliyuncs.com\n            ],\n        ],\n    ],\n```\n\n1. 其他环境的配置文件类似,别忘了修改\n2. 具体代码 [http://xxxx/-/merge_requests/49/diffs](http://git.c5game.com/zbt/www.zbt.com/-/merge_requests/49/diffs)\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch1 id=\"查询示例\"\u003e查询示例\u003c/h1\u003e\n\u003cp\u003e网址:https://sls.console.aliyun.com/lognext/project/xxx\u003c/p\u003e","title":"Yii2项目整合阿里云日志服务"},{"content":"背景 目前存在一些应用频繁重启的问题,查看日志,有的显示容器被驱逐(evic),这种情况一般为资源使用超过配置的限制,k8s为了整体稳定性进行服务的重启,防止一些应用层的内存泄漏问题导致整体服务稳定性下降\n针对此问题,需要调研k8s集群中,容器的内存分配,以及应用是否能正确识别到分配的容器内存限制\n现状 目前线上的java应用使用的基础镜像为:library/openjdk8-skywalking:latest 此镜像的项目地址为 :middleware/docker-image-openjdk8-skywalking 此基础镜像包含的软件为: openjdk-8u212 nginx curl vim php7 php7-pecl-mongodb skywalking 线上使用配置 通过设置环境变量,运行脚本使用环境变量配置jvm参数实现 ![](https://i.loli.net/2021/04/29/nQJ4sRZp791HAhi.png) 本地测试结果 自动方案 不带jvm参数运行,看默认配置为多少内存,给容器设置最大内存限制为2GB,jvm设置的最大堆内存为455.50M,约为1/4最大内存,符合预期\ndocker run -m 2GB \u0026ndash;rm reg.xxxx.cn/library/openjdk8-skywalking:latest java -XshowSettings:vm -version VM settings: Max. Heap Size (Estimated): 455.50M Ergonomics Machine Class: server Using VM: OpenJDK 64-Bit Server VM\nopenjdk version \u0026ldquo;1.8.0_212\u0026rdquo; OpenJDK Runtime Environment (IcedTea 3.12.0) (Alpine 8.212.04-r0) OpenJDK 64-Bit Server VM (build 25.212-b04, mixed mode)\nOpenJdk\n自动识别到容器限制后，OpenJdk把最大堆设置为了大概容器内存的1/4，对内存的浪费不可谓不大。\n我们尝试将MaxRAMFraction设置手动设置为1,可以看到最大堆内存将被设置为1.78G,我觉得这样的资源利用率比较高\n$ docker run -m 2GB \u0026ndash;rm reg.xxxx.cn/library/openjdk8-skywalking:latest java -XshowSettings:vm -XX:MaxRAMFraction=1 -version VM settings: Max. Heap Size (Estimated): 1.78G Ergonomics Machine Class: server Using VM: OpenJDK 64-Bit Server VM\nopenjdk version \u0026ldquo;1.8.0_212\u0026rdquo; OpenJDK Runtime Environment (IcedTea 3.12.0) (Alpine 8.212.04-r0) OpenJDK 64-Bit Server VM (build 25.212-b04, mixed mode)\n手动方案 手动设置最大堆内存,有效,能避免内存过大导致的被kill $ docker run -m 2GB \u0026ndash;rm reg.xxxx.cn/library/openjdk8-skywalking:latest java -XshowSettings:vm -Xmx1800m -version VM settings: Max. Heap Size: 1.76G Ergonomics Machine Class: server Using VM: OpenJDK 64-Bit Server VM\nopenjdk version \u0026ldquo;1.8.0_212\u0026rdquo; OpenJDK Runtime Environment (IcedTea 3.12.0) (Alpine 8.212.04-r0) OpenJDK 64-Bit Server VM (build 25.212-b04, mixed mode)\n结论 目前的手动设置jvm的堆内存的方案可行,缺点是为了每个应用都需要按情况配置不同的jvm参数,比较麻烦容易出错 如果直接使用自动化方案,不带jvm参数,应用能够正确识别分配给容器的最大内存,但是内存使用率较低,约为25%,存在大量的浪费,因此推荐统一加上-XX:MaxRAMFraction=1参数,提高资源利用率,且方便进行资源限制更改,只需要在k8s后台进行配置就ok 参考文章 Java程序在K8S容器部署CPU和Memory资源限制相关设置 容器中的JVM资源该如何被安全的限制？ 容器环境的JVM内存设置最佳实践Kubernetes 案例分享：如何避免 JVM 应用内存耗尽 容器环境的JVM内存设置最佳实践 • devops • K8S • JVM\n","permalink":"https://blog.jimersylee.com/posts/%E5%85%B3%E4%BA%8Ek8s%E9%9B%86%E7%BE%A4%E4%B8%AD%E7%9A%84%E5%AE%B9%E5%99%A8%E8%B5%84%E6%BA%90%E9%99%90%E5%88%B6%E4%BB%A5%E5%8F%8Ajava%E5%BA%94%E7%94%A8%E8%BF%90%E8%A1%8C%E5%8F%82%E6%95%B0%E8%AE%BE%E7%BD%AE/","summary":"\u003ch1 id=\"背景\"\u003e背景\u003c/h1\u003e\n\u003cp\u003e目前存在一些应用频繁重启的问题,查看日志,有的显示容器被驱逐(evic),这种情况一般为资源使用超过配置的限制,k8s为了整体稳定性进行服务的重启,防止一些应用层的内存泄漏问题导致整体服务稳定性下降\u003c/p\u003e\n\u003cp\u003e针对此问题,需要调研k8s集群中,容器的内存分配,以及应用是否能正确识别到分配的容器内存限制\u003c/p\u003e\n\u003ch1 id=\"现状\"\u003e现状\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e目前线上的java应用使用的基础镜像为:library/openjdk8-skywalking:latest\u003c/li\u003e\n\u003cli\u003e此镜像的项目地址为 :middleware/docker-image-openjdk8-skywalking\u003c/li\u003e\n\u003cli\u003e此基础镜像包含的软件为:\n\u003cul\u003e\n\u003cli\u003eopenjdk-8u212\u003c/li\u003e\n\u003cli\u003enginx\u003c/li\u003e\n\u003cli\u003ecurl\u003c/li\u003e\n\u003cli\u003evim\u003c/li\u003e\n\u003cli\u003ephp7\u003c/li\u003e\n\u003cli\u003ephp7-pecl-mongodb\u003c/li\u003e\n\u003cli\u003eskywalking\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e线上使用配置\n\u003cul\u003e\n\u003cli\u003e通过设置环境变量,运行脚本使用环境变量配置jvm参数实现\u003c/li\u003e\n\u003cli\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cpre\u003e\u003ccode\u003e    ![](https://i.loli.net/2021/04/29/nQJ4sRZp791HAhi.png)\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 id=\"本地测试结果\"\u003e本地测试结果\u003c/h2\u003e\n\u003ch2 id=\"自动方案\"\u003e自动方案\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e不带jvm参数运行,看默认配置为多少内存,给容器设置最大内存限制为2GB,jvm设置的最大堆内存为455.50M,约为1/4最大内存,符合预期\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003edocker run -m 2GB \u0026ndash;rm \u003ca href=\"http://reg.xxxx.cn/library/openjdk8-skywalking:latest\"\u003ereg.xxxx.cn/library/openjdk8-skywalking:latest\u003c/a\u003e java -XshowSettings:vm -version VM settings: Max. Heap Size (Estimated): 455.50M Ergonomics Machine Class: server Using VM: OpenJDK 64-Bit Server VM\u003c/p\u003e\n\u003cp\u003eopenjdk version \u0026ldquo;1.8.0_212\u0026rdquo; OpenJDK Runtime Environment (IcedTea 3.12.0) (Alpine 8.212.04-r0) OpenJDK 64-Bit Server VM (build 25.212-b04, mixed mode)\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eOpenJdk\u003c/p\u003e\n\u003cp\u003e自动识别到容器限制后，OpenJdk把最大堆设置为了大概容器内存的1/4，对内存的浪费不可谓不大。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e我们尝试将MaxRAMFraction设置手动设置为1,可以看到最大堆内存将被设置为1.78G,我觉得这样的资源利用率比较高\u003c/p\u003e","title":"关于k8s集群中的容器资源限制以及JAVA应用运行参数设置"},{"content":" 西蓝花一小颗 1.5/斤 1元 南瓜一小片 0.8/斤 4元 芹菜少点 6/斤 6.9 姜两块 5.5/斤 5.7 黄瓜 1.5/斤 元 茄子 2/斤 3.5 菠菜 4元/斤 香菜 8.5斤 小油菜 3/斤3元 生菜 3/斤 3 元 香菇 7.5/斤+10/斤*0.5=12 西红柿 2.5/斤 4.9元 大葱 2块/斤 5元 黄豆芽两块钱的 2元 鸡胸肉四块 7/斤 10元 干豆腐 4元 ","permalink":"https://blog.jimersylee.com/posts/%E6%9D%AD%E5%B7%9E%E6%B0%B8%E8%BE%89%E8%8F%9C%E4%BB%B7/","summary":"\u003cul\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 西蓝花一小颗 1.5/斤 1元\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 南瓜一小片 0.8/斤 4元\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 芹菜少点 6/斤 6.9\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 姜两块 5.5/斤 5.7\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 黄瓜 1.5/斤 元\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 茄子 2/斤 3.5\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 菠菜 4元/斤\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 香菜 8.5斤\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 小油菜 3/斤3元\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 生菜 3/斤 3 元\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 香菇 7.5/斤+10/斤*0.5=12\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 西红柿 2.5/斤 4.9元\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 大葱 2块/斤 5元\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 黄豆芽两块钱的 2元\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 鸡胸肉四块 7/斤 10元\u003c/li\u003e\n\u003cli\u003e\u003cinput checked=\"\" disabled=\"\" type=\"checkbox\"\u003e 干豆腐 4元\u003c/li\u003e\n\u003c/ul\u003e","title":"杭州永辉菜价"},{"content":"以mvn运行 #使用prod配置运行 mvn spring-boot:run -Dspring-boot.run.profiles=prod #后台运行无额外日志文件 nohup mvn spring-boot:run -Dspring-boot.run.profiles=prod \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 \u0026amp; 以包运行 #默认配置 java -jar xxxx.jar #指定配置文件或者参数 java -Xms128m -Xmx256m -jar xxxx.jar --spring.profiles.active=prod #后台运行无额外日志文件 nohup java -Xms128m -Xmx256m -jar xxxx.jar --spring.profiles.active=prod \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 \u0026amp; ","permalink":"https://blog.jimersylee.com/posts/spring-boot%E4%B8%8D%E5%90%8C%E7%9A%84%E5%90%AF%E5%8A%A8%E7%9A%84%E6%96%B9%E5%BC%8F/","summary":"\u003ch1 id=\"以mvn运行\"\u003e以mvn运行\u003c/h1\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#使用prod配置运行\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emvn spring-boot:run -Dspring-boot.run.profiles\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eprod\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#后台运行无额外日志文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enohup mvn spring-boot:run -Dspring-boot.run.profiles\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eprod \u0026gt;/dev/null 2\u0026gt;\u0026amp;\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u0026amp; \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch1 id=\"以包运行\"\u003e以包运行\u003c/h1\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#默认配置\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejava -jar xxxx.jar\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#指定配置文件或者参数\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ejava -Xms128m -Xmx256m -jar xxxx.jar --spring.profiles.active\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eprod\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#后台运行无额外日志文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enohup java -Xms128m -Xmx256m -jar xxxx.jar --spring.profiles.active\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003eprod \u0026gt;/dev/null 2\u0026gt;\u0026amp;\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u0026amp;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"spring-boot不同的启动的方式"},{"content":"📝美团年货分享 感谢关注美团技术团队。我们每周会推送来自一线的实践技术文章，涵盖前端（Web、iOS和Android）、后台、大数据、AI/算法、测试、运维等技术领域。在2019年春节到来之际，我们精选了114篇技术干货，制作成一本厚达1200多页的电子书呈送给大家。温馨提醒：文件有点大（全部下载约350M），请耐心等候下载。建议通过WIFI下载，或将地址复制到PC端，使用浏览器下载。【前端系列】下载链接：http://dpurl.cn/DWVhPpS【后台系列】下载链接：http://dpurl.cn/obtIVn【系统系列】下载链接：http://dpurl.cn/zTDWq8Q【算法系列】下载链接：http://dpurl.cn/ODj5qAB【运维系列】下载链接：http://dpurl.cn/6zjgHFj【测试系列】下载链接：http://dpurl.cn/wfKQqy【工程师成长系列】下载链接：http://dpurl.cn/p5sPoKT【2018年美团点评技术文章合辑】下载链接：http://dpurl.cn/iLGBtuS\n🤗总结归纳 致谢：\n","permalink":"https://blog.jimersylee.com/posts/%E7%BE%8E%E5%9B%A22019%E5%B9%B4%E8%B4%A7%E5%88%86%E4%BA%AB/","summary":"\u003ch1 id=\"美团年货分享\"\u003e📝美团年货分享\u003c/h1\u003e\n\u003cp\u003e感谢关注美团技术团队。我们每周会推送来自一线的实践技术文章，涵盖前端（Web、iOS和Android）、后台、大数据、AI/算法、测试、运维等技术领域。在2019年春节到来之际，我们精选了114篇技术干货，制作成一本厚达1200多页的电子书呈送给大家。温馨提醒：文件有点大（全部下载约350M），请耐心等候下载。建议通过WIFI下载，或将地址复制到PC端，使用浏览器下载。【前端系列】下载链接：\u003ca href=\"http://dpurl.cn/DWVhPpS%E3%80%90%E5%90%8E%E5%8F%B0%E7%B3%BB%E5%88%97%E3%80%91%E4%B8%8B%E8%BD%BD%E9%93%BE%E6%8E%A5%EF%BC%9Ahttp://dpurl.cn/obtIVn%E3%80%90%E7%B3%BB%E7%BB%9F%E7%B3%BB%E5%88%97%E3%80%91%E4%B8%8B%E8%BD%BD%E9%93%BE%E6%8E%A5%EF%BC%9Ahttp://dpurl.cn/zTDWq8Q%E3%80%90%E7%AE%97%E6%B3%95%E7%B3%BB%E5%88%97%E3%80%91%E4%B8%8B%E8%BD%BD%E9%93%BE%E6%8E%A5%EF%BC%9Ahttp://dpurl.cn/ODj5qAB%E3%80%90%E8%BF%90%E7%BB%B4%E7%B3%BB%E5%88%97%E3%80%91%E4%B8%8B%E8%BD%BD%E9%93%BE%E6%8E%A5%EF%BC%9Ahttp://dpurl.cn/6zjgHFj%E3%80%90%E6%B5%8B%E8%AF%95%E7%B3%BB%E5%88%97%E3%80%91%E4%B8%8B%E8%BD%BD%E9%93%BE%E6%8E%A5%EF%BC%9Ahttp://dpurl.cn/wfKQqy%E3%80%90%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%88%90%E9%95%BF%E7%B3%BB%E5%88%97%E3%80%91%E4%B8%8B%E8%BD%BD%E9%93%BE%E6%8E%A5%EF%BC%9Ahttp://dpurl.cn/p5sPoKT%E3%80%902018%E5%B9%B4%E7%BE%8E%E5%9B%A2%E7%82%B9%E8%AF%84%E6%8A%80%E6%9C%AF%E6%96%87%E7%AB%A0%E5%90%88%E8%BE%91%E3%80%91%E4%B8%8B%E8%BD%BD%E9%93%BE%E6%8E%A5%EF%BC%9Ahttp://dpurl.cn/iLGBtuS\"\u003ehttp://dpurl.cn/DWVhPpS【后台系列】下载链接：http://dpurl.cn/obtIVn【系统系列】下载链接：http://dpurl.cn/zTDWq8Q【算法系列】下载链接：http://dpurl.cn/ODj5qAB【运维系列】下载链接：http://dpurl.cn/6zjgHFj【测试系列】下载链接：http://dpurl.cn/wfKQqy【工程师成长系列】下载链接：http://dpurl.cn/p5sPoKT【2018年美团点评技术文章合辑】下载链接：http://dpurl.cn/iLGBtuS\u003c/a\u003e\u003c/p\u003e\n\u003ch1 id=\"总结归纳\"\u003e🤗总结归纳\u003c/h1\u003e\n\u003cp\u003e致谢：\u003c/p\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e","title":"美团2019年货分享"},{"content":"当使用go开发web程序时，修改点代码就得编译，虽然编译速度很快，但是也累啊，想起java的spring-boot有热更新插件， php根本都不需要重启，go怎么可以落后。\n一顿搜索后，找到了gin和fresh,都挺好用的\ngin cd path/to/app gin run main.go fresh cd path/to/app fresh 懒人有懒福～\n","permalink":"https://blog.jimersylee.com/posts/go-web%E7%A8%8B%E5%BA%8F%E7%9A%84%E7%83%AD%E6%9B%B4%E6%96%B0/","summary":"\u003cp\u003e当使用go开发web程序时，修改点代码就得编译，虽然编译速度很快，但是也累啊，想起java的spring-boot有热更新插件， php根本都不需要重启，go怎么可以落后。\u003c/p\u003e\n\u003cp\u003e一顿搜索后，找到了\u003ca href=\"https://github.com/codegangsta/gin\"\u003egin\u003c/a\u003e和\u003ca href=\"https://github.com/pilu/fresh\"\u003efresh\u003c/a\u003e,都挺好用的\u003c/p\u003e\n\u003ch1 id=\"gin\"\u003e\u003cstrong\u003egin\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ecd path/to/app\ngin run main.go\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"fresh\"\u003e\u003cstrong\u003efresh\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ecd path/to/app\nfresh\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e懒人有懒福～\u003c/p\u003e","title":"go-web程序的热更新"},{"content":"Manjaro安装fusuma 环境:Manjaro\n██████████████████ ████████ jimersylee@jimersylee-laptop ██████████████████ ████████ OS: Manjaro 17.1.12 Hakoila ██████████████████ ████████ Kernel: x86_64 Linux 4.14.67-1-MANJARO ██████████████████ ████████ Uptime: 3h 12m ████████ ████████ Packages: 1184 ████████ ████████ ████████ Shell: zsh 5.5.1 ████████ ████████ ████████ Resolution: 1920x1080 ████████ ████████ ████████ DE: GNOME ████████ ████████ ████████ WM: GNOME Shell ████████ ████████ ████████ WM Theme: Adapta-Nokto-Maia ████████ ████████ ████████ GTK Theme: Adapta-Nokto-Maia [GTK2/3] ████████ ████████ ████████ Icon Theme: Papirus-Adapta-Maia ████████ ████████ ████████ Font: Noto Sans 11 ████████ ████████ ████████ CPU: Intel Core i7-8550U @ 8x 4GHz [47.0°C] GPU: Mesa DRI Intel(R) UHD Graphics 620 (Kabylake GT2) RAM: 6157MiB / 15928MiB 将用户加入输入组 sudo gpasswd -a $USER input sudo reboot 安装ruby环境，安装fusuma包 #安装ruby sudo pacman -S ruby sudo gem install fusuma # 配置 #将ruby程序目录加入路径 echo \u0026#34;export PATH=$PATH:/home/jimersylee/.gem/ruby/2.5.0/bin\u0026#34; \u0026gt;\u0026gt; ~/.profile source ~/.profile #人工启动 fusuma -d #加入开机启动 /usr/share/applications 新建一个.desktop快捷方式，配置好 使用tweaks 添加 startup application ","permalink":"https://blog.jimersylee.com/posts/manjaro%E5%AE%89%E8%A3%85fusuma/","summary":"\u003ch1 id=\"manjaro安装fusuma\"\u003eManjaro安装fusuma\u003c/h1\u003e\n\u003cp\u003e环境:Manjaro\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e ██████████████████  ████████     jimersylee@jimersylee-laptop\n ██████████████████  ████████     OS: Manjaro 17.1.12 Hakoila\n ██████████████████  ████████     Kernel: x86_64 Linux 4.14.67-1-MANJARO\n ██████████████████  ████████     Uptime: 3h 12m\n ████████            ████████     Packages: 1184\n ████████  ████████  ████████     Shell: zsh 5.5.1\n ████████  ████████  ████████     Resolution: 1920x1080\n ████████  ████████  ████████     DE: GNOME\n ████████  ████████  ████████     WM: GNOME Shell\n ████████  ████████  ████████     WM Theme: Adapta-Nokto-Maia\n ████████  ████████  ████████     GTK Theme: Adapta-Nokto-Maia [GTK2/3]\n ████████  ████████  ████████     Icon Theme: Papirus-Adapta-Maia\n ████████  ████████  ████████     Font: Noto Sans 11\n ████████  ████████  ████████     CPU: Intel Core i7-8550U @ 8x 4GHz [47.0°C]\n                                  GPU: Mesa DRI Intel(R) UHD Graphics 620 (Kabylake GT2)\n                                  RAM: 6157MiB / 15928MiB\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"将用户加入输入组\"\u003e\u003cstrong\u003e将用户加入输入组\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esudo gpasswd -a $USER input\nsudo reboot\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"安装ruby环境安装fusuma包\"\u003e\u003cstrong\u003e安装ruby环境，安装fusuma包\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#安装ruby\nsudo pacman -S ruby\nsudo gem install fusuma\n#\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"配置\"\u003e\u003cstrong\u003e配置\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#将ruby程序目录加入路径\necho \u0026#34;export PATH=$PATH:/home/jimersylee/.gem/ruby/2.5.0/bin\u0026#34; \u0026gt;\u0026gt; ~/.profile\nsource ~/.profile\n#人工启动\nfusuma -d\n#加入开机启动\n/usr/share/applications 新建一个.desktop快捷方式，配置好\n使用tweaks 添加 startup application\n\u003c/code\u003e\u003c/pre\u003e","title":"Manjaro安装fusuma"},{"content":"Manjaro安装Mariadb 环境:Manjaro\n██████████████████ ████████ jimersylee@jimersylee-laptop ██████████████████ ████████ OS: Manjaro 17.1.12 Hakoila ██████████████████ ████████ Kernel: x86_64 Linux 4.14.67-1-MANJARO ██████████████████ ████████ Uptime: 3h 12m ████████ ████████ Packages: 1184 ████████ ████████ ████████ Shell: zsh 5.5.1 ████████ ████████ ████████ Resolution: 1920x1080 ████████ ████████ ████████ DE: GNOME ████████ ████████ ████████ WM: GNOME Shell ████████ ████████ ████████ WM Theme: Adapta-Nokto-Maia ████████ ████████ ████████ GTK Theme: Adapta-Nokto-Maia [GTK2/3] ████████ ████████ ████████ Icon Theme: Papirus-Adapta-Maia ████████ ████████ ████████ Font: Noto Sans 11 ████████ ████████ ████████ CPU: Intel Core i7-8550U @ 8x 4GHz [47.0°C] GPU: Mesa DRI Intel(R) UHD Graphics 620 (Kabylake GT2) RAM: 6157MiB / 15928MiB 安装 sudo pacman -S mariadb 配置 #初始化 sudo mysql_install_db --user=mysql --basedir=/usr --datadir=/var/lib/mysql #启动 sudo systemctl start mariadb #设置密码 mysql_secure_installation ","permalink":"https://blog.jimersylee.com/posts/manjaro%E5%AE%89%E8%A3%85mariadb/","summary":"\u003ch1 id=\"manjaro安装mariadb\"\u003eManjaro安装Mariadb\u003c/h1\u003e\n\u003cp\u003e环境:Manjaro\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e ██████████████████  ████████     jimersylee@jimersylee-laptop\n ██████████████████  ████████     OS: Manjaro 17.1.12 Hakoila\n ██████████████████  ████████     Kernel: x86_64 Linux 4.14.67-1-MANJARO\n ██████████████████  ████████     Uptime: 3h 12m\n ████████            ████████     Packages: 1184\n ████████  ████████  ████████     Shell: zsh 5.5.1\n ████████  ████████  ████████     Resolution: 1920x1080\n ████████  ████████  ████████     DE: GNOME\n ████████  ████████  ████████     WM: GNOME Shell\n ████████  ████████  ████████     WM Theme: Adapta-Nokto-Maia\n ████████  ████████  ████████     GTK Theme: Adapta-Nokto-Maia [GTK2/3]\n ████████  ████████  ████████     Icon Theme: Papirus-Adapta-Maia\n ████████  ████████  ████████     Font: Noto Sans 11\n ████████  ████████  ████████     CPU: Intel Core i7-8550U @ 8x 4GHz [47.0°C]\n                                  GPU: Mesa DRI Intel(R) UHD Graphics 620 (Kabylake GT2)\n                                  RAM: 6157MiB / 15928MiB\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"安装\"\u003e\u003cstrong\u003e安装\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esudo pacman -S mariadb\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"配置\"\u003e\u003cstrong\u003e配置\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#初始化\nsudo mysql_install_db --user=mysql --basedir=/usr --datadir=/var/lib/mysql\n#启动\nsudo systemctl start mariadb\n#设置密码\nmysql_secure_installation\n\u003c/code\u003e\u003c/pre\u003e","title":"Manjaro安装Mariadb"},{"content":"在上一章中,我们探索了在web应用中如何处理URLS和指引他们转到不同的页面.同时,我们通过net/http中的handle创建了动态的链接和动态的结果.\n通过实现和扩展Gorilla toolkit的mux路由,我们通过正则表达式扩展了路由的能力,使其给予我们的应用更大的灵活性.\n其实这是一些最流行的web服务器的特性.比如说Apache和Nginx都在路由中提供了方法去解析正则表达式.\n但是这仅仅是构成web应用的基石.为了更加深入,我们需要去看看如何引入数据.\n前一章的例子中静态文件服务依赖于硬编码,这显然是过时的且难以控制的.\n但是幸运的是,从90年代末期开始,网站变得动态化,数据库开始统治世界.虽然APIs,微服务和NoSQL在某些领域替代了这些架构,但是这个架构在当今的Web开发中还是万金油的角色.\n所以,事不宜迟,让我们开始获取一些动态数据\n在这一章中,我们将学习以下几个主题\n连接数据库 使用GUID美化URLs 处理404页面 连接一个数据库 为了连接数据库,Go的SQL接口提供了一个非常简单且可信赖的方式去连接拥有驱动的不同种类的数据库服务器.\n目前,大部分流行的数据库都支持-MySQL,Postgres,SQLite,MSSQL和相当多的实现了Go提供的database/sql接口的数据库驱动.\nNote:在本书中,我们将会把MySQL和Postgres数据库使用最好的实践运用在多个例子上.安装MySQL和Postgres在Nix,Windows,OS X 系统的机器上是相当基础的工作\n创建MySQL数据库 你可以选择设计任何你想要的应用,但是在这些例子中,我们将着手一个非常的简单的博客.\n我们的目标是尽可能地在数据库中创建一些博客的入口,最好可以使用GUID在数据库中直接地获取数据和展示,如果博客的入口不存在,将展示错误页面.\n为了实现这个需求,我们将创建一个包含了我们的页面的MySQL数据库.这个数据库将包含一个整数型的,自动递增的ID,一个全局唯一的标识,或者GUID,还有一些博客的初始数据.\n简单起见,我们创建一个叫存储标题的page_title字段,存储页面内容的page_content字段,还有一个使用Unix时间戳的字段page_date.\nCREATETABLE `pages` ( `id` int(11) unsignedNOT NULL AUTO_INCREMENT, `page_guid` varchar(256)NOT NULLDEFAULT \u0026#39;\u0026#39;, `page_title` varchar(256)DEFAULT NULL, `page_content` mediumtext, `page_date`timestampNOT NULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP, PRIMARYKEY (`id`), UNIQUEKEY `page_guid` (`page_guid`) )ENGINE=InnoDB AUTO_INCREMENT=2DEFAULTCHARSET=utf-8; 将page_guid标记为UNIQUE_KEY相当重要,如果我们允许出现重复的page_guid,在浏览某个网址的时候可能出现不准确的情况. 我们使用以下的语句插入一些blog数据\nINSERTINTO `pages` (`id`, `page_guid`, `page_title`, `page_content`, `page_date`)VALUES (NULL, \u0026#39;hello-world\u0026#39;, \u0026#39;Hello, World\u0026#39;, \u0026#39;I\\\u0026#39;m so glad you found this page! It\\\u0026#39;s been sitting patiently on the Internet for some time, just waiting for a visitor.\u0026#39;,CURRENT_TIMESTAMP); 执行了上面的语句之后,我们就获得了初始数据 使用下面的代码来获得连接数据库的能力\npackage main import ( \u0026#34;database/sql\u0026#34; \u0026#34;fmt\u0026#34; _ \u0026#34;github.com/go-sql-driver/mysql\u0026#34; \u0026#34;log\u0026#34; ) 我们导入MySQL驱动包来完成需求.通常,这意味着驱动是另一个包的实现.你能注意到使用 符号来导入包.你可能已经熟悉这点,作为一种快速且脏的方式去忽略类的实例的返回值.比如说x, :=something() 允许你去忽略第二个返回值.这经常也被开发者用在计划去使用一个库,但是目前还没有用到的情况.通过这种方式准备包,它允许导入声明而不引起编译期报错.虽然这不是推荐的做法,但是在预载入方法中使用下划线_或者空白标识符,好处是这是很常见的做法也普遍被接受. 其实,这全部依赖于你怎样以及为何使用标识符.\nconst ( DBHost = \u0026#34;127.0.0.1\u0026#34; DBPort = \u0026#34;:3306\u0026#34; DBUser = \u0026#34;root\u0026#34; DBPass = \u0026#34;password!\u0026#34; DBDbase = \u0026#34;cms\u0026#34; ) 记得使用你自己的配置去替换以上值\nvar database *sql.db 为了避免大量重复代码,我们可以将数据库连接引用作为一个全局变量.为了清晰可见,我们将在代码开头定义.其实也没有什么事会阻止你把这个定义为一个常量,但是如果这样我们将会失去一定的灵活性,比如说添加多个数据库到单个应用中\ntype Pagestruct { Title string Content string Date string } 这个结构,跟我们的数据表结构非常匹配了,有标题,内容,时间.我们马上也将在本书中看到更好的数据结构设计.你需要确保你结构的字段是可以导出的或者公共的.任何小写的字段将不会被导出,因此也不能被模板化.我们将在后面讨论更多.\nfunc main(){ dbConn:=fmt.Sprintf(\u0026#34;%s:%s@tcp(%s)/%s\u0026#34;,DBUser,DBPass,DBHost,DBDbase) db,err:=sql.Open(\u0026#34;mysql\u0026#34;,dbConn) if err!=nil{ log.Println(\u0026#34;Couldn\u0026#39;t connect\u0026#34;) log.Println(err.Error()) } log.Println(\u0026#34;Connect successfully\u0026#34;) database=db } 正如我们之前提到的一样,这主要是脚手架.我们唯一想做的就是确保我们可以连接我们的数据库.如果出现一个错误,检查你的连接配置和输出的日志. 如果如我们期望的那样,我们使用上面的代码连接上数据库,我们就可以创建通用的路由代码来匹配请求中GUID,然后去数据库查询数据.\n为了以上的目标,我们需要去重新实现Gorilla,创建单个路由,然后实现一个handler去匹配我们的数据库\n看看下面的修改\npackage main import ( \u0026#34;database/sql\u0026#34; \u0026#34;fmt\u0026#34; _ \u0026#34;github.com/go-sql-driver/mysql\u0026#34; \u0026#34;github.com/gorilla/mux\u0026#34; \u0026#34;log\u0026#34; \u0026#34;net/http\u0026#34; ) 比较大的变化是我们在项目中引入了Gorilla和net/http 库.很显然我们需要这些来构建服务\nconst ( DBHost = \u0026#34;127.0.0.1\u0026#34; DBPort = \u0026#34;:3306\u0026#34; DBUser = \u0026#34;root\u0026#34; DBPass = \u0026#34;password!\u0026#34; DBDbase = \u0026#34;cms\u0026#34; PORT = \u0026#34;:8080\u0026#34; ) 我们增加了PORT常量,用来绑定HTTP 服务器端口\nvar database *sql.DB type Pagestruct{ Titlestring Contentstring Datestring } /** 数据库连接测试 */funcmain(){ dbConn:=fmt.Sprintf(\u0026#34;%s:%s@/%s\u0026#34;,DBUser,DBPass,DBDbase) db,err:=sql.Open(\u0026#34;mysql\u0026#34;,dbConn) if err!=nil{ log.Println(\u0026#34;Couldn\u0026#39;t connect\u0026#34;) log.Println(err.Error()) } log.Println(\u0026#34;Connect successfully\u0026#34;) database=db //设置路由 routes:=mux.NewRouter() routes.HandleFunc(\u0026#34;/page/{id:[0-9a-zA\\\\-]+\u0026#34;,ServePage) http.Handle(\u0026#34;/\u0026#34;,routes) http.ListenAndServe(PORT,nil) } funcServePage(w http.ResponseWriter,r *http.Request){ vars:=mux.Vars(r) pageID:=vars[\u0026#34;id\u0026#34;] thisPage:=Page{} fmt.Println(\u0026#34;pageID:\u0026#34;+pageID,\u0026#34;guid:\u0026#34;+pageGUID) err:=database.QueryRow(\u0026#34;select page_title,page_content,page_date from pages where id=?\u0026#34;,pageID).Scan(\u0026amp;thisPage.Title,\u0026amp;thisPage.Content,\u0026amp;thisPage.Date) if err!=nil{ log.Println(\u0026#34;Couldn\u0026#39;t get page: +pageID\u0026#34;) log.Println(err.Error()) } html:=`\u0026lt;html\u0026gt;\u0026lt;head\u0026gt;\u0026lt;title\u0026gt;` + thisPage.Title + `\u0026lt;/title\u0026gt;\u0026lt;/head\u0026gt;\u0026lt;body\u0026gt;\u0026lt;h1\u0026gt;` + thisPage.Title + `\u0026lt;/h1\u0026gt;\u0026lt;div\u0026gt;` + thisPage.Content + `\u0026lt;/div\u0026gt;\u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt;` fmt.Fprintln(w,html) } ServePage()是一个从mux.Vars中获取id,然后查询数据库的方法.最简单的查询数据库的方法就是使用使用预处理语句,比如Query,QueryRow或者Prepare.利用其中任何一个包含可注入的变量的声明语句,可以避免手工构建查询语句的风险.\nScan方法获取结果然后解析成数据结构.在这个例子中,我们解析page_title,page_content,page_date到Page结构中的Title,Content,Date\nfunc main() { dbConn := fmt.Sprintf(\u0026#34;%s:%s@/%s\u0026#34;, DBUser, DBPass, DBDbase) fmt.Println(dbConn) db, err := sql.Open(\u0026#34;mysql\u0026#34;, dbConn) if err != nil { log.Println(\u0026#34;Couldn\u0026#39;t connect to\u0026#34;+DBDbase) log.Println(err.Error) } database = db routes := mux.NewRouter() routes.HandleFunc(\u0026#34;/page/{id:[0-9]+}\u0026#34;, ServePage) http.Handle(\u0026#34;/\u0026#34;, routes) http.ListenAndServe(PORT, nil) } 看看我们这的正则表达式:只获取数字\n还记得们谈论过使用内置的GUID?我们将马上会用到,现在我们看看访问localhost:8080/page/1的结果\nHello, World I\u0026#39;m so glad you found this page! It\u0026#39;s been sitting patiently on the Internetfor some time, just waitingfor a visitor. 在前面的例子中,我们可以看到在数据库中的博客内容.\n使用GUID来美化URLs 在本章的前几段我们讨论过使用GUID来作为所有请求的URL标识符.\n我们需要去修改正则表达式和SQL语句\nroutes.HandleFunc(\u0026#34;/page/{id:[0-9a-zA\\\\-]+}\u0026#34;, ServePage) 修改为\nroutes.HandleFunc(\u0026#34;/page/{guid:[0-9a-zA\\\\-]+}\u0026#34;, ServePage) 修改相关方法和SQL\nfunc ServePage(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) pageGUID := vars[\u0026#34;guid\u0026#34;] thisPage := Page{} fmt.Println(pageGUID) err := database.QueryRow(\u0026#34;SELECT page_title,page_content,page_date FROM pages WHERE page_guid=?\u0026#34;, pageGUID).Scan(\u0026amp;thisPage.Title, \u0026amp;thisPage.Content, \u0026amp;thisPage.Date) 在完成之后,我们访问 localhost:8080/page/hello-world,可以获得之前访问localhost:8080/page/1同样的结果,但是修改后的url可读性更高,而且对搜索引擎更加友好\n处理404 我们之前的代码有个显而易见的问题就是它不处理无效的ID(或GUID)的请求.\n真实的情况是,一个访问/page/999的请求,将返回空白页,控制台将会输出Couldn\u0026rsquo;t get page!\n解决这个问题最简单的方法就是使用合适的错误.在上一章中,我们探索过定制的404页面,在这里我们可以实现其中一种,但是在一个请求不能找到时最简单的方法就是直接返回一个HTTP 状态码,允许浏览器去处理.\n在我们之前的代码中,我们有一个错误处理器,仅仅是写日志,让我们把它变得更加丰富\nerr:=database.QueryRow(\u0026#34;select page_title,page_content,page_date from pages where page_guid=?\u0026#34;,pageGUID).Scan(\u0026amp;thisPage.Title,\u0026amp;thisPage.Content,\u0026amp;thisPage.Date) if err!=nil{ http.Error(w,http.StatusText(404),http.StatusNotFound) log.Println(\u0026#34;Couldn\u0026#39;t get page: +pageID\u0026#34;) log.Println(err.Error()) return } 这样子的话当遇到错误页面的时候将会有一个友好的提示页面\nhttp://127.0.0.1:8080/page/hello-world22 Not Found ","permalink":"https://blog.jimersylee.com/posts/go-build-web-application%E7%9A%84%E4%B8%AD%E6%96%87%E7%BF%BB%E8%AF%91%E7%89%88-%E7%AC%AC%E4%B8%89%E7%AB%A0-%E8%BF%9E%E6%8E%A5%E6%95%B0%E6%8D%AE/","summary":"\u003cp\u003e在上一章中,我们探索了在web应用中如何处理URLS和指引他们转到不同的页面.同时,我们通过net/http中的handle创建了动态的链接和动态的结果.\u003c/p\u003e\n\u003cp\u003e通过实现和扩展Gorilla toolkit的mux路由,我们通过正则表达式扩展了路由的能力,使其给予我们的应用更大的灵活性.\u003c/p\u003e\n\u003cp\u003e其实这是一些最流行的web服务器的特性.比如说Apache和Nginx都在路由中提供了方法去解析正则表达式.\u003c/p\u003e\n\u003cp\u003e但是这仅仅是构成web应用的基石.为了更加深入,我们需要去看看如何引入数据.\u003c/p\u003e\n\u003cp\u003e前一章的例子中静态文件服务依赖于硬编码,这显然是过时的且难以控制的.\u003c/p\u003e\n\u003cp\u003e但是幸运的是,从90年代末期开始,网站变得动态化,数据库开始统治世界.虽然APIs,微服务和NoSQL在某些领域替代了这些架构,但是这个架构在当今的Web开发中还是万金油的角色.\u003c/p\u003e\n\u003cp\u003e所以,事不宜迟,让我们开始获取一些动态数据\u003c/p\u003e\n\u003cp\u003e在这一章中,我们将学习以下几个主题\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e连接数据库\u003c/li\u003e\n\u003cli\u003e使用GUID美化URLs\u003c/li\u003e\n\u003cli\u003e处理404页面\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"连接一个数据库\"\u003e\u003cstrong\u003e连接一个数据库\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e为了连接数据库,Go的SQL接口提供了一个非常简单且可信赖的方式去连接拥有驱动的不同种类的数据库服务器.\u003c/p\u003e\n\u003cp\u003e目前,大部分流行的数据库都支持-MySQL,Postgres,SQLite,MSSQL和相当多的实现了Go提供的database/sql接口的数据库驱动.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eNote:在本书中,我们将会把MySQL和Postgres数据库使用最好的实践运用在多个例子上.安装MySQL和Postgres在Nix,Windows,OS X 系统的机器上是相当基础的工作\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"创建mysql数据库\"\u003e\u003cstrong\u003e创建MySQL数据库\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e你可以选择设计任何你想要的应用,但是在这些例子中,我们将着手一个非常的简单的博客.\u003c/p\u003e\n\u003cp\u003e我们的目标是尽可能地在数据库中创建一些博客的入口,最好可以使用GUID在数据库中直接地获取数据和展示,如果博客的入口不存在,将展示错误页面.\u003c/p\u003e\n\u003cp\u003e为了实现这个需求,我们将创建一个包含了我们的页面的MySQL数据库.这个数据库将包含一个整数型的,自动递增的ID,一个全局唯一的标识,或者GUID,还有一些博客的初始数据.\u003c/p\u003e\n\u003cp\u003e简单起见,我们创建一个叫存储标题的page_title字段,存储页面内容的page_content字段,还有一个使用Unix时间戳的字段page_date.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eCREATETABLE\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`pages`\u003c/span\u003e (\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e`id`\u003c/span\u003e int(\u003cspan style=\"color:#ae81ff\"\u003e11\u003c/span\u003e) \u003cspan style=\"color:#a6e22e\"\u003eunsignedNOT\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eNULL\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eAUTO_INCREMENT\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e`page_guid`\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003evarchar\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e256\u003c/span\u003e)\u003cspan style=\"color:#a6e22e\"\u003eNOT\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eNULLDEFAULT\u003c/span\u003e \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e`page_title`\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003evarchar\u003c/span\u003e(\u003cspan style=\"color:#ae81ff\"\u003e256\u003c/span\u003e)\u003cspan style=\"color:#a6e22e\"\u003eDEFAULT\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eNULL\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e`page_content`\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emediumtext\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e`page_date`\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003etimestampNOT\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ePRIMARYKEY\u003c/span\u003e (\u003cspan style=\"color:#e6db74\"\u003e`id`\u003c/span\u003e),\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eUNIQUEKEY\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`page_guid`\u003c/span\u003e (\u003cspan style=\"color:#e6db74\"\u003e`page_guid`\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\u003cspan style=\"color:#a6e22e\"\u003eENGINE\u003c/span\u003e=\u003cspan style=\"color:#a6e22e\"\u003eInnoDB\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eAUTO_INCREMENT\u003c/span\u003e=\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eDEFAULTCHARSET\u003c/span\u003e=\u003cspan style=\"color:#a6e22e\"\u003eutf\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e将page_guid标记为UNIQUE_KEY相当重要,如果我们允许出现重复的page_guid,在浏览某个网址的时候可能出现不准确的情况. 我们使用以下的语句插入一些blog数据\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eINSERTINTO\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e`pages`\u003c/span\u003e (\u003cspan style=\"color:#e6db74\"\u003e`id`\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e`page_guid`\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e`page_title`\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e`page_content`\u003c/span\u003e, \u003cspan style=\"color:#e6db74\"\u003e`page_date`\u003c/span\u003e)\u003cspan style=\"color:#a6e22e\"\u003eVALUES\u003c/span\u003e (\u003cspan style=\"color:#a6e22e\"\u003eNULL\u003c/span\u003e, \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003ehello\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eworld\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eHello\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eWorld\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u0026#39;\u003c/span\u003e, \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eI\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\\\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003em\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eso\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eglad\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eyou\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003efound\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ethis\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003epage\u003c/span\u003e! \u003cspan style=\"color:#a6e22e\"\u003eIt\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\\\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003es\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ebeen\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esitting\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003epatiently\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eon\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ethe\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eInternet\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esome\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003etime\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ejust\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ewaiting\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ea\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003evisitor\u003c/span\u003e.\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\u0026#39;\u003c/span\u003e,\u003cspan style=\"color:#a6e22e\"\u003eCURRENT_TIMESTAMP\u003c/span\u003e);\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e执行了上面的语句之后,我们就获得了初始数据 使用下面的代码来获得连接数据库的能力\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003epackage\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e (\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;database/sql\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;fmt\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003e_\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;github.com/go-sql-driver/mysql\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;log\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e我们导入MySQL驱动包来完成需求.通常,这意味着驱动是另一个包的实现.你能注意到使用 \u003cem\u003e符号来导入包.你可能已经熟悉这点,作为一种快速且脏的方式去忽略类的实例的返回值.比如说x,\u003c/em\u003e :=something() 允许你去忽略第二个返回值.这经常也被开发者用在计划去使用一个库,但是目前还没有用到的情况.通过这种方式准备包,它允许导入声明而不引起编译期报错.虽然这不是推荐的做法,但是在预载入方法中使用下划线_或者空白标识符,好处是这是很常见的做法也普遍被接受. 其实,这全部依赖于你怎样以及为何使用标识符.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003econst\u003c/span\u003e (\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDBHost\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;127.0.0.1\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDBPort\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;:3306\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDBUser\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;root\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDBPass\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;password!\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eDBDbase\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;cms\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e记得使用你自己的配置去替换以上值\u003c/p\u003e","title":"\u003cGo build web application\u003e的中文翻译版-第三章-连接数据"},{"content":"目录 模块1:学习Go的Web开发 第一章:介绍和安装Go环境 安装Go 构建一个项目 引入包 介绍net包 你好,Web 本章总结 第二章:服务和路由 直接的文件服务 基本路由 使用Gorilla实现更复杂的路由 转发请求 处理基本的错误 本章总结 第三章:连接数据 连接一个数据库 使用GUID美化URLs 处理404错误 总结 第四章:使用模板 介绍模板,上下文和可视化 HTML模板和文本模板 渲染变量和安全性 使用逻辑和控制结构 本章总结 第五章:使用RESTful APIs进行前端集成 设置基本的API结构 RESTful架构和最佳实践 创建我们的第一个API 实现安全 使用POST创建数据 使用PUT更改数据 本章总结 第六章:Session和Cookies 设置cookies 存储用户的信息 初始化一个服务端的session 本章总结 第七章:微服务和通讯 介绍引入微服务 使用微服务的利弊 理解微服务的内核 微服务之间的通讯 在线上放一个信息 从其他服务读取信息 本章总结 第八章:记录日志和测试 介绍Go中的日志 记录日志到IO中 格式化你的输出 使用panics和fatal errors 介绍Go中的测试 本章总结 第九章:安全 在任何地方使用HTTPS-实现TLS 防止SQL注入 防范XSS攻击 防范CSRF跨站攻击 加密cookies 使用安全中间件 本章总结 第十章:缓存,代理和提高性能 确定瓶颈 实现反向代理 实现缓存 实现HTTP/2 本章总结 模块2:Go 编程蓝皮书 第一章:使用Web Sockets构建的聊天应用 一个简单的Web服务器 在服务器上建模一个聊天室和客户端 构建一个使用HTML和JavaScript的聊天客户端 跟踪代码获取内在的流程 本章总结 第二章:增加权限 拦截所有请求 创建一个社交化的登录页面 动态路径 OAuth2 把你的APP告诉权限提供者 实现额外的登录 本章总结 第三章:3个方法去实现文件缩略图 在权限服务器上的头像 实现Gravatar 上传头像图片 整合3种实现 本章总结 第四章:查询域名的命令行工具 命令行工具的管道设计 5个简单的程序 编写所有5个程序 本章总结 第五章:构建分布式系统,与复杂的数据交互 系统设计 安装环境 获取Twitter的投票 计算投票 运行我们的解决方案 本章总结 第六章:通过RESTful web数据接口对外提供数据和功能 RESTful API 设计 在处理器间共享数据 包装处理函数 响应 理解请求 一个简单的主函数去处理我们的API 处理节点 一个web客户端去消费API 运行解决方案 本章总结 第七章:随机推荐Web服务 项目预览 用代码展示数据 构造随机推荐 本章总结 第八章:文件备份系统 解决方案设计 备份包 用户命令行工具 备份守护工具 测试解决方案 本章总结 模块3:精通Go的高并发 第一章:介绍Go并发编程 介绍协程 实现延迟控制机制 理解协程与协同 实现通道 关闭与协程 构建一个使用协程和通道的爬虫 本章总结 第二章:理解并发模型 理解协程如何工作 同步和异步协程 可视化并发 RSS实战 CSP的一点介绍 Go和角色模型 面向对象 使用并发 管理线程 使用同步和互斥锁住数据 本章总结 第三章:开发并行策略 在复杂的并发中提高效率 使用竞争检查识别竞争条件 同步我们的并发操作 项目-多用户预约日历 一个多用户预约日历 风格说明 不变性说明 本章总结 第四章:应用中的数据完整性 深入理解互斥与同步 协程的代价 处理文件 更底层-实现C 分布式的Go 几种常见的一致性模型 使用memcached 本章总结 第五章:锁,区块和更好的通道 理解Go中的区块方法 清除协程 创建通道的通道 Pprof-一个令人惊叹的工具 处理死锁和错误 本章总结 第六章:C10K-Go中的一个无锁的Web服务器 攻克C10K问题 创建我们的C10K Web服务器 提供页面服务 多线程和利用多核 探索我们的Web服务器 本章总结 第七章:性能与可扩展性 Go的高性能 使用App Engine 分布式的Go 一些有用的库 内存维护 本章总结 第八章:并发程序架构 设计我们的并发程序 确定我们的需求 在Go中使用NoSQL作为数据存储 监控文件系统的变化 管理日志文件 处理配置文件 检测文件变化 备份文件 设计Web接口 还原文件的历史-命令行 检查服务器的健康度 本章总结 第九章:在Go中记录日志和测试并发 处理错误和记录日志 使用log4go包作为健壮的日志组件 使用runtime包作为细粒度的堆栈跟踪组件 本章总结 第十章:先进的并发和最佳实践 使用channels跨越基础 构建工作者 实现空通道区块 使用tomb实现对协程更多的细粒度的控制 使用通道定时 通过并发模式构建负载均衡器 选择单向和双向通道 使用泛型通道 使用Go的单元测试 使用Google的App Engine 使用最佳实践 本章总结 ","permalink":"https://blog.jimersylee.com/posts/go-build-web-application%E7%9A%84%E4%B8%AD%E6%96%87%E7%BF%BB%E8%AF%91%E7%89%88%E6%9C%AC/","summary":"\u003ch1 id=\"目录\"\u003e\u003cstrong\u003e目录\u003c/strong\u003e\u003c/h1\u003e\n\u003ch1 id=\"模块1学习go的web开发\"\u003e\u003cstrong\u003e模块1:学习Go的Web开发\u003c/strong\u003e\u003c/h1\u003e\n\u003ch3 id=\"第一章介绍和安装go环境\"\u003e\u003cstrong\u003e第一章:介绍和安装Go环境\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e安装Go\u003c/li\u003e\n\u003cli\u003e构建一个项目\u003c/li\u003e\n\u003cli\u003e引入包\u003c/li\u003e\n\u003cli\u003e介绍net包\u003c/li\u003e\n\u003cli\u003e你好,Web\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第二章服务和路由\"\u003e\u003cstrong\u003e第二章:服务和路由\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e直接的文件服务\u003c/li\u003e\n\u003cli\u003e基本路由\u003c/li\u003e\n\u003cli\u003e使用Gorilla实现更复杂的路由\u003c/li\u003e\n\u003cli\u003e转发请求\u003c/li\u003e\n\u003cli\u003e处理基本的错误\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第三章连接数据\"\u003e\u003cstrong\u003e第三章:连接数据\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e连接一个数据库\u003c/li\u003e\n\u003cli\u003e使用GUID美化URLs\u003c/li\u003e\n\u003cli\u003e处理404错误\u003c/li\u003e\n\u003cli\u003e总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第四章使用模板\"\u003e\u003cstrong\u003e第四章:使用模板\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e介绍模板,上下文和可视化\u003c/li\u003e\n\u003cli\u003eHTML模板和文本模板\u003c/li\u003e\n\u003cli\u003e渲染变量和安全性\u003c/li\u003e\n\u003cli\u003e使用逻辑和控制结构\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第五章使用restful-apis进行前端集成\"\u003e\u003cstrong\u003e第五章:使用RESTful APIs进行前端集成\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e设置基本的API结构\u003c/li\u003e\n\u003cli\u003eRESTful架构和最佳实践\u003c/li\u003e\n\u003cli\u003e创建我们的第一个API\u003c/li\u003e\n\u003cli\u003e实现安全\u003c/li\u003e\n\u003cli\u003e使用POST创建数据\u003c/li\u003e\n\u003cli\u003e使用PUT更改数据\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第六章session和cookies\"\u003e\u003cstrong\u003e第六章:Session和Cookies\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e设置cookies\u003c/li\u003e\n\u003cli\u003e存储用户的信息\u003c/li\u003e\n\u003cli\u003e初始化一个服务端的session\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第七章微服务和通讯\"\u003e\u003cstrong\u003e第七章:微服务和通讯\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e介绍引入微服务\u003c/li\u003e\n\u003cli\u003e使用微服务的利弊\u003c/li\u003e\n\u003cli\u003e理解微服务的内核\u003c/li\u003e\n\u003cli\u003e微服务之间的通讯\u003c/li\u003e\n\u003cli\u003e在线上放一个信息\u003c/li\u003e\n\u003cli\u003e从其他服务读取信息\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第八章记录日志和测试\"\u003e\u003cstrong\u003e第八章:记录日志和测试\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e介绍Go中的日志\u003c/li\u003e\n\u003cli\u003e记录日志到IO中\u003c/li\u003e\n\u003cli\u003e格式化你的输出\u003c/li\u003e\n\u003cli\u003e使用panics和fatal errors\u003c/li\u003e\n\u003cli\u003e介绍Go中的测试\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第九章安全\"\u003e\u003cstrong\u003e第九章:安全\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e在任何地方使用HTTPS-实现TLS\u003c/li\u003e\n\u003cli\u003e防止SQL注入\u003c/li\u003e\n\u003cli\u003e防范XSS攻击\u003c/li\u003e\n\u003cli\u003e防范CSRF跨站攻击\u003c/li\u003e\n\u003cli\u003e加密cookies\u003c/li\u003e\n\u003cli\u003e使用安全中间件\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第十章缓存代理和提高性能\"\u003e\u003cstrong\u003e第十章:缓存,代理和提高性能\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e确定瓶颈\u003c/li\u003e\n\u003cli\u003e实现反向代理\u003c/li\u003e\n\u003cli\u003e实现缓存\u003c/li\u003e\n\u003cli\u003e实现HTTP/2\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"模块2go-编程蓝皮书\"\u003e\u003cstrong\u003e模块2:Go 编程蓝皮书\u003c/strong\u003e\u003c/h1\u003e\n\u003ch3 id=\"第一章使用web-sockets构建的聊天应用\"\u003e\u003cstrong\u003e第一章:使用Web Sockets构建的聊天应用\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e一个简单的Web服务器\u003c/li\u003e\n\u003cli\u003e在服务器上建模一个聊天室和客户端\u003c/li\u003e\n\u003cli\u003e构建一个使用HTML和JavaScript的聊天客户端\u003c/li\u003e\n\u003cli\u003e跟踪代码获取内在的流程\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第二章增加权限\"\u003e\u003cstrong\u003e第二章:增加权限\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e拦截所有请求\u003c/li\u003e\n\u003cli\u003e创建一个社交化的登录页面\u003c/li\u003e\n\u003cli\u003e动态路径\u003c/li\u003e\n\u003cli\u003eOAuth2\u003c/li\u003e\n\u003cli\u003e把你的APP告诉权限提供者\u003c/li\u003e\n\u003cli\u003e实现额外的登录\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第三章3个方法去实现文件缩略图\"\u003e\u003cstrong\u003e第三章:3个方法去实现文件缩略图\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e在权限服务器上的头像\u003c/li\u003e\n\u003cli\u003e实现Gravatar\u003c/li\u003e\n\u003cli\u003e上传头像图片\u003c/li\u003e\n\u003cli\u003e整合3种实现\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第四章查询域名的命令行工具\"\u003e\u003cstrong\u003e第四章:查询域名的命令行工具\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e命令行工具的管道设计\u003c/li\u003e\n\u003cli\u003e5个简单的程序\u003c/li\u003e\n\u003cli\u003e编写所有5个程序\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第五章构建分布式系统与复杂的数据交互\"\u003e\u003cstrong\u003e第五章:构建分布式系统,与复杂的数据交互\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e系统设计\u003c/li\u003e\n\u003cli\u003e安装环境\u003c/li\u003e\n\u003cli\u003e获取Twitter的投票\u003c/li\u003e\n\u003cli\u003e计算投票\u003c/li\u003e\n\u003cli\u003e运行我们的解决方案\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第六章通过restful-web数据接口对外提供数据和功能\"\u003e\u003cstrong\u003e第六章:通过RESTful web数据接口对外提供数据和功能\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eRESTful API 设计\u003c/li\u003e\n\u003cli\u003e在处理器间共享数据\u003c/li\u003e\n\u003cli\u003e包装处理函数\u003c/li\u003e\n\u003cli\u003e响应\u003c/li\u003e\n\u003cli\u003e理解请求\u003c/li\u003e\n\u003cli\u003e一个简单的主函数去处理我们的API\u003c/li\u003e\n\u003cli\u003e处理节点\u003c/li\u003e\n\u003cli\u003e一个web客户端去消费API\u003c/li\u003e\n\u003cli\u003e运行解决方案\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第七章随机推荐web服务\"\u003e\u003cstrong\u003e第七章:随机推荐Web服务\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e项目预览\u003c/li\u003e\n\u003cli\u003e用代码展示数据\u003c/li\u003e\n\u003cli\u003e构造随机推荐\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第八章文件备份系统\"\u003e\u003cstrong\u003e第八章:文件备份系统\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e解决方案设计\u003c/li\u003e\n\u003cli\u003e备份包\u003c/li\u003e\n\u003cli\u003e用户命令行工具\u003c/li\u003e\n\u003cli\u003e备份守护工具\u003c/li\u003e\n\u003cli\u003e测试解决方案\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"模块3精通go的高并发\"\u003e\u003cstrong\u003e模块3:精通Go的高并发\u003c/strong\u003e\u003c/h1\u003e\n\u003ch3 id=\"第一章介绍go并发编程\"\u003e\u003cstrong\u003e第一章:介绍Go并发编程\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e介绍协程\u003c/li\u003e\n\u003cli\u003e实现延迟控制机制\u003c/li\u003e\n\u003cli\u003e理解协程与协同\u003c/li\u003e\n\u003cli\u003e实现通道\u003c/li\u003e\n\u003cli\u003e关闭与协程\u003c/li\u003e\n\u003cli\u003e构建一个使用协程和通道的爬虫\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第二章理解并发模型\"\u003e\u003cstrong\u003e第二章:理解并发模型\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e理解协程如何工作\u003c/li\u003e\n\u003cli\u003e同步和异步协程\u003c/li\u003e\n\u003cli\u003e可视化并发\u003c/li\u003e\n\u003cli\u003eRSS实战\u003c/li\u003e\n\u003cli\u003eCSP的一点介绍\u003c/li\u003e\n\u003cli\u003eGo和角色模型\u003c/li\u003e\n\u003cli\u003e面向对象\u003c/li\u003e\n\u003cli\u003e使用并发\u003c/li\u003e\n\u003cli\u003e管理线程\u003c/li\u003e\n\u003cli\u003e使用同步和互斥锁住数据\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第三章开发并行策略\"\u003e\u003cstrong\u003e第三章:开发并行策略\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e在复杂的并发中提高效率\u003c/li\u003e\n\u003cli\u003e使用竞争检查识别竞争条件\u003c/li\u003e\n\u003cli\u003e同步我们的并发操作\u003c/li\u003e\n\u003cli\u003e项目-多用户预约日历\u003c/li\u003e\n\u003cli\u003e一个多用户预约日历\u003c/li\u003e\n\u003cli\u003e风格说明\u003c/li\u003e\n\u003cli\u003e不变性说明\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第四章应用中的数据完整性\"\u003e\u003cstrong\u003e第四章:应用中的数据完整性\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e深入理解互斥与同步\u003c/li\u003e\n\u003cli\u003e协程的代价\u003c/li\u003e\n\u003cli\u003e处理文件\u003c/li\u003e\n\u003cli\u003e更底层-实现C\u003c/li\u003e\n\u003cli\u003e分布式的Go\u003c/li\u003e\n\u003cli\u003e几种常见的一致性模型\u003c/li\u003e\n\u003cli\u003e使用memcached\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第五章锁区块和更好的通道\"\u003e\u003cstrong\u003e第五章:锁,区块和更好的通道\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e理解Go中的区块方法\u003c/li\u003e\n\u003cli\u003e清除协程\u003c/li\u003e\n\u003cli\u003e创建通道的通道\u003c/li\u003e\n\u003cli\u003ePprof-一个令人惊叹的工具\u003c/li\u003e\n\u003cli\u003e处理死锁和错误\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第六章c10k-go中的一个无锁的web服务器\"\u003e\u003cstrong\u003e第六章:C10K-Go中的一个无锁的Web服务器\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e攻克C10K问题\u003c/li\u003e\n\u003cli\u003e创建我们的C10K Web服务器\u003c/li\u003e\n\u003cli\u003e提供页面服务\u003c/li\u003e\n\u003cli\u003e多线程和利用多核\u003c/li\u003e\n\u003cli\u003e探索我们的Web服务器\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第七章性能与可扩展性\"\u003e\u003cstrong\u003e第七章:性能与可扩展性\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eGo的高性能\u003c/li\u003e\n\u003cli\u003e使用App Engine\u003c/li\u003e\n\u003cli\u003e分布式的Go\u003c/li\u003e\n\u003cli\u003e一些有用的库\u003c/li\u003e\n\u003cli\u003e内存维护\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第八章并发程序架构\"\u003e\u003cstrong\u003e第八章:并发程序架构\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e设计我们的并发程序\u003c/li\u003e\n\u003cli\u003e确定我们的需求\u003c/li\u003e\n\u003cli\u003e在Go中使用NoSQL作为数据存储\u003c/li\u003e\n\u003cli\u003e监控文件系统的变化\u003c/li\u003e\n\u003cli\u003e管理日志文件\u003c/li\u003e\n\u003cli\u003e处理配置文件\u003c/li\u003e\n\u003cli\u003e检测文件变化\u003c/li\u003e\n\u003cli\u003e备份文件\u003c/li\u003e\n\u003cli\u003e设计Web接口\u003c/li\u003e\n\u003cli\u003e还原文件的历史-命令行\u003c/li\u003e\n\u003cli\u003e检查服务器的健康度\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第九章在go中记录日志和测试并发\"\u003e\u003cstrong\u003e第九章:在Go中记录日志和测试并发\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e处理错误和记录日志\u003c/li\u003e\n\u003cli\u003e使用log4go包作为健壮的日志组件\u003c/li\u003e\n\u003cli\u003e使用runtime包作为细粒度的堆栈跟踪组件\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"第十章先进的并发和最佳实践\"\u003e\u003cstrong\u003e第十章:先进的并发和最佳实践\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e使用channels跨越基础\u003c/li\u003e\n\u003cli\u003e构建工作者\u003c/li\u003e\n\u003cli\u003e实现空通道区块\u003c/li\u003e\n\u003cli\u003e使用tomb实现对协程更多的细粒度的控制\u003c/li\u003e\n\u003cli\u003e使用通道定时\u003c/li\u003e\n\u003cli\u003e通过并发模式构建负载均衡器\u003c/li\u003e\n\u003cli\u003e选择单向和双向通道\u003c/li\u003e\n\u003cli\u003e使用泛型通道\u003c/li\u003e\n\u003cli\u003e使用Go的单元测试\u003c/li\u003e\n\u003cli\u003e使用Google的App Engine\u003c/li\u003e\n\u003cli\u003e使用最佳实践\u003c/li\u003e\n\u003cli\u003e本章总结\u003c/li\u003e\n\u003c/ul\u003e","title":"\u003cGo build web application\u003e的中文翻译版本"},{"content":"Redis高可用架构 前言 Redis是一个高性能的key-value数据库，现时越来越多企业与应用使用Redis作为缓存服务器。楼主是一枚JAVA后端程序员，也算是半个运维工程师了。在Linux服务器上搭建Redis，怎么可以不会呢？下面楼主就带着大家从0开始，依次搭建：Redis单机服务器 -\u0026gt; Redis主从复制 -\u0026gt;Redis-Sentinel高可用。逐步搭建出高可用的Redis缓存服务器。\n搭建Redis 1. 下载并解压 首先从Redis官网下载Redis并解压，楼主使用的版本是4.0.2。依次执行如下命令：\ncd /opt wget http://download.redis.io/releases/redis-4.0.2.tar.gz tar -zcvf redis-4.0.2.tar.gz 如果没有安装gcc依赖包，则安装对应依赖包\nyum install -y gcc-c++ tcl 2. 编译并安装 下载并解压完毕后，则对源码包进行编译安装，楼主的Redis安装路径为/usr/local/redis，同学们可以自行修改语句：make install PREFIX=你想要安装的路径\ncd /opt/redis-4.0.2 make install PREFIX=/usr/local 复制Redis相关命令到/usr/sbin目录下，这样就可以直接执行这些命令，不用写全路径\ncd /usr/local/redis/bin sudo cp redis-* /usr/sbin 3. 建立Redis配置文件 安装完成之后将 Redis 配置文件拷贝到系统配置目录/etc/下，redis.conf 是 Redis 的配置文件，redis.conf 在 Redis 源码目录，port默认 6379。\ncp /usr/local/redis-4.0.2/redis.conf /etc/ Redis配置文件主要参数解析参考\ndaemonize no#redis进程是否以守护进程的方式运行,yes为是,no为否(不以守护进程的方式运行会占用一个终端) pidfile /var/run/redis.pid#指定redis进程的PID文件存放位置 port 6379#redis进程的端口号 bind 127.0.0.1#绑定的主机地址 timeout 300#客户端闲置多长时间后关闭连接,默认此参数为0即关闭此功能 loglevel verbose#redis日志级别,可用的级别有debug.verbose.notice.warning logfile stdout#log文件输出位置,如果进程以守护进程的方式运行,此处又将输出文件设置为stdout的话,就会将日志信息输出到/dev/null里面去了 databases 16#设置数据库的数量,默认为0可以使用select \u0026lt;dbid\u0026gt;命令在连接上指定数据库id save \u0026lt;seconds\u0026gt;\u0026lt;changes\u0026gt;#指定在多少时间内刷新次数达到多少的时候会将数据同步到数据文件; rdbcompression yes#指定存储至本地数据库时是否压缩文件,默认为yes即启用存储; dbfilename dump.db#指定本地数据库文件名 dir ./#指定本地数据问就按存放位置; slaveof \u0026lt;masterip\u0026gt;\u0026lt;masterport\u0026gt;#指定当本机为slave服务时,设置master服务的IP地址及端口,在redis启动的时候他会自动跟master进行数据同步 masterauth \u0026lt;master-password\u0026gt;#当master设置了密码保护时,slave服务连接master的密码; requirepass footbared#设置redis连接密码,如果配置了连接密码,客户端在连接redis是需要通过AUTH\u0026lt;password\u0026gt;命令提供密码,默认关闭 maxclients 128#设置同一时间最大客户连接数,默认无限制;redis可以同时连接的客户端数为redis程序可以打开的最大文件描述符,如果设置 maxclients 0，表示不作限制。当客户端连接数到达限制时，Redis会关闭新的连接并向客户端返回max number of clients reached错误信息 maxmemory\u0026lt;bytes\u0026gt;#指定Redis最大内存限制，Redis在启动时会把数据加载到内存中，达到最大内存后，Redis会先尝试清除已到期或即将到期的Key，当此方法处理 后，仍然到达最大内存设置，将无法再进行写入操作，但仍然可以进行读取操作。Redis新的vm机制，会把Key存放内存，Value会存放在swap区 appendonly no#指定是否在每次更新操作后进行日志记录，Redis在默认情况下是异步的把数据写入磁盘，如果不开启，可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的，所以有的数据会在一段时间内只存在于内存中。默认为no appendfilename appendonly.aof#指定跟新日志文件名默认为appendonly.aof appendfsync everysec#指定更新日志的条件,有三个可选参数no：表示等操作系统进行数据缓存同步到磁盘(快),always：表示每次更新操作后手动调用fsync()将数据写到磁盘(慢，安全), everysec：表示每秒同步一次(折衷，默认值); 3.1 设置后端启动： 由于Redis默认是前端启动，必须保持在当前的窗口中，如果使用ctrl + c退出，那么Redis也就退出，不建议使用。\nvi /etc/redis.conf 修改Redis配置文件把旧值daemonize no 改为 新值daemonize yes\n3.2 设置访问： Redis默认只允许本机访问，可是有时候我们也需要 Redis 被远程访问。\nvi /etc/redis.conf 找到 bind 那行配置，默认是： # bind 127.0.0.1\n去掉#注释并改为： bind 0.0.0.0 此设置会变成允许所有远程访问。如果想指定限制访问，可设置对应的IP。\n3.3 配置REDIS日志记录： 找到logfile那行配置，默认是：logfile \u0026quot;\u0026quot;，改为logfile /var/log/redis_6379.log\n3.4 设置 REDIS 请求密码： vi /etc/redis.conf 找到默认是被注释的这一行：# requirepass foobared\n去掉注释，把 foobared 改为你想要设置的密码，比如我打算设置为：123456，所以我改为：requirepass \u0026quot;123456\u0026quot;\n修改之后重启下服务\n有了密码之后，进入客户端，就得这样访问：redis-cli -h 127.0.0.1 -p 6379 -a 123456\n4. Redis常用操作 4.1 启动 /usr/local/redis/bin/redis-server /etc/redis.conf 4.2 关闭 /usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 6379shutdown 4.3 查看是否启动 ps -ef | grep redis 4.4 进入客户端 redis-cli 4.5 关闭客户端 redis-clishutdown 4.6 设置开机自动启动配置 echo \u0026#34;/usr/local/redis/bin/redis-server /etc/redis.conf\u0026#34; \u0026gt;/etc/rc.local 4.7 开放防火墙端口 添加规则：iptables -I INPUT -p tcp -m tcp--dport 6379 -j ACCEPT 保存规则：service iptables save 重启 iptables：service iptables restart 5. 将Redis注册为系统服务 在/etc/init.d目录下添加Redis服务的启动，暂停和重启脚本：\nvi /etc/init.d/redis\n脚本内容如下： ```#!/bin/sh## redis - this script starts and stops the redis-server daemon## chkconfig: - 85 15# description: Redis is a persistent key-value database# processname: redis-server# config: /usr/local/redis/bin/redis-server# config: /etc/redis.conf# Source function library. . /etc/rc.d/init.d/functions # Source networking configuration. . /etc/sysconfig/network # Check that networking is up. [ \u0026#34;$NETWORKING\u0026#34; = \u0026#34;no\u0026#34; ] \u0026amp;amp;\u0026amp;amp; exit 0 redis=\u0026#34;/usr/local/redis/bin/redis-server\u0026#34; prog=$(basename $redis) REDIS_CONF_FILE=\u0026#34;/etc/redis.conf\u0026#34; [ -f /etc/sysconfig/redis ] \u0026amp;amp;\u0026amp;amp; . /etc/sysconfig/redis lockfile=/var/lock/subsys/redis \u0026lt;span class=\u0026#34;hljs-function\u0026#34;\u0026gt;\u0026lt;span class=\u0026#34;hljs-title\u0026#34;\u0026gt;start() { [ -x $redis ] || exit 5 [ -f $REDIS_CONF_FILE ] || exit 6 echo -n $\u0026#34;Starting $prog: \u0026#34; daemon $redis $REDIS_CONF_FILE retval=$? echo [ $retval -eq 0 ] \u0026amp;amp;\u0026amp;amp; touch $lockfile return $retval } \u0026lt;span class=\u0026#34;hljs-function\u0026#34;\u0026gt;\u0026lt;span class=\u0026#34;hljs-title\u0026#34;\u0026gt;stop() { echo -n $\u0026#34;Stopping $prog: \u0026#34; killproc $prog -QUIT retval=$? echo [ $retval -eq 0 ] \u0026amp;amp;\u0026amp;amp; rm -f $lockfile return $retval } \u0026lt;span class=\u0026#34;hljs-function\u0026#34;\u0026gt;\u0026lt;span class=\u0026#34;hljs-title\u0026#34;\u0026gt;restart() { stop start } \u0026lt;span class=\u0026#34;hljs-function\u0026#34;\u0026gt;\u0026lt;span class=\u0026#34;hljs-title\u0026#34;\u0026gt;reload() { echo -n $\u0026#34;Reloading $prog: \u0026#34; killproc $redis -HUP RETVAL=$? echo } \u0026lt;span class=\u0026#34;hljs-function\u0026#34;\u0026gt;\u0026lt;span class=\u0026#34;hljs-title\u0026#34;\u0026gt;force_reload() { restart } \u0026lt;span class=\u0026#34;hljs-function\u0026#34;\u0026gt;\u0026lt;span class=\u0026#34;hljs-title\u0026#34;\u0026gt;rh_status() { status $prog } \u0026lt;span class=\u0026#34;hljs-function\u0026#34;\u0026gt;\u0026lt;span class=\u0026#34;hljs-title\u0026#34;\u0026gt;rh_status_q() { rh_status \u0026gt;/dev/null 2\u0026gt;\u0026amp;amp;1 } case \u0026#34;$1\u0026#34;in start) rh_status_q \u0026amp;amp;\u0026amp;amp; exit 0 $1 ;; stop) rh_status_q || exit 0 $1 ;; restart|configtest) $1 ;; reload) rh_status_q || exit 7 $1 ;; force-reload) force_reload ;; status) rh_status ;; condrestart|try-restart) rh_status_q || exit 0 ;; *) echo $\u0026#34;Usage: $0 {start|stop|status|restart|condrestart|try-restart| reload|orce-reload}\u0026#34; exit 2 esac 赋予脚本权限\n启动、停止和重启：\nservice redis stop service redis restart 至此，Redis单机服务器已搭建完毕，下面我们看看主从架构如何搭建。\n搭建Redis主从架构### 1. redis-server说明 172.16.2.181:6379 从 ### **2. Redis主从架构配置** - 编辑从机的 `Redis` 配置文件，找到 210 行（大概），默认这一行应该是注释的： `# slaveof \u0026lt;masterip\u0026gt; \u0026lt;masterport\u0026gt;` - 我们需要去掉该注释，并且填写我们自己的主机的 IP 和 端口，比如：`slaveof 172.16.2.185 6379`，如果主机设置了密码，还需要找到`masterauth \u0026lt;master-password\u0026gt;`这一行，去掉注释，改为`masterauth 主机密码`。 - 配置完成后重启从机`Redis` 服务 - 重启完之后，进入主机的 `redis-cli` 状态下`redis-cli -h 127.0.0.1 -p 6379 -a 123456`，输入：`INFO replication` 可以查询到当前主机的 `Redis`处于什么角色，有哪些从机已经连上主机。 主机信息`172.16.2.185` ``` # Replication role:master connected_slaves:1 slave0:ip=172.16.2.181,port=6379,state=online,offset=28,lag=1 master_replid:625ae9f362643da5337835beaeabfdca426198c7 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:28 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:28 ``` 从机信息`172.16.2.181` ``` # Replication role:slave master_host:172.16.2.185 master_port:6379 master_link_status:up master_last_io_seconds_ago:3 master_sync_in_progress:0 slave_repl_offset:210 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:625ae9f362643da5337835beaeabfdca426198c7 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:210 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:210 ``` - 此时已经完成了主从配置，我们可以测试下： 我们进入主机的 `redis-cli` 状态，然后 `set` 某个值，比如：`set myblog YouMeek.com` - 我们切换进入从机的 `redis-cli` 的状态下，获取刚刚设置的值看是否存在：`get myblog`，此时，我们可以发现是可以获取到值的。 ### **3. Redis主从架构总结** - 需要注意的是：从库不具备写入数据能力，不然会报错。 从库只有只读能力。 - 主从架构的优点：除了减少主库连接的压力，还有可以关掉主库的持久化功能，把持久化的功能交给从库进行处理。 - 第一个从库配置的信息是连上主库，后面的第二个从库配置的连接信息是连上第一个从库， 假如还有第三个从库的话，我们可以把第三个从库的配置信息连上第二个从库上，以此类推。 # **Redis Sentinel高可用架构搭建** ### **1. 自动故障转移** - 虽然使用主从架构配置`Redis`做了备份，看上去很完美。但由于`Redis`目前只支持主从复制备份（不支持主主复制），当主`Redis`挂了，从`Redis`只能提供读服务，无法提供写服务。所以，还得想办法，当主`Redis`挂了，让从`Redis`升级成为主`Redis`。 - 这就需要自动故障转移，`Redis Sentinel`带有这个功能，当一个主`Redis`不能提供服务时，`Redis Sentinel`可以将一个从`Redis`升级为主`Redis`，并对其他从`Redis`进行配置，让它们使用新的主`Redis`进行复制备份。 ![Redis Sentinel架构图- 图片来自于CSDN 在Redis Sentinel环境下，jedis该如何配置](https://user-gold-cdn.xitu.io/2017/12/7/160305c2382f7401?imageView2/0/w/1280/h/960/ignore-error/1)\u0026#34; class=\u0026#34;inited\u0026#34;\u0026gt; 注意：搭建`Redis Sentinel`推荐至少3台服务器，但由于楼主偷懒，下面用例只用了2台服务器。 `Redis Sentinel`的主要功能如下： 1. 监控：哨兵不断的检查`master`和`slave`是否正常的运行。 2. 通知：当监控的某台`Redis`实例发生问题时，可以通过`API`通知系统管理员和其他的应用程序。 3. 自动故障转移：如果一个`master`不正常运行了，哨兵可以启动一个故障转移进程，将一个`slave`升级成为`master`，其他的`slave`被重新配置使用新的`master`，并且应用程序使用`Redis`服务端通知的新地址。 4. 配置提供者：哨兵作为`Redis`客户端发现的权威来源：客户端连接到哨兵请求当前可靠的`master`的地址。如果发生故障，哨兵将报告新地址。 默认情况下，每个`Sentinel`节点会以每秒一次的频率对`Redis`节点和其它的`Sentinel`节点发送`PING`命令，并通过节点的回复来判断节点是否在线。 如果在`down-after-millisecondes`毫秒内，没有收到有效的回复，则会判定该节点为主观下线。 如果该节点为`master`，则该`Sentinel`节点会通过`sentinel is-master-down-by-addr`命令向其它`sentinel`节点询问对该节点的判断，如果超过`\u0026lt;quorum\u0026gt;`个数的节点判定`master`不可达，则该`sentinel`节点会将`master`判断为客观下线。 这个时候，各个`Sentinel`会进行协商，选举出一个领头`Sentinel`，由该领头`Sentinel`对`master`节点进行故障转移操作。 故障转移包含如下三个操作： 5. 在所有的`slave`服务器中，挑选出一个`slave`，并将其转换为`master`。 6. 让其它`slave`服务器，改为复制新的`master`。 7. 将旧`master`设置为新`master`的`slave`，这样，当旧的`master`重新上线时，它会成为新`master`的`slave`。 ### **2. 搭建Redis Sentinel高可用架构** 这里使用两台服务器，每台服务器上开启一个`redis-server`和`redis-sentinel`服务。 redis-server说明 ``` 172.16.2.185:6379 主 172.16.2.181:6379 从 ``` redis-sentinel说明 ``` 172.16.2.185:26379 172.16.2.181:26379 ``` ### **2.1 建立REDIS配置文件** 如果要做自动故障转移，则建议所有的`redis.conf`都设置`masterauth`，因为自动故障只会重写主从关系，即`slaveof`，不会自动写入`masterauth`。如果`Redis`原本没有设置密码，则可以忽略。 `Redis`程序上面已经安装过了，我们只需增加`redis-sentinel`的相关配置即可，将 `redis-sentinel`的配置文件拷贝到系统配置目录`/etc/`下，`sentinel.conf` 是 `redis-sentinel`的配置文件，`sentinel.conf` 在 `Redis` 源码目录。 ``` cp /usr/local/redis-4.0.2/sentinel.conf /etc/ ``` 修改`sentinel.conf`配置文件内容如下： ``` vi /etc/sentinel.conf ``` ``` protected-mode no sentinel monitor mymaster 172.16.2.185 6379 2 # redis在搭建时设置了密码，所以要进行密码配置 sentinel auth-pass mymaster “123456“ #5秒内mymaster没有响应，就认为SDOWN sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 15000 ``` 在配置最后加上 ``` logfile /var/log/sentinel.log pidfile /var/run/sentinel.pid daemonize yes ``` 配置文件说明： 1.port :当前Sentinel服务运行的端口 2.dir : Sentinel服务运行时使用的临时文件夹 3.sentinel monitor master001 192.168.110.10163792:Sentinel去监视一个名为master001的主redis实例，这个主实例的IP地址为本机地址192.168.110.101，端口号为6379，而将这个主实例判断为失效至少需要2个 Sentinel进程的同意，只要同意Sentinel的数量不达标，自动failover就不会执行 4.sentinel down-after-milliseconds master001 30000:指定了Sentinel认为Redis实例已经失效所需的毫秒数。当实例超过该时间没有返回PING，或者直接返回错误，那么Sentinel将这个实例标记为主观下线。只有一个 Sentinel进程将实例标记为主观下线并不一定会引起实例的自动故障迁移：只有在足够数量的Sentinel都将一个实例标记为主观下线之后，实例才会被标记为客观下线，这时自动故障迁移才会执行 5.sentinel parallel-syncs master001 1：指定了在执行故障转移时，最多可以有多少个从Redis实例在同步新的主实例，在从Redis实例较多的情况下这个数字越小，同步的时间越长，完成故障转移所需的时间就越长 6.sentinel failover-timeout master001 180000：如果在该时间（ms）内未能完成failover操作，则认为该failover失败 7.sentinel notification-script ：指定sentinel检测到该监控的redis实例指向的实例异常时，调用的报警脚本。该配置项可选，但是很常用 ### **2.2 开放防火墙端口** ``` 添加规则：iptables -I INPUT -p tcp -m tcp--dport 26379 -j ACCEPT 保存规则：service iptables save 重启iptables：service iptables restart ``` ### **2.3 启动REDIS-SENTINEL** ``` redis-sentinel /etc/sentinel.conf ``` 在任意一台机子均可查看到相关服务信息 ``` redis-cli -h 127.0.0.1 -p 26379 INFO sentinel ``` ``` # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=172.16.2.185:6379,slaves=1,sentinels=2 ``` ### **3. 自动故障转移测试** ### **3.1 停止主REDIS** ``` redis-cli -h 172.16.2.185 -p 6379 -a 123456 shutdown ``` ### **3.2 查看REDIS-SENTINEL的监控状态** ``` # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=172.16.2.181:6379,slaves=1,sentinels=2 ``` 发现从库提升为主库。 ### **3.3 注意事项** - 如果停掉`master`后，`Sentinel`显示足够数量的`sdown`后，没有出现`odown`或`try-failover`，则检查密码等配置是否正确 - 如果停掉`master`后，试图切换的时候，发现日志出现 `failover-abort-not-elected`，则分2种情况分别解决： 1. 如果`Redis`实例没有配置 ``` protected-mode yes bind 172.16.2.185 ``` 则在`Sentinel` 配置文件加上`protected-mode no`即可 2. 如果`Redis`实例有配置 ``` protected-mode yes bind 172.16.2.185 ``` 则在`Sentinel`配置文件加上 ``` protected-mode yes bind 172.16.2.185 ``` 至此，redis的高可用方案已经搭建完成。 # **VIP对外提供虚拟IP实现高可用** ### **1. 现有情况概述** 客户端程序（如JAVA程序）连接`Redis`时需要`ip`和`port`，但`redis-server`进行故障转移时，主`Redis`是变化的，所以`ip`地址也是变化的。客户端程序如何感知当前主`Redis`的`ip`地址和端口呢？`redis-sentinel`提供了接口，请求任何一个`Sentinel`，发送`SENTINEL get-master-addr-by-name \u0026lt;master name\u0026gt;`就能得到当前主`Redis`的`ip`和`port`。 客户端每次连接`Redis`前，先向`sentinel`发送请求，获得主`Redis`的`ip`和`port`，然后用返回的`ip`和`port`连接`Redis`。 这种方法的缺点是显而易见的，每次操作`Redis`至少需要发送两次连接请求，第一次请求`Sentinel`，第二次请求`Redis`。 更好的办法是使用`VIP`，当然这对配置的环境有一定的要求，比如`Redis`搭建在阿里云服务器上，可能不支持`VIP`。 `VIP`方案是，`Redis`系统对外始终是同一ip地址，当`Redis`进行故障转移时，需要做的是将`VIP`从之前的`Redis`服务器漂移到现在新的主`Redis`服务器上。 比如：当前`Redis`系统中主`Redis`的`ip`地址是`172.16.2.185`，那么`VIP(172.16.2.250)`指向`172.16.2.185`，客户端程序用`VIP(172.16.2.250)`地址连接`Redis`，实际上连接的就是当前主`Redis`，这样就避免了向`Sentinel`发送请求。 当主`Redis`宕机，进行故障转移时，`172.16.2.181`这台服务器上的`Redis`提升为主，这时`VIP（172.16.2.250）`指向`172.16.2.181`，这样客户端程序不需要修改任何代码，连接的是`172.16.2.181`这台主`Redis`。 ### **2.漂移VIP实现Redis故障转移** 那么现在的问题是，如何在进行`Redis`故障转移时，将`VIP`漂移到新的主`Redis`服务器上。 这里可以使用`Redis Sentinel`的一个参数`client-reconfig-script`，这个参数配置执行脚本，`Sentinel`在做`failover`的时候会执行这个脚本，并且传递6个参数`\u0026lt;master-name\u0026gt;、 \u0026lt;role\u0026gt;、 \u0026lt;state\u0026gt;、 \u0026lt;from-ip\u0026gt;、 \u0026lt;from-port\u0026gt;、 \u0026lt;to-ip\u0026gt;、\u0026lt;to-port\u0026gt;`，其中`\u0026lt;to-ip\u0026gt;`是新主`Redis`的`IP`地址，可以在这个脚本里做`VIP`漂移操作。 ``` sentinel client-reconfig-script mymaster /opt/notify_mymaster.sh ``` 修改两个服务器的`redis-sentinel`配置文件`/etc/sentinel.conf`，增加上面一行。然后在`/opt/`目录下创建`notify_mymaster.sh`脚本文件，这个脚本做`VIP`漂移操作，内容如下: ``` vi /opt/notify_mymaster.sh ``` ``` #!/bin/bashecho \u0026#34;File Name: $0\u0026#34; echo \u0026#34;Quoted Values: $@\u0026#34; echo \u0026#34;Quoted Values: $*\u0026#34; echo \u0026#34;Total Number of Parameters : $#\u0026#34; MASTER_IP=${6}#第六个参数是新主redis的ip地址 LOCAL_IP=\u0026#39;172.16.2.185\u0026#39;#当前服务器IP，主机172.16.2.185，从机172.16.2.181 VIP=\u0026#39;172.16.2.250\u0026#39; NETMASK=\u0026#39;24\u0026#39; INTERFACE=\u0026#39;eth1\u0026#39; if [ ${MASTER_IP} = ${LOCAL_IP} ];then sudo /sbin/ip addr add ${VIP}/${NETMASK} dev ${INTERFACE}#将VIP绑定到该服务器上 sudo /sbin/arping -q -c 3 -A ${VIP} -I ${INTERFACE} exit 0 else sudo /sbin/ip addr del ${VIP}/${NETMASK} dev ${INTERFACE}#将VIP从该服务器上删除exit 0 fiexit 1#如果返回1，sentinel会一直执行这个脚本 ``` 赋予脚本权限 ``` chmod 755 /opt/notify_mymaster.sh ``` 现在当前主`Redis`是`172.16.2.185`，需要手动绑定`VIP`到该服务器上。 ``` /sbin/ip addr add 172.16.2.250/24 dev eth1 /sbin/arping -q -c 3 -A 172.16.2.250 -I eth1 ``` 由于VIP只能绑定只有一台机子，所以建议将改为`bind 0.0.0.0`添加至`redis.conf` 中 ``` vi /etc/redis.conf ``` 设置`bind 0.0.0.0` 由于VIP只能绑定只有一台机子，所以建议将改为`bind 0.0.0.0`添加至`sentinel.conf`中 ``` vi /etc/sentinel.conf ``` 设置`bind 0.0.0.0` 重启`Redis` ``` service redis restart` ``` 重启`Sentinel` ``` redis-sentinel /etc/sentinel.conf ``` 随后我们在另一台机器`172.16.2.181`上，通过`VIP`访问主机 ``` redis-cli -h 172.16.2.250 -p 6379 -a 123456 INFO replication ``` 可正常通讯，信息如下： ``` # Replication role:master connected_slaves:1 slave0:ip=172.16.2.181,port=6379,state=online,offset=0,lag=0 master_replid:325b0bccab611d329d9c2cd2c35a1fe3c01ae196 master_replid2:c1f7a7d17d2c35575a34b00eb10c8abf32df2243 master_repl_offset:22246293 second_repl_offset:22241024 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:22237293 repl_backlog_histlen:9001 ``` 访问主机的`Sentinel` ``` redis-cli -h 172.16.2.250 -p 26379 INFO sentinel ``` 可正常通讯，信息如下： ``` # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=172.16.2.185:6379,slaves=1,sentinels=3 ``` 下面关闭主机的`Redis`服务，看看VIP是否漂移到另一台服务器上。 ``` redis-cli -h 172.16.2.185 -p 6379 -a 123456 shutdown ``` 查看是否已进行切换 ``` redis-cli -h 172.16.2.250 -p 26379 INFO sentinel ``` ``` # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=172.16.2.181:6379,slaves=1,sentinels=3 ``` 通过查询`Sentinel`发现从机`172.16.2.181`提升为主。 通过访问`VIP`的方式连接`Redis` ``` redis-cli -h 172.16.2.250 -p 6379 -a 123456 INFO replication ``` ``` # Replicationrole:master connected_slaves:0 master_replid:cab30a4083f35652053ffcd099d70b9aaf7a80f3 master_replid2:3da856dd33cce4bedd54926df6797b410f1ab9e8 master_repl_offset:74657 second_repl_offset:36065 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:74657 ``` 从上面信息可知，`VIP`已经飘移成功。可喜可贺，大吉大利，晚上吃鸡。 # **总结** 至此，高可用`Redis`缓存服务已搭建完毕，迟点会再出一篇文章教大家如何通过`JAVA`连接`Redis`进行相关操作。至于`Redis Cluster`集群方案，等有空再搭建然后再和大家一同分享。 # **参考文章** [搭建一个redis高可用系统](https://link.juejin.im/?target=http%3A%2F%2Fwww.jianshu.com%2Fp%2Fc2ab606b00b7) [Redis 安装和配置](https://link.juejin.im/?target=https%3A%2F%2Fgithub.com%2Fjudasn%2FLinux-Tutorial%2Fblob%2Fmaster%2FRedis-Install-And-Settings.md) [Redis 复制、Sentinel的搭建和原理说明](https://link.juejin.im/?target=http%3A%2F%2Fwww.cnblogs.com%2Fzhoujinyi%2Fp%2F5570024.html) [Redis 快速入门(官网翻译)](https://link.juejin.im/?target=http%3A%2F%2Fwww.jianshu.com%2Fp%2F237b628beaae) [Redis Sentinel机制与用法（一）](https://link.juejin.im/?target=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000002680804) [Redis哨兵-实现Redis高可用](https://link.juejin.im/?target=http%3A%2F%2Fredis.majunwei.com%2Ftopics%2Fsentinel.html) [读懂Redis并配置主从集群及高可用部署](https://link.juejin.im/?target=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzIxNTYzOTQ0Ng%3D%3D%26amp%3Bmid%3D2247483668%26amp%3Bidx%3D1%26amp%3Bsn%3Dcd31574877d38cf7ff9c047b86c9bf23%26amp%3Bchksm%3D979475eda0e3fcfb6b5006bcd19c5a838eca9e369252847dbdf97820bf418201dd75c1dadda3%26amp%3Bmpshare%3D1%26amp%3Bscene%3D23%26amp%3Bsrcid%3D0117KUiiITwi2ETRan16xRVg%23rd) [在Redis Sentinel环境下，jedis该如何配置](https://link.juejin.im/?target=http%3A%2F%2Fwww.cnblogs.com%2Fivictor%2Fp%2F6720481.html) [redis sentinel 主从切换(failover)解决方案，详细配置](https://link.juejin.im/?target=http%3A%2F%2Fblog.csdn.net%2Fpi9nc%2Farticle%2Fdetails%2F17735653) [Redis-3.2.1主从故障测试实例](https://link.juejin.im/?target=http%3A%2F%2Fblog.csdn.net%2Fyypzye%2Farticle%2Fdetails%2F52281282) ","permalink":"https://blog.jimersylee.com/posts/redis%E9%AB%98%E5%8F%AF%E7%94%A8%E6%9E%B6%E6%9E%84/","summary":"\u003ch1 id=\"redis高可用架构\"\u003e\u003cstrong\u003eRedis高可用架构\u003c/strong\u003e\u003c/h1\u003e\n\u003ch1 id=\"前言\"\u003e\u003cstrong\u003e前言\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003eRedis\u003c/code\u003e是一个高性能的\u003ccode\u003ekey-value\u003c/code\u003e数据库，现时越来越多企业与应用使用\u003ccode\u003eRedis\u003c/code\u003e作为缓存服务器。楼主是一枚\u003ccode\u003eJAVA\u003c/code\u003e后端程序员，也算是半个运维工程师了。在\u003ccode\u003eLinux\u003c/code\u003e服务器上搭建\u003ccode\u003eRedis\u003c/code\u003e，怎么可以不会呢？下面楼主就带着大家从0开始，依次搭建：\u003ccode\u003eRedis\u003c/code\u003e单机服务器 -\u0026gt; \u003ccode\u003eRedis\u003c/code\u003e主从复制 -\u0026gt;\u003ccode\u003eRedis-Sentinel高可用\u003c/code\u003e。逐步搭建出高可用的Redis缓存服务器。\u003c/p\u003e\n\u003ch1 id=\"搭建redis\"\u003e\u003cstrong\u003e搭建Redis\u003c/strong\u003e\u003c/h1\u003e\n\u003ch3 id=\"1-下载并解压\"\u003e\u003cstrong\u003e1. 下载并解压\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e首先从\u003ccode\u003eRedis\u003c/code\u003e官网下载\u003ccode\u003eRedis\u003c/code\u003e并解压，楼主使用的版本是4.0.2。依次执行如下命令：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ecd /opt\nwget http://download.redis.io/releases/redis-4.0.2.tar.gz\ntar -zcvf redis-4.0.2.tar.gz\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e如果没有安装\u003ccode\u003egcc\u003c/code\u003e依赖包，则安装对应依赖包\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eyum install -y gcc-c++ tcl\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"2-编译并安装\"\u003e\u003cstrong\u003e2. 编译并安装\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e下载并解压完毕后，则对源码包进行编译安装，楼主的\u003ccode\u003eRedis\u003c/code\u003e安装路径为\u003ccode\u003e/usr/local/redis\u003c/code\u003e，同学们可以自行修改语句：\u003ccode\u003emake install PREFIX=你想要安装的路径\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ecd /opt/redis-4.0.2\nmake install PREFIX=/usr/local\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e复制\u003ccode\u003eRedis\u003c/code\u003e相关命令到\u003ccode\u003e/usr/sbin\u003c/code\u003e目录下，这样就可以直接执行这些命令，不用写全路径\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ecd /usr/local/redis/bin\nsudo cp redis-* /usr/sbin\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"3-建立redis配置文件\"\u003e\u003cstrong\u003e3. 建立Redis配置文件\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e安装完成之后将 \u003ccode\u003eRedis\u003c/code\u003e 配置文件拷贝到系统配置目录\u003ccode\u003e/etc/\u003c/code\u003e下，\u003ccode\u003eredis.conf\u003c/code\u003e 是 \u003ccode\u003eRedis\u003c/code\u003e 的配置文件，\u003ccode\u003eredis.conf\u003c/code\u003e 在 \u003ccode\u003eRedis\u003c/code\u003e 源码目录，\u003ccode\u003eport\u003c/code\u003e默认 6379。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ecp /usr/local/redis-4.0.2/redis.conf  /etc/\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003ccode\u003eRedis\u003c/code\u003e配置文件主要参数解析参考\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edaemonize  no#redis进程是否以守护进程的方式运行,yes为是,no为否(不以守护进程的方式运行会占用一个终端)\n    pidfile /var/run/redis.pid#指定redis进程的PID文件存放位置\n    port 6379#redis进程的端口号\n    bind 127.0.0.1#绑定的主机地址\n    timeout  300#客户端闲置多长时间后关闭连接,默认此参数为0即关闭此功能\n    loglevel verbose#redis日志级别,可用的级别有debug.verbose.notice.warning\n    logfile stdout#log文件输出位置,如果进程以守护进程的方式运行,此处又将输出文件设置为stdout的话,就会将日志信息输出到/dev/null里面去了\n    databases 16#设置数据库的数量,默认为0可以使用select \u0026lt;dbid\u0026gt;命令在连接上指定数据库id\n    save \u0026lt;seconds\u0026gt;\u0026lt;changes\u0026gt;#指定在多少时间内刷新次数达到多少的时候会将数据同步到数据文件;\n    rdbcompression yes#指定存储至本地数据库时是否压缩文件,默认为yes即启用存储;\n    dbfilename dump.db#指定本地数据库文件名\n    dir ./#指定本地数据问就按存放位置;\n    slaveof \u0026lt;masterip\u0026gt;\u0026lt;masterport\u0026gt;#指定当本机为slave服务时,设置master服务的IP地址及端口,在redis启动的时候他会自动跟master进行数据同步\n    masterauth \u0026lt;master-password\u0026gt;#当master设置了密码保护时,slave服务连接master的密码;\n    requirepass footbared#设置redis连接密码,如果配置了连接密码,客户端在连接redis是需要通过AUTH\u0026lt;password\u0026gt;命令提供密码,默认关闭\n    maxclients 128#设置同一时间最大客户连接数,默认无限制;redis可以同时连接的客户端数为redis程序可以打开的最大文件描述符,如果设置 maxclients 0，表示不作限制。当客户端连接数到达限制时，Redis会关闭新的连接并向客户端返回max number of clients reached错误信息\n    maxmemory\u0026lt;bytes\u0026gt;#指定Redis最大内存限制，Redis在启动时会把数据加载到内存中，达到最大内存后，Redis会先尝试清除已到期或即将到期的Key，当此方法处理 后，仍然到达最大内存设置，将无法再进行写入操作，但仍然可以进行读取操作。Redis新的vm机制，会把Key存放内存，Value会存放在swap区\n    appendonly no#指定是否在每次更新操作后进行日志记录，Redis在默认情况下是异步的把数据写入磁盘，如果不开启，可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的，所以有的数据会在一段时间内只存在于内存中。默认为no\n    appendfilename appendonly.aof#指定跟新日志文件名默认为appendonly.aof\n    appendfsync everysec#指定更新日志的条件,有三个可选参数no：表示等操作系统进行数据缓存同步到磁盘(快),always：表示每次更新操作后手动调用fsync()将数据写到磁盘(慢，安全), everysec：表示每秒同步一次(折衷，默认值);\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"31-设置后端启动\"\u003e\u003cstrong\u003e3.1 设置后端启动：\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e由于\u003ccode\u003eRedis\u003c/code\u003e默认是前端启动，必须保持在当前的窗口中，如果使用\u003ccode\u003ectrl + c\u003c/code\u003e退出，那么\u003ccode\u003eRedis\u003c/code\u003e也就退出，不建议使用。\u003c/p\u003e","title":"Redis高可用架构"},{"content":"我眼中规范的开发流程 基于自建SVN的信息存储 存放开发文档 存放测试用例 存放三方对接文档 存放同事的学习记录与分享 存放会议纪要 基于git的开发流程 代码全部托管于git服务上 完善的分支规范,区分开发,测试,生产分支 基于gitflow的开发流程,区分feature,bugfix,hotfix等分支创建规则 完善的测试流程 产品出文档测试即开始编写测试用例 测试版本开发完成开始测试 使用bugout进行记录与跟踪bug 开发处理bug,在bugout上提交 测试再次测试直到通过 基于持续构建工具的构建流程 使用jenkin持续构建,单元测试,Sonar代码分析 每个项目至少3个分支,dev,master,release分支 测试在dev分支,预发布在master分支,线上使用release分支 dev分支自动提交构建 平台架构 分布式的微服务架构 保证单机宕机对整体服务没有影响,做到自动切换主备 统一开发环境与工具 后端开发统一Jetbrains全家桶 接口调试使用Postman 文档使用showdoc 代码管理使用git 统一使用邮件沟通 致谢：\n","permalink":"https://blog.jimersylee.com/posts/%E6%88%91%E7%9C%BC%E4%B8%AD%E8%A7%84%E8%8C%83%E7%9A%84%E5%BC%80%E5%8F%91%E6%B5%81%E7%A8%8B/","summary":"\u003ch1 id=\"我眼中规范的开发流程\"\u003e我眼中规范的开发流程\u003c/h1\u003e\n\u003ch1 id=\"基于自建svn的信息存储\"\u003e\u003cstrong\u003e基于自建SVN的信息存储\u003c/strong\u003e\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e存放开发文档\u003c/li\u003e\n\u003cli\u003e存放测试用例\u003c/li\u003e\n\u003cli\u003e存放三方对接文档\u003c/li\u003e\n\u003cli\u003e存放同事的学习记录与分享\u003c/li\u003e\n\u003cli\u003e存放会议纪要\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"基于git的开发流程\"\u003e\u003cstrong\u003e基于git的开发流程\u003c/strong\u003e\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e代码全部托管于git服务上\u003c/li\u003e\n\u003cli\u003e完善的分支规范,区分开发,测试,生产分支\u003c/li\u003e\n\u003cli\u003e基于gitflow的开发流程,区分feature,bugfix,hotfix等分支创建规则\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"完善的测试流程\"\u003e\u003cstrong\u003e完善的测试流程\u003c/strong\u003e\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e产品出文档测试即开始编写测试用例\u003c/li\u003e\n\u003cli\u003e测试版本开发完成开始测试\u003c/li\u003e\n\u003cli\u003e使用bugout进行记录与跟踪bug\u003c/li\u003e\n\u003cli\u003e开发处理bug,在bugout上提交\u003c/li\u003e\n\u003cli\u003e测试再次测试直到通过\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"基于持续构建工具的构建流程\"\u003e\u003cstrong\u003e基于持续构建工具的构建流程\u003c/strong\u003e\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e使用jenkin持续构建,单元测试,Sonar代码分析\u003c/li\u003e\n\u003cli\u003e每个项目至少3个分支,dev,master,release分支\u003c/li\u003e\n\u003cli\u003e测试在dev分支,预发布在master分支,线上使用release分支\u003c/li\u003e\n\u003cli\u003edev分支自动提交构建\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"平台架构\"\u003e\u003cstrong\u003e平台架构\u003c/strong\u003e\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e分布式的微服务架构\u003c/li\u003e\n\u003cli\u003e保证单机宕机对整体服务没有影响,做到自动切换主备\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"统一开发环境与工具\"\u003e\u003cstrong\u003e统一开发环境与工具\u003c/strong\u003e\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e后端开发统一Jetbrains全家桶\u003c/li\u003e\n\u003cli\u003e接口调试使用Postman\u003c/li\u003e\n\u003cli\u003e文档使用showdoc\u003c/li\u003e\n\u003cli\u003e代码管理使用git\u003c/li\u003e\n\u003cli\u003e统一使用邮件沟通\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e致谢：\u003c/p\u003e\n\u003c!-- raw HTML omitted --\u003e\n\u003c!-- raw HTML omitted --\u003e","title":"我眼中规范的开发流程"},{"content":"Spring Cloud 之actuator模块简介 此模块提供监控与采集功能,通过在pom中引用此依赖,可以方便地使用其功能\n\u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-boot-starter-actuator\u0026lt;/artifactId\u0026gt; \u0026lt;/dependency\u0026gt; 原生端点 应用配置类:获取应用程序中加载的应用配置,环境变量,自动化配置报告等与SpringBoot应用密切相关的配置类信息 度量指标类:获取应用程序运行过程中用户监控的度量指标,比如内存信息,线程池信息,HTTP请求统计等 操作控制类:提供了对应用的关闭操作类功能 应用配置类 /autoconfig:该端点用来获取应用的自动化配置报告.包括条件匹配成功以及不成功的配置\npositiveMatches中返回是条件匹配成功的自动化配置 negativeMatches中返回的是条件匹配不成功的自动化配置 /beans:该端点用来获取应用上下文中创建的所有Bean\n[ { \u0026#34;context\u0026#34;:\u0026#34;application\u0026#34;, \u0026#34;parent\u0026#34;:null, \u0026#34;beans\u0026#34;:[ { \u0026#34;bean\u0026#34;:\u0026#34;helloApplication\u0026#34;, \u0026#34;aliases\u0026#34;:[ ], \u0026#34;scope\u0026#34;:\u0026#34;singleton\u0026#34;, \u0026#34;type\u0026#34;:\u0026#34;com.jimersylee.hello.HelloApplication$$EnhancerBySpringCGLIB$$d3ebe421\u0026#34;, \u0026#34;resource\u0026#34;:\u0026#34;null\u0026#34;, \u0026#34;dependencies\u0026#34;:[ ] }, 每个Bean中都包含了下面这些信息\nbean:Bean的名称 scope:Bean的作用域 type:Bean的Java类型 resource:class文件的具体路径 dependencies:依赖的Bean名称 /configprops:该端点用来获取应用中配置的属性信息报告,可以通过使用endpoints.configprops.enabled=false来关闭\nenv:该端点与/configprops不同,它用来获取应用所有可用的环境属性报告\n{ \u0026#34;profiles\u0026#34;:[ ], \u0026#34;server.ports\u0026#34;:{ \u0026#34;local.server.port\u0026#34;:8080 }, \u0026#34;servletContextInitParams\u0026#34;:{ }, \u0026#34;systemProperties\u0026#34;:{ \u0026#34;java.runtime.name\u0026#34;:\u0026#34;Java(TM) SE Runtime Environment\u0026#34;, \u0026#34;awt.useSystemAAFontSettings\u0026#34;:\u0026#34;gasp\u0026#34;, \u0026#34;sun.boot.library.path\u0026#34;:\u0026#34;/opt/jdk1.8.0_144/jre/lib/amd64\u0026#34;, \u0026#34;java.vm.version\u0026#34;:\u0026#34;25.144-b01\u0026#34;, \u0026#34;maven.multiModuleProjectDirectory\u0026#34;:\u0026#34;/home/jimersylee/projects/java/spring-cloud-action/hello\u0026#34;, ... /mappings:该端点用来返回所有Spring MVC的控制器映射关系报告.\n{ \u0026#34;/webjars/**\u0026#34;:{ \u0026#34;bean\u0026#34;:\u0026#34;resourceHandlerMapping\u0026#34; }, \u0026#34;/**\u0026#34;:{ \u0026#34;bean\u0026#34;:\u0026#34;resourceHandlerMapping\u0026#34; }, \u0026#34;/**/favicon.ico\u0026#34;:{ \u0026#34;bean\u0026#34;:\u0026#34;faviconHandlerMapping\u0026#34; }, \u0026#34;{[/hello]}\u0026#34;:{ \u0026#34;bean\u0026#34;:\u0026#34;requestMappingHandlerMapping\u0026#34;, \u0026#34;method\u0026#34;:\u0026#34;public java.lang.String com.jimersylee.hello.web.HelloController.index()\u0026#34; }, \u0026#34;{[/error],produces=[text/html]}\u0026#34;:{ \u0026#34;bean\u0026#34;:\u0026#34;requestMappingHandlerMapping\u0026#34;, \u0026#34;method\u0026#34;:\u0026#34;public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)\u0026#34; }, . . . /info:该端点用来返回一些应用自定义的信息.我们可以在application.properties中通过info前缀来设置一些属性,比如:\ninfo.app.name=spring-boot-hello info.app.version=v1.0.0 再访问/info.获得下面的报告\n{ \u0026#34;app\u0026#34;:{ \u0026#34;name\u0026#34;:\u0026#34;spring-boot-hello\u0026#34;, \u0026#34;version\u0026#34;:\u0026#34;v1.0.0\u0026#34; } } 度量指标类 /metrics:该端点用来返回当前应用的各类重要度量指标,比如内存信息,线程信息,垃圾回收信息等\n{ \u0026#34;mem\u0026#34;:565018, \u0026#34;mem.free\u0026#34;:427032, \u0026#34;processors\u0026#34;:4, \u0026#34;instance.uptime\u0026#34;:1276647, \u0026#34;uptime\u0026#34;:1287345, \u0026#34;systemload.average\u0026#34;:1.98, \u0026#34;heap.committed\u0026#34;:496128, \u0026#34;heap.init\u0026#34;:176128, \u0026#34;heap.used\u0026#34;:69095, \u0026#34;heap\u0026#34;:2494464, \u0026#34;nonheap.committed\u0026#34;:70608, \u0026#34;nonheap.init\u0026#34;:2496, \u0026#34;nonheap.used\u0026#34;:68891, \u0026#34;nonheap\u0026#34;:0, ... 系统信息:处理器processors,运行时间uptime和instance.uptime,系统平均负载,systemload.average mem.*:内存概要信息 heap.*:堆内存使用情况. nonheap.*:非堆内存使用情况. threads.*:线程使用情况,包括线程数,守护线程数(daemon),线程峰值(peak) classes.*:应用加载和卸载的类统计. gc.*:垃圾收集器的详细信息.包括垃圾回收次数gc.ps_scavenge.count,垃圾回收消耗时间gc.ps_scavenge.time,标记-清除算法的次数gc.ps_marksweep.count,标记-清除算法的消耗时间,gc.ps_marksweep.time httpsessions.*:Tomcat容器的会话使用情况. gauge.*:HTTP请求的性能指标之一,如gauge.response.hello: 5,它表示上一次hello请求的延迟时间为5毫秒 counter.*:HTTP请求的性能指标之一,,它主要作为计数器来使用.如counter.status.200.hello:11代表hello请求返回的200状态的次数为11 我们还可以通过/metrics/{name}接口来更细粒度地获取度量信息,如果可以通过访问/metrics/mem.free来获取当前可用内存数量\n/health:获取健康指标信息. 有时候我们还会用到Spring Boot封装的产品进行开发,比如RocketMQ.我们需要实现org.springframework.boot.actuate.health.HealthIndicator接口来实现一个对RocketMQ的检测器类\n@Component publicclassRocketMQHealthIndicatorimplementsHealthIndicator { @Override public Healthhealth() { int errorCode=this._check(); if(errorCode!=0){ return Health.down().withDetail(\u0026#34;Error Code\u0026#34;,errorCode).build(); } return Health.up().build(); } privateint_check() { //对监控对象的检测操作return 0; } } 返回结果\n{ \u0026#34;status\u0026#34;: \u0026#34;UP\u0026#34;, \u0026#34;rocketMQ\u0026#34;: { \u0026#34;status\u0026#34;: \u0026#34;UP\u0026#34; }, \u0026#34;diskSpace\u0026#34;: { \u0026#34;status\u0026#34;: \u0026#34;UP\u0026#34;, \u0026#34;total\u0026#34;: 125484777472, \u0026#34;free\u0026#34;: 65173798912, \u0026#34;threshold\u0026#34;: 10485760 } } /dump:该端点用来暴露程序运行中的线程信息\n/trace:该端点用来返回基本的HTTP跟踪信息,始终保留最近的100条请求记录\n操作类控制器 可以进行关键操作,需要通过属性来配置开启操作.在原生端点中,只提供了一个用来关闭应用的端点:/shutdown(后续引入Eureka之后,会引入更多控制端点).我们可以通过配置开启它\nendpoint.shutdown.enable=true 由于开放关闭应用本身是一件非常危险的事,需要对其加入一定的保护机制,比如定制actuator的端口路径,整合Spring Security进行安全校验.\n","permalink":"https://blog.jimersylee.com/posts/spring-cloud-%E4%B9%8Bactuator%E6%A8%A1%E5%9D%97%E7%AE%80%E4%BB%8B/","summary":"\u003ch1 id=\"spring-cloud-之actuator模块简介\"\u003eSpring Cloud 之actuator模块简介\u003c/h1\u003e\n\u003cp\u003e此模块提供监控与采集功能,通过在pom中引用此依赖,可以方便地使用其功能\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\u0026lt;dependency\u0026gt;\n            \u0026lt;groupId\u0026gt;org.springframework.boot\u0026lt;/groupId\u0026gt;\n            \u0026lt;artifactId\u0026gt;spring-boot-starter-actuator\u0026lt;/artifactId\u0026gt;\n\u0026lt;/dependency\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"原生端点\"\u003e\u003cstrong\u003e原生端点\u003c/strong\u003e\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e应用配置类:获取应用程序中加载的应用配置,环境变量,自动化配置报告等与SpringBoot应用密切相关的配置类信息\u003c/li\u003e\n\u003cli\u003e度量指标类:获取应用程序运行过程中用户监控的度量指标,比如内存信息,线程池信息,HTTP请求统计等\u003c/li\u003e\n\u003cli\u003e操作控制类:提供了对应用的关闭操作类功能\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"应用配置类\"\u003e\u003cstrong\u003e应用配置类\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e/autoconfig:该端点用来获取应用的自动化配置报告.包括条件匹配成功以及不成功的配置\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003epositiveMatches中返回是条件匹配成功的自动化配置\u003c/li\u003e\n\u003cli\u003enegativeMatches中返回的是条件匹配不成功的自动化配置\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e/beans:该端点用来获取应用上下文中创建的所有Bean\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;context\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;application\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;parent\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#66d9ef\"\u003enull\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;beans\u0026#34;\u003c/span\u003e:[\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;bean\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;helloApplication\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;aliases\u0026#34;\u003c/span\u003e:[\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;scope\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;singleton\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;type\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;com.jimersylee.hello.HelloApplication$$EnhancerBySpringCGLIB$$d3ebe421\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;resource\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;null\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                \u003cspan style=\"color:#f92672\"\u003e\u0026#34;dependencies\u0026#34;\u003c/span\u003e:[\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e                ]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            },\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e每个Bean中都包含了下面这些信息\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003ebean:Bean的名称\u003c/li\u003e\n\u003cli\u003escope:Bean的作用域\u003c/li\u003e\n\u003cli\u003etype:Bean的Java类型\u003c/li\u003e\n\u003cli\u003eresource:class文件的具体路径\u003c/li\u003e\n\u003cli\u003edependencies:依赖的Bean名称\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e/configprops:该端点用来获取应用中配置的属性信息报告,可以通过使用endpoints.configprops.enabled=false来关闭\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eenv:该端点与/configprops不同,它用来获取应用所有可用的环境属性报告\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-json\" data-lang=\"json\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;profiles\u0026#34;\u003c/span\u003e:[\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ],\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;server.ports\u0026#34;\u003c/span\u003e:{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;local.server.port\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#ae81ff\"\u003e8080\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;servletContextInitParams\u0026#34;\u003c/span\u003e:{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    },\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#f92672\"\u003e\u0026#34;systemProperties\u0026#34;\u003c/span\u003e:{\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;java.runtime.name\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Java(TM) SE Runtime Environment\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;awt.useSystemAAFontSettings\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;gasp\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;sun.boot.library.path\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/opt/jdk1.8.0_144/jre/lib/amd64\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;java.vm.version\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;25.144-b01\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#f92672\"\u003e\u0026#34;maven.multiModuleProjectDirectory\u0026#34;\u003c/span\u003e:\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;/home/jimersylee/projects/java/spring-cloud-action/hello\u0026#34;\u003c/span\u003e,\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        \u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e/mappings:该端点用来返回所有Spring MVC的控制器映射关系报告.\u003c/p\u003e","title":"Spring Cloud 之actuator模块简介"},{"content":"Spring Cloud 入门 Spring Cloud Config:配置管理工具\nSpring Cloud Netflix:核心组件\nEureka:服务治理组件 Hystrix:容错管理组件 Ribbon:客户端负载均衡的服务调用组件 Feign:基于Ribbon和Hystrix的生命是服务调用组件 Zuul:网关组件 外部化配置组件 Spring Cloud Bus:事件,消息总线\nSpring Cloud Cluster:针对Zookeeper,Redis,Hazelcast,Consul的选举算法和通用状态模式的实现\nSpring Cloud CloudFoundry:与Pivatal Cloudfoundy的整合支持\nSpring Cloud Consul:服务发现与配置管理工具\nSpring Cloud Stream:通过Redis,RabbitMQ和Kafka实现的消费微服务,可以通过简单的声明式模型来发送和接收消息\nSpring Cloud AWS:用于简化整合Amazon Web Service的组件\nSpring Cloud Security:安全工具包,提供在Zuul代理中对OAuth2客户端请求的中继器\nSpring Cloud Sleuth:Spring Cloud的应用分布式跟踪系统,可以完美整合Zipkin\nSpring Cloud Zookeeper:基于Zookeeper的服务发现与配置管理组件\nSpring Cloud Starters:Spring Cloud的基础组件,它是基于Spring Boot风格项目的基础依赖模块\nSpring Cloud CLI:用于在Groovy中快速创建Spring Cloud应用的Spring Boot CLI的插件\n","permalink":"https://blog.jimersylee.com/posts/spring-cloud-%E5%85%A5%E9%97%A8/","summary":"\u003ch1 id=\"spring-cloud-入门\"\u003eSpring Cloud 入门\u003c/h1\u003e\n\u003cp\u003eSpring Cloud Config:配置管理工具\u003c/p\u003e\n\u003cp\u003eSpring Cloud Netflix:核心组件\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eEureka:服务治理组件\u003c/li\u003e\n\u003cli\u003eHystrix:容错管理组件\u003c/li\u003e\n\u003cli\u003eRibbon:客户端负载均衡的服务调用组件\u003c/li\u003e\n\u003cli\u003eFeign:基于Ribbon和Hystrix的生命是服务调用组件\u003c/li\u003e\n\u003cli\u003eZuul:网关组件\u003c/li\u003e\n\u003cli\u003e外部化配置组件\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eSpring Cloud Bus:事件,消息总线\u003c/p\u003e\n\u003cp\u003eSpring Cloud Cluster:针对Zookeeper,Redis,Hazelcast,Consul的选举算法和通用状态模式的实现\u003c/p\u003e\n\u003cp\u003eSpring Cloud CloudFoundry:与Pivatal Cloudfoundy的整合支持\u003c/p\u003e\n\u003cp\u003eSpring Cloud Consul:服务发现与配置管理工具\u003c/p\u003e\n\u003cp\u003eSpring Cloud Stream:通过Redis,RabbitMQ和Kafka实现的消费微服务,可以通过简单的声明式模型来发送和接收消息\u003c/p\u003e\n\u003cp\u003eSpring Cloud AWS:用于简化整合Amazon Web Service的组件\u003c/p\u003e\n\u003cp\u003eSpring Cloud Security:安全工具包,提供在Zuul代理中对OAuth2客户端请求的中继器\u003c/p\u003e\n\u003cp\u003eSpring Cloud Sleuth:Spring Cloud的应用分布式跟踪系统,可以完美整合Zipkin\u003c/p\u003e\n\u003cp\u003eSpring Cloud Zookeeper:基于Zookeeper的服务发现与配置管理组件\u003c/p\u003e\n\u003cp\u003eSpring Cloud Starters:Spring Cloud的基础组件,它是基于Spring Boot风格项目的基础依赖模块\u003c/p\u003e\n\u003cp\u003eSpring Cloud CLI:用于在Groovy中快速创建Spring Cloud应用的Spring Boot CLI的插件\u003c/p\u003e","title":"Spring Cloud 入门"},{"content":"\u0026lt;深入理解Java虚拟机\u0026gt;学习笔记-第三章 项目地址: http://github.com/jimersylee/javaStudy\n3.1概述 垃圾收集(Garbage Collection,GC),大部分人都把这项技术当做Java语言的伴生产物,事实上,1960年诞生于MIT的Lisp蚕食第一门真正使用内存动态分配和垃圾收集技术的语言\nGC的3件事情\n那些内存需要回收 什么时候回收 如何回收 目前GC的技术已经相当成熟,为什么我们要了解\n当需要排查各种内存溢出,内存泄露问题时 当垃圾收集成为系统达到更高并发量瓶颈时 3.2对象已死吗 Java堆中存在对象实例,垃圾收集器在对堆进行回收前,第一件事就是要确定哪些对象还\u0026quot;活着\u0026quot;,哪些已经\u0026quot;死去\u0026quot;\n3.2.1 引用计数算法 给对象中添加一个引用计数器:每当有一个地方引用它时,计数器加1;当引用失效时,计数器减1;任何时刻计数器为0的对象就是不可能再被使用的\n引用计数算法(Reference Counting)的实现简单,判定效率也高,例如FlashPlayer,Python都使用引用计数算法了内存管理,但是Java虚拟机中没有选用引用计数算法来管理内存,其实最重要的原因是它很难解决对象之间相互循环引用的问题\ntestGC()方法,虽然两个对象已经不可能被访问,但是因为互相引用着,导致他们计数器都不为0,于是引用计数算法无法通知GC收集器来回收它们\nGC日志中包含了6747K-\u0026gt;416K(51200K),意味着虚拟机并没有因为这两个对象互相引用就不回收它们,这也从侧面说明虚拟机并不是通过引用计数算法来判断对象是否是存活的\n3.2.2 可达性分析算法 在主流的商用程序语言(Java,C#,甚至古老的Lisp)的主流实现中,都是通过可达性分析(Reachability Analysis)来判定对象是否存活的.这个算法的基本思路就是通过一系列的称为\u0026quot;GC Roots\u0026quot;的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链(用图论的话来说,就是从GC Root到这个对象不可达),则证明此对象是不可用的.\n在Java语言中,可作为GC Roots的对象包括下面几种:\n虚拟机栈(栈帧中的本地变量表)中引用的对象 方法区中类静态属性引用的对象 方法区中常量引用的对象 本地方法栈中JNI(Native方法)引用的对象 3.2.3 再谈引用 jdk1.2后,对引用的概念进行了扩充\n强引用 类型Object obj=new Object();这类的,只要强引用还在,垃圾收集器永远不会回收掉被引用的对象 软引用 软引用用来描述一些还有用但并非必需的对象.在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围内进行二次回收.如果这次回收还没有足够的内存,才会抛出内存溢出异常.在JDK1.2后,提供了SoftReference类来实现软引用. 弱引用 弱引用也是用来描述非必需对象.当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象.在JDK1.2后,提供了WeakReference类实现弱引用. 虚引用 虚引用也称为幽灵引用或者幻影引用.一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获得一个对象实例.为一个对象设置虚引用是为了能在这个对象被回收时能收到系统通知.在JDK1.2后,提供了PhantomReference类实现虚引用 3.2.4 生存还是死亡 即使在可达性分析算法中不可达的对象,也并非是\u0026quot;非死不可\u0026quot;的,要宣告一个对象死亡,至少经历两次标记过程 代码清单3-2 一次对象自我拯救的演示\n/** * 此代码演示了两点 * 1.对象可以在被GC时自我拯救 * 2.这个自救机会只有一次 * * @author jimersylee */publicclassFinalizeEscapeGC { publicstatic FinalizeEscapeGC SAVE_HOOK =null; publicvoidisAlive() { System.out.println(\u0026#34;yes,i am still alive\u0026#34;); } @Override protectedvoidfinalize() throws Throwable { super.finalize(); System.out.println(\u0026#34;finalize method executed\u0026#34;); FinalizeEscapeGC.SAVE_HOOK =this; } publicstaticvoidmain(String[] args) throws Throwable { SAVE_HOOK =new FinalizeEscapeGC(); //对象第一次成功拯救自己演示//释放对象 SAVE_HOOK =null; System.gc(); //因为finalize方法优先度低,所以暂停0.5秒等待它 Thread.sleep(500); if (SAVE_HOOK !=null) { SAVE_HOOK.isAlive(); }else { System.out.println(\u0026#34;no,i am dead :(\u0026#34;); } //下面这段代码与上面的完全相同,但是这次自救却失败了 SAVE_HOOK =null; System.gc(); //因为finalize方法优先度低,所以暂停0.5秒等待它 Thread.sleep(500); if (SAVE_HOOK !=null) { SAVE_HOOK.isAlive(); }else { System.out.println(\u0026#34;no,i am dead :(\u0026#34;); } } } 3.2.5 回收方法区 ","permalink":"https://blog.jimersylee.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3java%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E7%AC%AC%E4%B8%89%E7%AB%A0/","summary":"\u003ch1 id=\"深入理解java虚拟机学习笔记-第三章\"\u003e\u0026lt;深入理解Java虚拟机\u0026gt;学习笔记-第三章\u003c/h1\u003e\n\u003cp\u003e项目地址: \u003ca href=\"http://github.com/jimersylee/javaStudy\"\u003ehttp://github.com/jimersylee/javaStudy\u003c/a\u003e\u003c/p\u003e\n\u003ch1 id=\"31概述\"\u003e\u003cstrong\u003e3.1概述\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e垃圾收集(Garbage Collection,GC),大部分人都把这项技术当做Java语言的伴生产物,事实上,1960年诞生于MIT的Lisp蚕食第一门真正使用内存动态分配和垃圾收集技术的语言\u003c/p\u003e\n\u003cp\u003eGC的3件事情\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e那些内存需要回收\u003c/li\u003e\n\u003cli\u003e什么时候回收\u003c/li\u003e\n\u003cli\u003e如何回收\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e目前GC的技术已经相当成熟,为什么我们要了解\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e当需要排查各种内存溢出,内存泄露问题时\u003c/li\u003e\n\u003cli\u003e当垃圾收集成为系统达到更高并发量瓶颈时\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"32对象已死吗\"\u003e\u003cstrong\u003e3.2对象已死吗\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003eJava堆中存在对象实例,垃圾收集器在对堆进行回收前,第一件事就是要确定哪些对象还\u0026quot;活着\u0026quot;,哪些已经\u0026quot;死去\u0026quot;\u003c/p\u003e\n\u003ch1 id=\"321-引用计数算法\"\u003e\u003cstrong\u003e3.2.1 引用计数算法\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e给对象中添加一个引用计数器:每当有一个地方引用它时,计数器加1;当引用失效时,计数器减1;任何时刻计数器为0的对象就是不可能再被使用的\u003c/p\u003e\n\u003cp\u003e引用计数算法(Reference Counting)的实现简单,判定效率也高,例如FlashPlayer,Python都使用引用计数算法了内存管理,但是Java虚拟机中没有选用引用计数算法来管理内存,其实最重要的原因是它很难解决对象之间相互循环引用的问题\u003c/p\u003e\n\u003cp\u003etestGC()方法,虽然两个对象已经不可能被访问,但是因为互相引用着,导致他们计数器都不为0,于是引用计数算法无法通知GC收集器来回收它们\u003c/p\u003e\n\u003cp\u003eGC日志中包含了6747K-\u0026gt;416K(51200K),意味着虚拟机并没有因为这两个对象互相引用就不回收它们,这也从侧面说明虚拟机并不是通过引用计数算法来判断对象是否是存活的\u003c/p\u003e\n\u003ch1 id=\"322-可达性分析算法\"\u003e\u003cstrong\u003e3.2.2 可达性分析算法\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e在主流的商用程序语言(Java,C#,甚至古老的Lisp)的主流实现中,都是通过可达性分析(Reachability Analysis)来判定对象是否存活的.这个算法的基本思路就是通过一系列的称为\u0026quot;GC Roots\u0026quot;的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链(用图论的话来说,就是从GC Root到这个对象不可达),则证明此对象是不可用的.\u003c/p\u003e\n\u003cp\u003e在Java语言中,可作为GC Roots的对象包括下面几种:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e虚拟机栈(栈帧中的本地变量表)中引用的对象\u003c/li\u003e\n\u003cli\u003e方法区中类静态属性引用的对象\u003c/li\u003e\n\u003cli\u003e方法区中常量引用的对象\u003c/li\u003e\n\u003cli\u003e本地方法栈中JNI(Native方法)引用的对象\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"323-再谈引用\"\u003e\u003cstrong\u003e3.2.3 再谈引用\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003ejdk1.2后,对引用的概念进行了扩充\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e强引用 类型Object obj=new Object();这类的,只要强引用还在,垃圾收集器永远不会回收掉被引用的对象\u003c/li\u003e\n\u003cli\u003e软引用 软引用用来描述一些还有用但并非必需的对象.在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围内进行二次回收.如果这次回收还没有足够的内存,才会抛出内存溢出异常.在JDK1.2后,提供了SoftReference类来实现软引用.\u003c/li\u003e\n\u003cli\u003e弱引用 弱引用也是用来描述非必需对象.当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象.在JDK1.2后,提供了WeakReference类实现弱引用.\u003c/li\u003e\n\u003cli\u003e虚引用 虚引用也称为幽灵引用或者幻影引用.一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获得一个对象实例.为一个对象设置虚引用是为了能在这个对象被回收时能收到系统通知.在JDK1.2后,提供了PhantomReference类实现虚引用\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"324-生存还是死亡\"\u003e\u003cstrong\u003e3.2.4 生存还是死亡\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e即使在可达性分析算法中不可达的对象,也并非是\u0026quot;非死不可\u0026quot;的,要宣告一个对象死亡,至少经历两次标记过程 代码清单3-2 一次对象自我拯救的演示\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 此代码演示了两点\n * 1.对象可以在被GC时自我拯救\n * 2.这个自救机会只有一次\n *\n * @author jimersylee\n */publicclassFinalizeEscapeGC {\npublicstatic FinalizeEscapeGC SAVE_HOOK =null;\n\npublicvoidisAlive() {\n        System.out.println(\u0026#34;yes,i am still alive\u0026#34;);\n    }\n\n    @Override\nprotectedvoidfinalize() throws Throwable {\n        super.finalize();\n        System.out.println(\u0026#34;finalize method executed\u0026#34;);\n        FinalizeEscapeGC.SAVE_HOOK =this;\n    }\n\npublicstaticvoidmain(String[] args) throws Throwable {\n        SAVE_HOOK =new FinalizeEscapeGC();\n//对象第一次成功拯救自己演示//释放对象\n        SAVE_HOOK =null;\n        System.gc();\n//因为finalize方法优先度低,所以暂停0.5秒等待它\n        Thread.sleep(500);\nif (SAVE_HOOK !=null) {\n            SAVE_HOOK.isAlive();\n        }else {\n            System.out.println(\u0026#34;no,i am dead :(\u0026#34;);\n        }\n\n//下面这段代码与上面的完全相同,但是这次自救却失败了\n        SAVE_HOOK =null;\n        System.gc();\n//因为finalize方法优先度低,所以暂停0.5秒等待它\n        Thread.sleep(500);\nif (SAVE_HOOK !=null) {\n            SAVE_HOOK.isAlive();\n        }else {\n            System.out.println(\u0026#34;no,i am dead :(\u0026#34;);\n        }\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"325-回收方法区\"\u003e\u003cstrong\u003e3.2.5 回收方法区\u003c/strong\u003e\u003c/h1\u003e","title":"\u003c深入理解Java虚拟机\u003e学习笔记-第三章"},{"content":"\u0026lt;深入理解Java虚拟机\u0026gt;学习笔记-第二章 项目地址: http://github.com/jimersylee/javaStudy\n2.1 概述 内存管理领域\nc,c++开发人员拥有最高权利,可以操作每一个对象,又需要维护每个对象的开始与销毁\njava开发人员不需要为每一个对象写配对的delete/free代码,不容易出现内存泄露与内存溢出问题,不过,出现内存方面的问题,排查又是一项异常艰难的工作\n2.2运行时数据区域 方法区Method Area\n虚拟机栈VM Stack\n本地方法栈Native Method Stack\n堆 Heap\n程序计数器 program Counter Register\n2.2.1 程序计数器 java虚拟机通过线程轮换来分配处理器执行时间,为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,为线程私有内存\n2.2.2 Java虚拟机栈 与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期与线程相同.\n2.2.3 本地方法栈 本地方法栈为虚拟机使用到的Native方法服务\n2.2.4 Java堆 Java堆(Heap)是Java虚拟机所管理的 内存中最大的一块,Java对是被所有线程共享的一块内存区域,在虚拟机启动时创建.此内存区域的唯一目的就是存放对象实例. Java堆是垃圾收集器管理的主要区域 现在的收集器基本采用分代收集算法\nJava堆 新生代\n老年代 更细致\nEden空间\nFrom Survivor空间\nTo Survivor空间\nJava堆可以处于物理上不连续的空间中,只要逻辑上连续即可,既可以实现成固定大小的,也可以是可扩展的,通过 -Xmx 和 -Xms控制,如果堆中没有足够的内存完成实例分配,则报 OutOfMemory 异常\n2.2.5 方法区与Java堆一样,是各个线程共享的内存区域,用于存放已被虚拟机加载的类信息,敞亮,静态变量,即时编译器编译后的代码等数据\nHotSpot上,方法区习惯性称为 永久代(Permanent Generation)\n永久代有 -XX:MaxPermSize 上限\n2.2.6 运行时常量池 运行时常量池(Runtime Constant Pool)是方法区的一部分.用于存放编译期生成的各种字面量和符号引用.\n当常量池无法申请到内存报OutOfMemory异常\n直接内存 JDK1.4中新加入NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用记性操作. 作用:提高性能 直接内存不收Java堆大小的限制,但是受本机总内存限制.除了设置 -Xmx等参数信息,也要注意直接内存\n2.3 HotSpot虚拟机对象探秘 2.3.1 对象的创建 划分内存 虚拟机遇到一条new指令时,首先检查常量池中有没有这个类的符号引用,检查是否已被加载,解析,初始化过.如果没有,那必须先执行相应的类加载过程 通过检查之后,接下来虚拟机将为新生对象分配内存.\n指针碰撞(Bump the Pointer): 如果内存是规整的,那么就将指针从已使用内存向空闲内存移动对象大小的距离\n空闲列表(Free List): 如果内存是不规整的,已使用的内存和空闲的内存项目交错,虚拟机就必须尾货一个列表,记录那些内存块是可用的,分配内存块,更新列表\n选择那种分配方式由垃圾收集器是否带有压缩整理功能决定\n指针碰撞,代用Compact过程的收集器\nSerial ParNew 空闲列表,基于Mark-Sweep算法的收集器\nCMS 保证线程安全 对分配内存空间的动作进行同步处理\u0026mdash;实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性\n把内存分配动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(Thread Local Allocation Buffer).虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB 参数设定\n2.3.2 对象的内存布局 在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域\n对象头(Header)\n运行时数据,如哈希码,GC分代你年龄,锁状态标识,线程持有的锁,偏向线程Id,偏向时间戳 类型指针,虚拟机通过这个指针确定这个对象是哪个类的示例 实例数据(Instance Data)\n各种类型的字段内容 对齐填充(Padding)\n不是必然存在,由于HotSpot Vm的自动内存管理系统要求对象起始地址必须是8字节的整数倍,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全\n2.3.3 对象的访问定位 主流方式\nHotSpot采用直接指针方式\n使用句柄 原理:划分出一块内存作为句柄池.reference中存储的就是对象的句柄地址,句柄中包含了对象实例数据与类型数据各自的具体地址信息 优点:reference中存储的是稳定的句柄地址,在对象被移动时,只会改变句柄中的实例数据指针,而reference本身不需要修改 直接指针 原理:Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference中存储的直接就是对象地址 优点:速度更快,少了一次通过句柄定位指针的开销 2.4 实战:OutOfMemoryError异常 通过代码验证Java虚拟机规范中描述的各个运行时区域存储的内容 根据异常的信息快速判断是哪个区域的内存一处,知道什么的样的代码可能导致这些区域内存溢出,以及出现这些异常后如何处理 2.4.1 Java堆溢出 复现过程,设置jvm启动参数来实现,本文使用Idea,在Run/Debug configuration中的VM options选项中填入如下参数\n-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError 以上代码限制Java堆的大小为20MB,不可扩展(将堆的最小值 -Xms参数与最大值-Xmx参数设置为一样即可避免堆自动扩展),通过-XX:+HeapDumpOnOutOfMemoryError可以让虚拟机在出现内存溢出异常时Dump出当前的内存对转储快照以便时候进行分析\n代码清单\nimport java.util.ArrayList; import java.util.List; /** * VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * @author jimersylee */publicclassHeapOOM { privatestaticclassOOMObject{ } publicstatic void main(String[] args){ List\u0026lt;OOMObject\u0026gt;list=new ArrayList\u0026lt;\u0026gt;(); while(true){ list.add(new OOMObject()); } } } 运行结果\njava.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid27497.hprof ... Exceptionin thread \u0026#34;main\u0026#34; Heap dump file created [27789642 bytesin 0.164 secs] java.lang.OutOfMemoryError: Java heap space 2.4.2 虚拟机栈和本地方法栈溢出 对于HotSpot来说,虽然-Xoss参数(设置本地方法栈大小)存在,但实际上是无效的,栈容量实际由-Xss参数设定.关于虚拟机栈和本地方法栈,在Java虚拟机中描述了两种异常:\n如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常. 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常. 将实验范围限制于单线程操作,下面两种方法均无法让虚拟机产生OutOfMemoryError异常,尝试的结果都是StackOverflowError异常,测试代码2-4\n使用-Xss参数减少栈内存容量.结果抛出StackOverflowError异常,异常出现时输出的堆栈深度相应缩小 定义了大量的本地变量,增大此方法帧中本地变量表的长度.结果:抛出StackOverflowError异常时堆栈深度相应缩小 代码清单2-4\n/** * 虚拟机和本地方法栈OOM测试(仅作为第一点测试程序) * VM Args:-Xss228k * @author Jimersy Lee */publicclassJavaVMStackSOF { privateint stackLength=1; publicvoidstackLeak(){ stackLength++; stackLeak(); } publicstaticvoidmain(String[] args) throws Throwable{ JavaVMStackSOF oom=new JavaVMStackSOF(); try { oom.stackLeak(); }catch (Throwable e){ System.out.println(\u0026#34;stack length:\u0026#34;+oom.stackLength); throw e; } } } 运行结果\nException in thread \u0026#34;main\u0026#34; java.lang.StackOverflowError stack length:1517 at JVM.chapter2.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:11) at JVM.chapter2.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12) at JVM.chapter2.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12) at JVM.chapter2.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12) .....后续异常堆栈信息省略 实验结果表明:在单个线程下,无论是由于栈帧太大或是虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出都是StackOverflowError异常\n如果测试时不限于单线程,通过不断地建立线程的方式到时可以产生内存溢出异常,如代码清单2-5所示.但是这样的内存溢出异常与栈空间足够大并不存在任何关联,或者准确地说,在这种情况下,为每个线程的栈分配的内存越大,反而越容易产生内存溢出异常.\n代码清单2-5 创建线程导致内存溢出异常\n/** * 创建线程导致内存溢出异常 * VM Args: -Xss2M * @author Jimersy Lee * todo:没有等到抛出异常 */publicclassJavaVMStackOOM { privatevoiddontStop(){ while (true){ } } publicvoidstackLeakByThread(){ int count=0; while(true){ Thread thread=new Thread(new Runnable() { @Override publicvoidrun() { dontStop(); } }); thread.start(); System.out.println(count++); } } publicstaticvoidmain(String[] args){ JavaVMStackOOM oom=new JavaVMStackOOM(); oom.stackLeakByThread(); } } 2.4.3 方法区和运行时常量池溢出 运行时常量池是方法区的一部分,因此这两个区域的溢出测试放在一起进行.\njdk1.6之前的版本,常量池分配在永久代中,可以通过-XX:PermSize和-XX:MaxPermSize限制方法区大小,从而间接限制其中常量池的容量\n代码清单2-6 运行时常量池导致的内存溢出异常\nimport java.util.ArrayList; import java.util.List; /** * 运行时常量池导致的内存溢出异常,适用版本jdk1.6,1.8已经去除永久代 * @author jimersylee * VM Args: -XX:PermSize=10m -XX:MaxPermSize=10m * String.intern()是一个Native方法,它的作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用 */publicclassRuntimeConstantPoolOOM { publicstatic void main(String[] args){ //使用List保持着常量池引用,避免Full GC回收常量池行为List\u0026lt;String\u0026gt;list=new ArrayList\u0026lt;\u0026gt;(); //10MB的PermSize在Integer范围内足够产生OOM了 int i=0; while (true){ list.add(String.valueOf(i++).intern()); System.out.println(list.size()); } } } 关于字符串常量池的实现问题,引出一个有意思的影响\n/** * String.intern()返回引用测试 */publicclassStringIntern { publicstaticvoidmain(String[] args) { String str1 =new StringBuilder(\u0026#34;计算机\u0026#34;).append(\u0026#34;软件\u0026#34;).toString(); System.out.println(str1.intern()==str1); String str2=new StringBuilder(\u0026#34;ja\u0026#34;).append(\u0026#34;va\u0026#34;).toString(); System.out.println(str2.intern()==str2); } } jdk1.6中,得到两个false;jdk1.7以后,得到true,和false;\njdk1.6中,intern()方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中的这个字符串实例的引用,而StringBuilder创建的字符串实例在Java堆上,必然不是同一个引用,将返回false;\njdk1.7中,intern()实现不会在复制实例,只是在常量池中记录首次出现的实例引用,因此intern()返回的引用和由StringBuilder创建的那个字符创实例是同一个.\n方法区用于存放Class的相关信息,如类名,访问修饰符,常量池,字段描述,方法描述\n对于这些区域的测试,基本思路是运行时产生大量的类去填满方法区,直到溢出\n2.2.4 本机直接内存溢出 DirectMemory容量可以通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆最大值(-Xmx指定)一样\n由DirectMemory导致的内存溢出,一个明显的特征是在Heap Dump文件中不会看见明显的异常,如果读者发现OOM之后的Dump文件很小,而程序中又直接或间接使用了NIO,那就可以考虑检查一下是不是这方面的原因\n本章小结 通过本章的学习,我们明白了虚拟机中的内存是如何划分的,哪部分区域,什么样的代码和操作可能导致内存溢出异常.Java虽然有垃圾收集机制,但是内存溢出离我们并不遥远,本章只是讲解了各个区域出现异常的原因,下一章将详细讲解Java垃圾收集机制为了避免内存溢出异常的出现做出了哪些努力\n","permalink":"https://blog.jimersylee.com/posts/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3java%E8%99%9A%E6%8B%9F%E6%9C%BA-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E7%AC%AC%E4%BA%8C%E7%AB%A0/","summary":"\u003ch1 id=\"深入理解java虚拟机学习笔记-第二章\"\u003e\u0026lt;深入理解Java虚拟机\u0026gt;学习笔记-第二章\u003c/h1\u003e\n\u003cp\u003e项目地址: \u003ca href=\"http://github.com/jimersylee/javaStudy\"\u003ehttp://github.com/jimersylee/javaStudy\u003c/a\u003e\u003c/p\u003e\n\u003ch1 id=\"21-概述\"\u003e\u003cstrong\u003e2.1 概述\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e内存管理领域\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003ec,c++开发人员拥有最高权利,可以操作每一个对象,又需要维护每个对象的开始与销毁\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003ejava开发人员不需要为每一个对象写配对的delete/free代码,不容易出现内存泄露与内存溢出问题,不过,出现内存方面的问题,排查又是一项异常艰难的工作\u003c/p\u003e\n\u003ch1 id=\"22运行时数据区域\"\u003e\u003cstrong\u003e2.2运行时数据区域\u003c/strong\u003e\u003c/h1\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e方法区Method Area\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e虚拟机栈VM Stack\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e本地方法栈Native Method Stack\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e堆 Heap\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e程序计数器 program Counter Register\u003c/p\u003e\n\u003ch3 id=\"221-程序计数器\"\u003e\u003cstrong\u003e2.2.1 程序计数器\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003ejava虚拟机通过线程轮换来分配处理器执行时间,为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,为\u003cem\u003e线程私有\u003c/em\u003e内存\u003c/p\u003e\n\u003ch3 id=\"222-java虚拟机栈\"\u003e\u003cstrong\u003e2.2.2 Java虚拟机栈\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期与线程相同.\u003c/p\u003e\n\u003ch3 id=\"223-本地方法栈\"\u003e\u003cstrong\u003e2.2.3 本地方法栈\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e本地方法栈为虚拟机使用到的Native方法服务\u003c/p\u003e\n\u003ch3 id=\"224-java堆\"\u003e\u003cstrong\u003e2.2.4 Java堆\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eJava堆(Heap)是Java虚拟机所管理的 内存中最大的一块,Java对是被所有线程共享的一块内存区域,在虚拟机启动时创建.此内存区域的唯一目的就是存放对象实例. Java堆是垃圾收集器管理的主要区域 现在的收集器基本采用分代收集算法\u003c/p\u003e\n\u003ch3 id=\"java堆\"\u003e\u003cstrong\u003eJava堆\u003c/strong\u003e\u003c/h3\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e新生代\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e老年代 更细致\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eEden空间\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eFrom Survivor空间\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eTo Survivor空间\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eJava堆可以处于物理上不连续的空间中,只要逻辑上连续即可,既可以实现成固定大小的,也可以是可扩展的,通过 \u003cem\u003e-Xmx\u003c/em\u003e 和 \u003cem\u003e-Xms\u003c/em\u003e控制,如果堆中没有足够的内存完成实例分配,则报 \u003cem\u003eOutOfMemory\u003c/em\u003e 异常\u003c/p\u003e\n\u003ch3 id=\"225\"\u003e\u003cstrong\u003e2.2.5\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e方法区与Java堆一样,是各个线程共享的内存区域,用于存放已被虚拟机加载的类信息,敞亮,静态变量,即时编译器编译后的代码等数据\u003c/p\u003e\n\u003cp\u003eHotSpot上,方法区习惯性称为 \u003cem\u003e永久代(Permanent Generation)\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e永久代有 \u003cem\u003e-XX:MaxPermSize\u003c/em\u003e 上限\u003c/p\u003e\n\u003ch3 id=\"226-运行时常量池\"\u003e\u003cstrong\u003e2.2.6 运行时常量池\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e运行时常量池(Runtime Constant Pool)是方法区的一部分.用于存放编译期生成的各种字面量和符号引用.\u003c/p\u003e\n\u003cp\u003e当常量池无法申请到内存报OutOfMemory异常\u003c/p\u003e\n\u003ch3 id=\"直接内存\"\u003e\u003cstrong\u003e直接内存\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eJDK1.4中新加入NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用记性操作. 作用:提高性能 直接内存不收Java堆大小的限制,但是受本机总内存限制.除了设置 -Xmx等参数信息,也要注意直接内存\u003c/p\u003e","title":"深入理解Java虚拟机 学习笔记-第二章"},{"content":"我的Linux桌面环境配置与必备软件 Idea 主题 Monokai_2 字体 YaHei Consolas Hybrid\nphpstorm sublime jdk 设置环境变量 vim /etc/profile JAVA_HOME=/opt/jdk1.8.0_141 PATH=$JAVA_HOME/bin:$PATH CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar export JAVA_HOME export PATH export CLASSPATH source /etc/profile mysql workbench apt install mysql-workbench maven java包管理软件\nshadowsocks,必备梯子 sudo apt install shadowsocks-qt5 tomcat redis-desktop-manager redis跨平台客户端 下载地址\n配置lamp环境 安装xammp 到/opt 将bin路径加入path vim /etc/profile export $PTAH=$PATH:/opt/lampp/bin 这样就可以直接使用pecl来安装扩展了 pecl search redis pecl install redis nodejs npm apt install nodejs apt install npm 设置淘宝镜像 npm config set registry https://registry.npm.taobao.org 编译环境组件 sudo apt install build-essential Nginx 编译安装nginx 下载源码 tar zxvf nginx-x.tar cd nginx ./configure \\ --prefix=/usr \\ --sbin-path=/usr/sbin/nginx \\ --conf-path=/etc/nginx/nginx.conf \\ --error-log-path=/var/log/nginx/error.log \\ --pid-path=/var/run/nginx/nginx.pid \\ --user=jimersylee \\ --group=jimersylee \\ --with-http_ssl_module \\ --with-http_flv_module \\ --with-http_gzip_static_module \\ --http-log-path=/var/log/nginx/access.log \\ --http-client-body-temp-path=/var/tmp/nginx/client \\ --http-proxy-temp-path=/var/tmp/nginx/proxy \\ --http-fastcgi-temp-path=/var/tmp/nginx/fcgi \\ --with-http_stub_status_module ","permalink":"https://blog.jimersylee.com/posts/%E6%88%91%E7%9A%84linux%E6%A1%8C%E9%9D%A2%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE%E4%B8%8E%E5%BF%85%E5%A4%87%E8%BD%AF%E4%BB%B6/","summary":"\u003ch1 id=\"我的linux桌面环境配置与必备软件\"\u003e我的Linux桌面环境配置与必备软件\u003c/h1\u003e\n\u003ch1 id=\"idea\"\u003e\u003cstrong\u003eIdea\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e主题 Monokai_2 字体 YaHei Consolas Hybrid\u003c/p\u003e\n\u003ch1 id=\"phpstorm\"\u003e\u003cstrong\u003ephpstorm\u003c/strong\u003e\u003c/h1\u003e\n\u003ch1 id=\"sublime\"\u003e\u003cstrong\u003esublime\u003c/strong\u003e\u003c/h1\u003e\n\u003ch1 id=\"jdk\"\u003e\u003cstrong\u003ejdk\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e设置环境变量\nvim /etc/profile\n\nJAVA_HOME=/opt/jdk1.8.0_141\nPATH=$JAVA_HOME/bin:$PATH\nCLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar\nexport JAVA_HOME\nexport PATH\nexport CLASSPATH\n\nsource  /etc/profile\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"mysql-workbench\"\u003e\u003cstrong\u003emysql workbench\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eapt install mysql-workbench\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"maven\"\u003e\u003cstrong\u003emaven\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003ejava包管理软件\u003c/p\u003e\n\u003ch1 id=\"shadowsocks必备梯子\"\u003e\u003cstrong\u003eshadowsocks,必备梯子\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esudo apt install shadowsocks-qt5\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"tomcat\"\u003e\u003cstrong\u003etomcat\u003c/strong\u003e\u003c/h1\u003e\n\u003ch1 id=\"redis-desktop-manager\"\u003e\u003cstrong\u003eredis-desktop-manager\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003eredis跨平台客户端 \u003ca href=\"http://download.csdn.net/download/shuai825644975/9854471\"\u003e下载地址\u003c/a\u003e\u003c/p\u003e\n\u003ch1 id=\"配置lamp环境\"\u003e\u003cstrong\u003e配置lamp环境\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e安装xammp\n到/opt\n将bin路径加入path\n\nvim /etc/profile\n\nexport $PTAH=$PATH:/opt/lampp/bin\n这样就可以直接使用pecl来安装扩展了\npecl search redis\npecl install redis\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"nodejs-npm\"\u003e\u003cstrong\u003enodejs npm\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eapt install  nodejs\napt install npm\n设置淘宝镜像\nnpm config set registry https://registry.npm.taobao.org\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"编译环境组件\"\u003e\u003cstrong\u003e编译环境组件\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esudo apt install build-essential\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"nginx\"\u003e\u003cstrong\u003eNginx\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e编译安装nginx\n下载源码\ntar zxvf nginx-x.tar\ncd nginx\n./configure \\\n--prefix=/usr \\\n--sbin-path=/usr/sbin/nginx \\\n--conf-path=/etc/nginx/nginx.conf \\\n--error-log-path=/var/log/nginx/error.log \\\n--pid-path=/var/run/nginx/nginx.pid \\\n--user=jimersylee \\\n--group=jimersylee \\\n--with-http_ssl_module \\\n--with-http_flv_module \\\n--with-http_gzip_static_module \\\n--http-log-path=/var/log/nginx/access.log \\\n--http-client-body-temp-path=/var/tmp/nginx/client \\\n--http-proxy-temp-path=/var/tmp/nginx/proxy \\\n--http-fastcgi-temp-path=/var/tmp/nginx/fcgi \\\n--with-http_stub_status_module\n\u003c/code\u003e\u003c/pre\u003e","title":"我的Linux桌面环境配置与必备软件"},{"content":"为域名配置免费SSL证书 Nginx配置ssl 腾讯云申请证书\n阿里云域名解析验证证书\n安装证书\n下载证书\n上传配置证书\n#将本地的jenkins.jimersylee.com.zip上传至jimersylee.com这个机子上的/data/ssl_cert目录scp jenkins.jimersylee.com.zip root@jimersylee.com:/data/ssl_cert #登录主机解压文件 ssh root@jimersylee.com cd /data/ssl_cert unzip jenkins.jimersylee.com.zip #各种web服务器的证书就解压完成了,然后去配置Nginx [root@VM_77_132_centos ssl_cert]# tree . ├── Apache │ ├── 1_root_bundle.crt │ ├── 2_blog.jimersylee.com.crt │ ├── 2_jenkins.jimersylee.com.crt │ ├── 2_jimersylee.com.crt │ ├── 3_blog.jimersylee.com.key │ ├── 3_jenkins.jimersylee.com.key │ └── 3_jimersylee.com.key ├── blog.jimersylee.com.cert.zip ├── IIS │ ├── blog.jimersylee.com.pfx │ ├── jenkins.jimersylee.com.pfx │ ├── jimersylee.com.pfx │ └── keystorePass.txt ├── jenkins.jimersylee.com.zip ├── jimersylee.com.cert.zip ├── Nginx │ ├── 1_blog.jimersylee.com_bundle.crt │ ├── 1_jenkins.jimersylee.com_bundle.crt │ ├── 1_jimersylee.com_bundle.crt │ ├── 2_blog.jimersylee.com.key │ ├── 2_jenkins.jimersylee.com.key │ └── 2_jimersylee.com.key └── Tomcat ├── jenkins.jimersylee.com.jks └── keystorePass.txt nginx配置\n#创建配置文件vim /etc/nginx/conf.d/jenkins.conf 写入以下内容 配置443端口 server { listen 443; server_name jenkins.jimersylee.com; ssl on; ssl_certificate /data/ssl_cert/Nginx/1_jenkins.jimersylee.com_bundle.crt; ssl_certificate_key /data/ssl_cert/Nginx/2_jenkins.jimersylee.com.key; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #按照这个协议配置 ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;#按照这个套件配置 ssl_prefer_server_ciphers on; root /data/java_app/tomcat9/webapps;\nindexindex.htmlindex.htmindex.php; location / { try_files $uri @jenkins; } location @jenkins { internal; proxy_pass http://127.0.0.1:8080; } access_log /data/logs/jenkins/jenkins.log main; } 转发80的访问到jenkins server { listen 80; server_name jenkins.jimersylee.com; rewrite ^ https://servernamerequest_uri? permanent; }\n- 重启Nginx生效 nginx -s stop#停止 nginx -t#测试Nginx配置是否正确 nginx#启动Nginx ","permalink":"https://blog.jimersylee.com/posts/%E4%B8%BA%E5%9F%9F%E5%90%8D%E9%85%8D%E7%BD%AE%E5%85%8D%E8%B4%B9ssl%E8%AF%81%E4%B9%A6/","summary":"\u003ch1 id=\"为域名配置免费ssl证书\"\u003e为域名配置免费SSL证书\u003c/h1\u003e\n\u003ch1 id=\"nginx配置ssl\"\u003e\u003cstrong\u003eNginx配置ssl\u003c/strong\u003e\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e腾讯云申请证书\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"http://jimersyleetest.qiniudn.com/%E7%94%B3%E8%AF%B7%E8%AF%81%E4%B9%A6.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"http://jimersyleetest.qiniudn.com/%E9%AA%8C%E8%AF%81%E8%AF%81%E4%B9%A6.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"http://jimersyleetest.qiniudn.com/%E6%B7%BB%E5%8A%A0%E8%AE%B0%E5%BD%95.png\"\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e阿里云域名解析验证证书\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"http://jimersyleetest.qiniudn.com/%E8%A7%A3%E6%9E%90%E8%AE%BE%E7%BD%AE.png\"\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e安装证书\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e下载证书\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e上传配置证书\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#将本地的jenkins.jimersylee.com.zip上传至jimersylee.com这个机子上的/data/ssl_cert目录scp jenkins.jimersylee.com.zip root@jimersylee.com:/data/ssl_cert\n#登录主机解压文件\nssh root@jimersylee.com\ncd /data/ssl_cert\nunzip jenkins.jimersylee.com.zip\n#各种web服务器的证书就解压完成了,然后去配置Nginx\n[root@VM_77_132_centos ssl_cert]# tree\n.\n├── Apache\n│   ├── 1_root_bundle.crt\n│   ├── 2_blog.jimersylee.com.crt\n│   ├── 2_jenkins.jimersylee.com.crt\n│   ├── 2_jimersylee.com.crt\n│   ├── 3_blog.jimersylee.com.key\n│   ├── 3_jenkins.jimersylee.com.key\n│   └── 3_jimersylee.com.key\n├── blog.jimersylee.com.cert.zip\n├── IIS\n│   ├── blog.jimersylee.com.pfx\n│   ├── jenkins.jimersylee.com.pfx\n│   ├── jimersylee.com.pfx\n│   └── keystorePass.txt\n├── jenkins.jimersylee.com.zip\n├── jimersylee.com.cert.zip\n├── Nginx\n│   ├── 1_blog.jimersylee.com_bundle.crt\n│   ├── 1_jenkins.jimersylee.com_bundle.crt\n│   ├── 1_jimersylee.com_bundle.crt\n│   ├── 2_blog.jimersylee.com.key\n│   ├── 2_jenkins.jimersylee.com.key\n│   └── 2_jimersylee.com.key\n└── Tomcat\n    ├── jenkins.jimersylee.com.jks\n    └── keystorePass.txt\n\u003c/code\u003e\u003c/pre\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003enginx配置\u003c/p\u003e","title":"为域名配置免费SSL证书"},{"content":"项目地址:http://github.com/jimersylee/MachineLearningAction\nLogistic回归 本章内容 Sigmoid函数和Logistic回归分类器 最优化理论初步 梯度下降最优化算法 数据中的缺失项处理 最优化算法 比如如何在最短时间内从A点到B点?如何投入最少的工作量获得最大的收益\nLogistic回归的一般过程 收集数据:采用任意方法收集数据 准备数据:由于需要进行距离计算,因此要求数据类型为数值型.另外,结构化数据格式则最佳 分析数据:采用任意方法对数据进行分析 训练算法:大部分时间将将用于测试,训练的目的是为了找到最佳的分类回归系数 测试算法:一旦训练步骤完成,分类将会很快 使用算法:首先,我们需要输入一些数据,并将其转换成对应的结构化数值;接着,基于训练好的回归系数就可以对这些数值进行简单的回归计算,判定他们属于那个类别了;在这之后,我们就可以在输出的类别上做一些其他分析工作 基于Logistic回归和Sigmoid函数的分类 优点:计算代价不高,易于理解和实现 缺点:容易欠拟合,分类精度可能不高 适用数据类型:数值型和标称型数据\n我们想要的函数,能够接受所有的输入然后预测出类别. 使用单位阶跃函数 Sigmoid函数 f(z)=1/(1+e^-z)\n训练算法:随机梯度上升 梯度上升算法在每次更新回归系数时都需要遍历整个数据集,当数据集增加时,计算复杂度就太高了. 改进方法是一次禁用一个样本点来更新回归系数,该方法成为随机梯度上升算法.\n报错,未解决 weights = weights + alpha error dataMatIn[i] ValueError: operands could not be broadcast together with shapes (3,) (0,)\n示例:从疝气病预测兵马的死亡率 本节使用Logistic回归来预测患有疝气病的马的存货问题 样本数据包含368个样本和28个特征 有30%的数据的值是缺失的.下面将介绍如何处理数据集中的数据确实问题,然后再用Logistic回归和随机梯度上升算法来预测病马的生死\n准备数据 数据缺失是个麻烦的问题.如何解决\n使用可用特征的均值来填补缺失值 使用特殊值来填补缺失值,如-1 忽略有缺失值的样本 使用相似样本的均值填补缺失值 使用另外的机器学习算法来预测缺失值 from numpyimport * import matplotlib.pyplotas plt defloadDataSet(): \u0026#34;\u0026#34;\u0026#34; 载入测试数据 :return: \u0026#34;\u0026#34;\u0026#34; dataMat = [] labelMat = [] fr = open(\u0026#39;testSet.txt\u0026#39;) for linein fr.readlines(): lineArr = line.strip().split() dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])# 为了计算方便,将X0设置为1.0 labelMat.append(int(lineArr[2])) return dataMat, labelMat defsigmoid(inX): \u0026#34;\u0026#34;\u0026#34; Sigmoid函数,单位阶跃函数 :param inX: :return: \u0026#34;\u0026#34;\u0026#34; return 1.0 / (1 + exp(-inX)) defgradAscent(dataMatIn, classLabels): \u0026#34;\u0026#34;\u0026#34; 梯度上升算法 :param dataMatIn: 输入的数据矩阵,存放的100*3的矩阵 :param classLabels: 输入的数据类别矩阵 :return:返回训练好的迭代次数 \u0026#34;\u0026#34;\u0026#34; # 转换为NumPy矩阵类型 dataMatrix = mat(dataMatIn) labelMat = mat(classLabels).transpose()# 初始为1*100的行向量,为了便于矩阵运算,使用transpose转置为列向量100*1 m, n = shape(dataMatrix)# 得到矩阵大小 alpha = 0.001# 目标移动的步长 maxCycles = 500# 最大迭代次数 weights = ones((n, 1)) for kin range(maxCycles): # 矩阵相乘,下面两行,计算真实类别与预测类别的差值,接下来就是按照该差值的方向调整回归系数 h = sigmoid(dataMatrix * weights)# 代表的不是一次乘积计算,事实上该运算包含了300次的乘积,变量h不是一个数,而是一个列向量,100 error = (labelMat - h) weights = weights + alpha * dataMatrix.transpose() * error return weights defstocGradAscent0(dataMatIn, classLabels): m, n = shape(dataMatIn) alpha = 0.01 weights = ones(n) for iin range(m): h = sigmoid(sum(dataMatIn[i] * weights))# h是向量 error = classLabels[i] - h# error是向量 weights = weights + alpha * error * dataMatIn[i] return weights defstocGradAscent1(dataMatIn, classLabels, numIter=150): \u0026#34;\u0026#34;\u0026#34; 改进的随机梯度上升算法 :param dataMatIn: :param classLabels: :param numIter: :return: \u0026#34;\u0026#34;\u0026#34; m, n = shape(dataMatIn) weights = ones(n) for jin range(numIter): dataIndex = range(m) for iin range(m): alpha = 4 / (1.0 + j + i) + 0.01# alpha每次迭代时需要调整 randIndex = int(random.uniform(0, len(dataIndex)))# 随机选取更新 h = sigmoid(sum(dataMatIn[randIndex] * weights)) error = classLabels[randIndex] - h weights = weights + alpha * error * dataMatIn[randIndex] del (dataIndex[randIndex]) return weights defplotBestFit(weights): \u0026#34;\u0026#34;\u0026#34; 画出数据集和Logistic回归最佳拟合直线的函数 :param weights:系数 :return: \u0026#34;\u0026#34;\u0026#34; weights = weights.getA() dataMat, labelMat = loadDataSet() dataArr = array(dataMat) n = shape(dataArr)[0] xcord1 = [] ycord1 = [] xcord2 = [] ycord2 = [] for iin range(n): if int(labelMat[i]) == 1: xcord1.append(dataArr[i, 1]) ycord1.append(dataArr[i, 2]) else: xcord2.append(dataArr[i, 1]) ycord2.append(dataArr[i, 2]) fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(xcord1, ycord1, s=30, c=\u0026#39;red\u0026#39;, marker=\u0026#39;s\u0026#39;) ax.scatter(xcord2, ycord2, s=30, c=\u0026#34;green\u0026#34;) x = arange(-3.0, 3.0, 0.1) y = (-weights[0] - weights[1] * x) / weights[2] ax.plot(x, y) plt.xlabel(\u0026#34;X1\u0026#34;) plt.ylabel(\u0026#34;X2\u0026#34;) plt.show() defclassifyVector(inX, weights): prob = sigmoid(sum(inX * weights)) if prob \u0026gt; 0.5: return 1.0 else: return 0.0 defcolicTest(): \u0026#34;\u0026#34;\u0026#34; 疝气病马死亡分类测试 :return: \u0026#34;\u0026#34;\u0026#34; frTrain = open(\u0026#39;horseColicTraining.txt\u0026#39;) frTest = open(\u0026#39;horseColicTest.txt\u0026#39;) trainingSet = [] trainingLabels = [] for linein frTrain.readlines(): currLine = line.strip().split(\u0026#39;\\t\u0026#39;) lineArr = [] for iin range(21): lineArr.append(float(currLine[i])) trainingSet.append(lineArr) trainingLabels.append(float(currLine[i])) trainWeights = stocGradAscent1(array(trainingSet), trainingLabels, 500) errorCount = 0 numTestVec = 0.0 for linein frTest.readlines(): numTestVec += 1.0 currLine = line.strip().split(\u0026#39;\\t\u0026#39;) lineArr = [] for iin range(21): lineArr.append(float(currLine[i])) if int(classifyVector(array(lineArr), trainWeights)) != int(currLine[21]): errorCount += 1 errorRate = (float(errorCount / numTestVec)) print \u0026#34;the error rate of this test is: %f\u0026#34; % errorRate return errorRate defmultiTest(): numTests = 10 errorSum = 0.0 for kin range(numTests): errorSum += colicTest() print \u0026#34;after %d iterations the average error rate is: %f\u0026#34; % (numTests, errorSum / float(numTests)) deftestCal(): dataArr, labelMat = loadDataSet() weights = gradAscent(dataArr, labelMat) print weights \u0026#34;\u0026#34;\u0026#34; 得到一组回归系数,它确定了不同类别数据之间的分割线 [[ 4.12414349] [ 0.48007329] [-0.6168482 ]] \u0026#34;\u0026#34;\u0026#34; deftestGradAscent(): \u0026#34;\u0026#34;\u0026#34; 测试梯度上升算法,画图 :return: \u0026#34;\u0026#34;\u0026#34; dataArr, labelMat = loadDataSet() weights = gradAscent(dataArr, labelMat) plotBestFit(weights) deftestStocGradAscent0(): \u0026#34;\u0026#34;\u0026#34; 测试随机梯度上升算法,画图 :return: \u0026#34;\u0026#34;\u0026#34; dataArr, labelMat = loadDataSet() weights = stocGradAscent0(dataArr, labelMat) plotBestFit(weights) deftestStocGradAscent1(): \u0026#34;\u0026#34;\u0026#34; 测试随机梯度上升算法,画图 :return: \u0026#34;\u0026#34;\u0026#34; dataArr, labelMat = loadDataSet() weights = stocGradAscent1(dataArr, labelMat) plotBestFit(weights) # testCal()#testGradAscent()# testStocGradAscent0() multiTest() ","permalink":"https://blog.jimersylee.com/posts/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%AE%9E%E6%88%98-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B9%8Blogistic/","summary":"\u003cp\u003e项目地址:\u003ca href=\"http://github.com/jimersylee/MachineLearningAction\"\u003ehttp://github.com/jimersylee/MachineLearningAction\u003c/a\u003e\u003c/p\u003e\n\u003ch1 id=\"logistic回归\"\u003e\u003cstrong\u003eLogistic回归\u003c/strong\u003e\u003c/h1\u003e\n\u003ch1 id=\"本章内容\"\u003e\u003cstrong\u003e本章内容\u003c/strong\u003e\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003eSigmoid函数和Logistic回归分类器\u003c/li\u003e\n\u003cli\u003e最优化理论初步\u003c/li\u003e\n\u003cli\u003e梯度下降最优化算法\u003c/li\u003e\n\u003cli\u003e数据中的缺失项处理\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"最优化算法\"\u003e\u003cstrong\u003e最优化算法\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e比如如何在最短时间内从A点到B点?如何投入最少的工作量获得最大的收益\u003c/p\u003e\n\u003ch1 id=\"logistic回归的一般过程\"\u003e\u003cstrong\u003eLogistic回归的一般过程\u003c/strong\u003e\u003c/h1\u003e\n\u003col\u003e\n\u003cli\u003e收集数据:采用任意方法收集数据\u003c/li\u003e\n\u003cli\u003e准备数据:由于需要进行距离计算,因此要求数据类型为数值型.另外,结构化数据格式则最佳\u003c/li\u003e\n\u003cli\u003e分析数据:采用任意方法对数据进行分析\u003c/li\u003e\n\u003cli\u003e训练算法:大部分时间将将用于测试,训练的目的是为了找到最佳的分类回归系数\u003c/li\u003e\n\u003cli\u003e测试算法:一旦训练步骤完成,分类将会很快\u003c/li\u003e\n\u003cli\u003e使用算法:首先,我们需要输入一些数据,并将其转换成对应的结构化数值;接着,基于训练好的回归系数就可以对这些数值进行简单的回归计算,判定他们属于那个类别了;在这之后,我们就可以在输出的类别上做一些其他分析工作\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch1 id=\"基于logistic回归和sigmoid函数的分类\"\u003e\u003cstrong\u003e基于Logistic回归和Sigmoid函数的分类\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e优点:计算代价不高,易于理解和实现 缺点:容易欠拟合,分类精度可能不高 适用数据类型:数值型和标称型数据\u003c/p\u003e\n\u003cp\u003e我们想要的函数,能够接受所有的输入然后预测出类别. 使用单位阶跃函数 Sigmoid函数 f(z)=1/(1+e^-z)\u003c/p\u003e\n\u003ch1 id=\"训练算法随机梯度上升\"\u003e\u003cstrong\u003e训练算法:随机梯度上升\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e梯度上升算法在每次更新回归系数时都需要遍历整个数据集,当数据集增加时,计算复杂度就太高了. 改进方法是一次禁用一个样本点来更新回归系数,该方法成为随机梯度上升算法.\u003c/p\u003e\n\u003ch1 id=\"报错未解决\"\u003e\u003cstrong\u003e报错,未解决\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003eweights = weights + alpha \u003cem\u003eerror\u003c/em\u003e dataMatIn[i] ValueError: operands could not be broadcast together with shapes (3,) (0,)\u003c/p\u003e\n\u003ch1 id=\"示例从疝气病预测兵马的死亡率\"\u003e\u003cstrong\u003e示例:从疝气病预测兵马的死亡率\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e本节使用Logistic回归来预测患有疝气病的马的存货问题 样本数据包含368个样本和28个特征 \u003cem\u003e有30%的数据的值是缺失的\u003c/em\u003e.下面将介绍如何处理数据集中的数据确实问题,然后再用Logistic回归和随机梯度上升算法来预测病马的生死\u003c/p\u003e\n\u003ch1 id=\"准备数据\"\u003e\u003cstrong\u003e准备数据\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e数据缺失是个麻烦的问题.如何解决\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e使用可用特征的均值来填补缺失值\u003c/li\u003e\n\u003cli\u003e使用特殊值来填补缺失值,如-1\u003c/li\u003e\n\u003cli\u003e忽略有缺失值的样本\u003c/li\u003e\n\u003cli\u003e使用相似样本的均值填补缺失值\u003c/li\u003e\n\u003cli\u003e使用另外的机器学习算法来预测缺失值\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e numpyimport \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e matplotlib.pyplotas plt\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edefloadDataSet():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    载入测试数据\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :return:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    dataMat \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    labelMat \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    fr \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e open(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;testSet.txt\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e linein fr\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ereadlines():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        lineArr \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e line\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003estrip()\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esplit()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        dataMat\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend([\u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e, float(lineArr[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]), float(lineArr[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e])])\u003cspan style=\"color:#75715e\"\u003e# 为了计算方便,将X0设置为1.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        labelMat\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(int(lineArr[\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e]))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e dataMat, labelMat\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edefsigmoid(inX):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    Sigmoid函数,单位阶跃函数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :param inX:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :return:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e exp(\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003einX))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edefgradAscent(dataMatIn, classLabels):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    梯度上升算法\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :param dataMatIn: 输入的数据矩阵,存放的100*3的矩阵\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :param classLabels: 输入的数据类别矩阵\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :return:返回训练好的迭代次数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 转换为NumPy矩阵类型\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    dataMatrix \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e mat(dataMatIn)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    labelMat \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e mat(classLabels)\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etranspose()\u003cspan style=\"color:#75715e\"\u003e# 初始为1*100的行向量,为了便于矩阵运算,使用transpose转置为列向量100*1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    m, n \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e shape(dataMatrix)\u003cspan style=\"color:#75715e\"\u003e# 得到矩阵大小\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    alpha \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0.001\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# 目标移动的步长\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    maxCycles \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e500\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# 最大迭代次数\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    weights \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e ones((n, \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e kin range(maxCycles):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 矩阵相乘,下面两行,计算真实类别与预测类别的差值,接下来就是按照该差值的方向调整回归系数\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        h \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e sigmoid(dataMatrix \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e weights)\u003cspan style=\"color:#75715e\"\u003e# 代表的不是一次乘积计算,事实上该运算包含了300次的乘积,变量h不是一个数,而是一个列向量,100\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        error \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (labelMat \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e h)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        weights \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e weights \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e alpha \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e dataMatrix\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003etranspose() \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e error\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e weights\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edefstocGradAscent0(dataMatIn, classLabels):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    m, n \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e shape(dataMatIn)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    alpha \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0.01\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    weights \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e ones(n)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e iin range(m):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        h \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e sigmoid(sum(dataMatIn[i] \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e weights))\u003cspan style=\"color:#75715e\"\u003e# h是向量\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        error \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e classLabels[i] \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e h\u003cspan style=\"color:#75715e\"\u003e# error是向量\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        weights \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e weights \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e alpha \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e error \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e dataMatIn[i]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e weights\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edefstocGradAscent1(dataMatIn, classLabels, numIter\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e150\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    改进的随机梯度上升算法\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :param dataMatIn:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :param classLabels:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :param numIter:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :return:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    m, n \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e shape(dataMatIn)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    weights \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e ones(n)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e jin range(numIter):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        dataIndex \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e range(m)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e iin range(m):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            alpha \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e (\u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e j \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e i) \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0.01\u003c/span\u003e\u003cspan style=\"color:#75715e\"\u003e# alpha每次迭代时需要调整\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            randIndex \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e int(random\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003euniform(\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e, len(dataIndex)))\u003cspan style=\"color:#75715e\"\u003e# 随机选取更新\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            h \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e sigmoid(sum(dataMatIn[randIndex] \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e weights))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            error \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e classLabels[randIndex] \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e h\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            weights \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e weights \u003cspan style=\"color:#f92672\"\u003e+\u003c/span\u003e alpha \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e error \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e dataMatIn[randIndex]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003edel\u003c/span\u003e (dataIndex[randIndex])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e weights\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edefplotBestFit(weights):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    画出数据集和Logistic回归最佳拟合直线的函数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :param weights:系数\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :return:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    weights \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e weights\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003egetA()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    dataMat, labelMat \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e loadDataSet()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    dataArr \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e array(dataMat)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    n \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e shape(dataArr)[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    xcord1 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ycord1 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    xcord2 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ycord2 \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e iin range(n):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e int(labelMat[i]) \u003cspan style=\"color:#f92672\"\u003e==\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            xcord1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(dataArr[i, \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            ycord1\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(dataArr[i, \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            xcord2\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(dataArr[i, \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            ycord2\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(dataArr[i, \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e])\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    fig \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e plt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003efigure()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ax \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e fig\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eadd_subplot(\u003cspan style=\"color:#ae81ff\"\u003e111\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ax\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003escatter(xcord1, ycord1, s\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e, c\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;red\u0026#39;\u003c/span\u003e, marker\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;s\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ax\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003escatter(xcord2, ycord2, s\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e, c\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;green\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    x \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e arange(\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e3.0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e3.0\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e0.1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    y \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003eweights[\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e weights[\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e] \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e x) \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e weights[\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ax\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eplot(x, y)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    plt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003exlabel(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;X1\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    plt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eylabel(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;X2\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    plt\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eshow()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edefclassifyVector(inX, weights):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    prob \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e sigmoid(sum(inX \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e weights))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e prob \u003cspan style=\"color:#f92672\"\u003e\u0026gt;\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0.5\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eelse\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edefcolicTest():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    疝气病马死亡分类测试\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :return:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    frTrain \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e open(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;horseColicTraining.txt\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    frTest \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e open(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;horseColicTest.txt\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    trainingSet \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    trainingLabels \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e linein frTrain\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ereadlines():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        currLine \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e line\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003estrip()\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esplit(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\t\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        lineArr \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e iin range(\u003cspan style=\"color:#ae81ff\"\u003e21\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            lineArr\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(float(currLine[i]))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        trainingSet\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(lineArr)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        trainingLabels\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(float(currLine[i]))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    trainWeights \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e stocGradAscent1(array(trainingSet), trainingLabels, \u003cspan style=\"color:#ae81ff\"\u003e500\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    errorCount \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    numTestVec \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e linein frTest\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003ereadlines():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        numTestVec \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        currLine \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e line\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003estrip()\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003esplit(\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\t\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        lineArr \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e []\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e iin range(\u003cspan style=\"color:#ae81ff\"\u003e21\u003c/span\u003e):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            lineArr\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eappend(float(currLine[i]))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e int(classifyVector(array(lineArr), trainWeights)) \u003cspan style=\"color:#f92672\"\u003e!=\u003c/span\u003e int(currLine[\u003cspan style=\"color:#ae81ff\"\u003e21\u003c/span\u003e]):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            errorCount \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    errorRate \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e (float(errorCount \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e numTestVec))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;the error rate of this test is: \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%f\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e errorRate\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e errorRate\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edefmultiTest():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    numTests \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e10\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    errorSum \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e kin range(numTests):\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e        errorSum \u003cspan style=\"color:#f92672\"\u003e+=\u003c/span\u003e colicTest()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;after \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%d\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e iterations the average error rate is: \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%f\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e%\u003c/span\u003e (numTests, errorSum \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e float(numTests))\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edeftestCal():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    dataArr, labelMat \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e loadDataSet()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    weights \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e gradAscent(dataArr, labelMat)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprint weights\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    得到一组回归系数,它确定了不同类别数据之间的分割线\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    [[ 4.12414349]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e     [ 0.48007329]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e     [-0.6168482 ]]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edeftestGradAscent():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    测试梯度上升算法,画图\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :return:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    dataArr, labelMat \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e loadDataSet()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    weights \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e gradAscent(dataArr, labelMat)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    plotBestFit(weights)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edeftestStocGradAscent0():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    测试随机梯度上升算法,画图\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :return:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    dataArr, labelMat \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e loadDataSet()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    weights \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e stocGradAscent0(dataArr, labelMat)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    plotBestFit(weights)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edeftestStocGradAscent1():\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    测试随机梯度上升算法,画图\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    :return:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    dataArr, labelMat \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e loadDataSet()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    weights \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e stocGradAscent1(dataArr, labelMat)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    plotBestFit(weights)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# testCal()#testGradAscent()# testStocGradAscent0()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emultiTest()\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"机器学习实战-学习笔记之Logistic"},{"content":"\u0026lt;机器学习实战\u0026gt;学习笔记之KNN-约会数据识别 项目地址:http://github.com/jimersylee/MachineLearningAction\n# -*- coding: utf-8 -*-\u0026#34;\u0026#34;\u0026#34; 约会数据kNN \u0026#34;\u0026#34;\u0026#34; from numpyimport * import matplotlib.pyplotas plt import operator defclassify0(inX, dataSet, labels, k): dataSetSize = dataSet.shape[0] diffMat = tile(inX, (dataSetSize, 1)) - dataSet sqDiffMat = diffMat ** 2 sqDistances = sqDiffMat.sum(axis=1) distances = sqDistances ** 0.5 sortedDistIndicies = distances.argsort() classCount = {} for iin range(k): voteIlabel = labels[sortedDistIndicies[i]] classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0] deffile2matrix(filename): fr = open(filename) numberOfLines = len(fr.readlines())# get the number of lines in the file returnMat = zeros((numberOfLines, 3))# prepare matrix to return classLabelVector = []# prepare labels return fr = open(filename) index = 0 for linein fr.readlines(): line = line.strip() listFromLine = line.split(\u0026#39;\\t\u0026#39;) returnMat[index, :] = listFromLine[0:3] classLabelVector.append(int(listFromLine[-1])) index += 1 return returnMat, classLabelVector defautoNorm(dataSet): \u0026#34;\u0026#34;\u0026#34; 归一化数值 因为计算距离的时候,差值最大的属性对计算结果的影响最大,如果对于此数据,我们认为权重一样,则需要将数据进行处理,将数值归一化 将属性的取值范围处理为0到1或者-1到1,使用下面的公式 newValue=(oldValue-min)/(max-min) :param dataSet:矩阵 :return:normDataSet:归一化后的矩阵 range:取值范围 minVals:最小值 \u0026#34;\u0026#34;\u0026#34; minVals = dataSet.min(0)# 0代表第一列,取得第一列最小值 maxVals = dataSet.max(0)# 取得第一列最大值 ranges = maxVals - minVals# 可能的取值范围 normDataSet = zeros(shape(dataSet))# 创建新的返回矩阵 m = dataSet.shape[0] normDataSet = dataSet - tile(minVals, (m, 1)) normDataSet = normDataSet / tile(ranges, (m, 1))# element wise dividereturn normDataSet, ranges, minVals defdatingClassTest(): hoRatio = 0.50# hold out 10% datingDataMat, datingLabels = file2matrix(\u0026#39;datingData/datingTestSet2.txt\u0026#39;)# load data setfrom file normMat, ranges, minVals = autoNorm(datingDataMat) m = normMat.shape[0] numTestVecs = int(m * hoRatio) errorCount = 0.0 for iin range(numTestVecs): classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 3) print \u0026#34;the classifier came back with: %d, the real answer is: %d\u0026#34; % (classifierResult, datingLabels[i]) if (classifierResult != datingLabels[i]): errorCount += 1.0 print \u0026#34;the total error rate is: %f\u0026#34; % (errorCount / float(numTestVecs)) print errorCount defshowNormal(): \u0026#34;\u0026#34;\u0026#34; 最基本散点图 没有样本类别标签的约会数据散点图.难以辨识途中的点究竟属于那个样本分类 :return: \u0026#34;\u0026#34;\u0026#34; datingDataMat, datingLables = file2matrix(\u0026#39;datingData/datingTestSet2.txt\u0026#39;) fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2]) plt.xlabel(\u0026#34;Percentage of Time Spent Playing Video Games\u0026#34;) plt.ylabel(\u0026#34;Liters of Icc Cream Consumed Per Week\u0026#34;) plt.show() defshowLable(): \u0026#34;\u0026#34;\u0026#34; 带有样本分类标签的约会数据散点图 虽然能够比较容易区分数据点丛书类别,但依然很那根据这张图得出结论性信息 :return: \u0026#34;\u0026#34;\u0026#34; datingDataMat, datingLables = file2matrix(\u0026#39;datingData/datingTestSet2.txt\u0026#39;) fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2], 15.0 * array(datingLables), 15.0 * array(datingLables)) ax.axis([-2, 25, -0.2, 2.0]) plt.xlabel(\u0026#34;Percentage of Time Spent Playing Video Games\u0026#34;) plt.ylabel(\u0026#34;Liters of Icc Cream Consumed Per Week\u0026#34;) plt.show() defshowClass(): \u0026#34;\u0026#34;\u0026#34; 显示不同的分类 标识了三个不同的样本分类区域,具有不同爱好的人其类别区域也不同 每年赢得的飞行常客里程数与玩游戏视频游戏所占百分比的约会数据散点图 约会数据有三个分类标签,通过途中展示的两个特征更容易区分数据点从属的类别 :return: \u0026#34;\u0026#34;\u0026#34; n = 1000# number of points to create xcord1 = []; ycord1 = [] xcord2 = []; ycord2 = [] xcord3 = []; ycord3 = [] markers = [] colors = [] fw = open(\u0026#39;datingData/datingTestSet.txt\u0026#39;, \u0026#39;w\u0026#39;) for iin range(n): [r0, r1] = random.standard_normal(2) myClass = random.uniform(0, 1) if (myClass \u0026lt;= 0.16): fFlyer = random.uniform(22000, 60000) tats = 3 + 1.6 * r1 markers.append(20) colors.append(2.1) classLabel = 1# \u0026#39;didntLike\u0026#39; xcord1.append(fFlyer); ycord1.append(tats) elif ((myClass \u0026gt; 0.16)and (myClass \u0026lt;= 0.33)): fFlyer = 6000 * r0 + 70000 tats = 10 + 3 * r1 + 2 * r0 markers.append(20) colors.append(1.1) classLabel = 1# \u0026#39;didntLike\u0026#39;if (tats \u0026lt; 0): tats = 0 if (fFlyer \u0026lt; 0): fFlyer = 0 xcord1.append(fFlyer); ycord1.append(tats) elif ((myClass \u0026gt; 0.33)and (myClass \u0026lt;= 0.66)): fFlyer = 5000 * r0 + 10000 tats = 3 + 2.8 * r1 markers.append(30) colors.append(1.1) classLabel = 2# \u0026#39;smallDoses\u0026#39;if (tats \u0026lt; 0): tats = 0 if (fFlyer \u0026lt; 0): fFlyer = 0 xcord2.append(fFlyer); ycord2.append(tats) else: fFlyer = 10000 * r0 + 35000 tats = 10 + 2.0 * r1 markers.append(50) colors.append(0.1) classLabel = 3# \u0026#39;largeDoses\u0026#39;if (tats \u0026lt; 0): tats = 0 if (fFlyer \u0026lt; 0): fFlyer = 0 xcord3.append(fFlyer); ycord3.append(tats) fw.close() fig = plt.figure() ax = fig.add_subplot(111) # ax.scatter(xcord,ycord, c=colors, s=markers) type1 = ax.scatter(xcord1, ycord1, s=20, c=\u0026#39;red\u0026#39;) type2 = ax.scatter(xcord2, ycord2, s=30, c=\u0026#39;green\u0026#39;) type3 = ax.scatter(xcord3, ycord3, s=50, c=\u0026#39;blue\u0026#39;) ax.legend([type1, type2, type3], [\u0026#34;Did Not Like\u0026#34;, \u0026#34;Liked in Small Doses\u0026#34;, \u0026#34;Liked in Large Doses\u0026#34;], loc=2) ax.axis([-5000, 100000, -2, 25]) plt.xlabel(\u0026#39;Frequent Flyier Miles Earned Per Year\u0026#39;) plt.ylabel(\u0026#39;Percentage of Time Spent Playing Video Games\u0026#39;) plt.show() showNormal() showLable() showClass() datingClassTest() ","permalink":"https://blog.jimersylee.com/posts/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%AE%9E%E6%88%98-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B9%8Bknn-%E7%BA%A6%E4%BC%9A%E6%95%B0%E6%8D%AE%E8%AF%86%E5%88%AB/","summary":"\u003ch1 id=\"机器学习实战学习笔记之knn-约会数据识别\"\u003e\u0026lt;机器学习实战\u0026gt;学习笔记之KNN-约会数据识别\u003c/h1\u003e\n\u003cp\u003e项目地址:\u003ca href=\"http://github.com/jimersylee/MachineLearningAction\"\u003ehttp://github.com/jimersylee/MachineLearningAction\u003c/a\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# -*- coding: utf-8 -*-\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e约会数据kNN\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003efrom numpyimport *\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eimport matplotlib.pyplotas plt\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eimport operator\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003edefclassify0(inX, dataSet, labels, k):\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    dataSetSize = dataSet.shape[0]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    diffMat = tile(inX, (dataSetSize, 1)) - dataSet\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    sqDiffMat = diffMat ** 2\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    sqDistances = sqDiffMat.sum(axis=1)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    distances = sqDistances ** 0.5\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    sortedDistIndicies = distances.argsort()\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    classCount = \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e{}\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003efor iin range(k):\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        voteIlabel = labels[sortedDistIndicies[i]]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003ereturn sortedClassCount[0][0]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003edeffile2matrix(filename):\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    fr = open(filename)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    numberOfLines = len(fr.readlines())# get the number of lines in the file\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    returnMat = zeros((numberOfLines, 3))# prepare matrix to return\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    classLabelVector = []# prepare labels return\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    fr = open(filename)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    index = 0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003efor linein fr.readlines():\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        line = line.strip()\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        listFromLine = line.split(\u0026#39;\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e\\t\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#39;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        returnMat[index, :] = listFromLine[0:3]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        classLabelVector.append(int(listFromLine[-1]))\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        index += 1\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003ereturn returnMat, classLabelVector\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003edefautoNorm(dataSet):\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    归一化数值\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    因为计算距离的时候,差值最大的属性对计算结果的影响最大,如果对于此数据,我们认为权重一样,则需要将数据进行处理,将数值归一化\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    将属性的取值范围处理为0到1或者\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e到1,使用下面的公式\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    newValue\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e(oldValue\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003emin)\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e(max\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003emin)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    :param dataSet:矩阵\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    :\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e:normDataSet:归一化后的矩阵\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            range:取值范围\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e            minVals:最小值\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    minVals = dataSet.min(0)# 0代表第一列,取得第一列最小值\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    maxVals = dataSet.max(0)# 取得第一列最大值\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    ranges = maxVals - minVals# 可能的取值范围\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    normDataSet = zeros(shape(dataSet))# 创建新的返回矩阵\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    m = dataSet.shape[0]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    normDataSet = dataSet - tile(minVals, (m, 1))\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    normDataSet = normDataSet / tile(ranges, (m, 1))# element wise dividereturn normDataSet, ranges, minVals\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003edefdatingClassTest():\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    hoRatio = 0.50# hold out 10%\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    datingDataMat, datingLabels = file2matrix(\u0026#39;datingData/datingTestSet2.txt\u0026#39;)# load data setfrom file\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    normMat, ranges, minVals = autoNorm(datingDataMat)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    m = normMat.shape[0]\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    numTestVecs = int(m * hoRatio)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    errorCount = 0.0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003efor iin range(numTestVecs):\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 3)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eprint \u0026#34;the classifier came back with: \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%d\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e, the real answer is: \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%d\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34; % (classifierResult, datingLabels[i])\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eif (classifierResult != datingLabels[i]): errorCount += 1.0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eprint \u0026#34;the total error rate is: \u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e%f\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34; % (errorCount / float(numTestVecs))\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eprint errorCount\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003edefshowNormal():\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    最基本散点图\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    没有样本类别标签的约会数据散点图\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003e难以辨识途中的点究竟属于那个样本分类\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    :\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    datingDataMat, datingLables = file2matrix(\u0026#39;datingData/datingTestSet2.txt\u0026#39;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    fig = plt.figure()\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    ax = fig.add_subplot(111)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2])\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    plt.xlabel(\u0026#34;Percentage of Time Spent Playing Video Games\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    plt.ylabel(\u0026#34;Liters of Icc Cream Consumed Per Week\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    plt.show()\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003edefshowLable():\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    带有样本分类标签的约会数据散点图\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    虽然能够比较容易区分数据点丛书类别,但依然很那根据这张图得出结论性信息\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    :\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    datingDataMat, datingLables = file2matrix(\u0026#39;datingData/datingTestSet2.txt\u0026#39;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    fig = plt.figure()\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    ax = fig.add_subplot(111)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2], 15.0 * array(datingLables), 15.0 * array(datingLables))\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    ax.axis([-2, 25, -0.2, 2.0])\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    plt.xlabel(\u0026#34;Percentage of Time Spent Playing Video Games\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    plt.ylabel(\u0026#34;Liters of Icc Cream Consumed Per Week\u0026#34;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    plt.show()\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003edefshowClass():\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    显示不同的分类\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    标识了三个不同的样本分类区域,具有不同爱好的人其类别区域也不同\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    每年赢得的飞行常客里程数与玩游戏视频游戏所占百分比的约会数据散点图\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    约会数据有三个分类标签,通过途中展示的两个特征更容易区分数据点从属的类别\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    :\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    n = 1000# number of points to create\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    xcord1 = [];\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    ycord1 = []\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    xcord2 = [];\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    ycord2 = []\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    xcord3 = [];\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    ycord3 = []\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    markers = []\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    colors = []\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    fw = open(\u0026#39;datingData/datingTestSet.txt\u0026#39;, \u0026#39;w\u0026#39;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003efor iin range(n):\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        [r0, r1] = random.standard_normal(2)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e        myClass = random.uniform(0, 1)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eif (myClass \u0026lt;= 0.16):\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            fFlyer = random.uniform(22000, 60000)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            tats = 3 + 1.6 * r1\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            markers.append(20)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            colors.append(2.1)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            classLabel = 1# \u0026#39;didntLike\u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            xcord1.append(fFlyer);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            ycord1.append(tats)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eelif ((myClass \u0026gt; 0.16)and (myClass \u0026lt;= 0.33)):\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            fFlyer = 6000 * r0 + 70000\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            tats = 10 + 3 * r1 + 2 * r0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            markers.append(20)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            colors.append(1.1)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            classLabel = 1# \u0026#39;didntLike\u0026#39;if (tats \u0026lt; 0): tats = 0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eif (fFlyer \u0026lt; 0): fFlyer = 0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            xcord1.append(fFlyer);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            ycord1.append(tats)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eelif ((myClass \u0026gt; 0.33)and (myClass \u0026lt;= 0.66)):\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            fFlyer = 5000 * r0 + 10000\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            tats = 3 + 2.8 * r1\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            markers.append(30)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            colors.append(1.1)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            classLabel = 2# \u0026#39;smallDoses\u0026#39;if (tats \u0026lt; 0): tats = 0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eif (fFlyer \u0026lt; 0): fFlyer = 0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            xcord2.append(fFlyer);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            ycord2.append(tats)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eelse:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            fFlyer = 10000 * r0 + 35000\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            tats = 10 + 2.0 * r1\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            markers.append(50)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            colors.append(0.1)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            classLabel = 3# \u0026#39;largeDoses\u0026#39;if (tats \u0026lt; 0): tats = 0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eif (fFlyer \u0026lt; 0): fFlyer = 0\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            xcord3.append(fFlyer);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e            ycord3.append(tats)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    fw.close()\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    fig = plt.figure()\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    ax = fig.add_subplot(111)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e# ax.scatter(xcord,ycord, c=colors, s=markers)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    type1 = ax.scatter(xcord1, ycord1, s=20, c=\u0026#39;red\u0026#39;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    type2 = ax.scatter(xcord2, ycord2, s=30, c=\u0026#39;green\u0026#39;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    type3 = ax.scatter(xcord3, ycord3, s=50, c=\u0026#39;blue\u0026#39;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    ax.legend([type1, type2, type3], [\u0026#34;Did Not Like\u0026#34;, \u0026#34;Liked in Small Doses\u0026#34;, \u0026#34;Liked in Large Doses\u0026#34;], loc=2)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    ax.axis([-5000, 100000, -2, 25])\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    plt.xlabel(\u0026#39;Frequent Flyier Miles Earned Per Year\u0026#39;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    plt.ylabel(\u0026#39;Percentage of Time Spent Playing Video Games\u0026#39;)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e    plt.show()\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eshowNormal()\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eshowLable()\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003eshowClass()\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003edatingClassTest()\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"机器学习实战 学习笔记之KNN-约会数据识别"},{"content":"原文出处： 江南白衣（@江南白衣Calvin)\n小学生作文的开头：光阴似箭，日月如梭…..半年过去了，床底下又多了不少书，更新一个2.0版。\n自从技术书的书架设定为”床底下“之后，又多了很多买书的空间。中国什么都贵，就是书便宜。\n很多书没有全部看完，看一部分觉得值得推荐就放上来了，但在碎片化的阅读下难免错评，不定期更新修正。\n书架主要针对Java后端开发。\n更偏爱那些能用简短流畅的话，把少壮不努力的程序员所需的基础补回来的薄书，而有些教课书可能很著名，但干涩枯燥，喋喋不休的把你带回到大学课堂上昏昏欲睡，不录。\n操作系统与网络的书 《Linux内核设计与实现 – Linux Kernel Development 第3版》 Robert Love用最薄的篇幅，顺畅的文字将Linux内核主要的算法讲清楚了，比《深入理解Linux内核》，《深入Linux内核架构》之类厚厚的全是代码的，不是专门的内核程序员看这本足够了。\n《Linux系统编程 第2版》 继续是Robert Love，比起APUE也是以薄见长，专门针对重要的系统调用讲解。\n《性能之巅》 操作系统的性能调优、监控、工具和方法论，看这本就够了，足够厚。还有本薄一点的，东抄西编格调没那么高的叫《Linux性能优化大师》。\n《TCP/IP详解 卷1:协议》 这么多年过去了，TCP的书好像还是只有这一本，有点旧了，看了也还是半懂不懂。后人在2011年写了第二版，看目录清晰明了与时俱进了很多，机械工业正在翻译。\n《现代操作系统 第3版》 如果看LKD未尽兴，可以回头看看这本基础概念，感觉比那本枯燥的《操作系统概念》(恐龙书)读起来舒服。\nPS. 《UNIX环境高级编程》和《UNIX网络编程》，APUE和UNP更多作为一本超厚工具书存在。《Unix 编程艺术》，扯的都是闲篇，厚厚的一本其实略读一下就行。\n算法的书 《数据结构与算法分析-Java语言描述 第2版》 够薄，数据结构与算法分析的点基本都涵盖了，而且示例代码还是Java写的。\n《算法 第4版》 可与上一本对比着读，厚一些，也多些图，但知识点没上面的全，也是Java的。\n《算法设计与分析基础 第3版》 数学系偏爱无比枯燥很多公式的《算法导论》， 计算机系喜欢这本实用主义的典型。翻开就看到一段很文艺很贴心的话：“效率能用数学的严密性进行精确定义，而简单性就像“美”一样，很大程度取决于审视者 的眼光。简单的算法更容易理解和实现，因而相应的程序也往往更少的Bug。当然对于简单性的美学诉求也是让人无法抗拒的。” PS. 《数学之美》、《编程珠玑》，都是专栏文章，讲得并不系统，可以当兴趣读物来看。\n架构设计的书 《恰如其分的软件架构 – 风险驱动的设计方法》 由于人类与生俱来的惰性，计算机原本科学的、精准的设计方式，有了敏捷的借口之后就很难再维持了。本书就是在这种背景下，提出由风险来决定设计的度，当然，这个风险是广义的。除了开始的风险驱动部分，其余部分就是规规矩矩标标准准的架构师教科书。\n《软件系统架构：使用视点和视角与利益相关者合作 第2版》 也是教科书，最难得的是，这本老书在十年后的去年升级了第二版。\n《程序员必读之软件架构 – Software Architecture for Developers》 作者维护着codingthearchitecture.com 。不过中文书名叫“必读”有点过。薄书里的两部分内容：\n编码的架构师：一直是我的职业模板，我记的笔记。 架构的表达： 当年我觉得RUP的4+1 UML视图不足以表达系统时，Simon Brown的模板给了很好的过渡范例。 《发布！软件的设计与部署 – Release It!: Design and Deploy Production-Ready Software 》 关于高可靠性的软件，学校里不会教，出来社会却要面对的那部分，英文的原标题更清晰。\n《大型网站技术架构:核心原理与案例分析》 淘宝出品，大型互联网站的科普入门书。\n《大规模分布式存储系统》 继续淘宝出品，分布式文件系统与数据库的科普入门书。\n《大数据日知录》 前几年参加各种技术会议，CAP，最终一致性，RWN，向量时钟，Paxos，一致性哈希，Gossip什么的能灌你一耳朵。而现在，你只要在家安安静静的看书就够了。不过这个领域发展太快，期望它可以持续出新版。\nPS. 关于设计模式，我以前曾经有过很多很多本，GOF23啦，企业应用架构模式啦，EIP啦， POSA 5卷本啦，反模式啦，JavaEE/SOA/Restful的模式啦。但现在觉得对新人来说，一本Java写的《Head First 设计模式》，知道什么叫设计模式就够了。\n语言的书 《Java并发编程实战》 人手一本不用多说了。\n《深入理解 Java 虚拟机 第2版》 深入理解虚拟机并不是那么难，Java程序员都该看看，很多知识其实是必须的。另外还有几本类似主题的，忽然一下子都出来了。\n《Java性能优化权威指南》 虽然后面的章节好像用处不大，前面有些部分还是值得看，不过Gosling说圣经有点过了。\n《写给大忙人看的Java SE 8》 事实上，为了保持兼容性，很多项目还保持在JDK5/6上，这本书一次过将JDK7/JDK8的更新讲了，虽然讲得还不全。\n《函数式编程思维》 Java8终于有函数式的影子了，不要落后太多，开始尝试跟上节奏。这本书是TW的Neal Ford面向Java程序员写的薄书。\n《七周七语言》 《七周七X》系列的开山之作，可能也是最好的一本。\nPS. 《Effective Java》外界一致推崇，但有点太过誉了。另外《Thinking in Java》有点旧了，而且作者思路随意，译者语言晦涩，新程序员还是建议同时再看两卷《Java核心技术 － Core Java》。\n具体技术的书 《Docker: 容器与容器云》 这本书叫Docker一本就够了，的确够了，在那些Docker操作指南书之上，不想着改Docker代码的看它就够了，别想着什么《Docker源码分析》。\n《Redis设计与实现》 用Redis的工程师桌面必备吧，不用再多说了。\n《从Paxos到Zookeeper》 Zookeeper的书，淘宝出品。\n《Spark技术内幕》 深度与厚度之间，选了这本200页的薄书，一样有很多的原理与代码解释，但不会像有的书那样贴20行代码只写一行字。\n《Netty权威指南 第2版》 虽然网上的吐槽较多，但Netty 快速入门也只有这一本了。\n程序员的自我修养 PS. 最近没买什么新书，随便说点旧书:\n《程序员修炼之道-从小工到专家》，Pragmatic Programmer-注重实效的程序员开山之作，翻译的马达维文笔也和熊节一样好。\n《代码整洁之道》和 《程序员的职业素养》，英文名是很相近的《Clean Code》和 《Clean Coder》，应该接替《代码大全2》成为必看的系列，因为后者太厚了，而且也有不少过时的东西，要自己去过滤。\n《重构》很厚，但最有价值就是前面几章的洗脑篇，具体实作不如薄薄的《重构手册》。\n关于敏捷的书，最开始的那本《解析极限编程–拥抱变化》就很好，再随便找本Scrum的流程看看就够了，《敏捷开发的艺术》也不错。\n《布道之道 – Driving Technical Change:Why People on Your Team Don’t Act on Good Ideas,and How to Convince Them They Should》，经常在组织里推行新技术的同学可以看下，七种怀疑论者模式，脑海中一幅幅熟悉的面孔。\nPS. 温伯格的书网上很推崇，《成为技术领导者》之类的，但我觉得年代太远，读起来其实没多大意思，一两个鸡汤观点还要自己从书里慢慢淘，有那功夫不如看点别的。\n没有覆盖到的内容 数据库如MySQL，我们DBA太专业，自己没机会搞。\n欢迎大家在评论里补充。\n","permalink":"https://blog.jimersylee.com/posts/%E6%88%91%E7%9A%84%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91%E4%B9%A6%E6%9E%B62015%E7%89%88/","summary":"\u003cp\u003e原文出处： 江南白衣（@江南白衣Calvin)\u003c/p\u003e\n\u003cp\u003e小学生作文的开头：光阴似箭，日月如梭…..半年过去了，床底下又多了不少书，更新一个2.0版。\u003c/p\u003e\n\u003cp\u003e自从技术书的书架设定为”床底下“之后，又多了很多买书的空间。中国什么都贵，就是书便宜。\u003c/p\u003e\n\u003cp\u003e很多书没有全部看完，看一部分觉得值得推荐就放上来了，但在碎片化的阅读下难免错评，不定期更新修正。\u003c/p\u003e\n\u003cp\u003e书架主要针对Java后端开发。\u003c/p\u003e\n\u003cp\u003e更偏爱那些能用简短流畅的话，把少壮不努力的程序员所需的基础补回来的薄书，而有些教课书可能很著名，但干涩枯燥，喋喋不休的把你带回到大学课堂上昏昏欲睡，不录。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e操作系统与网络的书\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e《Linux内核设计与实现 – Linux Kernel Development 第3版》 Robert Love用最薄的篇幅，顺畅的文字将Linux内核主要的算法讲清楚了，比《深入理解Linux内核》，《深入Linux内核架构》之类厚厚的全是代码的，不是专门的内核程序员看这本足够了。\u003c/p\u003e\n\u003cp\u003e《Linux系统编程 第2版》 继续是Robert Love，比起APUE也是以薄见长，专门针对重要的系统调用讲解。\u003c/p\u003e\n\u003cp\u003e《性能之巅》 操作系统的性能调优、监控、工具和方法论，看这本就够了，足够厚。还有本薄一点的，东抄西编格调没那么高的叫《Linux性能优化大师》。\u003c/p\u003e\n\u003cp\u003e《TCP/IP详解 卷1:协议》 这么多年过去了，TCP的书好像还是只有这一本，有点旧了，看了也还是半懂不懂。后人在2011年写了第二版，看目录清晰明了与时俱进了很多，机械工业正在翻译。\u003c/p\u003e\n\u003cp\u003e《现代操作系统 第3版》 如果看LKD未尽兴，可以回头看看这本基础概念，感觉比那本枯燥的《操作系统概念》(恐龙书)读起来舒服。\u003c/p\u003e\n\u003cp\u003ePS. 《UNIX环境高级编程》和《UNIX网络编程》，APUE和UNP更多作为一本超厚工具书存在。《Unix 编程艺术》，扯的都是闲篇，厚厚的一本其实略读一下就行。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e算法的书\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e《数据结构与算法分析-Java语言描述 第2版》 够薄，数据结构与算法分析的点基本都涵盖了，而且示例代码还是Java写的。\u003c/p\u003e\n\u003cp\u003e《算法 第4版》 可与上一本对比着读，厚一些，也多些图，但知识点没上面的全，也是Java的。\u003c/p\u003e\n\u003cp\u003e《算法设计与分析基础 第3版》 数学系偏爱无比枯燥很多公式的《算法导论》， 计算机系喜欢这本实用主义的典型。翻开就看到一段很文艺很贴心的话：“效率能用数学的严密性进行精确定义，而简单性就像“美”一样，很大程度取决于审视者 的眼光。简单的算法更容易理解和实现，因而相应的程序也往往更少的Bug。当然对于简单性的美学诉求也是让人无法抗拒的。” PS. 《数学之美》、《编程珠玑》，都是专栏文章，讲得并不系统，可以当兴趣读物来看。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e架构设计的书\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e《恰如其分的软件架构 – 风险驱动的设计方法》 由于人类与生俱来的惰性，计算机原本科学的、精准的设计方式，有了敏捷的借口之后就很难再维持了。本书就是在这种背景下，提出由风险来决定设计的度，当然，这个风险是广义的。除了开始的风险驱动部分，其余部分就是规规矩矩标标准准的架构师教科书。\u003c/p\u003e\n\u003cp\u003e《软件系统架构：使用视点和视角与利益相关者合作 第2版》 也是教科书，最难得的是，这本老书在十年后的去年升级了第二版。\u003c/p\u003e\n\u003cp\u003e《程序员必读之软件架构 – Software Architecture for Developers》 作者维护着codingthearchitecture.com 。不过中文书名叫“必读”有点过。薄书里的两部分内容：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e编码的架构师：一直是我的职业模板，我记的笔记。\u003c/li\u003e\n\u003cli\u003e架构的表达： 当年我觉得RUP的4+1 UML视图不足以表达系统时，Simon Brown的模板给了很好的过渡范例。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e《发布！软件的设计与部署 – Release It!: Design and Deploy Production-Ready Software 》 关于高可靠性的软件，学校里不会教，出来社会却要面对的那部分，英文的原标题更清晰。\u003c/p\u003e","title":"我的后端开发书架2015版"},{"content":"机器学习和数据挖掘的推荐书单 本文作者： 伯乐在线 - 天才白痴梦 。\n有了这些书，再也不愁下了班没妹纸该咋办了。慢慢来，认真学，揭开机器学习和数据挖掘这一神秘的面纱吧！\n《机器学习实战》： 本书第一部分主要介绍机器学习基础，以及如何利用算法进行分类，并逐步介绍了多种经典的监督学习算法，如k近邻算法、朴素贝叶斯算法、Logistic回 归算法、支持向量机、AdaBoost集成方法、基于树的回归算法和分类回归树（CART）算法等。第三部分则重点介绍无监督学习及其一些主要算法：k均 值聚类算法、Apriori算法、FP-Growth算法。第四部分介绍了机器学习算法的一些附属工具。\n全书通过精心编排的实例，切入日常工作任务，摒弃学术化语言，利用高效的可复用Python代码来阐释如何处理统计数据，进行数据分析及可视化。通 过各种实例，读者可从中学会机器学习的核心算法，并能将其运用于一些策略性任务中，如分类、预测、推荐。另外，还可用它们来实现一些更高级的功能，如汇总 和简化等。\n之前看过一部分这本书，但是实习工作涉及到用Java代码处理数据，所以暂时先搁一下，目前正在李航的那本书。\n《数据挖掘-实用机器学习技术》： 本书介绍数据挖掘的基本理论与实践方法。主要内容包括：各种模型(决策树，关联规则、线性模型、聚类、贝叶斯网以及神经网络)以及在实践中的运用，所存任 缺陷的分析。安全地清理数据集、建立以及评估模型的预测质量的方法，并且提供了一个公开的数据挖掘工作平台Weka。Weka系统拥有进行数据挖掘仟务的 图形用户界面，有助于理解模型，是一个实用并且深受欢迎的工具。\n《数据挖掘：概念与技术》： 本书全面地讲述数据挖掘领域的重要知识和技术创新。在第1版内容相当全面的基础上，第2版展示了该领域的最新研究成果，例如挖掘流、时序和序列数据以及挖 掘时间空间、多媒体、文本和Web数据。本书可作为数据挖掘和知识发现领域的教师、研究人员和开发人员的一本必读书。\n《统计学习基础 数据挖掘、推理与预测》：尽管应用的是统计学方法，但强调的是概念，而不是数学。许多例子附以彩图。《统计学习基础:数据挖掘、推 理与预测》内容广泛，从有指导的学习（预测）到无指导的学习，应有尽有。包括神经网络、支持向量机、分类树和提升等主题，是同类书籍中介绍得最全面的。计 算和信息技术的飞速发展带来了医学、生物学、财经和营销等诸多领域的海量数据。理解这些数据是一种挑战，这导致了统计学领域新工具的发展，并延伸到诸如数 据挖掘、机器学习和生物信息学等新领域。\n《机器学习》 （Mitchell）：展示了机器学习中核心的算法和理论，并阐明了算法的运行过程。《机器学习》综合了许多的研究成果，例如统计学、人工智能、哲学、信 息论、生物学、认知科学、计算复杂性和控制论等，并以此来理解问题的背景、算法和其中的隐含假定。《机器学习》可作为计算机专业 本科生、研究生教材，也 可作为相关领域研究人员、教师的参考书。\n《统计学习方法》： 本书全面系统地介绍了统计学习的主要方法，特别是监督学习方法，包括感知机、k近邻法、朴素贝叶斯法、决策树、逻辑斯谛回归与最大熵模型、支持向量机、提 升方法、em算法、隐马尔可夫模型和条件随机场等。除第1章概论和最后一章总结外，每章介绍一种方法。叙述从具体问题或实例入手，由浅入深，阐明思路，给 出必要的数学推导，便于读者掌握统计学习方法的实质，学会运用。为满足读者进一步学习的需要，书中还介绍了一些相关研究，给出了少量习题，列出了主要参考 文献。\n《机器学习导论》：对机器学习的定义和应用实例进行了介绍，涵盖了监督学习。贝叶斯决策理论。参数方法、多元方法、维度归约、聚类、非参数方法、决策树。线性判别式、多层感知器，局部模型、隐马尔可夫模型。分类算法评估和比较，组合多学习器以及增强学习等。\n《机器学习及其应用》：全书共分14章，内容分别涉及因果推断、流形学习与降维、迁移学习、类别不平衡学习、演化聚类、多标记学习、排序学习、半监督学习等技术和协同过滤、社区推荐、机器翻译等应用，以及互联网应用对机器学习技术需求的探讨。\n《模式分类》第二版：除了保留了第1版的关于统计模式识别和结构模式识别的主要内容以外，读者将会发现新增了许多近25年来的新理论和新方法，其中包括神经网络、机器学习、数据挖掘、进化计算、不变量理论、隐马尔可夫模型、统计学习理论和支持向量机等。\n《推荐系统实践》：过大量代码和图表全面系统地阐述了和推荐系统有关的理论基础，介绍了评价推荐系统优劣的各种标准(比如覆盖率、满意度)和方法(比如AB测试)，总结了当今互联网领域中各种和推荐有关的产品和服务。\n《深入搜索引擎：海量信息的压缩、索引和查询》：理论和实践并重，深入浅出地给出了海量信息数据处理的整套解决方案，包括压缩、索引和查询的方方面面。其最大的特色在于不仅仅满足信息检索理论学习的需要，更重要的是给出了实践中可能面对的各种问题及其解决方法。\n《概率论与数理统计》：这本书不用过多介绍了吧，普遍大学里大一时期的教科书，只恨当年没听课啊，现在正在慢慢啃。。。\n《大数据：互联网大规模数据挖掘与分布式处理》：主要内容包括分布式文件系统、相似性搜索、搜索引擎技术、频繁项集挖掘、聚类算法、广告管理及推荐系统。\n《Web数据挖掘》： 信息检索领域的书籍，该书深入讲解了从大量非结构化Web数据中提取和产生知识的技术。书中首先论述了Web的基础（包括Web信息采集机制、Web标引 机制以及基于关键字或基于相似性搜索机制），然后系统地描述了Web挖掘的基础知识，着重介绍基于超文本的机器学习和数据挖掘方法，如聚类、协同过滤、监 督学习、半监督学习，最后讲述了这些基本原理在Web挖掘中的应用。《Web数据挖掘》为读者提供了坚实的技术背景和最新的知识。\n《数据之巅》：对大数据追根溯源，提出当前信息技术的发展，已经让中国获得了后发优势，中国要在大数据时代的全球竞争中胜出，必须把大数据从科技符号提升成为文化符号，在全社会倡导数据文化。\n《深入浅出统计学》：本书涵盖的知识点包括：信息可视化、概率计算、几何分布、二项分布及泊松分布、正态分布、统计抽样、置信区间的构建、假设检验、卡方分布、相关与回归等等，完整涵盖AP考试范围。\n《矩阵分析》： 本书从数学分析的角度论述矩阵分析的经典方法和现代方法，取材新，有一定的深度，并给出在多元微积分、复分析、微分方程、量优化、逼近理论中的许多重要应 用。主要内容包括：特征值、特征向量和相似性，酉等价和正规矩阵，标准形，Hermite矩阵和对称矩阵，向量范数和矩阵范数，特征值和估计和扰动，正定 矩阵，非负矩阵。\n","permalink":"https://blog.jimersylee.com/posts/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%92%8C%E6%95%B0%E6%8D%AE%E6%8C%96%E6%8E%98%E7%9A%84%E6%8E%A8%E8%8D%90%E4%B9%A6%E5%8D%95/","summary":"\u003ch1 id=\"机器学习和数据挖掘的推荐书单\"\u003e机器学习和数据挖掘的推荐书单\u003c/h1\u003e\n\u003cp\u003e本文作者： 伯乐在线 - 天才白痴梦 。\u003c/p\u003e\n\u003cp\u003e有了这些书，再也不愁下了班没妹纸该咋办了。慢慢来，认真学，揭开机器学习和数据挖掘这一神秘的面纱吧！\u003c/p\u003e\n\u003cp\u003e《机器学习实战》： 本书第一部分主要介绍机器学习基础，以及如何利用算法进行分类，并逐步介绍了多种经典的监督学习算法，如k近邻算法、朴素贝叶斯算法、Logistic回 归算法、支持向量机、AdaBoost集成方法、基于树的回归算法和分类回归树（CART）算法等。第三部分则重点介绍无监督学习及其一些主要算法：k均 值聚类算法、Apriori算法、FP-Growth算法。第四部分介绍了机器学习算法的一些附属工具。\u003c/p\u003e\n\u003cp\u003e全书通过精心编排的实例，切入日常工作任务，摒弃学术化语言，利用高效的可复用Python代码来阐释如何处理统计数据，进行数据分析及可视化。通 过各种实例，读者可从中学会机器学习的核心算法，并能将其运用于一些策略性任务中，如分类、预测、推荐。另外，还可用它们来实现一些更高级的功能，如汇总 和简化等。\u003c/p\u003e\n\u003cp\u003e之前看过一部分这本书，但是实习工作涉及到用Java代码处理数据，所以暂时先搁一下，目前正在李航的那本书。\u003c/p\u003e\n\u003cp\u003e《数据挖掘-实用机器学习技术》： 本书介绍数据挖掘的基本理论与实践方法。主要内容包括：各种模型(决策树，关联规则、线性模型、聚类、贝叶斯网以及神经网络)以及在实践中的运用，所存任 缺陷的分析。安全地清理数据集、建立以及评估模型的预测质量的方法，并且提供了一个公开的数据挖掘工作平台Weka。Weka系统拥有进行数据挖掘仟务的 图形用户界面，有助于理解模型，是一个实用并且深受欢迎的工具。\u003c/p\u003e\n\u003cp\u003e《数据挖掘：概念与技术》： 本书全面地讲述数据挖掘领域的重要知识和技术创新。在第1版内容相当全面的基础上，第2版展示了该领域的最新研究成果，例如挖掘流、时序和序列数据以及挖 掘时间空间、多媒体、文本和Web数据。本书可作为数据挖掘和知识发现领域的教师、研究人员和开发人员的一本必读书。\u003c/p\u003e\n\u003cp\u003e《统计学习基础 数据挖掘、推理与预测》：尽管应用的是统计学方法，但强调的是概念，而不是数学。许多例子附以彩图。《统计学习基础:数据挖掘、推 理与预测》内容广泛，从有指导的学习（预测）到无指导的学习，应有尽有。包括神经网络、支持向量机、分类树和提升等主题，是同类书籍中介绍得最全面的。计 算和信息技术的飞速发展带来了医学、生物学、财经和营销等诸多领域的海量数据。理解这些数据是一种挑战，这导致了统计学领域新工具的发展，并延伸到诸如数 据挖掘、机器学习和生物信息学等新领域。\u003c/p\u003e\n\u003cp\u003e《机器学习》 （Mitchell）：展示了机器学习中核心的算法和理论，并阐明了算法的运行过程。《机器学习》综合了许多的研究成果，例如统计学、人工智能、哲学、信 息论、生物学、认知科学、计算复杂性和控制论等，并以此来理解问题的背景、算法和其中的隐含假定。《机器学习》可作为计算机专业 本科生、研究生教材，也 可作为相关领域研究人员、教师的参考书。\u003c/p\u003e\n\u003cp\u003e《统计学习方法》： 本书全面系统地介绍了统计学习的主要方法，特别是监督学习方法，包括感知机、k近邻法、朴素贝叶斯法、决策树、逻辑斯谛回归与最大熵模型、支持向量机、提 升方法、em算法、隐马尔可夫模型和条件随机场等。除第1章概论和最后一章总结外，每章介绍一种方法。叙述从具体问题或实例入手，由浅入深，阐明思路，给 出必要的数学推导，便于读者掌握统计学习方法的实质，学会运用。为满足读者进一步学习的需要，书中还介绍了一些相关研究，给出了少量习题，列出了主要参考 文献。\u003c/p\u003e\n\u003cp\u003e《机器学习导论》：对机器学习的定义和应用实例进行了介绍，涵盖了监督学习。贝叶斯决策理论。参数方法、多元方法、维度归约、聚类、非参数方法、决策树。线性判别式、多层感知器，局部模型、隐马尔可夫模型。分类算法评估和比较，组合多学习器以及增强学习等。\u003c/p\u003e\n\u003cp\u003e《机器学习及其应用》：全书共分14章，内容分别涉及因果推断、流形学习与降维、迁移学习、类别不平衡学习、演化聚类、多标记学习、排序学习、半监督学习等技术和协同过滤、社区推荐、机器翻译等应用，以及互联网应用对机器学习技术需求的探讨。\u003c/p\u003e\n\u003cp\u003e《模式分类》第二版：除了保留了第1版的关于统计模式识别和结构模式识别的主要内容以外，读者将会发现新增了许多近25年来的新理论和新方法，其中包括神经网络、机器学习、数据挖掘、进化计算、不变量理论、隐马尔可夫模型、统计学习理论和支持向量机等。\u003c/p\u003e\n\u003cp\u003e《推荐系统实践》：过大量代码和图表全面系统地阐述了和推荐系统有关的理论基础，介绍了评价推荐系统优劣的各种标准(比如覆盖率、满意度)和方法(比如AB测试)，总结了当今互联网领域中各种和推荐有关的产品和服务。\u003c/p\u003e\n\u003cp\u003e《深入搜索引擎：海量信息的压缩、索引和查询》：理论和实践并重，深入浅出地给出了海量信息数据处理的整套解决方案，包括压缩、索引和查询的方方面面。其最大的特色在于不仅仅满足信息检索理论学习的需要，更重要的是给出了实践中可能面对的各种问题及其解决方法。\u003c/p\u003e\n\u003cp\u003e《概率论与数理统计》：这本书不用过多介绍了吧，普遍大学里大一时期的教科书，只恨当年没听课啊，现在正在慢慢啃。。。\u003c/p\u003e\n\u003cp\u003e《大数据：互联网大规模数据挖掘与分布式处理》：主要内容包括分布式文件系统、相似性搜索、搜索引擎技术、频繁项集挖掘、聚类算法、广告管理及推荐系统。\u003c/p\u003e\n\u003cp\u003e《Web数据挖掘》： 信息检索领域的书籍，该书深入讲解了从大量非结构化Web数据中提取和产生知识的技术。书中首先论述了Web的基础（包括Web信息采集机制、Web标引 机制以及基于关键字或基于相似性搜索机制），然后系统地描述了Web挖掘的基础知识，着重介绍基于超文本的机器学习和数据挖掘方法，如聚类、协同过滤、监 督学习、半监督学习，最后讲述了这些基本原理在Web挖掘中的应用。《Web数据挖掘》为读者提供了坚实的技术背景和最新的知识。\u003c/p\u003e\n\u003cp\u003e《数据之巅》：对大数据追根溯源，提出当前信息技术的发展，已经让中国获得了后发优势，中国要在大数据时代的全球竞争中胜出，必须把大数据从科技符号提升成为文化符号，在全社会倡导数据文化。\u003c/p\u003e\n\u003cp\u003e《深入浅出统计学》：本书涵盖的知识点包括：信息可视化、概率计算、几何分布、二项分布及泊松分布、正态分布、统计抽样、置信区间的构建、假设检验、卡方分布、相关与回归等等，完整涵盖AP考试范围。\u003c/p\u003e\n\u003cp\u003e《矩阵分析》： 本书从数学分析的角度论述矩阵分析的经典方法和现代方法，取材新，有一定的深度，并给出在多元微积分、复分析、微分方程、量优化、逼近理论中的许多重要应 用。主要内容包括：特征值、特征向量和相似性，酉等价和正规矩阵，标准形，Hermite矩阵和对称矩阵，向量范数和矩阵范数，特征值和估计和扰动，正定 矩阵，非负矩阵。\u003c/p\u003e","title":"机器学习和数据挖掘的推荐书单"},{"content":"1. JS应该放到 .js 文件中 “额，只有那么几行而已…”，是的，我的意思是所有的 JS 都应该放在.js文件中。为什么呢？因为这有助于可读性，节省带宽。行内 JavaScript 在每次页面加载时都会重新下载，相反的，单独的.js文件则会被缓存起来。正如你所看到的，这个规则有助于支持如下一长串的其他规则。这就是为什么它的规则# 1。\n2. JS 应该是静态的 我看到过很多程序员喜欢动态的使用JavaScript。他们喜欢像使用服务器端语言如C#, Ruby, Java那样来动态的使用JavaScript。千万不要这么做。你失去了代码着色、语法高亮显示和智能感知的支持。记住，JavaScript 应该属于一个.js文件(见规则 #1)。\n然而，使用JSON引入动态行为。我把这称为JavaScript配置对象模式。具体方法如下：把JSON注入到你应用程序的头部，并根据业务逻辑 的需要利用这些数据。你可能会想：“嘿，这违背了规则 #1”。我把 JSON 看作是数据，而不是代码，所以我破例，为了支持静态的、单独的JavaScript文件。\nStackOverflow 使用的这种模式，Google 也是。\n3. JS 应该被压缩 压缩可以减小文件体积，从而提升页面加载速度。记住，性能也是一项功能。因为，为了压缩，你需要把 JS 放到一个单独的文件中(见规则 #1)。压缩JS曾经很麻烦，但现在完全是简单自动化的。有一打的方式可以做到，而Gulp和gulp-uglify是一种低摩擦和自动化的办法。\n4. JS 应该位于页面底部 如果你把标签放在中，它会阻碍页面渲染。位于中的脚本必须在页面显示前加载，因此把放在底部的 前面可以先显示页面，而不用等 JS 文件下载完毕。这有助于提升感知性能。如果你的JavaSctipe必须位于中，可以考虑使用 jQuery 的$(document).ready这样你的脚本可以等到 DOM 加载完毕后再执行。\n5. JS 应该实时的 Linted Linting 遵循代码风格、发现错别字、有助于避免错误。有很多这样的工具，我建议使用ESLint。你可以使用 Gulp 的gulp-eslint来运行它。Gulp 可以查看你所有的 JS 文件，并在你每次保存的时候运行 linter。另外，你需要把你的 JS 代码放在单独的 .js 文件中才能运行 linter 。\n6.JS应该有自动化测试 在过去的几年中，我们知道了测试的重要性。但它在很大程度上忽略了在JavaScript，直到最近才被重视。现在典型的JavaScript应用需要测试的部分远比你实际手动测试到的要多。使用JavaScript处理这么多的逻辑，关键的是具有自动测试。\n您可以通过工具，如Selenium自动化集成测试。然而，集成测试往往是脆弱的，所以我建议专注于自动化单元测试。自动化单元测试有多种选择。如果你是新手， 我建议你使用Jasmine，而如果你想要终极配置，可以使用MochawithChai。\n7. JS 需要封装 前些年我们了解了全局变量的风险，值得庆幸的是，现在有很多的方法来封装JS：\nImmediately Invoked Function Expressions(aka IIFE) Revealing Modules AMD(typically viaRequireJS) CommonJS(used byNode.js, use in browser viaBrowserifyorWebpack) ES6 modules ES6模块是未来。好消息是，虽然在浏览器中还不能很好的支持，但你可以用Babel来使用它。\n如果你不想transpile，CommonJS可能是你最佳的选择。由于 Node 使用的CommonJS 模式，所以你可以使用npm来下载数千个包。CommonJS 不能在浏览器中运行，所以你可能需要Browserify，Webpack, orJSPM.\n8. JS 依赖应当明确 这条规则与上述规则紧密相关。一旦你开始封装JavaScript，您需要一个简单的方法来引用其他模块。这就是常说的现代模块系统CommonJS和ES6模块的好处。你只需要在文件顶部指定依赖，就像Java 或 C# 那样一句声明：\n//CommonJS var react = require(\u0026lsquo;react\u0026rsquo;); //ES6 Modules import React from \u0026lsquo;React\u0026rsquo;\n9.Transpile to JS 最新版本的JavaScript，EcmaScript 2015(被大家熟知的名字是ES6) 官方版本在 6月份发布了。浏览器还不能很好的支持它的很多特性，但这并无关紧要。你可以用Babel来体验它的新特性。Babel 把 ES6 transpile 到 ES5，如果你能忍受这么做，你现在就可以享受 ES6 的新特性。JavaScript预计一年发布一次的新版本了，所以你可能一直需要transpiling 。\n或者你喜欢强类型？那么你可以考虑TypeScript。\n10.JS应该自动构建 我们已经谈到了linting、压缩、transpilation 和测试。但如何才能让这一切自动发生？很简单：使用自动构建。Gulp 就是这样一个结合了所有功能的工具。不过你也可以选择Grunt和Webpack。或者如果你是一个高手，你也可以使用npm 来构建。问题的关键是，不要指望人记得手动运行这些东西的，自动化是一个非常棒的选择。\n11. 使用框架或者库 拿一些现成的东西来用。想保持轻量级？试试Backbone或Knockout。或者jQuery就够了。想要更多更全功能的？试试Angular，Ember,，或者ReactwithFlux。\n关键是：\n不要试图从头开始。站在巨人的肩膀上。\n不管你选择哪个框架，都应该分开你的关注，这就是下一点..\n12. JS Should Separate Concerns 把 JS代码放到一个文件中的习惯很容易养成，或者盲目跟从你的框架的意见。当你移动到客户端的时候，不要忘记你在服务器端学到的经验教训。\n这里并不仅仅意味着就像你在Angular 和 Knockout等 MVC 框架中那样分离模型、视图、控制器。编写JavaScript的时候应该像服务器端开发者那样思考问题。把你的业务逻辑和数据访问分离出来。\n这意味着AJAX调用都应该在一个地方。创建一个集中的客户端“数据访问层”。业务逻辑模块应包含纯JavaScript的。这使得逻辑易于重用，易于测试，升级也不受影响。\n","permalink":"https://blog.jimersylee.com/posts/js%E8%A7%84%E5%88%99-%E4%BD%BF%E7%94%A8js%E7%9A%84%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/","summary":"\u003ch1 id=\"1-js应该放到-js-文件中\"\u003e\u003cstrong\u003e1. JS应该放到 .js 文件中\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e“额，只有那么几行而已…”，是的，我的意思是所有的 JS 都应该放在.js文件中。为什么呢？因为这有助于可读性，节省带宽。行内 JavaScript 在每次页面加载时都会重新下载，相反的，单独的.js文件则会被缓存起来。正如你所看到的，这个规则有助于支持如下一长串的其他规则。这就是为什么它的规则# 1。\u003c/p\u003e\n\u003ch1 id=\"2-js-应该是静态的\"\u003e\u003cstrong\u003e2. JS 应该是静态的\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e我看到过很多程序员喜欢动态的使用JavaScript。他们喜欢像使用服务器端语言如C#, Ruby, Java那样来动态的使用JavaScript。千万不要这么做。你失去了代码着色、语法高亮显示和智能感知的支持。记住，JavaScript 应该属于一个.js文件(见规则 #1)。\u003c/p\u003e\n\u003cp\u003e然而，使用JSON引入动态行为。我把这称为JavaScript配置对象模式。具体方法如下：把JSON注入到你应用程序的头部，并根据业务逻辑 的需要利用这些数据。你可能会想：“嘿，这违背了规则 #1”。我把 JSON 看作是数据，而不是代码，所以我破例，为了支持静态的、单独的JavaScript文件。\u003c/p\u003e\n\u003cp\u003eStackOverflow 使用的这种模式，Google 也是。\u003c/p\u003e\n\u003ch1 id=\"3-js-应该被压缩\"\u003e\u003cstrong\u003e3. JS 应该被压缩\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e压缩可以减小文件体积，从而提升页面加载速度。记住，性能也是一项功能。因为，为了压缩，你需要把 JS 放到一个单独的文件中(见规则 #1)。压缩JS曾经很麻烦，但现在完全是简单自动化的。有一打的方式可以做到，而Gulp和gulp-uglify是一种低摩擦和自动化的办法。\u003c/p\u003e\n\u003ch1 id=\"4-js-应该位于页面底部\"\u003e\u003cstrong\u003e4. JS 应该位于页面底部\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e如果你把\u003c!-- raw HTML omitted --\u003e标签放在\u003c!-- raw HTML omitted --\u003e中，它会阻碍页面渲染。位于\u003c!-- raw HTML omitted --\u003e中的脚本必须在页面显示前加载，因此把\u003c!-- raw HTML omitted --\u003e放在底部的 前面可以先显示页面，而不用等 JS 文件下载完毕。这有助于提升感知性能。如果你的JavaSctipe必须位于\u003c!-- raw HTML omitted --\u003e中，可以考虑使用 jQuery 的$(document).ready这样你的脚本可以等到 DOM 加载完毕后再执行。\u003c/p\u003e\n\u003ch1 id=\"5-js-应该实时的-linted\"\u003e\u003cstrong\u003e5. JS 应该实时的 Linted\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003eLinting 遵循代码风格、发现错别字、有助于避免错误。有很多这样的工具，我建议使用ESLint。你可以使用 Gulp 的gulp-eslint来运行它。Gulp 可以查看你所有的 JS 文件，并在你每次保存的时候运行 linter。另外，你需要把你的 JS 代码放在单独的 .js 文件中才能运行 linter 。\u003c/p\u003e","title":"JS规则-使用js的最佳实践"},{"content":"使用scrapy写爬虫 一开始想在windows环境下安装scrapy,无奈安装多次都失败,转向linux\nlinux自带python2.7 因此只需要安装scrapy模块就行\n先用pip安装Scrapy 失败\n于是安装easy_install\n命令行 sudo apt-get install python-setuptools\nsudo easy_install Scrapy\n出现错误,搜索知道必须使用python的dev版本\n于是 sudo apt-get install python-dev\n再次 sudo easy_install Scrapy\n安装成功\n然后安装mongodb\nsudo easy_instal pymongo\n","permalink":"https://blog.jimersylee.com/posts/%E4%BD%BF%E7%94%A8scrapy%E5%86%99%E7%88%AC%E8%99%AB/","summary":"\u003ch1 id=\"使用scrapy写爬虫\"\u003e使用scrapy写爬虫\u003c/h1\u003e\n\u003cp\u003e一开始想在windows环境下安装scrapy,无奈安装多次都失败,转向linux\u003c/p\u003e\n\u003cp\u003elinux自带python2.7 因此只需要安装scrapy模块就行\u003c/p\u003e\n\u003cp\u003e先用pip安装Scrapy 失败\u003c/p\u003e\n\u003cp\u003e于是安装easy_install\u003c/p\u003e\n\u003cp\u003e命令行 sudo apt-get install python-setuptools\u003c/p\u003e\n\u003cp\u003esudo easy_install Scrapy\u003c/p\u003e\n\u003cp\u003e出现错误,搜索知道必须使用python的dev版本\u003c/p\u003e\n\u003cp\u003e于是 sudo apt-get install python-dev\u003c/p\u003e\n\u003cp\u003e再次 sudo easy_install Scrapy\u003c/p\u003e\n\u003cp\u003e安装成功\u003c/p\u003e\n\u003cp\u003e然后安装mongodb\u003c/p\u003e\n\u003cp\u003esudo easy_instal pymongo\u003c/p\u003e","title":"使用scrapy写爬虫"},{"content":"安装 安装spacevim的前提是安装好vim,一般linux系统自带\n#redhat系列 sudo yuminstall vim / sudo dnfinstall vim #debian系列 sudo aptinstall vim #或者 sudo apt-getinstall vim 安装spacevim\ncurl -sLf https://spacevim.org/install.sh | bash 启动spacevim,等待下载插件\nvim test.txt 使用 #打开目录 vim path #右边就会出现目录,移动光标到目录上,按回车键,就能进入目录;移动到文件上,按回车键就能打开文件;按退格键能返回上级目录 #切换工程目录区与编辑的文件 \u0026lt;ctrl+tab\u0026gt;#创建文件 在工程区移动到文件夹下,按\u0026lt;shift+n\u0026gt;,下方提示栏就会出现提示,输入文件名,回车,创建文件 #删除文件 在工程区移动到文件夹下,按,下方提示栏就会出现提示,是否要删除,输入yes删除\n","permalink":"https://blog.jimersylee.com/posts/spacevim%E5%AE%89%E8%A3%85%E4%B8%8E%E4%BD%BF%E7%94%A8/","summary":"\u003ch1 id=\"安装\"\u003e\u003cstrong\u003e安装\u003c/strong\u003e\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e安装spacevim的前提是安装好vim,一般linux系统自带\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e#redhat系列\nsudo yuminstall vim / sudo dnfinstall vim\n#debian系列\nsudo aptinstall vim\n#或者\nsudo apt-getinstall vim\n\u003c/code\u003e\u003c/pre\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e安装spacevim\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ecurl -sLf https://spacevim.org/install.sh | bash\n\u003c/code\u003e\u003c/pre\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e启动spacevim,等待下载插件\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003evim test.txt\n\u003c/code\u003e\u003c/pre\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"使用\"\u003e\u003cstrong\u003e使用\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e#打开目录\nvim path\n#右边就会出现目录,移动光标到目录上,按回车键,就能进入目录;移动到文件上,按回车键就能打开文件;按退格键能返回上级目录\n#切换工程目录区与编辑的文件\n\u0026lt;ctrl+tab\u0026gt;#创建文件\n在工程区移动到文件夹下,按\u0026lt;shift+n\u0026gt;,下方提示栏就会出现提示,输入文件名,回车,创建文件\n#删除文件\n在工程区移动到文件夹下,按\u003c!-- raw HTML omitted --\u003e,下方提示栏就会出现提示,是否要删除,输入yes删除\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\u003c/code\u003e\u003c/pre\u003e","title":"spacevim安装与使用"},{"content":"PHP字符串常用函数学习 \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta http-equiv=\u0026#34;content-type\u0026#34; content=\u0026#34;text/html;charset=utf-8\u0026#34;\u0026gt; \u0026lt;body\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;/html\u0026gt; \u0026lt;?php echo \u0026#39;1 int crc32(string str),产生32位长的crc多项式,比如crc32(\u0026#34;helllo\u0026#34;)\u0026lt;br/\u0026gt;\u0026#39;; echo crc32 (\u0026#34;jkkajjjk\\n\u0026#34;); echo \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39;2 string bin2hex(string str) , 把二进制转换为十六进制,比如bin2hex(\u0026#34;helloworld\u0026#34;)\u0026lt;br/\u0026gt;\u0026#39;; echo bin2hex(\u0026#34;hello world\u0026#34;); echo \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39;3 string chop(string str),移除str后面多余的空白,返回新的字符串\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39;kkkj l \u0026#39;; echo \u0026#39;hahah\u0026#39;; echo chop(\u0026#34;kkkj l \u0026#34;); echo \u0026#39;hahah\u0026#39;; echo \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39;4 string chr(int ascii),返回指定ascii码表示的字符,如chr(100)\u0026lt;br/\u0026gt;\u0026#39;; echo chr(100); echo \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39;5 int ord(string str),返回str字符串第一个字符的ascii码,如ord(\u0026#34;d\u0026#34;),\u0026lt;br/\u0026gt;\u0026#39;; echo ord(\u0026#34;d\u0026#34;); echo \u0026#34;\u0026lt;br/\u0026gt;\u0026#34;; echo \u0026#39;6 string chunk_split(string str,int chunklen, string end),把字符串没隔一定数目就分割,如chunk_split(\u0026#34;jasdjkasjdkasadas\u0026#34;,5,\u0026#34;|\u0026#34;),就是把字符串每5个字符用|分割\u0026lt;br/\u0026gt;\u0026#39;; echo chunk_split(\u0026#34;jasdjkasjdkasadas\u0026#34;,5,\u0026#34;|\u0026#34;); echo \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39;7 string crypt(string str,string salt),单向加密,无解密函数~\u0026#39;; echo \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; echo crypt(\u0026#34;hello world\u0026#34;,\u0026#34;kk\u0026#34;); echo \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39; CRYPT_STD_DES - 基于标准 DES 算法的散列使用 \u0026#34;./0-9A-Za-z\u0026#34; 字符中的两个字符作为盐值。在盐值中使用非法的字符将导致 crypt() 失败。 CRYPT_EXT_DES - 扩展的基于 DES 算法的散列。其盐值为 9 个字符的字符串，由 1 个下划线后面跟着 4 字节循环次数和 4 字节盐值组成。它们被编码成可打印字符，每个字符 6 位，有效位最少的优先。0 到 63 被编码为 \u0026#34;./0-9A-Za-z\u0026#34;。在盐值中使用非法的字符将导致 crypt() 失败。 CRYPT_MD5 - MD5 散列使用一个以 $1$ 开始的 12 字符的字符串盐值。 CRYPT_BLOWFISH - Blowfish 算法使用如下盐值：“$2a$”，一个两位 cost 参数，“$” 以及 64 位由 “./0-9A-Za-z” 中的字符组合而成的字符串。在盐值中使用此范围之外的字符将导致 crypt() 返回一个空字符串。两位 cost 参数是循环次数以 2 为底的对数，它的范围是 04-31，超出这个范围将导致 crypt() 失败。 CRYPT_SHA256 - SHA-256 算法使用一个以 $5$ 开头的 16 字符字符串盐值进行散列。如果盐值字符串以 “rounds=\u0026lt;N\u0026gt;$” 开头，N 的数字值将被用来指定散列循环的执行次数，这点很像 Blowfish 算法的 cost 参数。默认的循环次数是 5000，最小是 1000，最大是 999,999,999。超出这个范围的 N 将会被转换为最接近的值。 CRYPT_SHA512 - SHA-512 算法使用一个以 $6$ 开头的 16 字符字符串盐值进行散列。如果盐值字符串以 “rounds=\u0026lt;N\u0026gt;$” 开头，N 的数字值将被用来指定散列循环的执行次数，这点很像 Blowfish 算法的 cost 参数。默认的循环次数是 5000，最小是 1000，最大是 999,999,999。超出这个范围的 N 将会被转换为最接近的值。 \u0026#39;; if (CRYPT_STD_DES == 1) { echo \u0026#34;Standard DES: \u0026#34;.crypt(\u0026#34;hello world\u0026#34;).\u0026#34;\\n\u0026lt;br /\u0026gt;\u0026#34;; } else { echo \u0026#34;Standard DES not supported.\\n\u0026lt;br /\u0026gt;\u0026#34;; } if (CRYPT_EXT_DES == 1) { echo \u0026#34;Extended DES: \u0026#34;.crypt(\u0026#34;hello world\u0026#34;).\u0026#34;\\n\u0026lt;br /\u0026gt;\u0026#34;; } else { echo \u0026#34;Extended DES not supported.\\n\u0026lt;br /\u0026gt;\u0026#34;; } if (CRYPT_MD5 == 1) { echo \u0026#34;MD5: \u0026#34;.crypt(\u0026#34;hello world\u0026#34;).\u0026#34;\\n\u0026lt;br /\u0026gt;\u0026#34;; } else { echo \u0026#34;MD5 not supported.\\n\u0026lt;br /\u0026gt;\u0026#34;; } if (CRYPT_BLOWFISH == 1) { echo \u0026#34;Blowfish: \u0026#34;.crypt(\u0026#34;hello world\u0026#34;); } else { echo \u0026#34;Blowfish DES not supported.\u0026#34;; } echo \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39; 8 array explode (string separator, string string [, int limit]),传回一个字符串的数组，以参数 separator为界线将参数 string切开，如果有设定参数 limit，则传回的数组最多将会包含 limit个元素，而最后一个元素将会包含 string全部剩余的部份。\u0026lt;br/\u0026gt;\u0026#39;; $pizza=\u0026#34;haa kkkk kllom lljjijj iioo \u0026#34;; $pieces=explode(\u0026#34; \u0026#34;,$pizza); foreach($pieces as $val){ echo $val; } echo \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39;9 string implode (string glue, array pieces) 以参数glue将数组pieces的各个元素结合起来成字符串返回.与join(string glue,array pieces)相同用法\u0026lt;br/\u0026gt;\u0026#39;; echo implode (\u0026#34;:\u0026#34;, $pieces); echo \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;; echo \u0026#39;10 array split (string pattern, string string [, int limit]),以正则把字符串切开 \u0026#39; ?\u0026gt; ","permalink":"https://blog.jimersylee.com/posts/php%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%B8%B8%E7%94%A8%E5%87%BD%E6%95%B0%E5%AD%A6%E4%B9%A0/","summary":"\u003ch1 id=\"php字符串常用函数学习\"\u003ePHP字符串常用函数学习\u003c/h1\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003ehtml\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003ehead\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003emeta\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehttp-equiv\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;content-type\u0026#34;\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003econtent\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;text/html;charset=utf-8\u0026#34;\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;\u003cspan style=\"color:#f92672\"\u003ebody\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ebody\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ehead\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u0026lt;/\u003cspan style=\"color:#f92672\"\u003ehtml\u003c/span\u003e\u0026gt;\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u0026lt;?php\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;1 int crc32(string str),产生32位长的crc多项式,比如crc32(\u0026#34;helllo\u0026#34;)\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho crc32 (\u0026#34;jkkajjjk\\n\u0026#34;);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;2 string bin2hex(string str) , 把二进制转换为十六进制,比如bin2hex(\u0026#34;helloworld\u0026#34;)\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho bin2hex(\u0026#34;hello world\u0026#34;);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;3 string chop(string str),移除str后面多余的空白,返回新的字符串\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;kkkj l \u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;hahah\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho chop(\u0026#34;kkkj l \u0026#34;);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;hahah\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;4 string chr(int ascii),返回指定ascii码表示的字符,如chr(100)\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho chr(100);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;5 int ord(string str),返回str字符串第一个字符的ascii码,如ord(\u0026#34;d\u0026#34;),\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho ord(\u0026#34;d\u0026#34;);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#34;\u0026lt;br/\u0026gt;\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;6 string chunk_split(string str,int chunklen, string end),把字符串没隔一定数目就分割,如chunk_split(\u0026#34;jasdjkasjdkasadas\u0026#34;,5,\u0026#34;|\u0026#34;),就是把字符串每5个字符用|分割\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho chunk_split(\u0026#34;jasdjkasjdkasadas\u0026#34;,5,\u0026#34;|\u0026#34;);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;7 string crypt(string str,string salt),单向加密,无解密函数~\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho crypt(\u0026#34;hello world\u0026#34;,\u0026#34;kk\u0026#34;);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eCRYPT_STD_DES - 基于标准 DES 算法的散列使用 \u0026#34;./0-9A-Za-z\u0026#34; 字符中的两个字符作为盐值。在盐值中使用非法的字符将导致 crypt() 失败。\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eCRYPT_EXT_DES - 扩展的基于 DES 算法的散列。其盐值为 9 个字符的字符串，由 1 个下划线后面跟着 4 字节循环次数和 4 字节盐值组成。它们被编码成可打印字符，每个字符 6 位，有效位最少的优先。0 到 63 被编码为 \u0026#34;./0-9A-Za-z\u0026#34;。在盐值中使用非法的字符将导致 crypt() 失败。\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eCRYPT_MD5 - MD5 散列使用一个以 $1$ 开始的 12 字符的字符串盐值。\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eCRYPT_BLOWFISH - Blowfish 算法使用如下盐值：“$2a$”，一个两位 cost 参数，“$” 以及 64 位由 “./0-9A-Za-z” 中的字符组合而成的字符串。在盐值中使用此范围之外的字符将导致 crypt() 返回一个空字符串。两位 cost 参数是循环次数以 2 为底的对数，它的范围是 04-31，超出这个范围将导致 crypt() 失败。\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eCRYPT_SHA256 - SHA-256 算法使用一个以 $5$ 开头的 16 字符字符串盐值进行散列。如果盐值字符串以 “rounds=\u0026lt;N\u0026gt;$” 开头，N 的数字值将被用来指定散列循环的执行次数，这点很像 Blowfish 算法的 cost 参数。默认的循环次数是 5000，最小是 1000，最大是 999,999,999。超出这个范围的 N 将会被转换为最接近的值。\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eCRYPT_SHA512 - SHA-512 算法使用一个以 $6$ 开头的 16 字符字符串盐值进行散列。如果盐值字符串以 “rounds=\u0026lt;N\u0026gt;$” 开头，N 的数字值将被用来指定散列循环的执行次数，这点很像 Blowfish 算法的 cost 参数。默认的循环次数是 5000，最小是 1000，最大是 999,999,999。超出这个范围的 N 将会被转换为最接近的值。\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eif (CRYPT_STD_DES == 1)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#34;Standard DES: \u0026#34;.crypt(\u0026#34;hello world\u0026#34;).\u0026#34;\\n\u0026lt;br /\u0026gt;\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eelse\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#34;Standard DES not supported.\\n\u0026lt;br /\u0026gt;\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eif (CRYPT_EXT_DES == 1)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#34;Extended DES: \u0026#34;.crypt(\u0026#34;hello world\u0026#34;).\u0026#34;\\n\u0026lt;br /\u0026gt;\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eelse\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#34;Extended DES not supported.\\n\u0026lt;br /\u0026gt;\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eif (CRYPT_MD5 == 1)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#34;MD5: \u0026#34;.crypt(\u0026#34;hello world\u0026#34;).\u0026#34;\\n\u0026lt;br /\u0026gt;\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eelse\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#34;MD5 not supported.\\n\u0026lt;br /\u0026gt;\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eif (CRYPT_BLOWFISH == 1)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#34;Blowfish: \u0026#34;.crypt(\u0026#34;hello world\u0026#34;);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eelse\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e{\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#34;Blowfish DES not supported.\u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39; 8 array explode (string separator, string string [, int limit]),传回一个字符串的数组，以参数 separator为界线将参数 string切开，如果有设定参数 limit，则传回的数组最多将会包含 limit个元素，而最后一个元素将会包含 string全部剩余的部份。\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e$pizza=\u0026#34;haa kkkk kllom lljjijj iioo \u0026#34;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e$pieces=explode(\u0026#34; \u0026#34;,$pizza);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eforeach($pieces as $val){\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho $val;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;9 string implode (string glue, array pieces) 以参数glue将数组pieces的各个元素结合起来成字符串返回.与join(string glue,array pieces)相同用法\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho implode (\u0026#34;:\u0026#34;, $pieces);\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;\u0026lt;br/\u0026gt;\u0026#39;;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003eecho \u0026#39;10 array split (string pattern, string string [, int limit]),以正则把字符串切开 \u0026#39;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e?\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"PHP字符串常用函数学习"},{"content":"项目地址:http://github.com/jimersylee/MachineLearningAction\n基于概率论的分类方法 朴素贝叶斯 使用概率分布进行分类 学习朴素贝叶斯分类器 解析RSS源数据 使用朴素贝叶斯来分析不同地区的态度 朴素贝叶斯 优点:在数据较少的情况下仍然有效,可以处理多类别问题 缺点:对于输入数据的准备方式较为敏感 使用数据类型:标称型数据\n贝叶斯决策理论的核心思想\u0026ndash;即选择具有最高概率的决策 条件概率 P(A|B)=\nP(AB)−−−−−−\\P(B)\n使用贝叶斯准则,进行条件与结果的互换计算 已知P(A|B),求P(B|A)\nP(B|A)=\nP(A|B)P(B)−−−−−−−−−−\\P(A)\n推导\n给定某个点(x,y),那么该点来自ci的概率为P(ci|x,y)\nP(\nci\n|x,y)=\nP(x,y|ci)P(ci)−−−−−−−−−−−−\\P(x,y)\n结论\n如果P(c1|x,y)\u0026gt;P(c2|x,y),那么(x,y)属于c1如果P(c2|x,y)\u0026gt;P(c1|x,y),那么(x,y)属于c2\n使用朴素贝叶斯进行文档分类 朴素贝叶斯是贝叶斯分类器的一个扩展,是用于文档分类的常用算法 朴素贝叶斯的一帮过程\n收集数据:可以使用任何方法,本章使用RSS源 准备数据:需要数值型或者布尔型数据 分析数据:有大量特征时,绘制特征作用不大,此时使用直方图效果较好 训练算法:计算不同的独立特征的条件概率 测试算法:计算错误率 使用算法:一个常见的朴素贝叶斯应用是文档分类.可以在任意的分类场景中使用朴素贝叶斯分类器,不一定非要是文本. 假设词汇表有1000个单词,要得到好的概率分布,需要足够的样本,假设样本数为N\n由统计学知,如果每个特征需要N个样本,那么对于x个特征,将需要Nx个样本\n对于1000个特征的词汇表将需要\nN1000\n个样本,可以看到,所需要的样本数会随着特征数目的增大而迅速增长 如果特征之间项目独立,那么样本数就能从\nN1000\n减少到N*1000.所谓独立,指的是\n统计学上的独立,即一个特征或者单词出现的可能性与它和其他相邻单词没关系\n示例:使用Python进行恶意留言分类 当论坛中用户发表评论时,总会存在一些恶意言论,如何识别是否为恶意评论,而不让其发送呢?\n准备数据:从文本中构建词向量,详见bayes.py中的 createVocabList setOfWords2Vec 训练算法:从词向量计算概率 重写贝叶斯准则,将之前的x,y替换为w,粗体w表示一个向量,即它是由多个数值组成.在这个例子中,数值个数与词汇表中的词个数相同 P(\nci\n|w)=\nP(w|ci)P(ci)−−−−−−−−−−\\P(w)\n如何计算?首先通过类别i(侮辱性或者非侮辱性留言)中的文档数除以总的文档数的计算概率p(\nci\n),接下来计算P(\nci\n|w),这里就用到了朴素贝叶斯假设\n如果将w展开为一个个独立特征,那么可以将上述概率写做P(w0,w1,\u0026hellip;,wN|ci).这里假设所有事件都独立,它意味着可以使用P(w0|ci)P(w1|ci)\u0026hellip;P(wN|ci)来计算概率,这极大地简化了计算过程 该函数的伪代码如下 计算每个类别中的文档数目 对每篇训练文档:\n对每个类别: 如果词条出现文档中-\u0026gt;增加该词条的计数值 增加所有词条的计数值 对每个类别: 对每个词条: 将该词条的数目除以总词条数目得到条件概率 返回每个类别的条件概率\n测试算法:根据现实情况修改分类器 利用贝叶斯分类器进行分类时,要计算多个概率的乘积,如果其中一个概率值为0,那么最后的乘积也是0.为降低这种影响,可以将所有的词的出现数初始化为1,并将分母初始化为2 准备数据:文档词袋模型 目前为止,我们将每个词的出现与否作为一个特征,这可以被描述为词集模型(set-of-words-model).如果一个词在文档中出现不止一次,这可能意味着包含该词是否出现在文档中所不能表达的某种信息,这种方法被成为词袋模型(bag-of-words-model).在词袋中,每个单词可以出现多次,而在词集中,每个词只出现一次.为适应词袋模型,需要对函数setOfWords2Vec()稍作修改,修改后的函数称为bagOfWords2Vec() 示例:使用朴素贝叶斯过滤垃圾邮件 邮件经常收到一些广告邮件,如何将广告邮件识别出来呢?\n收集数据:提供文本文件 准备数据:将文本解析成词条向量 分析数据:检查词条确保解析的正确性 训练算法:使用我们之前建立的trainNormalBayes0()函数 测试算法:使用classifyNormal(),并且构建一个新的测试函数来计算文档集的错误率 使用算法:构建一个完整的程序对一组文档进行分类,将错误分类的文档输出到屏幕上 示例:使用朴素贝叶斯分类器从个人广告中获取区域倾向 ","permalink":"https://blog.jimersylee.com/posts/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%AE%9E%E6%88%98-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B9%8Bnaivebayes/","summary":"\u003cp\u003e项目地址:\u003ca href=\"http://github.com/jimersylee/MachineLearningAction\"\u003ehttp://github.com/jimersylee/MachineLearningAction\u003c/a\u003e\u003c/p\u003e\n\u003ch3 id=\"基于概率论的分类方法\"\u003e\u003cstrong\u003e基于概率论的分类方法\u003c/strong\u003e\u003c/h3\u003e\n\u003ch3 id=\"朴素贝叶斯\"\u003e\u003cstrong\u003e朴素贝叶斯\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e使用概率分布进行分类\u003c/li\u003e\n\u003cli\u003e学习朴素贝叶斯分类器\u003c/li\u003e\n\u003cli\u003e解析RSS源数据\u003c/li\u003e\n\u003cli\u003e使用朴素贝叶斯来分析不同地区的态度\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"朴素贝叶斯-1\"\u003e\u003cstrong\u003e朴素贝叶斯\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e优点:在数据较少的情况下仍然有效,可以处理多类别问题 缺点:对于输入数据的准备方式较为敏感 使用数据类型:标称型数据\u003c/p\u003e\n\u003ch3 id=\"贝叶斯决策理论的核心思想\"\u003e\u003cstrong\u003e贝叶斯决策理论的核心思想\u0026ndash;即选择具有最高概率的决策\u003c/strong\u003e\u003c/h3\u003e\n\u003ch1 id=\"条件概率\"\u003e\u003cstrong\u003e条件概率\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003eP(A|B)=\u003c/p\u003e\n\u003cp\u003eP(AB)−−−−−−\\P(B)\u003c/p\u003e\n\u003ch3 id=\"使用贝叶斯准则进行条件与结果的互换计算\"\u003e\u003cstrong\u003e使用贝叶斯准则,进行条件与结果的互换计算\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e已知P(A|B),求P(B|A)\u003c/p\u003e\n\u003cp\u003eP(B|A)=\u003c/p\u003e\n\u003cp\u003eP(A|B)P(B)−−−−−−−−−−\\P(A)\u003c/p\u003e\n\u003cp\u003e推导\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e给定某个点(x,y),那么该点来自ci的概率为P(ci|x,y)\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003eP(\u003c/p\u003e\n\u003cp\u003eci\u003c/p\u003e\n\u003cp\u003e|x,y)=\u003c/p\u003e\n\u003cp\u003eP(x,y|ci)P(ci)−−−−−−−−−−−−\\P(x,y)\u003c/p\u003e\n\u003cp\u003e结论\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e如果P(c1|x,y)\u0026gt;P(c2|x,y),那么(x,y)属于c1如果P(c2|x,y)\u0026gt;P(c1|x,y),那么(x,y)属于c2\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch3 id=\"使用朴素贝叶斯进行文档分类\"\u003e\u003cstrong\u003e使用朴素贝叶斯进行文档分类\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e朴素贝叶斯是贝叶斯分类器的一个扩展,是用于文档分类的常用算法 朴素贝叶斯的一帮过程\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e收集数据:可以使用任何方法,本章使用RSS源\u003c/li\u003e\n\u003cli\u003e准备数据:需要数值型或者布尔型数据\u003c/li\u003e\n\u003cli\u003e分析数据:有大量特征时,绘制特征作用不大,此时使用直方图效果较好\u003c/li\u003e\n\u003cli\u003e训练算法:计算不同的独立特征的条件概率\u003c/li\u003e\n\u003cli\u003e测试算法:计算错误率\u003c/li\u003e\n\u003cli\u003e使用算法:一个常见的朴素贝叶斯应用是文档分类.可以在任意的分类场景中使用朴素贝叶斯分类器,不一定非要是文本.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e假设词汇表有1000个单词,要得到好的概率分布,需要足够的样本,假设样本数为N\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e由统计学知,如果每个特征需要N个样本,那么对于x个特征,将需要Nx个样本\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e对于1000个特征的词汇表将需要\u003c/p\u003e\n\u003cp\u003eN1000\u003c/p\u003e\n\u003cp\u003e个样本,可以看到,所需要的样本数会随着特征数目的增大而迅速增长 如果特征之间项目独立,那么样本数就能从\u003c/p\u003e\n\u003cp\u003eN1000\u003c/p\u003e\n\u003cp\u003e减少到N*1000.所谓独立,指的是\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e统计学上的独立,即一个特征或者单词出现的可能性与它和其他相邻单词没关系\u003c/strong\u003e\u003c/p\u003e\n\u003ch3 id=\"示例使用python进行恶意留言分类\"\u003e\u003cstrong\u003e示例:使用Python进行恶意留言分类\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e当论坛中用户发表评论时,总会存在一些恶意言论,如何识别是否为恶意评论,而不让其发送呢?\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e准备数据:从文本中构建词向量,详见bayes.py中的 createVocabList setOfWords2Vec\u003c/li\u003e\n\u003cli\u003e训练算法:从词向量计算概率\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e重写贝叶斯准则,将之前的x,y替换为\u003cem\u003ew\u003c/em\u003e,粗体\u003cem\u003ew\u003c/em\u003e表示一个向量,即它是由多个数值组成.在这个例子中,数值个数与词汇表中的词个数相同 P(\u003c/p\u003e\n\u003cp\u003eci\u003c/p\u003e\n\u003cp\u003e|w)=\u003c/p\u003e\n\u003cp\u003eP(w|ci)P(ci)−−−−−−−−−−\\P(w)\u003c/p\u003e\n\u003cp\u003e如何计算?首先通过类别i(侮辱性或者非侮辱性留言)中的文档数除以总的文档数的计算概率p(\u003c/p\u003e\n\u003cp\u003eci\u003c/p\u003e\n\u003cp\u003e),接下来计算P(\u003c/p\u003e\n\u003cp\u003eci\u003c/p\u003e\n\u003cp\u003e|w),这里就用到了朴素贝叶斯假设\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e如果将w展开为一个个独立特征,那么可以将上述概率写做P(w0,w1,\u0026hellip;,wN|ci).这里假设所有事件都独立,它意味着可以使用P(w0|ci)P(w1|ci)\u0026hellip;P(wN|ci)来计算概率,这极大地简化了计算过程 该函数的伪代码如下 计算每个类别中的文档数目 对每篇训练文档:\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e对每个类别: 如果词条出现文档中-\u0026gt;增加该词条的计数值 增加所有词条的计数值 对每个类别: 对每个词条: 将该词条的数目除以总词条数目得到条件概率 返回每个类别的条件概率\u003c/p\u003e","title":"机器学习实战-学习笔记之NaiveBayes"},{"content":"模板方法模式 模板方法模式在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤\n这个模式是用来创建一个算法的模板.什么是模板?模板就是一个方法.更具体地说,这个方法将算法定义为一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现.这可以确保算法的结构保持不变,同时由子类提供部分实现.\n快速搞定咖啡和茶的类 /** * 这是我们的咖啡类,用来煮咖啡 */publicclassCoffee { voidprepareRecipe(){ boilWater(); brewCoffeeGrinds(); pourInCup(); addSugarAndMilk(); } privatevoidaddSugarAndMilk() { System.out.println(\u0026#34;addSugarAndMilk\u0026#34;); } privatevoidpourInCup() { System.out.println(\u0026#34;pourInCup\u0026#34;); } privatevoidbrewCoffeeGrinds() { System.out.println(\u0026#34;brewCoffeeGrinds\u0026#34;); } privatevoidboilWater() { System.out.println(\u0026#34;boilWater\u0026#34;); } } publicclassTea { voidprepareRecipe(){ boilWater(); steepTeaBag(); pourInCup(); addLemon(); } privatevoidaddLemon() { System.out.println(\u0026#34;addLemon\u0026#34;); } privatevoidpourInCup() { System.out.println(\u0026#34;pourInCup\u0026#34;); } privatevoidsteepTeaBag() { System.out.println(\u0026#34;steepTeaBag\u0026#34;); } privatevoidboilWater() { System.out.println(\u0026#34;boilWater\u0026#34;); } } 请注意,boilWater()和pourCup()这两个方法完全一样,也就是说这里出现了重复的代码\n在这里,茶和咖啡是如此的相似,可以提取基类\n注意两份冲泡法都采用了相同的算法\n抽象prepareRecipe()\nvoidprepareRecipe(){ boilwater(); brew(); pourInCup(); addCondiments(); } prepareRecipe()就是我们的模板方法\n它是一个方法 它用作一个算法的模板,在这个例子中,算法是用来制作咖啡饮料的 在这个模板中,算法内的每一个步骤都被一个方法代表了 某些方法是由这个类(也就是超类)处理的 某些方法是由子类处理的 需要由子类提供的方法,必须在超类中声明为抽象 优劣对比 不好的茶和咖啡的实现\nCoffee和Tea主导一切;他们控制了算法 Coffee和Tea之间存在重复的代码 对于算法所做的代码改变,需要打开子类修改许多地方 由于类的组织方式不具有弹性,所以加入新种类的咖啡因饮料需要做许多工作 算法的知识和它的实现会分散在很多类中 模板方法提供的酷炫咖啡因饮料\n由CaffeineBeverage类主导一切,它拥有算法,而且保护这个算法 对子类来说,CaffeineBeverage类的存在,可以将代码的复用最大化 算法只存在于一个地方,所以容易修改 这个模板方法提供了一个框架,可以让其他的咖啡因饮料插进来.新的咖啡因饮料只需要实现自己的方法就可以了 CaffeineBeverage类专注于算法本身,而子类提供完整的实现 要点 \u0026ldquo;模板方法\u0026quot;定义了算法的步骤,把这些步骤的实现延迟到子类 模板方法模式为我们提供了一种代码复用的重要技巧 模板方法的抽象类可以定义具体方法,抽象方法和钩子 抽象方法由子类实现 钩子是一种方法,它在抽象类中不做事,或者只做默认的事情,子类可以选择要不要去覆盖它 为了防止子类改变模板方法中的算法,可以将模板方法声明为final 好莱坞原则告诉我们,将决策权房子高层模块中,以便决定如何以及何时调用底层模块 策略模式和模板方法模式都封装算法,一个用组合,一个用继承 工厂方法是模板方法的一个特殊版本 项目地址 java设计模式实现 如果觉得有点收获,记得在项目上点star哦!\n","permalink":"https://blog.jimersylee.com/posts/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E6%A8%A1%E6%9D%BF%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F/","summary":"\u003ch1 id=\"模板方法模式\"\u003e\u003cstrong\u003e模板方法模式\u003c/strong\u003e\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e模板方法模式在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e这个模式是用来创建一个算法的模板.什么是模板?模板就是一个方法.更具体地说,这个方法将算法定义为一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现.这可以确保算法的结构保持不变,同时由子类提供部分实现.\u003c/p\u003e\n\u003ch1 id=\"快速搞定咖啡和茶的类\"\u003e\u003cstrong\u003e快速搞定咖啡和茶的类\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 这是我们的咖啡类,用来煮咖啡\n */publicclassCoffee {\nvoidprepareRecipe(){\n        boilWater();\n        brewCoffeeGrinds();\n        pourInCup();\n        addSugarAndMilk();\n    }\n\nprivatevoidaddSugarAndMilk() {\n        System.out.println(\u0026#34;addSugarAndMilk\u0026#34;);\n    }\n\nprivatevoidpourInCup() {\n        System.out.println(\u0026#34;pourInCup\u0026#34;);\n    }\n\nprivatevoidbrewCoffeeGrinds() {\n        System.out.println(\u0026#34;brewCoffeeGrinds\u0026#34;);\n    }\n\nprivatevoidboilWater() {\n        System.out.println(\u0026#34;boilWater\u0026#34;);\n    }\n}\n\npublicclassTea {\nvoidprepareRecipe(){\n        boilWater();\n        steepTeaBag();\n        pourInCup();\n        addLemon();\n    }\n\nprivatevoidaddLemon() {\n        System.out.println(\u0026#34;addLemon\u0026#34;);\n    }\n\nprivatevoidpourInCup() {\n        System.out.println(\u0026#34;pourInCup\u0026#34;);\n    }\n\nprivatevoidsteepTeaBag() {\n        System.out.println(\u0026#34;steepTeaBag\u0026#34;);\n    }\n\nprivatevoidboilWater() {\n        System.out.println(\u0026#34;boilWater\u0026#34;);\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e请注意,boilWater()和pourCup()这两个方法完全一样,也就是说这里出现了重复的代码\u003c/p\u003e\n\u003cp\u003e在这里,茶和咖啡是如此的相似,可以提取基类\u003c/p\u003e\n\u003cp\u003e注意两份冲泡法都采用了相同的算法\u003c/p\u003e\n\u003cp\u003e抽象prepareRecipe()\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003evoidprepareRecipe(){\n    boilwater();\n    brew();\n    pourInCup();\n    addCondiments();\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eprepareRecipe()就是我们的模板方法\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e它是一个方法\u003c/li\u003e\n\u003cli\u003e它用作一个算法的模板,在这个例子中,算法是用来制作咖啡饮料的\u003c/li\u003e\n\u003cli\u003e在这个模板中,算法内的每一个步骤都被一个方法代表了\u003c/li\u003e\n\u003cli\u003e某些方法是由这个类(也就是超类)处理的\u003c/li\u003e\n\u003cli\u003e某些方法是由子类处理的\u003c/li\u003e\n\u003cli\u003e需要由子类提供的方法,必须在超类中声明为抽象\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"优劣对比\"\u003e\u003cstrong\u003e优劣对比\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e不好的茶和咖啡的实现\u003c/p\u003e","title":"设计模式之模板方法模式"},{"content":"适配器模式与外观模式 适配器模式 需求 现在已经存在IDuck接口,Turkey接口,假设你缺鸭子对象,想用一些火鸡对象来冒充.显而易见,因为火鸡的接口不同,所以我们不能公然拿来用,那么写个适配器吧\nFAQ 一个适配器需要做多少适配工作?\n实现一个适配器所需要进行的工作,和目标接口的大小成正.如果不用适配器,你就必须改写客户端的代码来调用这个新的接口.相比之下,使用适配器成本更少\n一个适配器只能封装一个类吗?\n虽然大多数的适配器模式所采取的例子都是让一个适配器包装一个被适配者,但还是会有状况需要让一个适配器包装多个被适配者.这设计到另一个模式,被称为外观模式(Facade Pattern),人们常常将外观模式和适配器模式混为一谈,本章稍后对此详细说明\n万一我的系统中新旧并存,是不是不使用适配器更好\n可以创建一个双向的适配器,支持两边的接口.这样,这个适配器可以当做旧的接口,或者当做新的接口使用\n外观模式 外观模式提供了一个统一的接口,用来访问子系统中的一群接口.外观定了一个高层接口,让子系统更容易使用.\n外观模式的意图是提供一个简单的接口,好让一个子系统更易于使用\n要点 当需要使用一个现有的类而其接口并不符合你的需求时,就使用适配器 当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观 适配器改变接口以符合客户的期望 外观将客户从一个复杂的子系统中解耦 实现一个适配器可能需要一番功夫,也可能不费功夫,视目标接口的大小与复杂度而定 实现一个外观,需要将子系统组合进外观中,然后将工作委托给给子系统执行 适配器模式有两种形式:对象适配器和类适配器.类适配器需要用到多重继承 你可以为一个子系统实现一个以上的外观 适配器将一个对象包装起来以改变其接口;装饰着将一个对象包装起来以增加新的行为和责任;而外观将一群对象\u0026quot;包装\u0026quot;起来以简化其接口 项目地址 java设计模式实现 如果觉得有点收获,记得在项目上点star哦!\n","permalink":"https://blog.jimersylee.com/posts/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F/","summary":"\u003ch1 id=\"适配器模式与外观模式\"\u003e\u003cstrong\u003e适配器模式与外观模式\u003c/strong\u003e\u003c/h1\u003e\n\u003ch1 id=\"适配器模式\"\u003e\u003cstrong\u003e适配器模式\u003c/strong\u003e\u003c/h1\u003e\n\u003ch3 id=\"需求\"\u003e\u003cstrong\u003e需求\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e现在已经存在IDuck接口,Turkey接口,假设你缺鸭子对象,想用一些火鸡对象来冒充.显而易见,因为火鸡的接口不同,所以我们不能公然拿来用,那么写个适配器吧\u003c/p\u003e\n\u003ch3 id=\"faq\"\u003e\u003cstrong\u003eFAQ\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e一个适配器需要做多少适配工作?\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e实现一个适配器所需要进行的工作,和目标接口的大小成正.如果不用适配器,你就必须改写客户端的代码来调用这个新的接口.相比之下,使用适配器成本更少\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e一个适配器只能封装一个类吗?\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e虽然大多数的适配器模式所采取的例子都是让一个适配器包装一个被适配者,但还是会有状况需要让一个适配器包装多个被适配者.这设计到另一个模式,被称为外观模式(Facade Pattern),人们常常将外观模式和适配器模式混为一谈,本章稍后对此详细说明\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e万一我的系统中新旧并存,是不是不使用适配器更好\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e可以创建一个双向的适配器,支持两边的接口.这样,这个适配器可以当做旧的接口,或者当做新的接口使用\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"外观模式\"\u003e\u003cstrong\u003e外观模式\u003c/strong\u003e\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e外观模式提供了一个统一的接口,用来访问子系统中的一群接口.外观定了一个高层接口,让子系统更容易使用.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e外观模式的意图是提供一个简单的接口,好让一个子系统更易于使用\u003c/p\u003e\n\u003ch1 id=\"要点\"\u003e\u003cstrong\u003e要点\u003c/strong\u003e\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e当需要使用一个现有的类而其接口并不符合你的需求时,就使用适配器\u003c/li\u003e\n\u003cli\u003e当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观\u003c/li\u003e\n\u003cli\u003e适配器改变接口以符合客户的期望\u003c/li\u003e\n\u003cli\u003e外观将客户从一个复杂的子系统中解耦\u003c/li\u003e\n\u003cli\u003e实现一个适配器可能需要一番功夫,也可能不费功夫,视目标接口的大小与复杂度而定\u003c/li\u003e\n\u003cli\u003e实现一个外观,需要将子系统组合进外观中,然后将工作委托给给子系统执行\u003c/li\u003e\n\u003cli\u003e适配器模式有两种形式:对象适配器和类适配器.类适配器需要用到多重继承\u003c/li\u003e\n\u003cli\u003e你可以为一个子系统实现一个以上的外观\u003c/li\u003e\n\u003cli\u003e适配器将一个对象包装起来以改变其接口;装饰着将一个对象包装起来以增加新的行为和责任;而外观将一群对象\u0026quot;包装\u0026quot;起来以简化其接口\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"项目地址\"\u003e\u003cstrong\u003e项目地址\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/jimersylee/DesignPattern\"\u003ejava设计模式实现\u003c/a\u003e 如果觉得有点收获,记得在项目上点star哦!\u003c/p\u003e","title":"设计模式之适配器模式"},{"content":"命令模式 命令模式将发出请求的对象和执行请求的对象解耦 在被解耦的两者之间是通过命令对象进行沟通的.命令对象封装了接收者和一个或者一组动作 调用者通过调用命令封装execute()发出请求,这会使得接收者的动作被调用 调用者可以接受命令当做参数,甚至在运行时动态地进行 命令可以支持撤销,做法是实现一个undo()方法来回到execute()被执行前的状态 宏命令是命令的一种简单的延伸,允许调用多个命令.宏方法也可以支持注销 实际操作时,很常见使用\u0026quot;聪明\u0026quot;命令对象,也就是直接实现了请求,而不是将工作委托给接收者 命令也可以用来实现日志和事务系统 项目地址 java设计模式实现 如果觉得有点收获,记得在项目上点star哦!\n","permalink":"https://blog.jimersylee.com/posts/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F/","summary":"\u003ch1 id=\"命令模式\"\u003e\u003cstrong\u003e命令模式\u003c/strong\u003e\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e命令模式将发出请求的对象和执行请求的对象解耦\u003c/li\u003e\n\u003cli\u003e在被解耦的两者之间是通过命令对象进行沟通的.命令对象封装了接收者和一个或者一组动作\u003c/li\u003e\n\u003cli\u003e调用者通过调用命令封装execute()发出请求,这会使得接收者的动作被调用\u003c/li\u003e\n\u003cli\u003e调用者可以接受命令当做参数,甚至在运行时动态地进行\u003c/li\u003e\n\u003cli\u003e命令可以支持撤销,做法是实现一个undo()方法来回到execute()被执行前的状态\u003c/li\u003e\n\u003cli\u003e宏命令是命令的一种简单的延伸,允许调用多个命令.宏方法也可以支持注销\u003c/li\u003e\n\u003cli\u003e实际操作时,很常见使用\u0026quot;聪明\u0026quot;命令对象,也就是直接实现了请求,而不是将工作委托给接收者\u003c/li\u003e\n\u003cli\u003e命令也可以用来实现日志和事务系统\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"项目地址\"\u003e\u003cstrong\u003e项目地址\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/jimersylee/DesignPattern\"\u003ejava设计模式实现\u003c/a\u003e 如果觉得有点收获,记得在项目上点star哦!\u003c/p\u003e","title":"设计模式之命令模式"},{"content":"有什么用处 有些对象其实我们只需要一个,比方说:线程池,缓存,对话框,注册表,日志对象\u0026hellip;如果制造出多个实例,就会导致许多问题产生,例如程序的行为异常,资源使用过量,数据不一致\n如何做 利用静态类变量,静态方法和适当的访问修饰符\n定义 单例模式,确保一个类只有一个实例,并提供一个全局访问点\n注意点,多线程中使用单例 如果有多个线程同时调用getInstance(),可能会产生多个实例,那就用synchronized(同步)关键字修饰 但是同步会降低性能,实际上也就第一次getInstance()时需要考虑同步问题,之后就没有同步问题.\n1.如果getInstance()的性能对应用程序不是很关键,就加上synchronized关键字 /** * 单例模式之懒汉模式 * 优点:在需要实例的时候才进行第一次实例化,在资源紧缺的时候,可以减少不必要的资源消耗 * 缺点:同步了getInstance(),会造成性能浪费 */publicclassSingletonLazy { /** * 利用一个静态变量来记录Singleton类的唯一实例 */privatestatic SingletonLazy instance; /** * 构造器声明为私有的,只有自己Singleton类才可以调用构造器 */privateSingletonLazy() { } /** * 用getInstance()实例化对象,并返回这个实例 * 在多线程中必须使用synchronized关键字修饰 * @return */publicstatic synchronized SingletonLazygetInstance() { //懒汉模式//如果未被实例化,则newif (instance ==null) { instance =new SingletonLazy(); } //如果已经实例化,则返回实例return instance; } } 2.如果getInstance()的性能对应用程序很关键,那就使用饿汉模式 使用饿汉模式\npublicclassSingletonLazy{ //在静态初始化器(static initialize)中创建单例.这段代码保证了线程安全(Thread Safe)privatestatic SingletonLazy instance=new SingletonLazy(); privateSingletonLazy(){ } publicstatic SingletonLazygetInstance(){ //到这里,一定存在实例了,直接使用它return instance; } } 3.使用\u0026quot;双重检查锁\u0026quot;(double-checked locking),在getInstance()中减少使用同步 /** * 单例模式之使用\u0026#34;双重检查加锁\u0026#34; * 过程:在getInstance()中进行双重检查,确保一个实例 * 优点:在getInstance()中减少同步,增强性能,可以在多线程中使用 * 缺点:暂无 */publicclassSingletonDoubleCheckedLocking { /** * 利用一个静态变量来记录Singleton类的唯一实例 * volatile关键字确保:当instance变量被初始化为Singleton实例时,多个线程正确地处理instance变量 */privatestaticvolatile SingletonDoubleCheckedLocking instance; /** * 构造器声明为私有的,只有自己Singleton类才可以调用构造器 */privateSingletonDoubleCheckedLocking() { } /** * 用getInstance()实例化对象,并返回这个实例 * 方法不必用synchronized关键字修饰 * @return */publicstatic SingletonDoubleCheckedLockinggetInstance() { if(instance==null){//检查实例,如果不存在,则进入同步区块//注意:只有第一次调用getInstance()方法才彻底执行这里的代码 synchronized (SingletonDoubleCheckedLocking.class){ if(instance==null){//进入区块后,再检查一次,如果是null,才创建实例 instance=new SingletonDoubleCheckedLocking(); } } } return instance; } } 项目地址 java设计模式实现 如果觉得有点收获,记得在项目上点star哦!\n","permalink":"https://blog.jimersylee.com/posts/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/","summary":"\u003ch1 id=\"有什么用处\"\u003e\u003cstrong\u003e有什么用处\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e有些对象其实我们只需要一个,比方说:线程池,缓存,对话框,注册表,日志对象\u0026hellip;如果制造出多个实例,就会导致许多问题产生,例如程序的行为异常,资源使用过量,数据不一致\u003c/p\u003e\n\u003ch1 id=\"如何做\"\u003e\u003cstrong\u003e如何做\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e利用静态类变量,静态方法和适当的访问修饰符\u003c/p\u003e\n\u003ch1 id=\"定义\"\u003e\u003cstrong\u003e定义\u003c/strong\u003e\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e单例模式,确保一个类只有一个实例,并提供一个全局访问点\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"注意点多线程中使用单例\"\u003e\u003cstrong\u003e注意点,多线程中使用单例\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e如果有多个线程同时调用getInstance(),可能会产生多个实例,那就用synchronized(同步)关键字修饰 但是同步会降低性能,实际上也就第一次getInstance()时需要考虑同步问题,之后就没有同步问题.\u003c/p\u003e\n\u003ch1 id=\"1如果getinstance的性能对应用程序不是很关键就加上synchronized关键字\"\u003e\u003cstrong\u003e1.如果getInstance()的性能对应用程序不是很关键,就加上synchronized关键字\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 单例模式之懒汉模式\n * 优点:在需要实例的时候才进行第一次实例化,在资源紧缺的时候,可以减少不必要的资源消耗\n * 缺点:同步了getInstance(),会造成性能浪费\n */publicclassSingletonLazy {\n/**\n     * 利用一个静态变量来记录Singleton类的唯一实例\n     */privatestatic SingletonLazy instance;\n\n/**\n     * 构造器声明为私有的,只有自己Singleton类才可以调用构造器\n     */privateSingletonLazy() {\n\n    }\n\n/**\n     * 用getInstance()实例化对象,并返回这个实例\n     * 在多线程中必须使用synchronized关键字修饰\n     * @return\n     */publicstatic synchronized SingletonLazygetInstance() {\n\n//懒汉模式//如果未被实例化,则newif (instance ==null) {\n            instance =new SingletonLazy();\n        }\n//如果已经实例化,则返回实例return instance;\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"2如果getinstance的性能对应用程序很关键那就使用饿汉模式\"\u003e\u003cstrong\u003e2.如果getInstance()的性能对应用程序很关键,那就使用饿汉模式\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e使用饿汉模式\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epublicclassSingletonLazy{\n//在静态初始化器(static initialize)中创建单例.这段代码保证了线程安全(Thread Safe)privatestatic SingletonLazy instance=new SingletonLazy();\n\nprivateSingletonLazy(){\n    }\n\npublicstatic SingletonLazygetInstance(){\n//到这里,一定存在实例了,直接使用它return instance;\n    }\n\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"3使用双重检查锁double-checked-locking在getinstance中减少使用同步\"\u003e\u003cstrong\u003e3.使用\u0026quot;双重检查锁\u0026quot;(double-checked locking),在getInstance()中减少使用同步\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 单例模式之使用\u0026#34;双重检查加锁\u0026#34;\n * 过程:在getInstance()中进行双重检查,确保一个实例\n * 优点:在getInstance()中减少同步,增强性能,可以在多线程中使用\n * 缺点:暂无\n */publicclassSingletonDoubleCheckedLocking {\n/**\n     * 利用一个静态变量来记录Singleton类的唯一实例\n     * volatile关键字确保:当instance变量被初始化为Singleton实例时,多个线程正确地处理instance变量\n     */privatestaticvolatile SingletonDoubleCheckedLocking instance;\n\n/**\n     * 构造器声明为私有的,只有自己Singleton类才可以调用构造器\n     */privateSingletonDoubleCheckedLocking() {\n    }\n\n/**\n     * 用getInstance()实例化对象,并返回这个实例\n     * 方法不必用synchronized关键字修饰\n     * @return\n     */publicstatic SingletonDoubleCheckedLockinggetInstance() {\nif(instance==null){//检查实例,如果不存在,则进入同步区块//注意:只有第一次调用getInstance()方法才彻底执行这里的代码\n            synchronized (SingletonDoubleCheckedLocking.class){\nif(instance==null){//进入区块后,再检查一次,如果是null,才创建实例\n                    instance=new SingletonDoubleCheckedLocking();\n                }\n            }\n        }\n\nreturn instance;\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"项目地址\"\u003e\u003cstrong\u003e项目地址\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/jimersylee/DesignPattern\"\u003ejava设计模式实现\u003c/a\u003e 如果觉得有点收获,记得在项目上点star哦!\u003c/p\u003e","title":"设计模式之单例模式"},{"content":"\u0026lt;机器学习实战\u0026gt;学习笔记之KNN-手写数据识别 ","permalink":"https://blog.jimersylee.com/posts/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E5%AE%9E%E6%88%98-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B9%8Bknn-%E6%89%8B%E5%86%99%E6%95%B0%E6%8D%AE%E8%AF%86%E5%88%AB/","summary":"\u003ch1 id=\"机器学习实战学习笔记之knn-手写数据识别\"\u003e\u0026lt;机器学习实战\u0026gt;学习笔记之KNN-手写数据识别\u003c/h1\u003e","title":"机器学习实战-学习笔记之KNN-手写数据识别"},{"content":"工厂模式 针对接口编程,可以隔离掉以后系统可能发生的一大堆改变 为了让系统有弹性,我们希望一个类是抽象类或接口.但如果这样,这些类或接口就无法直接实例化 根据类的类型,我们实例化正确的具体类,然后返回具体类的对象,这些具体类必须实现抽象类接口 但是压力来自于增加更多的具体类类型 把创建对象的代码从具体方法中抽离,把创建的过程搬到另一个对象中,这个对象只管如何创建对象. 我们称这个对象为工厂,现在我们就来实现一个披萨工厂\n简单工厂模式 创建对象的过程在工厂类中\n/** * 简单工厂 * 披萨工厂类 */publicclassSimplePizzaFactory { /** * 首先,在这个工厂类定义一个createPizza()方法,所有客户使用这个方法来实例化对象 * @param type:披萨类型 * @return Pizza */public Pizza createPizza(String type){ Pizza pizza=null; if(type.equals(\u0026#34;cheese\u0026#34;)){ pizza=new CheesePizza(); }elseif(type.equals(\u0026#34;pepperoni\u0026#34;)){ pizza=new Pepperoni(); } return pizza; } } 工厂方法模式 工厂方法用来处理对象的创建,并将这样的行为封装在子类中.这样,客户程序中关于超类的代码就和子类对象创建代码解耦了.工厂方法可能需要参数,也可能不需要.\nabstract Product factoryMethod(String type) abstract:工厂方法是抽象的 Product:工厂方法必须返回一个产品.超类中定义的方法,通常使用工厂方法的返回值 factoryMethod:工厂方法将客户(也就是超类中的代码,列入orderPizza())和实际创建具体产品的代码分割开来 type:工厂方法可能需要/不需要参数来制定所要的产品 项目地址 java设计模式实现 如果觉得有点收获,记得在项目上点star哦!\n","permalink":"https://blog.jimersylee.com/posts/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F/","summary":"\u003ch1 id=\"工厂模式\"\u003e\u003cstrong\u003e工厂模式\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e针对接口编程,可以隔离掉以后系统可能发生的一大堆改变 为了让系统有弹性,我们希望一个类是抽象类或接口.但如果这样,这些类或接口就无法直接实例化 根据类的类型,我们实例化正确的具体类,然后返回具体类的对象,这些具体类必须实现抽象类接口 但是压力来自于增加更多的具体类类型 把创建对象的代码从具体方法中抽离,把创建的过程搬到另一个对象中,这个对象只管如何创建对象. 我们称这个对象为\u003cem\u003e工厂\u003c/em\u003e,现在我们就来实现一个披萨工厂\u003c/p\u003e\n\u003ch1 id=\"简单工厂模式\"\u003e\u003cstrong\u003e简单工厂模式\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e创建对象的过程在工厂类中\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 简单工厂\n * 披萨工厂类\n */publicclassSimplePizzaFactory {\n/**\n     * 首先,在这个工厂类定义一个createPizza()方法,所有客户使用这个方法来实例化对象\n     * @param type:披萨类型\n     * @return Pizza\n     */public Pizza createPizza(String type){\n        Pizza pizza=null;\nif(type.equals(\u0026#34;cheese\u0026#34;)){\n            pizza=new CheesePizza();\n        }elseif(type.equals(\u0026#34;pepperoni\u0026#34;)){\n            pizza=new Pepperoni();\n        }\n\nreturn pizza;\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"工厂方法模式\"\u003e\u003cstrong\u003e工厂方法模式\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e工厂方法用来处理对象的创建,并将这样的行为封装在子类中.这样,客户程序中关于超类的代码就和子类对象创建代码解耦了.工厂方法可能需要参数,也可能不需要.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eabstract Product factoryMethod(String type)\nabstract:工厂方法是抽象的\nProduct:工厂方法必须返回一个产品.超类中定义的方法,通常使用工厂方法的返回值\nfactoryMethod:工厂方法将客户(也就是超类中的代码,列入orderPizza())和实际创建具体产品的代码分割开来\ntype:工厂方法可能需要/不需要参数来制定所要的产品\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"项目地址\"\u003e\u003cstrong\u003e项目地址\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/jimersylee/DesignPattern\"\u003ejava设计模式实现\u003c/a\u003e 如果觉得有点收获,记得在项目上点star哦!\u003c/p\u003e","title":"设计模式之工厂模式"},{"content":"观察者模式(Observer) 让你的对象知悉现况\n使用自定义的Subject(主题)与Observer(观察者模式)\n设计原则\n找出程序中会变化的方面,然后将其和固定不变的部分分离\n在观察者模式中,会改变的事主题的状态,以及观察者的数目和类型.用这个模式,你可以改变依赖于主题状态的对象,却不改变主题.这就叫提前规划\n针对接口编程,不针对实现编程\n主题与观察者都使用接口:观察者利用主题的接口向主题注册,二主题利用观察者接口通知观察者.这样可以让两者之前运作正常,同时具有松耦合的优点\n多用组合,少用继承\n观察者模式利用\u0026quot;组合\u0026quot;将许多观察者组合进主题中.对象之前的这种关系不是通过继承产生的,而是在运行时利用组合的方式而产生的.\n自己实现观察者模式 我们先定义主题接口 /** * 主题接口 */publicinterfaceSubject { publicvoidregisterObserver(Observer o); publicvoidremoveObserver(Observer o); publicvoidnotifyObservers(); } 定义观察者接口 /** * 观察者接口 */publicinterfaceObserver { publicvoidupdate(float temp,float humidity,float pressure); } 显示元素接口 /** * 显示元素接口 */publicinterfaceDisplayElement { publicvoiddisplay(); } 编写公告板实现,实现了观察者接口与显示元素接口 /** * 公告板实现 */publicclassCurrentConditionDisplayimplementsObserver,DisplayElement { privatefloat temperature; privatefloat humidity; private Subject weatherData; /** * 构造器需要weatherData对象(也就是主题)作为注册之用 * @param weatherData:天气对象 */publicCurrentConditionDisplay(Subject weatherData){ this.weatherData=weatherData; weatherData.registerObserver(this); } /** * display()方法就只是把最近的问的和湿度显示出来 */@Override publicvoiddisplay() { System.out.println(\u0026#34;Current conditions:\u0026#34;+temperature+\u0026#34;F degree and \u0026#34;+humidity+\u0026#34;% humidity\u0026#34;); } /** * 当update被调用时,我们把温度和湿度保存起来,然后调用display * @param temp * @param humidity * @param pressure */@Override publicvoidupdate(float temp,float humidity,float pressure) { this.temperature=temp; this.humidity=humidity; display(); } } 天气数据实现主题接口 import java.util.ArrayList; /** * 天气数据类实现了Subject(主题)接口 */publicclassWeatherDataimplementsSubject { private ArrayList\u0026lt;Observer\u0026gt; observers; privatefloat temperature; privatefloat humidity; privatefloat pressure; publicWeatherData(){ observers=new ArrayList\u0026lt;\u0026gt;(); } @Override publicvoidregisterObserver(Observer o) { observers.add(o); } @Override publicvoidremoveObserver(Observer o) { int i=observers.indexOf(o); if(i\u0026gt;=0){ observers.remove(i); } } @Override publicvoidnotifyObservers() { for (Observer observer : observers) { observer.update(temperature, humidity, pressure); } } /** * 此方法会在气象值变化时被调用 */publicvoidmeasurementsChanged(){ notifyObservers(); } publicvoidsetMeasurements(float temperature,float humidity,float pressure){ this.temperature=temperature; this.humidity=humidity; this.pressure=pressure; measurementsChanged(); } } 来个测试吧 publicclass WeatherStation { publicstaticvoidmain(String[] args){ WeatherData weatherData=new WeatherData(); CurrentConditionDisplay currentConditionDisplay=new CurrentConditionDisplay(weatherData); weatherData.setMeasurements(80,65,30.4f); } } #输出 Current conditions:80.0F degree and 65.0% humidity 使用java自带的Observer 定义显示元素接口 package Observable; publicinterfaceDisplayElement { publicvoiddisplay(); } 实现观察者接口和显示元素接口 package Observable; import java.util.Observable; import java.util.Observer; /** * 天气状况布告板 * Created by jimersylee on */publicclassCurrentConditionDisplayimplementsObserver,DisplayElement { Observable observable; privatefloat temperature; privatefloat humidity; publicCurrentConditionDisplay(Observable observable){ this.observable=observable; observable.addObserver(this); } publicvoidupdate(Observable obs,Object arg){ if(obsinstanceof WeatherData){ WeatherData weatherData=(WeatherData)obs; this.temperature=weatherData.getTemperature(); this.humidity=weatherData.getHumidity(); display(); } } publicvoiddisplay(){ System.out.println(\u0026#34;Current conditions:\u0026#34;+temperature+\u0026#34;F degrees and \u0026#34;+humidity+\u0026#34;% humidity\u0026#34;); } } 实现观察者抽象类 package Observable; import java.util.Observable; /** * 使用java.util内置的观察者模式实现 * Created by jimersylee */publicclassWeatherDataextendsObservable { privatefloat temperature; privatefloat humidity; privatefloat pressure; publicWeatherData(){ } publicvoidmeasurementsChanged(){ setChanged(); notifyObservers(); } publicfloatgetTemperature(){ return temperature; } publicfloatgetHumidity(){ return humidity; } publicfloatgetPressure(){ return pressure; } publicvoidsetMeasurements(float temperature,float humidity,float pressure){ this.temperature=temperature; this.humidity=humidity; this.pressure=pressure; measurementsChanged(); } } 写个测试吧 package Observable; publicclass WeatherStation { publicstaticvoidmain(String[] args){ WeatherData weatherData=new WeatherData(); CurrentConditionDisplay currentConditionDisplay=new CurrentConditionDisplay(weatherData); weatherData.setMeasurements(80,30,33.2f); } } #输出 Current conditions:80.0F degrees and 30.0% humidity 项目地址 java设计模式实现 如果觉得有点收获,记得在项目上点star哦!\n","permalink":"https://blog.jimersylee.com/posts/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F/","summary":"\u003ch1 id=\"观察者模式observer\"\u003e\u003cstrong\u003e观察者模式(Observer)\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e让你的对象知悉现况\u003c/p\u003e\n\u003cp\u003e使用自定义的Subject(主题)与Observer(观察者模式)\u003c/p\u003e\n\u003cp\u003e设计原则\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e找出程序中会变化的方面,然后将其和固定不变的部分分离\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e在观察者模式中,会改变的事主题的状态,以及观察者的数目和类型.用这个模式,你可以改变依赖于主题状态的对象,却不改变主题.这就叫提前规划\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e针对接口编程,不针对实现编程\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e主题与观察者都使用接口:观察者利用主题的接口向主题注册,二主题利用观察者接口通知观察者.这样可以让两者之前运作正常,同时具有松耦合的优点\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e多用组合,少用继承\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e观察者模式利用\u0026quot;组合\u0026quot;将许多观察者组合进主题中.对象之前的这种关系不是通过继承产生的,而是在运行时利用组合的方式而产生的.\u003c/p\u003e\n\u003ch1 id=\"自己实现观察者模式\"\u003e\u003cstrong\u003e自己实现观察者模式\u003c/strong\u003e\u003c/h1\u003e\n\u003ch1 id=\"我们先定义主题接口\"\u003e\u003cstrong\u003e我们先定义主题接口\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 主题接口\n */publicinterfaceSubject {\npublicvoidregisterObserver(Observer o);\npublicvoidremoveObserver(Observer o);\npublicvoidnotifyObservers();\n\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"定义观察者接口\"\u003e\u003cstrong\u003e定义观察者接口\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 观察者接口\n */publicinterfaceObserver {\npublicvoidupdate(float temp,float humidity,float pressure);\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"显示元素接口\"\u003e\u003cstrong\u003e显示元素接口\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 显示元素接口\n */publicinterfaceDisplayElement {\npublicvoiddisplay();\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"编写公告板实现实现了观察者接口与显示元素接口\"\u003e\u003cstrong\u003e编写公告板实现,实现了观察者接口与显示元素接口\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 公告板实现\n */publicclassCurrentConditionDisplayimplementsObserver,DisplayElement {\nprivatefloat temperature;\nprivatefloat humidity;\nprivate Subject weatherData;\n\n/**\n     * 构造器需要weatherData对象(也就是主题)作为注册之用\n     * @param weatherData:天气对象\n     */publicCurrentConditionDisplay(Subject weatherData){\nthis.weatherData=weatherData;\n        weatherData.registerObserver(this);\n    }\n\n/**\n     * display()方法就只是把最近的问的和湿度显示出来\n     */@Override\npublicvoiddisplay() {\n        System.out.println(\u0026#34;Current conditions:\u0026#34;+temperature+\u0026#34;F degree and \u0026#34;+humidity+\u0026#34;% humidity\u0026#34;);\n    }\n\n/**\n     * 当update被调用时,我们把温度和湿度保存起来,然后调用display\n     * @param temp\n     * @param humidity\n     * @param pressure\n     */@Override\npublicvoidupdate(float temp,float humidity,float pressure) {\nthis.temperature=temp;\nthis.humidity=humidity;\n            display();\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"天气数据实现主题接口\"\u003e\u003cstrong\u003e天气数据实现主题接口\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eimport java.util.ArrayList;\n\n/**\n * 天气数据类实现了Subject(主题)接口\n */publicclassWeatherDataimplementsSubject {\nprivate ArrayList\u0026lt;Observer\u0026gt; observers;\nprivatefloat temperature;\nprivatefloat humidity;\nprivatefloat pressure;\n\npublicWeatherData(){\n        observers=new ArrayList\u0026lt;\u0026gt;();\n    }\n\n    @Override\npublicvoidregisterObserver(Observer o) {\n        observers.add(o);\n    }\n\n    @Override\npublicvoidremoveObserver(Observer o) {\nint i=observers.indexOf(o);\nif(i\u0026gt;=0){\n            observers.remove(i);\n        }\n    }\n\n    @Override\npublicvoidnotifyObservers() {\nfor (Observer observer : observers) {\n            observer.update(temperature, humidity, pressure);\n        }\n    }\n\n/**\n     * 此方法会在气象值变化时被调用\n     */publicvoidmeasurementsChanged(){\n        notifyObservers();\n    }\n\npublicvoidsetMeasurements(float temperature,float humidity,float pressure){\nthis.temperature=temperature;\nthis.humidity=humidity;\nthis.pressure=pressure;\n        measurementsChanged();\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"来个测试吧\"\u003e\u003cstrong\u003e来个测试吧\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epublicclass WeatherStation {\npublicstaticvoidmain(String[] args){\n        WeatherData weatherData=new WeatherData();\n\n        CurrentConditionDisplay currentConditionDisplay=new CurrentConditionDisplay(weatherData);\n        weatherData.setMeasurements(80,65,30.4f);\n    }\n}\n\n#输出\nCurrent conditions:80.0F degree and 65.0% humidity\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"使用java自带的observer\"\u003e\u003cstrong\u003e使用java自带的Observer\u003c/strong\u003e\u003c/h1\u003e\n\u003ch1 id=\"定义显示元素接口\"\u003e\u003cstrong\u003e定义显示元素接口\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage Observable;\n\npublicinterfaceDisplayElement {\npublicvoiddisplay();\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"实现观察者接口和显示元素接口\"\u003e\u003cstrong\u003e实现观察者接口和显示元素接口\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage Observable;\n\nimport java.util.Observable;\nimport java.util.Observer;\n\n/**\n * 天气状况布告板\n * Created by jimersylee on\n */publicclassCurrentConditionDisplayimplementsObserver,DisplayElement {\n    Observable observable;\nprivatefloat temperature;\nprivatefloat humidity;\n\npublicCurrentConditionDisplay(Observable observable){\nthis.observable=observable;\n        observable.addObserver(this);\n    }\n\npublicvoidupdate(Observable obs,Object arg){\nif(obsinstanceof WeatherData){\n            WeatherData weatherData=(WeatherData)obs;\nthis.temperature=weatherData.getTemperature();\nthis.humidity=weatherData.getHumidity();\n            display();\n        }\n    }\n\npublicvoiddisplay(){\n        System.out.println(\u0026#34;Current conditions:\u0026#34;+temperature+\u0026#34;F degrees and \u0026#34;+humidity+\u0026#34;% humidity\u0026#34;);\n    }\n\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"实现观察者抽象类\"\u003e\u003cstrong\u003e实现观察者抽象类\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage Observable;\n\nimport java.util.Observable;\n\n/**\n * 使用java.util内置的观察者模式实现\n * Created by jimersylee\n */publicclassWeatherDataextendsObservable {\nprivatefloat temperature;\nprivatefloat humidity;\nprivatefloat pressure;\n\npublicWeatherData(){\n\n    }\n\npublicvoidmeasurementsChanged(){\n        setChanged();\n        notifyObservers();\n    }\n\npublicfloatgetTemperature(){\nreturn temperature;\n    }\n\npublicfloatgetHumidity(){\nreturn humidity;\n    }\n\npublicfloatgetPressure(){\nreturn pressure;\n    }\n\npublicvoidsetMeasurements(float temperature,float humidity,float pressure){\nthis.temperature=temperature;\nthis.humidity=humidity;\nthis.pressure=pressure;\n        measurementsChanged();\n    }\n\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"写个测试吧\"\u003e\u003cstrong\u003e写个测试吧\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage Observable;\n\npublicclass WeatherStation {\npublicstaticvoidmain(String[] args){\n        WeatherData weatherData=new WeatherData();\n\n        CurrentConditionDisplay currentConditionDisplay=new CurrentConditionDisplay(weatherData);\n\n        weatherData.setMeasurements(80,30,33.2f);\n    }\n}\n\n#输出\nCurrent conditions:80.0F degrees and 30.0% humidity\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"项目地址\"\u003e\u003cstrong\u003e项目地址\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/jimersylee/DesignPattern\"\u003ejava设计模式实现\u003c/a\u003e 如果觉得有点收获,记得在项目上点star哦!\u003c/p\u003e","title":"设计模式之观察者模式"},{"content":"软件开发的一个不变真理 不变的就是变化\n驱动改变的因素很多.找出你的应用中需要改变代码的原因\n用户需要新的功能 需要推出新的活动 应用改版 为了更好的性能 继承不能很好的解决问题,因为对象的行为在子类里不断地改变,并且让所有子类都有这些行为是不恰当的. 使用Fooable等接口,只用能实现的类才继承Fooable接口,但是java接口不具有实现代码,所以继承接口无法达到代码的复用.\n引出一个设计原则\n找出应用中可能需要变化之处,把它独立出来,不要和那些不需要变化的代码混在一起\n设计原则\n针对接口编程,而不是针对实现编程\n假设有很多鸭子,有真鸭,模型鸭,如何实现他们的行为呢? 先定义个一个抽象的Duck类 /** * 鸭子的抽象类 */publicabstractclassDuck{ private String _headColor=\u0026#34;yellow\u0026#34;;//Duck对象必备的属性public Stringget_headColor() { return _headColor; } publicvoidset_headColor(String _headColor) { this._headColor = _headColor; } public IFlyBehavior flyBehavior;//为行为接口类型声明引用变量,所有鸭子子类都继承他们public IFlyBehaviorgetFlyBehavior() { return flyBehavior; } publicvoidsetFlyBehavior(IFlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; } public IQuackBehaviorgetQuackBehavior() { return quackBehavior; } publicvoidsetQuackBehavior(IQuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; } public IQuackBehavior quackBehavior;//同上publicDuck(){ } publicvoidperformQuack(){ quackBehavior.quack();//委托给行为类 } publicvoidperformFly(){ flyBehavior.fly();//委托给行为类 } publicabstractvoiddisplay(); /** * Duck必备的行为 */publicvoidswim(){ System.out.println(\u0026#34;All ducks float,even decoys!\u0026#34;); } } 绿头鸭继承Duck /** * 绿头鸭类 */publicclassMallardDuckextendsDuck { publicMallardDuck(){ quackBehavior=new Quack();//绿头鸭使用Quack类处理叫,所以当performQuack()被调用时,叫的职责被委托给Quack flyBehavior=new FlyWithWings();//同理 } publicvoiddisplay(){ System.out.println(\u0026#34;I\u0026#39;m a real Mallard duck\u0026#34;); } } 模型鸭 /* * 模型鸭 */publicclassModelDuckextendsDuck { publicModelDuck(){ flyBehavior=new FlyNoWay();//一开始,模型鸭不会飞 quackBehavior=new Quack();//一开始,模型鸭会呱呱叫 } @Override publicvoiddisplay() { } } 鸭子的鸣叫行为接口类 /** * 叫行为接口类 */publicinterfaceIQuackBehavior{ publicvoidquack(); } 鸭子的飞行行为接口类 /** * 飞行行为接口类 */publicinterfaceIFlyBehavior{ publicvoidfly(); } 各种实现了飞行行为的实现类 /** * 这是飞行行为的实现,给真会飞的鸭子用 */publicclassFlyWithWingsimplementsIFlyBehavior { @Override publicvoidfly() { System.out.println(\u0026#34;fly with wings\u0026#34;); } } /** * 火箭动力的飞行行为 */publicclassFlyWithRocketimplementsIFlyBehavior { @Override publicvoidfly() { System.out.println(\u0026#34;I\u0026#39;m flying with a rocket!\u0026#34;); } } /** * 这是飞行行为的实现,给不会飞的鸭子用 */publicclassFlyNoWayimplementsIFlyBehavior { @Override publicvoidfly() { System.out.println(\u0026#34;I can\u0026#39;t fly!\u0026#34;); } } 各种实现了鸣叫行为的实现类 /** * 叫的实现,给会呱呱叫的鸭子用 */publicclassQuackimplementsIQuackBehavior { @Override publicvoidquack() { System.out.println(\u0026#34;Quack,gua gua gua!\u0026#34;); } } /** * 鸭子叫的沉默实现,给不会叫的鸭子用 */publicclassQuackMuteimplementsIQuackBehavior { @Override publicvoidquack() { System.out.println(\u0026#34;\u0026lt;\u0026lt;Silence\u0026gt;\u0026gt;\u0026#34;); } } 测试我们的鸭子们 publicclassTest { publicstatic void main(String args[]){ MallardDuck mallardDuck=new MallardDuck(); mallardDuck.display(); mallardDuck.performFly(); mallardDuck.performQuack(); //搞一只模型鸭 ModelDuck md=new ModelDuck(); md.performFly();//第一次调用飞行时,委托给FlyNoWay md.setFlyBehavior(new FlyWithRocket());//继承来的设置飞行模式的方法,给予火箭动力 md.performFly();//现在能飞啦~ } } //输出 Bobble gobble I\u0026#39;m flying a short distance gua!gua!gua! I\u0026#39;m flying a long distance Bobble gobble I\u0026#39;m flying a short distance I\u0026#39;m flying a short distance I\u0026#39;m flying a short distance I\u0026#39;m flying a short distance I\u0026#39;m flying a short distance 项目地址 java设计模式实现 如果觉得有点收获,记得在项目上点star哦!\n","permalink":"https://blog.jimersylee.com/posts/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E9%9D%A2%E5%90%91%E6%8E%A5%E5%8F%A3/","summary":"\u003ch1 id=\"软件开发的一个不变真理\"\u003e\u003cstrong\u003e软件开发的一个不变真理\u003c/strong\u003e\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e不变的就是变化\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e驱动改变的因素很多.找出你的应用中需要改变代码的原因\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e用户需要新的功能\u003c/li\u003e\n\u003cli\u003e需要推出新的活动\u003c/li\u003e\n\u003cli\u003e应用改版\u003c/li\u003e\n\u003cli\u003e为了更好的性能\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e继承不能很好的解决问题,因为对象的行为在子类里不断地改变,并且让所有子类都有这些行为是不恰当的. 使用Fooable等接口,只用能实现的类才继承Fooable接口,但是java接口不具有实现代码,所以继承接口无法达到代码的复用.\u003c/p\u003e\n\u003cp\u003e引出一个\u003cstrong\u003e设计原则\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e找出应用中可能需要变化之处,把它独立出来,不要和那些不需要变化的代码混在一起\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cstrong\u003e设计原则\u003c/strong\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e针对接口编程,而不是针对实现编程\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"假设有很多鸭子有真鸭模型鸭如何实现他们的行为呢\"\u003e\u003cstrong\u003e假设有很多鸭子,有真鸭,模型鸭,如何实现他们的行为呢?\u003c/strong\u003e\u003c/h1\u003e\n\u003ch1 id=\"先定义个一个抽象的duck类\"\u003e\u003cstrong\u003e先定义个一个抽象的Duck类\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 鸭子的抽象类\n */publicabstractclassDuck{\nprivate String _headColor=\u0026#34;yellow\u0026#34;;//Duck对象必备的属性public Stringget_headColor() {\nreturn _headColor;\n    }\n\npublicvoidset_headColor(String _headColor) {\nthis._headColor = _headColor;\n    }\n\npublic IFlyBehavior flyBehavior;//为行为接口类型声明引用变量,所有鸭子子类都继承他们public IFlyBehaviorgetFlyBehavior() {\nreturn flyBehavior;\n    }\n\npublicvoidsetFlyBehavior(IFlyBehavior flyBehavior) {\nthis.flyBehavior = flyBehavior;\n    }\n\npublic IQuackBehaviorgetQuackBehavior() {\nreturn quackBehavior;\n    }\n\npublicvoidsetQuackBehavior(IQuackBehavior quackBehavior) {\nthis.quackBehavior = quackBehavior;\n    }\n\npublic IQuackBehavior quackBehavior;//同上publicDuck(){\n\n    }\n\npublicvoidperformQuack(){\n        quackBehavior.quack();//委托给行为类\n    }\n\npublicvoidperformFly(){\n        flyBehavior.fly();//委托给行为类\n    }\n\npublicabstractvoiddisplay();\n\n/**\n     * Duck必备的行为\n     */publicvoidswim(){\n        System.out.println(\u0026#34;All ducks float,even decoys!\u0026#34;);\n    }\n\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"绿头鸭继承duck\"\u003e\u003cstrong\u003e绿头鸭继承Duck\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 绿头鸭类\n */publicclassMallardDuckextendsDuck {\npublicMallardDuck(){\n        quackBehavior=new Quack();//绿头鸭使用Quack类处理叫,所以当performQuack()被调用时,叫的职责被委托给Quack\n        flyBehavior=new FlyWithWings();//同理\n    }\n\npublicvoiddisplay(){\n        System.out.println(\u0026#34;I\u0026#39;m a real Mallard duck\u0026#34;);\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"模型鸭\"\u003e\u003cstrong\u003e模型鸭\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/*\n* 模型鸭\n*/publicclassModelDuckextendsDuck {\npublicModelDuck(){\n        flyBehavior=new FlyNoWay();//一开始,模型鸭不会飞\n        quackBehavior=new Quack();//一开始,模型鸭会呱呱叫\n    }\n    @Override\npublicvoiddisplay() {\n\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"鸭子的鸣叫行为接口类\"\u003e\u003cstrong\u003e鸭子的鸣叫行为接口类\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 叫行为接口类\n */publicinterfaceIQuackBehavior{\npublicvoidquack();\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"鸭子的飞行行为接口类\"\u003e\u003cstrong\u003e鸭子的飞行行为接口类\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 飞行行为接口类\n */publicinterfaceIFlyBehavior{\npublicvoidfly();\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"各种实现了飞行行为的实现类\"\u003e\u003cstrong\u003e各种实现了飞行行为的实现类\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 这是飞行行为的实现,给真会飞的鸭子用\n */publicclassFlyWithWingsimplementsIFlyBehavior {\n    @Override\npublicvoidfly() {\n        System.out.println(\u0026#34;fly with wings\u0026#34;);\n    }\n}\n\n/**\n * 火箭动力的飞行行为\n */publicclassFlyWithRocketimplementsIFlyBehavior {\n    @Override\npublicvoidfly() {\n        System.out.println(\u0026#34;I\u0026#39;m flying with a rocket!\u0026#34;);\n    }\n}\n\n/**\n * 这是飞行行为的实现,给不会飞的鸭子用\n */publicclassFlyNoWayimplementsIFlyBehavior {\n    @Override\npublicvoidfly() {\n        System.out.println(\u0026#34;I can\u0026#39;t fly!\u0026#34;);\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"各种实现了鸣叫行为的实现类\"\u003e\u003cstrong\u003e各种实现了鸣叫行为的实现类\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e/**\n * 叫的实现,给会呱呱叫的鸭子用\n */publicclassQuackimplementsIQuackBehavior {\n    @Override\npublicvoidquack() {\n        System.out.println(\u0026#34;Quack,gua gua gua!\u0026#34;);\n    }\n}\n\n/**\n * 鸭子叫的沉默实现,给不会叫的鸭子用\n */publicclassQuackMuteimplementsIQuackBehavior {\n    @Override\npublicvoidquack() {\n        System.out.println(\u0026#34;\u0026lt;\u0026lt;Silence\u0026gt;\u0026gt;\u0026#34;);\n    }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"测试我们的鸭子们\"\u003e\u003cstrong\u003e测试我们的鸭子们\u003c/strong\u003e\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epublicclassTest {\npublicstatic void main(String args[]){\n        MallardDuck mallardDuck=new MallardDuck();\n        mallardDuck.display();\n        mallardDuck.performFly();\n        mallardDuck.performQuack();\n\n//搞一只模型鸭\n        ModelDuck md=new ModelDuck();\n        md.performFly();//第一次调用飞行时,委托给FlyNoWay\n        md.setFlyBehavior(new FlyWithRocket());//继承来的设置飞行模式的方法,给予火箭动力\n        md.performFly();//现在能飞啦~\n\n    }\n}\n\n//输出\nBobble gobble\nI\u0026#39;m flying a short distance\ngua!gua!gua!\nI\u0026#39;m flying a long distance\nBobble gobble\nI\u0026#39;m flying a short distance\nI\u0026#39;m flying a short distance\nI\u0026#39;m flying a short distance\nI\u0026#39;m flying a short distance\nI\u0026#39;m flying a short distance\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"项目地址\"\u003e\u003cstrong\u003e项目地址\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/jimersylee/DesignPattern\"\u003ejava设计模式实现\u003c/a\u003e 如果觉得有点收获,记得在项目上点star哦!\u003c/p\u003e","title":"设计模式之面向接口"},{"content":"文件和目录: # cd /home 进入 \u0026#39;/home\u0026#39; 目录 # cd .. 返回上一级目录 # cd ../.. 返回上两级目录 # cd - 返回上次所在目录 # cp file1 file2 将file1复制为file2 # cp -a dir1 dir2 复制一个目录 # cp -a /tmp/dir1 . 复制一个目录到当前工作目录（.代表当前目录） # ls 查看目录中的文件 # ls -a 显示隐藏文件 # ls -l 显示详细信息 # ls -lrt 按时间显示文件（l表示详细列表，r表示反向排序，t表示按时间排序） # pwd 显示工作路径 # mkdir dir1 创建 \u0026#39;dir1\u0026#39; 目录 # mkdir dir1 dir2 同时创建两个目录 # mkdir -p /tmp/dir1/dir2 创建一个目录树 # mv dir1 dir2 移动/重命名一个目录 # rm -f file1 删除 \u0026#39;file1\u0026#39; # rm -rf dir1 删除 \u0026#39;dir1\u0026#39; 目录及其子目录内容 查看文件内容: # cat file1 从第一个字节开始正向查看文件的内容 # head -2 file1 查看一个文件的前两行 # more file1 查看一个长文件的内容 # tac file1 从最后一行开始反向查看一个文件的内容 # tail -3 file1 查看一个文件的最后三行 文本处理: # grep str /tmp/test 在文件 \u0026#39;/tmp/test\u0026#39; 中查找 \u0026#34;str\u0026#34; # grep ^str /tmp/test 在文件 \u0026#39;/tmp/test\u0026#39; 中查找以 \u0026#34;str\u0026#34; 开始的行 # grep [0-9] /tmp/test 查找 \u0026#39;/tmp/test\u0026#39; 文件中所有包含数字的行 # grep str -r /tmp/* 在目录 \u0026#39;/tmp\u0026#39; 及其子目录中查找 \u0026#34;str\u0026#34; # diff file1 file2 找出两个文件的不同处 # sdiff file1 file2 以对比的方式显示两个文件的不同 查找: # find / -name file1 从 \u0026#39;/\u0026#39; 开始进入根文件系统查找文件和目录 # find / -user user1 查找属于用户 \u0026#39;user1\u0026#39; 的文件和目录 # find /home/user1 -name \\*.bin 在目录 \u0026#39;/ home/user1\u0026#39; 中查找以 \u0026#39;.bin\u0026#39; 结尾的文件 # find /usr/bin -type f -atime +100 查找在过去100天内未被使用过的执行文件 # find /usr/bin -type f -mtime -10 查找在10天内被创建或者修改过的文件 # locate \\*.ps 寻找以 \u0026#39;.ps\u0026#39; 结尾的文件，先运行 \u0026#39;updatedb\u0026#39; 命令 # find -name \u0026#39;*.[ch]\u0026#39; | xargs grep -E \u0026#39;expr\u0026#39; 在当前目录及其子目录所有.c和.h文件中查找 \u0026#39;expr\u0026#39; # find -type f -print0 | xargs -r0 grep -F \u0026#39;expr\u0026#39; 在当前目录及其子目录的常规文件中查找 \u0026#39;expr\u0026#39; # find -maxdepth 1 -type f | xargs grep -F \u0026#39;expr\u0026#39; 在当前目录中查找 \u0026#39;expr\u0026#39; 压缩和解压: # bzip2 file1 压缩 file1 # bunzip2 file1.bz2 解压 file1.bz2 # gzip file1 压缩 file1 # gzip -9 file1 最大程度压缩 file1 # gunzip file1.gz 解压 file1.gz # tar -cvf archive.tar file1 把file1打包成 archive.tar （-c: 建立压缩档案；-v: 显示所有过程；-f: 使用档案名字，是必须的，是最后一个参数） # tar -cvf archive.tar file1 dir1 把 file1，dir1 打包成 archive.tar # tar -tf archive.tar 显示一个包中的内容 # tar -xvf archive.tar 释放一个包 # tar -xvf archive.tar -C /tmp 把压缩包释放到 /tmp目录下 # zip file1.zip file1 创建一个zip格式的压缩包 # zip -r file1.zip file1 dir1 把文件和目录压缩成一个zip格式的压缩包 # unzip file1.zip 解压一个zip格式的压缩包到当前目录 # unzip test.zip -d /tmp/ 解压一个zip格式的压缩包到 /tmp 目录 yum工具: # yum -y install [package] 下载并安装一个rpm包 # yum localinstall [package.rpm] 安装一个rpm包，使用你自己的软件仓库解决所有依赖关系 # yum -y update 更新当前系统中安装的所有rpm包 # yum update [package] 更新一个rpm包 # yum remove [package] 删除一个rpm包 # yum list 列出当前系统中安装的所有包 # yum search [package] 在rpm仓库中搜寻软件包 # yum clean [package] 清除缓存目录（/var/cache/yum）下的软件包 # yum clean headers 删除所有头文件 # yum clean all 删除所有缓存的包和头文件 网络: # ifconfig eth0 显示一个以太网卡的配置 # ifconfig eth0 192.168.1.1 netmask 255.255.255.0 配置网卡的IP地址 # ifdown eth0 禁用 \u0026#39;eth0\u0026#39; 网络设备 # ifup eth0 启用 \u0026#39;eth0\u0026#39; 网络设备 # iwconfig eth1 显示一个无线网卡的配置 # iwlist scan 显示无线网络 # ip addr show 显示网卡的IP地址 其他: # su - 切换到root权限（与su有区别） # shutdown -h now 关机 # shutdown -r now 重启 # top 罗列使用CPU资源最多的linux任务 （输入q退出） # pstree 以树状图显示程序 # man ping 查看参考手册（例如ping 命令） # passwd 修改密码 # df -h 显示磁盘的使用情况 # cal -3 显示前一个月，当前月以及下一个月的月历 # cal 10 1988 显示指定月，年的月历 # date --date \u0026#39;1970-01-01 UTC 1427888888 seconds\u0026#39; 把一相对于1970-01-01 00:00的秒数转换成时间 Ctrl + u 删除光标之前到行首的字符 Ctrl + k 删除光标之前到行尾的字符 Ctrl + c 取消当前行输入的命令，相当于Ctrl + Break Ctrl + a 光标移动到行首（ahead of line），相当于通常的Home键 Ctrl + e 光标移动到行尾（end of line） Ctrl + f 光标向前（forward）移动一个字符位置 Ctrl + b 光标往回（backward）移动一个字符位置 Ctrl + l 清屏，相当于执行clear命令 Ctrl + r 显示:号提示，根据用户输入查找相关历史命令（reverse-i-search） Ctrl + w 删除从光标位置前到当前所处单词（word）的开头 Ctrl + t 交换光标位置前的两个字符 Ctrl + y 粘贴最后一次被删除的单词 Ctrl + Alt + d 显示桌面 Alt + b 光标往回（backward）移动到前一个单词 Alt + d 删除从光标位置到当前所处单词的末尾 Alt + F2 运行 Alt + F4 关闭当前窗口 Alt + F9 最小化当前窗口 Alt + F10 最大化当前窗口 Alt + Tab 切换窗口 Alt +按住左键 移动窗口（或在最下面的任务栏滚动鼠标滑轮） [鼠标中间键] 粘贴突出显示的文本。使用鼠标左键来选择文本。把光标指向想粘贴文本的地方。点击鼠标中间键来粘贴。 [Tab] 命令行自动补全。使用 shell 提示时可使用这一方式。键入命令或文件名的前几个字符，然后按 [Tab] 键，它会自动补全命令或显示匹配键入字符的所有命令。 在桌面或文件管理器中直接按 / 就可以输入位置，打开文件管理器。 快速搜索：在 vi 或 Firefox 中直接按 / 即可进入搜索状态。 网站链接和图片可直接拖放到桌面或者目录，可以马上下载。 直接将文件管理器中的文件拖到终端中就可以在终端中得到完整的路径名。 在滚动条的空白处点击鼠标中键，屏幕即滚动到那个地方。 ","permalink":"https://blog.jimersylee.com/posts/linux%E7%B3%BB%E7%BB%9F%E5%91%BD%E4%BB%A4/","summary":"\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e文件和目录:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# cd /home                        进入 \u0026#39;/home\u0026#39; 目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# cd ..                                返回上一级目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# cd ../..                             返回上两级目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# cd -                                 返回上次所在目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# cp file1 file2                    将file1复制为file2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# cp -a dir1 dir2                 复制一个目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# cp -a /tmp/dir1 .              复制一个目录到当前工作目录（.代表当前目录）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# ls                                    查看目录中的文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# ls -a                                显示隐藏文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# ls -l                                 显示详细信息\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# ls -lrt                               按时间显示文件（l表示详细列表，r表示反向排序，t表示按时间排序）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# pwd                                显示工作路径\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# mkdir dir1                       创建 \u0026#39;dir1\u0026#39; 目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# mkdir dir1 dir2                同时创建两个目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# mkdir -p /tmp/dir1/dir2    创建一个目录树\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# mv dir1 dir2                    移动/重命名一个目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# rm -f file1                        删除 \u0026#39;file1\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# rm -rf dir1                       删除 \u0026#39;dir1\u0026#39; 目录及其子目录内容\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e查看文件内容:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# cat file1                          从第一个字节开始正向查看文件的内容\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# head -2 file1                   查看一个文件的前两行\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# more file1                       查看一个长文件的内容\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# tac file1                          从最后一行开始反向查看一个文件的内容\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# tail -3 file1                      查看一个文件的最后三行\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e文本处理:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# grep str /tmp/test            在文件 \u0026#39;/tmp/test\u0026#39; 中查找 \u0026#34;str\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# grep ^str /tmp/test           在文件 \u0026#39;/tmp/test\u0026#39; 中查找以 \u0026#34;str\u0026#34; 开始的行\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# grep [0-9] /tmp/test         查找 \u0026#39;/tmp/test\u0026#39; 文件中所有包含数字的行\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# grep str -r /tmp/*             在目录 \u0026#39;/tmp\u0026#39; 及其子目录中查找 \u0026#34;str\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# diff file1 file2                   找出两个文件的不同处\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# sdiff file1 file2                 以对比的方式显示两个文件的不同\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e查找:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# find / -name file1                                                 从 \u0026#39;/\u0026#39; 开始进入根文件系统查找文件和目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# find / -user user1                                                查找属于用户 \u0026#39;user1\u0026#39; 的文件和目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# find /home/user1 -name \\*.bin                            在目录 \u0026#39;/ home/user1\u0026#39; 中查找以 \u0026#39;.bin\u0026#39; 结尾的文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# find /usr/bin -type f -atime +100                         查找在过去100天内未被使用过的执行文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# find /usr/bin -type f -mtime -10                           查找在10天内被创建或者修改过的文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# locate \\*.ps                                                         寻找以 \u0026#39;.ps\u0026#39; 结尾的文件，先运行 \u0026#39;updatedb\u0026#39; 命令\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# find -name \u0026#39;*.[ch]\u0026#39; | xargs grep -E \u0026#39;expr\u0026#39;              在当前目录及其子目录所有.c和.h文件中查找 \u0026#39;expr\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# find -type f -print0 | xargs -r0 grep -F \u0026#39;expr\u0026#39;        在当前目录及其子目录的常规文件中查找 \u0026#39;expr\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# find -maxdepth 1 -type f | xargs grep -F \u0026#39;expr\u0026#39;    在当前目录中查找 \u0026#39;expr\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e压缩和解压:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# bzip2 file1                                   压缩 file1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# bunzip2 file1.bz2                        解压 file1.bz2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# gzip file1                                     压缩 file1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# gzip -9 file1                                最大程度压缩 file1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# gunzip file1.gz                            解压 file1.gz\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# tar -cvf archive.tar file1               把file1打包成 archive.tar\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e（-c: 建立压缩档案；-v: 显示所有过程；-f: 使用档案名字，是必须的，是最后一个参数）\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# tar -cvf archive.tar file1 dir1        把 file1，dir1 打包成 archive.tar\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# tar -tf archive.tar                         显示一个包中的内容\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# tar -xvf archive.tar                      释放一个包\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# tar -xvf archive.tar -C /tmp         把压缩包释放到 /tmp目录下\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# zip file1.zip file1                          创建一个zip格式的压缩包\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# zip -r file1.zip file1 dir1               把文件和目录压缩成一个zip格式的压缩包\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# unzip file1.zip                             解压一个zip格式的压缩包到当前目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# unzip test.zip -d /tmp/                 解压一个zip格式的压缩包到 /tmp 目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eyum工具:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# yum -y install [package]              下载并安装一个rpm包\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# yum localinstall [package.rpm]    安装一个rpm包，使用你自己的软件仓库解决所有依赖关系\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# yum -y update                              更新当前系统中安装的所有rpm包\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# yum update [package]                 更新一个rpm包\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# yum remove [package]                删除一个rpm包\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# yum list                                        列出当前系统中安装的所有包\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# yum search [package]                 在rpm仓库中搜寻软件包\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# yum clean [package]                   清除缓存目录（/var/cache/yum）下的软件包\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# yum clean headers                      删除所有头文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# yum clean all                                删除所有缓存的包和头文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e网络:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# ifconfig eth0                                                                       显示一个以太网卡的配置\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# ifconfig eth0 192.168.1.1 netmask 255.255.255.0            配置网卡的IP地址\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# ifdown eth0                                                                        禁用 \u0026#39;eth0\u0026#39; 网络设备\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# ifup eth0                                                                            启用 \u0026#39;eth0\u0026#39; 网络设备\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# iwconfig eth1                                                                     显示一个无线网卡的配置\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# iwlist scan                                                                         显示无线网络\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# ip addr show                                                                     显示网卡的IP地址\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e其他:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# su -                                 切换到root权限（与su有区别）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# shutdown -h now           关机\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# shutdown -r now            重启\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# top                                  罗列使用CPU资源最多的linux任务 （输入q退出）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# pstree                             以树状图显示程序\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# man ping                        查看参考手册（例如ping 命令）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# passwd                          修改密码\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# df -h                               显示磁盘的使用情况\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# cal -3                             显示前一个月，当前月以及下一个月的月历\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# cal 10 1988                   显示指定月，年的月历\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# date --date \u0026#39;1970-01-01 UTC 1427888888 seconds\u0026#39;   把一相对于1970-01-01 00:00的秒数转换成时间\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCtrl + u            删除光标之前到行首的字符\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCtrl + k            删除光标之前到行尾的字符\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCtrl + c            取消当前行输入的命令，相当于Ctrl + Break\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCtrl + a            光标移动到行首（ahead of line），相当于通常的Home键\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCtrl + e            光标移动到行尾（end of line）\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCtrl + f             光标向前（forward）移动一个字符位置\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCtrl + b            光标往回（backward）移动一个字符位置\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCtrl + l             清屏，相当于执行clear命令\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCtrl + r            显示:号提示，根据用户输入查找相关历史命令（reverse-i-search）\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCtrl + w           删除从光标位置前到当前所处单词（word）的开头\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCtrl + t             交换光标位置前的两个字符\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCtrl + y            粘贴最后一次被删除的单词\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCtrl + Alt + d   显示桌面\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAlt + b             光标往回（backward）移动到前一个单词\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAlt + d             删除从光标位置到当前所处单词的末尾\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAlt + F2           运行\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAlt + F4           关闭当前窗口\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAlt + F9           最小化当前窗口\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAlt + F10         最大化当前窗口\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAlt + Tab         切换窗口\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eAlt +按住左键  移动窗口（或在最下面的任务栏滚动鼠标滑轮）\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003e鼠标中间键\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e 粘贴突出显示的文本。使用鼠标左键来选择文本。把光标指向想粘贴文本的地方。点击鼠标中间键来粘贴。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eTab\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e 命令行自动补全。使用 shell 提示时可使用这一方式。键入命令或文件名的前几个字符，然后按 \u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003eTab\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e 键，它会自动补全命令或显示匹配键入字符的所有命令。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e在桌面或文件管理器中直接按 / 就可以输入位置，打开文件管理器。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e快速搜索：在 vi 或 Firefox 中直接按 / 即可进入搜索状态。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e网站链接和图片可直接拖放到桌面或者目录，可以马上下载。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e直接将文件管理器中的文件拖到终端中就可以在终端中得到完整的路径名。\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e在滚动条的空白处点击鼠标中键，屏幕即滚动到那个地方。\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e","title":"linux系统命令"},{"content":"安装了一个新的app 叫微习惯 督促自己每天都做一些事 今天努力完成了所有小事 就差一个30分钟的写作了 于是现在在写 每做完一件事然后打勾 还是很有成就感的 父母在我很小的时候就教育我 说人生最难的就是坚持 小时候不以为意 现在明白了 人都是倾向于虎头蛇尾的 坚持的确是件最难的事 但是 我希望可以改变 可以从一个个的小习惯开始 改变自己 加油 明天早点起床\n","permalink":"https://blog.jimersylee.com/posts/%E5%85%BB%E6%88%90%E5%A5%BD%E4%B9%A0%E6%83%AF/","summary":"\u003cp\u003e安装了一个新的app 叫微习惯 督促自己每天都做一些事 今天努力完成了所有小事 就差一个30分钟的写作了 于是现在在写 每做完一件事然后打勾 还是很有成就感的 父母在我很小的时候就教育我 说人生最难的就是坚持 小时候不以为意 现在明白了 人都是倾向于虎头蛇尾的 坚持的确是件最难的事 但是 我希望可以改变 可以从一个个的小习惯开始 改变自己 加油 明天早点起床\u003c/p\u003e","title":"养成好习惯"},{"content":"七月二十三日， 晴，好热\n于是去游泳。\n一个人，一条裤衩，不戴泳镜，更不需要救生圈。 地点是泄洪坝底下下冲刷而成的水潭，虽没有桃花潭水深千尺，但是十尺还是有的。前几日连绵大雨，所以泄洪了，于是水潭中都是新鲜的水库水，经过水的冲刷，现在水底很清澈，可以直接看到水底。也没有水藻。非常适合游泳呐。 在靠近岸边的地方下水，岸边还是很浅的，踩着圆润的水底砂石，仿佛有种回到童年的感觉。那个瘦瘦小小的小孩，双手撑在河底，双脚扑腾着学习游泳的小孩。嘴巴进水，鼻子进水，咳嗽，鼻酸，长时间潜水后强烈想要呼吸的感觉，潜到深处胸腔感受到的压力，以及水中唯一听得到的隆隆声，虽历历在目，声声在耳，却是多久没感受过了。\n自由泳，蛙泳，仰泳，潜泳，狗刨式，全部来一套。原来游泳这个技能，一旦学会，就完全不会忘呢。什么学会呢，先天技能，幼时就在子宫里游来游去。 水底的世界就像是近视眼看到的世界一样，模糊，但是色彩斑斓。尤其对白色的物体尤为敏感。白色的石头，闪着光。随意潜入水中，就可以捞起一些石头，这是幼时最喜爱的游泳游戏。可是现在，却没有幼时的欣喜。\n","permalink":"https://blog.jimersylee.com/posts/%E6%B8%B8%E6%B3%B3%E6%9D%82%E6%84%9F/","summary":"\u003cp\u003e七月二十三日， 晴，好热\u003c/p\u003e\n\u003cp\u003e于是去游泳。\u003c/p\u003e\n\u003cp\u003e一个人，一条裤衩，不戴泳镜，更不需要救生圈。 地点是泄洪坝底下下冲刷而成的水潭，虽没有桃花潭水深千尺，但是十尺还是有的。前几日连绵大雨，所以泄洪了，于是水潭中都是新鲜的水库水，经过水的冲刷，现在水底很清澈，可以直接看到水底。也没有水藻。非常适合游泳呐。 在靠近岸边的地方下水，岸边还是很浅的，踩着圆润的水底砂石，仿佛有种回到童年的感觉。那个瘦瘦小小的小孩，双手撑在河底，双脚扑腾着学习游泳的小孩。嘴巴进水，鼻子进水，咳嗽，鼻酸，长时间潜水后强烈想要呼吸的感觉，潜到深处胸腔感受到的压力，以及水中唯一听得到的隆隆声，虽历历在目，声声在耳，却是多久没感受过了。\u003c/p\u003e\n\u003cp\u003e自由泳，蛙泳，仰泳，潜泳，狗刨式，全部来一套。原来游泳这个技能，一旦学会，就完全不会忘呢。什么学会呢，先天技能，幼时就在子宫里游来游去。 水底的世界就像是近视眼看到的世界一样，模糊，但是色彩斑斓。尤其对白色的物体尤为敏感。白色的石头，闪着光。随意潜入水中，就可以捞起一些石头，这是幼时最喜爱的游泳游戏。可是现在，却没有幼时的欣喜。\u003c/p\u003e","title":"游泳杂感"},{"content":"理解Linux系统负荷load average Update time: July 21, 2022 10:27 AM\n一、查看系统负荷 如果你的电脑很慢，你或许想查看一下，它的工作量是否太大了。 在Linux系统中，我们一般使用uptime命令查看（w命令和top命令也行）。（另外，它们在苹果公司的Mac电脑上也适用。） 你在终端窗口键入uptime，系统会返回一行信息。\n这行信息的后半部分，显示\u0026quot;load average\u0026quot;，它的意思是\u0026quot;系统的平均负荷\u0026quot;，里面有三个数字，我们可以从中判断系统负荷是大还是小。\n为什么会有三个数字呢？你从手册中查到，它们的意思分别是1分钟、5分钟、15分钟内系统的平均负荷。 如果你继续看手册，它还会告诉你，当CPU完全空闲的时候，平均负荷为0；当CPU工作量饱和的时候，平均负荷为1。 那么很显然，\u0026ldquo;load average\u0026quot;的值越低，比如等于0.2或0.3，就说明电脑的工作量越小，系统负荷比较轻。 但是，什么时候能看出系统负荷比较重呢？等于1的时候，还是等于0.5或等于1.5的时候？如果1分钟、5分钟、15分钟三个值不一样，怎么办？\n二、一个类比 判断系统负荷是否过重，必须理解load average的真正含义。下面，我根据\u0026quot;Understanding Linux CPU Load\u0026quot;这篇文章，尝试用最通俗的语言，解释这个问题。 首先，假设最简单的情况，你的电脑只有一个CPU，所有的运算都必须由这个CPU来完成。 那么，我们不妨把这个CPU想象成一座大桥，桥上只有一根车道，所有车辆都必须从这根车道上通过。（很显然，这座桥只能单向通行。） 系统负荷为0，意味着大桥上一辆车也没有。\n系统负荷为0.5，意味着大桥一半的路段有车。\n系统负荷为1.0，意味着大桥的所有路段都有车，也就是说大桥已经\u0026quot;满\u0026quot;了。但是必须注意的是，直到此时大桥还是能顺畅通行的。\n系统负荷为1.7，意味着车辆太多了，大桥已经被占满了（100%），后面等着上桥的车辆为桥面车辆的70%。以此类推，系统负荷2.0，意味着等待上桥的车辆与桥面的车辆一样多；系统负荷3.0，意味着等待上桥的车辆是桥面车辆的2倍。总之，当系统负荷大于1，后面的车辆就必须等待了；系统负荷越大，过桥就必须等得越久。\nCPU的系统负荷，基本上等同于上面的类比。大桥的通行能力，就是CPU的最大工作量；桥梁上的车辆，就是一个个等待CPU处理的进程（process）。 如果CPU每分钟最多处理100个进程，那么系统负荷0.2，意味着CPU在这1分钟里只处理20个进程；系统负荷1.0，意味着CPU在这1分钟里正好处理100个进程；系统负荷1.7，意味着除了CPU正在处理的100个进程以外，还有70个进程正排队等着CPU处理。 为了电脑顺畅运行，系统负荷最好不要超过1.0，这样就没有进程需要等待了，所有进程都能第一时间得到处理。很显然，1.0是一个关键值，超过这个值，系统就不在最佳状态了，你要动手干预了。\n三、系统负荷的经验法则 1.0是系统负荷的理想值吗？ 不一定，系统管理员往往会留一点余地，当这个值达到0.7，就应当引起注意了。经验法则是这样的： 当系统负荷持续大于0.7，你必须开始调查了，问题出在哪里，防止情况恶化。 当系统负荷持续大于1.0，你必须动手寻找解决办法，把这个值降下来。 当系统负荷达到5.0，就表明你的系统有很严重的问题，长时间没有响应，或者接近死机了。你不应该让系统达到这个值。\n四、多处理器 上面，我们假设你的电脑只有1个CPU。如果你的电脑装了2个CPU，会发生什么情况呢？ 2个CPU，意味着电脑的处理能力翻了一倍，能够同时处理的进程数量也翻了一倍。 还是用大桥来类比，两个CPU就意味着大桥有两根车道了，通车能力翻倍了。\n所以，2个CPU表明系统负荷可以达到2.0，此时每个CPU都达到100%的工作量。推广开来，n个CPU的电脑，可接受的系统负荷最大为n.0。\n五、多核处理器 芯片厂商往往在一个CPU内部，包含多个CPU核心，这被称为多核CPU。 在系统负荷方面，多核CPU与多CPU效果类似，所以考虑系统负荷的时候，必须考虑这台电脑有几个CPU、每个CPU有几个核心。然后，把系统负荷除以总的核心数，只要每个核心的负荷不超过1.0，就表明电脑正常运行。 怎么知道电脑有多少个CPU核心呢？ \u0026ldquo;cat /proc/cpuinfo\u0026quot;命令，可以查看CPU信息。\u0026ldquo;grep -c \u0026lsquo;model name\u0026rsquo; /proc/cpuinfo\u0026quot;命令，直接返回CPU的总核心数。\n六、最佳观察时长 最后一个问题，\u0026ldquo;load average\u0026quot;一共返回三个平均值\u0026mdash;-1分钟系统负荷、5分钟系统负荷，15分钟系统负荷，\u0026mdash;-应该参考哪个值？ 如果只有1分钟的系统负荷大于1.0，其他两个时间段都小于1.0，这表明只是暂时现象，问题不大。 如果15分钟内，平均系统负荷大于1.0（调整CPU核心数之后），表明问题持续存在，不是暂时现象。所以，你应该主要观察\u0026quot;15分钟系统负荷\u0026rdquo;，将它作为电脑正常运行的指标。\n","permalink":"https://blog.jimersylee.com/posts/%E7%90%86%E8%A7%A3linux%E7%B3%BB%E7%BB%9F%E8%B4%9F%E8%8D%B7load-average/","summary":"\u003ch1 id=\"理解linux系统负荷load-average\"\u003e理解Linux系统负荷load average\u003c/h1\u003e\n\u003cp\u003eUpdate time: July 21, 2022 10:27 AM\u003c/p\u003e\n\u003ch1 id=\"一查看系统负荷\"\u003e\u003cstrong\u003e一、查看系统负荷\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e如果你的电脑很慢，你或许想查看一下，它的工作量是否太大了。 在Linux系统中，我们一般使用uptime命令查看（w命令和top命令也行）。（另外，它们在苹果公司的Mac电脑上也适用。） 你在终端窗口键入uptime，系统会返回一行信息。\u003c/p\u003e\n\u003cp\u003e这行信息的后半部分，显示\u0026quot;load average\u0026quot;，它的意思是\u0026quot;系统的平均负荷\u0026quot;，里面有三个数字，我们可以从中判断系统负荷是大还是小。\u003c/p\u003e\n\u003cp\u003e为什么会有三个数字呢？你从手册中查到，它们的意思分别是1分钟、5分钟、15分钟内系统的平均负荷。 如果你继续看手册，它还会告诉你，当CPU完全空闲的时候，平均负荷为0；当CPU工作量饱和的时候，平均负荷为1。 那么很显然，\u0026ldquo;load average\u0026quot;的值越低，比如等于0.2或0.3，就说明电脑的工作量越小，系统负荷比较轻。 但是，什么时候能看出系统负荷比较重呢？等于1的时候，还是等于0.5或等于1.5的时候？如果1分钟、5分钟、15分钟三个值不一样，怎么办？\u003c/p\u003e\n\u003ch1 id=\"二一个类比\"\u003e\u003cstrong\u003e二、一个类比\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e判断系统负荷是否过重，必须理解load average的真正含义。下面，我根据\u0026quot;Understanding Linux CPU Load\u0026quot;这篇文章，尝试用最通俗的语言，解释这个问题。 首先，假设最简单的情况，你的电脑只有一个CPU，所有的运算都必须由这个CPU来完成。 那么，我们不妨把这个CPU想象成一座大桥，桥上只有一根车道，所有车辆都必须从这根车道上通过。（很显然，这座桥只能单向通行。） 系统负荷为0，意味着大桥上一辆车也没有。\u003c/p\u003e\n\u003cp\u003e系统负荷为0.5，意味着大桥一半的路段有车。\u003c/p\u003e\n\u003cp\u003e系统负荷为1.0，意味着大桥的所有路段都有车，也就是说大桥已经\u0026quot;满\u0026quot;了。但是必须注意的是，直到此时大桥还是能顺畅通行的。\u003c/p\u003e\n\u003cp\u003e系统负荷为1.7，意味着车辆太多了，大桥已经被占满了（100%），后面等着上桥的车辆为桥面车辆的70%。以此类推，系统负荷2.0，意味着等待上桥的车辆与桥面的车辆一样多；系统负荷3.0，意味着等待上桥的车辆是桥面车辆的2倍。总之，当系统负荷大于1，后面的车辆就必须等待了；系统负荷越大，过桥就必须等得越久。\u003c/p\u003e\n\u003cp\u003eCPU的系统负荷，基本上等同于上面的类比。大桥的通行能力，就是CPU的最大工作量；桥梁上的车辆，就是一个个等待CPU处理的进程（process）。 如果CPU每分钟最多处理100个进程，那么系统负荷0.2，意味着CPU在这1分钟里只处理20个进程；系统负荷1.0，意味着CPU在这1分钟里正好处理100个进程；系统负荷1.7，意味着除了CPU正在处理的100个进程以外，还有70个进程正排队等着CPU处理。 为了电脑顺畅运行，系统负荷最好不要超过1.0，这样就没有进程需要等待了，所有进程都能第一时间得到处理。很显然，1.0是一个关键值，超过这个值，系统就不在最佳状态了，你要动手干预了。\u003c/p\u003e\n\u003ch1 id=\"三系统负荷的经验法则\"\u003e\u003cstrong\u003e三、系统负荷的经验法则\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e1.0是系统负荷的理想值吗？ 不一定，系统管理员往往会留一点余地，当这个值达到0.7，就应当引起注意了。经验法则是这样的： 当系统负荷持续大于0.7，你必须开始调查了，问题出在哪里，防止情况恶化。 当系统负荷持续大于1.0，你必须动手寻找解决办法，把这个值降下来。 当系统负荷达到5.0，就表明你的系统有很严重的问题，长时间没有响应，或者接近死机了。你不应该让系统达到这个值。\u003c/p\u003e\n\u003ch1 id=\"四多处理器\"\u003e\u003cstrong\u003e四、多处理器\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e上面，我们假设你的电脑只有1个CPU。如果你的电脑装了2个CPU，会发生什么情况呢？ 2个CPU，意味着电脑的处理能力翻了一倍，能够同时处理的进程数量也翻了一倍。 还是用大桥来类比，两个CPU就意味着大桥有两根车道了，通车能力翻倍了。\u003c/p\u003e\n\u003cp\u003e所以，2个CPU表明系统负荷可以达到2.0，此时每个CPU都达到100%的工作量。推广开来，n个CPU的电脑，可接受的系统负荷最大为n.0。\u003c/p\u003e\n\u003ch1 id=\"五多核处理器\"\u003e\u003cstrong\u003e五、多核处理器\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e芯片厂商往往在一个CPU内部，包含多个CPU核心，这被称为多核CPU。 在系统负荷方面，多核CPU与多CPU效果类似，所以考虑系统负荷的时候，必须考虑这台电脑有几个CPU、每个CPU有几个核心。然后，把系统负荷除以总的核心数，只要每个核心的负荷不超过1.0，就表明电脑正常运行。 怎么知道电脑有多少个CPU核心呢？ \u0026ldquo;cat /proc/cpuinfo\u0026quot;命令，可以查看CPU信息。\u0026ldquo;grep -c \u0026lsquo;model name\u0026rsquo; /proc/cpuinfo\u0026quot;命令，直接返回CPU的总核心数。\u003c/p\u003e\n\u003ch1 id=\"六最佳观察时长\"\u003e\u003cstrong\u003e六、最佳观察时长\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003e最后一个问题，\u0026ldquo;load average\u0026quot;一共返回三个平均值\u0026mdash;-1分钟系统负荷、5分钟系统负荷，15分钟系统负荷，\u0026mdash;-应该参考哪个值？ 如果只有1分钟的系统负荷大于1.0，其他两个时间段都小于1.0，这表明只是暂时现象，问题不大。 如果15分钟内，平均系统负荷大于1.0（调整CPU核心数之后），表明问题持续存在，不是暂时现象。所以，你应该主要观察\u0026quot;15分钟系统负荷\u0026rdquo;，将它作为电脑正常运行的指标。\u003c/p\u003e","title":"理解Linux系统负荷load average"},{"content":"环境:CentOS7\n1.下载安装包 在https://jenkins.io/download/找到相应的包下载地址\nwget https://pkg.jenkins.io/redhat/jenkins-2.76-1.1.noarch.rpm 2.安装包\nsudo rpm -i jenkins-2.76-1.1.noarch.rpm 3.启动jenkins\nsudo service jenkins start 4.配置nginx\nvim /etc/nginx/conf.d/jenkins.conf server { 2listen 443; 3 server_name jenkins.jimersylee.com; 4 ssl on; 5 ssl_certificate /data/ssl_cert/Nginx/1_jimersylee.com_bundle.crt; 6 ssl_certificate_key /data/ssl_cert/Nginx/2_jimersylee.com.key; 7 ssl_session_timeout 5m; 8 ssl_protocols TLSv1 TLSv1.1 TLSv1.2;#按照这个协议配置9 ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;#按照这个套件配置10 ssl_prefer_server_ciphers on; 11# root /data/www/www;12 13indexindex.htmlindex.htmindex.php; 14 location / 15 { 16 proxy_pass http://127.0.0.1:8080; 17 } 18 19 access_log /data/logs/jenkins/jenkins.log main; 20 } 21 22 server { 23listen 80; 24 server_name jenkins.jimersylee.com; 25 rewrite ^ https://$server_name$request_uri? permanent; 26 } 5.访问https://jenkins.jimersylee.com/ 发现没有账号密码,修改jenkins为不需要账号密码\nvim /var/lib/jenkins/config.xml \u0026lt;useSecurity\u0026gt;true\u0026lt;/useSecurity\u0026gt;修改为false 重启jenkins sudo service jenkins restart 6.访问https://jenkins.jimersylee.com/ 系统管理-\u0026gt;Configure Global Security 勾选启用安全,勾选使用jenkins专有数据库\n7.因为要使用某个特定账号如dev去执行shell,因此将jenkins的默认启动账号jenkins修改为dev\n8.如何修改运行jenkins进程的linux帐号？\n找到jenkins的配置文件，一般是/etc/sysconfig/jenkins\n修改下面的参数为相应的用户，比如JENKINS_USER=\u0026ldquo;dev\u0026rdquo;\n## Type: string## Default:\u0026#34;jenkins\u0026#34;## ServiceRestart: jenkins## Unix user account that runs the Jenkins daemon# Be careful when you change this, as you need to update# permissions of $JENKINS_HOME and /var/log/jenkins.# JENKINS_USER=\u0026#34;dev\u0026#34; 修改下来文件或目录的权限\nchown dev:dev file chown -R admin:admin directory /var/lib/jenkins/ /var/log/jenkins/ /var/cache/jenkins/ /usr/lib/jenkins/jenkins.war /etc/sysconfig/jenkins 重启jenkins：service jenkins restart\n通过war包安装 安装tomcat mkdir /data/java_app/jenkins \u0026amp;\u0026amp; cd /data/java_app/jenkins wget https://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-9/v9.0.0.M26/bin/apache-tomcat-9.0.0.M26.zip\nunzip apache-tomcat-9.0.0.M26.zip mv apache-tomcat-9.0.0.M26 tomcat9 cd tomcat9/webapps wget http://mirrors.jenkins.io/war/latest/jenkins.war cd ../bin sh catalina.sh\n访问127.0.0.1:8080/jenkins 需要输入初始密码 cat /home/username/.jenkins/secrets/initialAdminPassword 然后配置插件什么的 ","permalink":"https://blog.jimersylee.com/posts/centos7%E5%AE%89%E8%A3%85jenkins/","summary":"\u003cp\u003e环境:CentOS7\u003c/p\u003e\n\u003cp\u003e1.下载安装包 在https://jenkins.io/download/找到相应的包下载地址\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ewget https://pkg.jenkins.io/redhat/jenkins-2.76-1.1.noarch.rpm\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e2.安装包\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esudo rpm -i jenkins-2.76-1.1.noarch.rpm\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e3.启动jenkins\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003esudo service jenkins start\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e4.配置nginx\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003evim /etc/nginx/conf.d/jenkins.conf\nserver {\n 2listen       443;\n 3         server_name  jenkins.jimersylee.com;\n 4         ssl on;\n 5         ssl_certificate /data/ssl_cert/Nginx/1_jimersylee.com_bundle.crt;\n 6         ssl_certificate_key /data/ssl_cert/Nginx/2_jimersylee.com.key;\n 7         ssl_session_timeout 5m;\n 8         ssl_protocols TLSv1 TLSv1.1 TLSv1.2;#按照这个协议配置9         ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;#按照这个套件配置10         ssl_prefer_server_ciphers on;\n11# root /data/www/www;12\n13indexindex.htmlindex.htmindex.php;\n14         location /\n15         {\n16             proxy_pass http://127.0.0.1:8080;\n17         }\n18\n19         access_log /data/logs/jenkins/jenkins.log main;\n20         }\n21\n22 server {\n23listen 80;\n24     server_name jenkins.jimersylee.com;\n25     rewrite ^ https://$server_name$request_uri? permanent;\n26     }\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e5.访问https://jenkins.jimersylee.com/ 发现没有账号密码,修改jenkins为不需要账号密码\u003c/p\u003e","title":"CentOS7安装Jenkins"},{"content":"前言 近期,阅读了\u0026lt;谷歌的软件工程\u0026gt;,记录一下.\n我对谷歌这家公司还是比较好奇和欣赏的,不管都是对于拉里佩奇,还是现在阿尔法特(谷歌母公司)的CEO 桑达尔·皮查伊, 个人都非常敬佩.毕竟都是有真正实力的大佬.\n谷歌运行着目前世界上最大的服务器集群,这个不容置疑.我个人也非常好奇,如此庞大的规模的机器,他们如何调度,如何监控,我们这种小公司是否有可以借鉴的地方.因此,当我发现这本书的时候,我觉得这就是我一直寻找的书.\n其实在这之前,我已经看过\u0026lt;Sre google运维解密\u0026gt;,收获良多,我的DevOps概念就是从这本书中学来的.个人在之后的工作中也引入了这套工作流程,砍掉了运维工程师,由开发自己负责从开发部署,持续运维监控的全流程,开发人员反馈良好,接触到了很多之前不可能接触到的生产服务器,积累了宝贵的线上故障处理经验,培养了项目主人公的感觉.\n以下为书籍内容的摘抄,仅供参考.\n第一章 失败奖励 在谷歌X部门——该部门负责研究自动驾驶汽车和通过热气球提供互联网接入等 \u0026ldquo;登月计划\u0026rdquo;——故意将失败次数纳入其激励系统。人们会想出一些稀奇古怪的想法，同事们也会受到积极的鼓励尽快实现它们。每个人都会得到奖励（甚至是竞争），看看他们能在一段固定的时间内反驳或否定多少观点。只有当一个概念真的不能在白板上被所有同行揭穿时，它才能进入早期原型。\n无责的事后文化 在谷歌X部门——该部门负责研究自动驾驶汽车和通过热气球提供互联网接入等 \u0026ldquo;登月计划\u0026rdquo;——故意将失败次数纳入其激励系统。人们会想出一些稀奇古怪的想法，同事们也会受到积极的鼓励尽快实现它们。每个人都会得到奖励（甚至是竞争），看看他们能在一段固定的时间内反驳或否定多少观点。只有当一个概念真的不能在白板上被所有同行揭穿时，它才能进入早期原型。\n谷歌味儿 谷歌最终解决了这个问题，明确定义了我们所说的“谷歌特质”（Googleyness）——我们所寻找的一套属性和行为，代表了强大的领导力，体现了 \u0026ldquo;谦逊、尊重和信任\u0026rdquo;：\n在模棱两可中茁壮成长 即使在环境不断变化的情况下，也能处理相互冲突的信息或方向，建立共识，并对问题做出改进。 重视反馈 谦虚优雅地接受和给出反馈，理解反馈对个人（和团队）发展的价值。 走出舒适区 能够设定宏伟的目标并去追求，即使有来自他人的抵制或惰性。 客户第一 对谷歌产品的用户抱有同情和尊重，并追求符合其最佳利益的行动。 关心团队 对同事抱有同情心和尊重，并积极主动地帮助他们，提高团队凝聚力。 做正确的事 对自己所做的一切有强烈的主人感；愿意做出困难或不易的决定以保护团队和产品的完整。 每个专家都曾经是菜鸟：一个组织的成功取决于其员工的成长和投入。\n离开营地时要比你发现时更干净\n如何激励人们记录工作 传统上，鼓励工程师记录他们的工作可能是困难的。编写文档需要消耗编码的时间和精力，而且这些工作所带来的好处并不直接，大部分是由其他人获益的。鉴于许多人可以从少数人的时间中获益，像这样的不对称权衡对整个组织来说是好的，但如果没有好的激励措施，鼓励这样的行为是很有挑战性的。我们在第57页的 \u0026ldquo;激励和认可 \u0026ldquo;一节中讨论了其中的一些结构性激励。\n奖励方式 工作阶梯的期望是一种自上而下引导文化的方式，但文化也是自下而上形成的。在谷歌，同行奖金计划是我们拥抱自下而上文化的一种方式。同行奖金是一种货币奖励和正式认可，任何谷歌员工都可以将其授予任何其他谷歌员工，以表彰他们的超越性工作。例如，当Ravi将同行奖金发给Julia，因为她是一个邮件列表的顶级贡献者——定期回答问题，使许多读者受益，他公开承认她的知识共享工作及其对团队以外的影响。由于同行奖金是由员工驱动的，而不是由管理层驱动的，因此它们可以产生重要而强大的基层效应。\n第三章 知识分享 内容提要 心理安全是培养知识共享环境的基础。 从小事做起：问问题，把事情写下来。 让人们可以很容易地从专家和有记录的参考资料中获得他们需要的帮助。 在系统的层面上，鼓励和奖励那些花时间去教授和扩大他们的专业知识，而不仅仅是他们自己、他们的团队或他们的组织。 没有什么灵丹妙药：增强知识共享文化需要多种策略的结合，而最适合你的组织的确切组合可能会随着时间的推移而改变。 第四章 公平工程 偏见是默认的。 多样性是正确设计综合用户群所必需的。 包容性不仅对于改善代表不足的群体的招聘渠道至关重要，而且对于为所有人提供一个真正支持性的工作环境也至关重要。 产品速度必须根据提供对所有用户真正有用的产品来评估。与其发布一个可能对某些用户造成伤害的产品，还不如放慢速度。 第六章 规模优先 至此，假设我们已经知道了领导的本质，那么到底什么才能让你提升为一个真正优秀的管理者呢？这就是我们这里想要讨论的，我们称之为“管理上的三个总是”：始终保持决断力，始终保持离开，始终保持扩张。 第七章 测试工程效率 如何测量软件过程 在谷歌，我们使用目标/信号/指标（GSM/ goal signal metric）框架来指导指标创建。 目标是一个期望的最终结果。它是根据你希望在高层次上理解的内容来表述的，不应包含对具体测量方法的引用。。 信号是你如何知道你已经实现了最终结果。信号是我们想要衡量的东西，但它们本身可能是不可测量的。 指标是信号的代表。它是我们实际上可以测量的东西。它可能不是理想的测量，但它是我们认为足够接近的东西。 第十章 文档 工程师们越是把文档工作当作软件开发的必要任务之一，他们就越是不反感写文档的前期成本，也就越能获得长期的收益。此外，让文档工作变得更容易，可以减少这些前期成本。 写文档的秘诀 5W,who,what,when. where, why who: 文档受众 what: 确定文档用途的内容 when: 何时确定本文件的创建、审查或更新时间 where: 文档应该放在哪里 why: 设定文件的目的 第十一章 测试概述 GWS的经验告诉我们的一个重要启示是，你不能仅仅依靠程序员的能力来避免产品缺陷。即使每个工程师只是偶尔写一些bug，当你有足够多的人在同一个项目上工作时，你也会被不断增长的缺陷列表所淹没。想象一下，一个假设的100人的团队，其工程师非常优秀，他们每个人每月只写一个bug。而这群了不起的工程师在每个工作日仍然会产生5个新的bug。更糟糕的是，在一个复杂的系统中，修复一个错误往往会导致另一个错误，因为工程师们会适配已知的bug并围绕它们编写代码。 概要\n自动化测试是实现软件变革的基础。 为了使测试规模化，它们必须是自动化的。 平衡的测试套件对于保持健康的测试覆盖率是必要的。 \u0026ldquo;如果你喜欢它，你应该对它进行测试\u0026rdquo;。 改变组织中的测试文化需要时间。 第十二章 单元测试 努力实现稳定的测试。 通过公共API进行测试。 测试状态，而不是交互。 使你的测试完整和简明。 测试行为，而不是方法。 强调行为的结构测试。 使用被测试的行为来命名测试。 不要把逻辑放在测试中。 编写清晰的失败信息。 在共享测试的代码时，遵循DAMP而不是DRY。 第十五章 弃用 与直觉相反，推广强制性“弃用”工作的最佳方法是将迁移用户的工作交给一个专家团队——通常是负责完全删除旧系统的团队。该团队有动力帮助其他人从过时的系统迁移，并可以开发可在整个组织中使用的经验和工具。许多这些迁移可以使用第 22 章中讨论的相同工具来实现。 向用户发出的任何“弃用”警告都需要具有两个属性：可操作性和相关性。 如果用户可以使用警告来实际执行某些相关操作，则警告是可操作的，不仅在理论上，而且在实践中，即要提供可操作的迁移步骤，而不仅仅是一个警告。 TL;DRs - 软件系统具有持续的维护成本，应与删除它们的成本进行权衡。 - 删除东西通常比开始构建它们更困难，因为现有用户经常使用超出其原始设计意图的系统。 - 如果将停机成本包括在内，就地改进系统通常比更换新系统便宜。 - 很难如实地评估 “弃用”所涉及的成本：除了保留旧系统所涉及的直接维护成本外，还有多个相似系统可供选择 所涉及的生态成本，互有干涉。旧系统可能会暗中拖累新系统的功能开发。这些不同的生态所带来的成本则分散且难以衡量。“弃用”成本通常同样分散。 第十六章 对任何大于“只有一名开发人员且永远不会更新的玩具项目”的软件开发项目都要使用版本控制。- 当存在 \u0026ldquo;我应该依赖哪个版本 \u0026ldquo;的选择时，就会存在内在的扩展问题。 单一版本规则对组织效率的重要性出人意料。删除提交地点或依赖内容的选择可能会导致显著的简化。 在某些语言中，你可能会花一些精力来躲避这个问题，比如着色、单独编译、链接器隐藏等等技术方法。使这些方法正常工作的工作完全是徒劳的\u0026ndash;你的软件工程师并没有生产任何东西，他们只是在解决技术债务。 以前的研究（DORA/State of DevOps/Accelerate）表明，基于干线的开发是高绩效开发组织的一个预测因素。长周期的开发分支不是一个好的默认计划。 使用任何对你有意义的版本控制体系。如果你的组织想优先考虑为不同的项目建立独立的仓库，那么取消存储库间的依赖关系/“基于头部”/“基于主干”可能仍然是明智的越来越多的VCS和构建系统设施允许您拥有小型、细粒度的存储库以及整个组织一致的“虚拟”头/主干概念。 第十七章 最重要的一点可能是显而易见的：理解代码是开发和维护代码的关键，这意味着投资在理解代码上将产生可能难以衡量但实实在在的红利。我们添加到代码搜索中的每个功能都被开发人员用来帮助他们完成日常工作（诚然，其中一些功能比其他功能更多）。两个最重要的功能，Kythe 集成（即添加语义代码理解）和查找工作示例，也与理解代码最明显相关（例如，查找代码或查看代码如何更改）。就工具影响而言，没有人使用他们不知道存在的工具，因此让开发人员了解可用工具也很重要——在谷歌，它是“Noogler”培训的一部分，即新人的入职培训和聘请的软件工程师培训。 帮助您的开发人员理解代码可以大大提高工程生产力。在 Google，这方面的关键工具是代码搜索。 作为其他工具的基础以及作为所有文档和开发工具连接到的中心标准位置，代码搜索具有附加价值。 Google 代码库的庞大规模使得定制工具（例如，与 grep 或 IDE 的索引不同）成为必要。 作为一种交互式工具，代码搜索必须快速，允许“问题和回答”的工作流程。预计在各个方面都有低延迟：搜索，浏览和索引。 只有当它被信任时才会被广泛使用，并且只有当它索引所有代码、给出所有结果并首先给出期望的结果时才会被信任。但是，只要了解其局限性，较早的、功能较弱的版本既有用又可以使用。 第十八章 一个功能齐全的构建系统对于保持开发人员在组织规模扩大时的生产力是必要的 权利和灵活性是有代价的,适当地限制构建系统可以使开发人员更容易使用它 第十九章 体验:谷歌的代码审查工具 信任和沟通是代码审查过程的核心。工具可以增强体验，但不能替代它们。 与其他工具的紧密集成是获得优秀代码审查体验的关键。 小的工作流程优化，如增加一个明确的 \u0026ldquo;关注集\u0026rdquo;，可以提高清晰度并大大减少摩擦。 第二十章 静态分析 关注开发者的幸福感。我们投入了大量精力，在我们的工具中建立分析用户和作者之间的反馈渠道，并积极调整分析以减少误报的数量。 将静态分析作为核心开发人员工作流程的一部分。谷歌静态分析的主要集成点是通过代码评审，在这里，分析工具提供修复并让评审人员参与。然而，我们也在其他方面（通过编译器检查、选通代码提交、在IDE中以及在浏览代码时）集成分析。 授权用户做出贡献。通过利用领域专家的专业知识，我们可以扩展构建和维护分析工具和平台的工作。开发人员不断添加新的分析和检查，使他们的生活更轻松，使我们的代码库更好。 第二十一章 依赖管理 谷歌在推出不兼容的依赖的版本时,基本会提供迁移工具,这在大型项目重构中可以节约很多人力\n当谷歌的工程师试图导入依赖关系时，我们鼓励他们先问这个（不完整）的问题清单：\n该项目是否有你可以运行的测试？ 这些测试是否通过？ 谁在提供这个依赖关系？即使在 \u0026ldquo;无担保 \u0026ldquo;的开放源码软件项目中，也有相当大的经验和技能范围\u0026ndash;依赖C++标准库或Java的Guava库的兼容性，与从GitHub或npm中随机选择一个项目是完全不同的事情。信誉不是一切，但它值得调研。 该项目希望达到什么样的兼容性？ 该项目是否详细说明了预计会支持什么样的用法？ 该项目有多受欢迎？ 我们将在多长时间内依赖这个项目？ 该项目多长时间做一次突破性的改变？项目多久进行一次突破性的变更？ 在此基础上，添加一些简短的内部重点问题：\n在谷歌内部实现该功能会有多复杂？ 我们有什么激励措施来保持这个依赖性的最新状态？ 谁来执行升级？ 我们预计进行升级会有多大难度？ 致谢：\n","permalink":"https://blog.jimersylee.com/posts/20220823-%E8%B0%B7%E6%AD%8C%E7%9A%84%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B%E8%AF%BB%E5%90%8E%E6%84%9F/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e近期,阅读了\u0026lt;\u003ca href=\"https://u.jd.com/7ISYFMp\"\u003e谷歌的软件工程\u003c/a\u003e\u0026gt;,记录一下.\u003c/p\u003e\n\u003cp\u003e我对谷歌这家公司还是比较好奇和欣赏的,不管都是对于拉里佩奇,还是现在阿尔法特(谷歌母公司)的CEO 桑达尔·皮查伊, 个人都非常敬佩.毕竟都是有真正实力的大佬.\u003c/p\u003e\n\u003cp\u003e谷歌运行着目前世界上最大的服务器集群,这个不容置疑.我个人也非常好奇,如此庞大的规模的机器,他们如何调度,如何监控,我们这种小公司是否有可以借鉴的地方.因此,当我发现这本书的时候,我觉得这就是我一直寻找的书.\u003c/p\u003e\n\u003cp\u003e其实在这之前,我已经看过\u003ca href=\"https://u.jd.com/7KSnc8z\"\u003e\u0026lt;Sre google运维解密\u0026gt;\u003c/a\u003e,收获良多,我的DevOps概念就是从这本书中学来的.个人在之后的工作中也引入了这套工作流程,砍掉了运维工程师,由开发自己负责从开发部署,持续运维监控的全流程,开发人员反馈良好,接触到了很多之前不可能接触到的生产服务器,积累了宝贵的线上故障处理经验,培养了项目主人公的感觉.\u003c/p\u003e\n\u003cp\u003e以下为书籍内容的摘抄,仅供参考.\u003c/p\u003e\n\u003ch1 id=\"第一章\"\u003e第一章\u003c/h1\u003e\n\u003ch2 id=\"失败奖励\"\u003e失败奖励\u003c/h2\u003e\n\u003cp\u003e在谷歌X部门——该部门负责研究自动驾驶汽车和通过热气球提供互联网接入等 \u0026ldquo;登月计划\u0026rdquo;——故意将失败次数纳入其激励系统。人们会想出一些稀奇古怪的想法，同事们也会受到积极的鼓励尽快实现它们。每个人都会得到奖励（甚至是竞争），看看他们能在一段固定的时间内反驳或否定多少观点。只有当一个概念真的不能在白板上被所有同行揭穿时，它才能进入早期原型。\u003c/p\u003e\n\u003ch2 id=\"无责的事后文化\"\u003e无责的事后文化\u003c/h2\u003e\n\u003cp\u003e在谷歌X部门——该部门负责研究自动驾驶汽车和通过热气球提供互联网接入等 \u0026ldquo;登月计划\u0026rdquo;——故意将失败次数纳入其激励系统。人们会想出一些稀奇古怪的想法，同事们也会受到积极的鼓励尽快实现它们。每个人都会得到奖励（甚至是竞争），看看他们能在一段固定的时间内反驳或否定多少观点。只有当一个概念真的不能在白板上被所有同行揭穿时，它才能进入早期原型。\u003c/p\u003e\n\u003ch2 id=\"谷歌味儿\"\u003e谷歌味儿\u003c/h2\u003e\n\u003cp\u003e谷歌最终解决了这个问题，明确定义了我们所说的“谷歌特质”（Googleyness）——我们所寻找的一套属性和行为，代表了强大的领导力，体现了 \u0026ldquo;谦逊、尊重和信任\u0026rdquo;：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cem\u003e在模棱两可中茁壮成长\u003c/em\u003e\n即使在环境不断变化的情况下，也能处理相互冲突的信息或方向，建立共识，并对问题做出改进。\u003c/li\u003e\n\u003cli\u003e\u003cem\u003e重视反馈\u003c/em\u003e\n谦虚优雅地接受和给出反馈，理解反馈对个人（和团队）发展的价值。\u003c/li\u003e\n\u003cli\u003e\u003cem\u003e走出舒适区\u003c/em\u003e\n能够设定宏伟的目标并去追求，即使有来自他人的抵制或惰性。\u003c/li\u003e\n\u003cli\u003e\u003cem\u003e客户第一\u003c/em\u003e\n对谷歌产品的用户抱有同情和尊重，并追求符合其最佳利益的行动。\u003c/li\u003e\n\u003cli\u003e\u003cem\u003e关心团队\u003c/em\u003e\n对同事抱有同情心和尊重，并积极主动地帮助他们，提高团队凝聚力。\u003c/li\u003e\n\u003cli\u003e\u003cem\u003e做正确的事\u003c/em\u003e\n对自己所做的一切有强烈的主人感；愿意做出困难或不易的决定以保护团队和产品的完整。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003ca href=\"https://www.notion.so/8a4678c8b20844dd9fe2543fe8a378a0?pvs=21\"\u003e每个专家都曾经是菜鸟：一个组织的成功取决于其员工的成长和投入。\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://www.notion.so/4e666e67b8e94df5aff0e8046254d72d?pvs=21\"\u003e离开营地时要比你发现时更干净\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"如何激励人们记录工作\"\u003e如何激励人们记录工作\u003c/h2\u003e\n\u003cp\u003e传统上，鼓励工程师记录他们的工作可能是困难的。编写文档需要消耗编码的时间和精力，而且这些工作所带来的好处并不直接，大部分是由其他人获益的。鉴于许多人可以从少数人的时间中获益，像这样的不对称权衡对整个组织来说是好的，但如果没有好的激励措施，鼓励这样的行为是很有挑战性的。我们在第57页的 \u0026ldquo;激励和认可 \u0026ldquo;一节中讨论了其中的一些结构性激励。\u003c/p\u003e\n\u003ch2 id=\"奖励方式\"\u003e奖励方式\u003c/h2\u003e\n\u003cp\u003e工作阶梯的期望是一种自上而下引导文化的方式，但文化也是自下而上形成的。在谷歌，同行奖金计划是我们拥抱自下而上文化的一种方式。同行奖金是一种货币奖励和正式认可，任何谷歌员工都可以将其授予任何其他谷歌员工，以表彰他们的超越性工作。例如，当Ravi将同行奖金发给Julia，因为她是一个邮件列表的顶级贡献者——定期回答问题，使许多读者受益，他公开承认她的知识共享工作及其对团队以外的影响。由于同行奖金是由员工驱动的，而不是由管理层驱动的，因此它们可以产生重要而强大的基层效应。\u003c/p\u003e\n\u003ch2 id=\"第三章-知识分享-内容提要\"\u003e第三章 知识分享 内容提要\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e心理安全是培养知识共享环境的基础。\u003c/li\u003e\n\u003cli\u003e从小事做起：问问题，把事情写下来。\u003c/li\u003e\n\u003cli\u003e让人们可以很容易地从专家和有记录的参考资料中获得他们需要的帮助。\u003c/li\u003e\n\u003cli\u003e在系统的层面上，鼓励和奖励那些花时间去教授和扩大他们的专业知识，而不仅仅是他们自己、他们的团队或他们的组织。\u003c/li\u003e\n\u003cli\u003e没有什么灵丹妙药：增强知识共享文化需要多种策略的结合，而最适合你的组织的确切组合可能会随着时间的推移而改变。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"第四章-公平工程\"\u003e第四章 公平工程\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e偏见是默认的。\u003c/li\u003e\n\u003cli\u003e多样性是正确设计综合用户群所必需的。\u003c/li\u003e\n\u003cli\u003e包容性不仅对于改善代表不足的群体的招聘渠道至关重要，而且对于为所有人提供一个真正支持性的工作环境也至关重要。\u003c/li\u003e\n\u003cli\u003e产品速度必须根据提供对所有用户真正有用的产品来评估。与其发布一个可能对某些用户造成伤害的产品，还不如放慢速度。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"第六章-规模优先\"\u003e第六章 规模优先\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e至此，假设我们已经知道了领导的本质，那么到底什么才能让你提升为一个真正优秀的管理者呢？这就是我们这里想要讨论的，我们称之为“管理上的三个总是”：始终保持决断力，始终保持离开，始终保持扩张。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"第七章-测试工程效率\"\u003e第七章 测试工程效率\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e如何测量软件过程\n\u003cul\u003e\n\u003cli\u003e在谷歌，我们使用目标/信号/指标（GSM/ goal signal metric）框架来指导指标创建。\u003c/li\u003e\n\u003cli\u003e\u003cem\u003e目标\u003c/em\u003e是一个期望的最终结果。它是根据你希望在高层次上理解的内容来表述的，不应包含对具体测量方法的引用。。\u003c/li\u003e\n\u003cli\u003e\u003cem\u003e信号\u003c/em\u003e是你如何知道你已经实现了最终结果。信号是我们\u003cem\u003e想要\u003c/em\u003e衡量的东西，但它们本身可能是不可测量的。\u003c/li\u003e\n\u003cli\u003e\u003cem\u003e指标\u003c/em\u003e是信号的代表。它是我们实际上可以测量的东西。它可能不是理想的测量，但它是我们认为足够接近的东西。\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"第十章-文档\"\u003e第十章 文档\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e工程师们越是把文档工作当作软件开发的必要任务之一，他们就越是不反感写文档的前期成本，也就越能获得长期的收益。此外，让文档工作变得更容易，可以减少这些前期成本。\u003c/li\u003e\n\u003cli\u003e写文档的秘诀\n\u003cul\u003e\n\u003cli\u003e5W,who,what,when. where, why\u003c/li\u003e\n\u003cli\u003ewho: 文档受众\u003c/li\u003e\n\u003cli\u003ewhat: 确定文档用途的内容\u003c/li\u003e\n\u003cli\u003ewhen: 何时确定本文件的创建、审查或更新时间\u003c/li\u003e\n\u003cli\u003ewhere: 文档应该放在哪里\u003c/li\u003e\n\u003cli\u003ewhy: 设定文件的目的\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"第十一章-测试概述\"\u003e第十一章 测试概述\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eGWS的经验告诉我们的一个重要启示是，你不能仅仅依靠程序员的能力来避免产品缺陷。即使每个工程师只是偶尔写一些bug，当你有足够多的人在同一个项目上工作时，你也会被不断增长的缺陷列表所淹没。想象一下，一个假设的100人的团队，其工程师非常优秀，他们每个人每月只写一个bug。而这群了不起的工程师在每个工作日仍然会产生5个新的bug。更糟糕的是，在一个复杂的系统中，修复一个错误往往会导致另一个错误，因为工程师们会适配已知的bug并围绕它们编写代码。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e概要\u003c/p\u003e","title":"20220823-\u003c谷歌的软件工程\u003e读后感"}]