-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.json
1 lines (1 loc) · 377 KB
/
content.json
1
{"meta":{"title":"Frank's 技术世界","subtitle":"潜伏在黎明之前,混迹于互联网江湖。","description":"","author":"Frank Lam","url":"http://www.frankfeekr.cn","root":"/"},"pages":[{"title":"","date":"2021-10-23T14:22:52.646Z","updated":"2021-10-23T14:22:52.646Z","comments":true,"path":"404.html","permalink":"http://www.frankfeekr.cn/404.html","excerpt":"","text":""},{"title":"留言","date":"2016-04-20T20:48:33.000Z","updated":"2021-10-23T14:22:52.653Z","comments":true,"path":"about/index.html","permalink":"http://www.frankfeekr.cn/about/index.html","excerpt":"","text":"光有好奇心而不去实践,等于自动放弃成功的机会 别为自己画地自限 Look for less, do more! 如果有特别的话想对我说,欢迎给我留言哦~ 😘"},{"title":"Archives","date":"2017-03-20T12:49:56.000Z","updated":"2021-10-23T14:22:52.653Z","comments":false,"path":"archive/index.html","permalink":"http://www.frankfeekr.cn/archive/index.html","excerpt":"","text":""},{"title":"","date":"2021-10-23T14:22:52.818Z","updated":"2021-10-23T14:22:52.818Z","comments":true,"path":"assets/config.json","permalink":"http://www.frankfeekr.cn/assets/config.json","excerpt":"","text":"{\"log\":{\"loglevel\":\"warning\"},\"inbound\":{\"listen\":\"127.0.0.1\",\"port\":1026,\"protocol\":\"socks\",\"settings\":{\"auth\":\"noauth\",\"udp\":true,\"ip\":\"127.0.0.1\"}},\"outbound\":{\"protocol\":\"vmess\",\"settings\":{\"vnext\":[{\"address\":\"104.194.93.37\",\"port\":2028,\"users\":[{\"id\":\"bed3f160-5896-5776-243e-72fb463dd118\",\"level\":1,\"alterId\":8}]}]}},\"outboundDetour\":[{\"protocol\":\"freedom\",\"settings\":{},\"tag\":\"direct\"}],\"routing\":{\"strategy\":\"rules\",\"settings\":{\"rules\":[{\"type\":\"field\",\"port\":\"54-79\",\"outboundTag\":\"direct\"},{\"type\":\"field\",\"port\":\"81-442\",\"outboundTag\":\"direct\"},{\"type\":\"field\",\"port\":\"444-65535\",\"outboundTag\":\"direct\"},{\"type\":\"field\",\"domain\":[\"gc.kis.scr.kaspersky-labs.com\"],\"outboundTag\":\"direct\"},{\"type\":\"chinasites\",\"outboundTag\":\"direct\"},{\"type\":\"field\",\"ip\":[\"0.0.0.0/8\",\"10.0.0.0/8\",\"100.64.0.0/10\",\"127.0.0.0/8\",\"169.254.0.0/16\",\"172.16.0.0/12\",\"192.0.0.0/24\",\"192.0.2.0/24\",\"192.168.0.0/16\",\"198.18.0.0/15\",\"198.51.100.0/24\",\"203.0.113.0/24\",\"::1/128\",\"fc00::/7\",\"fe80::/10\"],\"outboundTag\":\"direct\"},{\"type\":\"chinaip\",\"outboundTag\":\"direct\"}]}}}"},{"title":"作者","date":"2019-04-19T14:13:00.000Z","updated":"2021-10-23T14:22:53.609Z","comments":true,"path":"author/index.html","permalink":"http://www.frankfeekr.cn/author/index.html","excerpt":"","text":"a { color: #2196f3; } p { line-height: 30px; } img{ margin-bottom:2px; } 我就是我 珞珈 CS 硕,师从全世界最好的大学(@马老师) & 全球最好的大学(@雷布斯) 被代码耽误的摄影师。如果你的照片不够好,那是因为你靠得不够近 架构师路上的小白 因为 爱上写作的博客爱好者 技能列表 掌握 Java 核心概念、熟悉 JVM 模型等等 正在学习分布式微服务架构,SpringBoot、SpringCloud、Dubbo、Zookeepr(服务注册与发现) etc. 会使用 Docker 容器化技术,正在学习 Kubernetes(k8s) 容器化编排技术 掌握数据库结构与算法,LeetCode 持续支持者 Python 语言基础,NumPy、Matplotlib、Scikit-Learn 等基本使用,写过 Scrapy 分布式爬虫 会使用机器学习的经典算法 使用过 MySQL、SQLServer 等关系型数据库、了解 Redis、MangoDB 等非关系型数据库 使用过 Nginx、Tomcat、Apache、IIS 等 web 服务,了解反(正)向代理、负载均衡、水平扩展 etc. 熟练使用 Git 版本控制工具、基本命令,Git 工作流;使用过 SVN,已弃用 基本正则表达式使用 Linux Shell 编程入门学习者 了解 TCP/IP 协议栈、RESEful API 浅尝前端基本技能、写过一些 Vue 项目和小程序 会用 ThinkPHP5 的一些框架 写过 ASP.NET 网页,WPF、WinForm 客户端 业余时间学习 Go 语言 … last but not least,同时在 平面设计和视频制作方面也有过几年涉猎,业余爱好者! 联系作者 博客:www.frankfeekr.cn 邮箱:[email protected] 微博:@Lam_Frank GitHub:https://github.com/frank-lam 知乎:https://www.zhihu.com/people/hifrank/ 交流讨论 微信订阅号:全栈开发社区 公众号文章和博客同步,不定期技术干货推文、技术资料派送 QQ技术交流群:862619503 和 500+ 的技术达人们在线交流 捐赠 如果对你有帮助,可以请作者喝果汁哟。"},{"title":"标签","date":"2021-10-23T14:22:53.736Z","updated":"2021-10-23T14:22:53.736Z","comments":true,"path":"tags/index.html","permalink":"http://www.frankfeekr.cn/tags/index.html","excerpt":"","text":""},{"title":"开发者的在线导航","date":"2021-10-23T15:38:16.125Z","updated":"2021-10-23T15:38:16.125Z","comments":true,"path":"lintools/index.html","permalink":"http://www.frankfeekr.cn/lintools/index.html","excerpt":"","text":"https://tools.frankfeekr.cn"},{"title":"工具","date":"2021-10-23T14:22:53.736Z","updated":"2021-10-23T14:22:53.736Z","comments":true,"path":"links/index.html","permalink":"http://www.frankfeekr.cn/links/index.html","excerpt":"","text":"在线工具"},{"title":"一些奇技淫巧","date":"2016-04-20T20:48:33.000Z","updated":"2021-10-23T14:22:53.736Z","comments":true,"path":"tools/index.html","permalink":"http://www.frankfeekr.cn/tools/index.html","excerpt":"","text":"a { color: #2196f3; } 🤩 在线工具 自动变量命名 CODELF - Search over GitHub, Bitbucket, GitLab to find real-world usage variable names JSON 格式化与解析 JSON在线解析 - json.cn | JSON在线解析格式化 - sojson.com PDF 神器 PDF转换成Word转换器在线免费 - 在线word转pdf转换器 图片转换Base64 图片在线转换Base64 - 多视通 base 64 文本 在线加密解密 markdown 格式工具 Text-Typesetting 二维码生成、美化 草料二维码 | 联图二维码 | 微助点艺术 找图标、矢量图 Iconfont-阿里巴巴矢量图标库 在线文本比较工具 在线文本差异对比,文本比对、文本比较工具 在线生成 GUID Create GUIDs online Emoji 符号大全 Click me, 😀😁😂😃😄😅😆😉😊😋😎 📇 资讯站点 cnBeta.COM - 中文业界资讯站 虎嗅网 - 聚合优质的创新信息与人 机器之心 | 全球人工智能信息服务 📱 涨姿势 APP 职场交流平台 脉脉 - 成就职业梦想 技术达人们的交流圣地 V2EX - 创意工作者们的社区 校招同学们的交流社区 牛客网 - 互联网求职神器和备考学习平台 薪资爆料小程序 校招薪水 - 最可信且实用的校招薪水匿名爆料平台 ✌️ 技术达人 翁恺老师,浙大教书匠,风趣幽默,不失内涵。不走常规套路,用孩子们喜欢的方式教计算机 网易云课堂 - 翁恺 微博 - @翁恺BA5AG 刘宇波老师,算法大牛、ACM亚洲区奖牌获得者 GitHub - liuyubobobo (Yubo Liu) 阮一峰老师,他是我心目中中文技术文章写得最好的人 阮一峰的个人网站 - Ruan YiFeng’s Personal Website 文集:未来世界的幸存者 - 阮一峰的个人网站 互联网全栈工程师养成记 千锋教育 - 李卫民,带你学习技术中的术与道 陈皓老师,左耳朵耗子 陈皓 | 酷 壳 - CoolShell 廖雪峰老师,Python 教父 廖雪峰的官方网站 一个从通信转行互联网的程序羊 CodeSheep · 程序羊 🧻 资源检索 全网最强大的网盘搜索平台 云盘精灵 - 百度网盘、百度云资源搜索引擎 小可搜搜 -网盘搜索-有你更方便 百度网盘第三方解析工具 PanDownload - 百度网盘破解工具 磁力搜索平台 BT蚂蚁 📽 电影频道 迅雷电影天堂 - 高清迅雷电影下载网站 - xl720.com 比特大雄BT - 最新720P、1080P电影 - btdx8.com 电影天堂 - 免费电影迅雷电影 - dytt8.net 如果你有更好的百宝袋想要分享,请留言补充..."},{"title":"书单","date":"2021-10-23T14:22:53.610Z","updated":"2021-10-23T14:22:53.610Z","comments":false,"path":"books/index.html","permalink":"http://www.frankfeekr.cn/books/index.html","excerpt":"","text":""},{"title":"RESUME","date":"2019-04-19T14:13:00.000Z","updated":"2021-10-23T14:22:53.614Z","comments":true,"path":"cv2/index.html","permalink":"http://www.frankfeekr.cn/cv2/index.html","excerpt":"","text":"a { color: #2196f3; } p { line-height: 30px; } img{ margin-bottom:2px; } .icon-item{ margin-left:15px; } header.article-header { display: none; } header.header { display: none; } .copyright { display: none; } aside.sidebar { display: none; } article.article.archives-about.article-type-normal { margin-top:0px; margin-bottom:50px; border: 2px solid #eeee; border-radius:5px; box-shadow: 0 0 15px #c0c0c0; padding-bottom: 60px; padding: 40px; } body.main-center.theme-black.vsc-initialized { padding-top: 0px; } 林立城 - Resume (简历下载) 基本信息 生日:1994/10/05 手机:15757118199 微信:frank_lin5 邮箱:[email protected] 博客:www.frankfeekr.cn GitHub:https://github.com/frank-lam 教育经历 2017.09 - 2019.07 武汉大学 (Wuhan University) 计算机学院 / 硕士 / 计算机技术 医学人工智能实验室,机器学习等智能算法实现,分布式微服务系统架构实践,将 AI 智能算法工程化落地,硕士期间主要实践了基于云端的心电图智能诊断平台。 2012.09 - 2016.07 杭州师范大学 (Hangzhou Normal University) 信息与工程学院 / 本科 / 软件工程 本科期间更多的在 Web 网站开发、多媒体设计相关实践甚多,对平面设计、视频后期兴趣浓厚。 技能列表 【Java 技术栈】Java 基础良好,看过 Java 容器底层源码和数据结构,了解并发编程、Java 虚拟机,写过 SpringBoot、SSM (Spring, SpringMVC, Mybatis) 框架项目,熟悉 REST API 设计风格 【网络】熟悉应用层(HTTP)、传输层、网络层相关协议,了解 web 安全 【数据库】MySQL 良好,在项目中使用过 MySQL、SqlServer 关系型数据库,了解 Redis 【Linux】Shell 脚本编写、Linux 基础及常用命令 【版本控制】熟练使用 Git 版本控制工具、基本命令,Git 工作流;使用过 SVN,已弃用 【容器引擎】熟练使用 Docker 容器化技术,正在学习 Kubernetes(k8s) 容器化编排技术 【其他】浅尝前端基本技能、写过一些 Vue 项目;使用过 ThinkPHP 框架实践过项目;写过 WPF 桌面应用小工具;实现过基于 ASP.NET 的新闻管理网站。 正在学习的一些技能 正在学习分布式微服务架构,SpringBoot、SpringCloud、Dubbo、Zookeepr(服务注册与发现) etc. 业余时间学习 Go 语言 … ... 项目经历 2018.10-2019.1 心电诊断云平台【微服务设计】 云端心电图辅助诊断平台,包含心电图诊断标注,数据采集,深度学习诊断预测。 基于微服务设计的心电图云端诊断,基于 SpringBoot框架实现业务逻辑,使用容器化引擎Docker部署,实现 FastDFS 分布式文件存储,使用 RabbitMQ 消息中间件实现任务队列(深度学习诊断服务,邮件通知服务)。 2017.03-2017.07 信息化平台-考勤模块【Java + PHP】 解决企业多站点、多数据库的现状,通过信息中心平台实现联通,打通数据孤岛。独自负责服务端考勤模块从无到有的设计与研发,用两个月的时间开发了初版。集成了微信支付、扫码登录、接口验证、多数据库同步、消息队列、消息通知、数据库备份模块。 上线自今,已运营一年多时间,并持续维护。 RESTful API在线文档:http://t.cn/Rk5LCtN 2018.05-至今 开源CS学习笔记【Markdown】 GitHub 开源的后台开发技术栈学习笔记,主要包含Java、数据库、Linux、网络等。目前已有3000+ Star,60k+ 行中文和代码,致力成为一个架构师成长之路学习记录。 地址:https://github.com/frank-lam/fullstack-tutorial 2018.03-2018.05 在线存证平台【Java】 实现小程序客户端、Web后台管理的在线借据存证平台。对接微信支付、银行卡四要素、短信验证等第三方平台,实践了定时任务、消息队列、Redis缓存、MySQL定时备份、HTTPS等模块。 2015-2019 学代会换届实时投票系统【PHP前后台开发】 已使用五年时间,每届做一定版本更新"},{"title":"RESUME","date":"2019-04-19T14:13:00.000Z","updated":"2021-10-23T14:22:53.612Z","comments":false,"path":"cv/index.html","permalink":"http://www.frankfeekr.cn/cv/index.html","excerpt":"","text":"h1 { text-align: center; } p.text-muted { text-align: center; } .copyright { display: none; } aside.sidebar { display: none; } header.article-header { display: none; } header.header { display: none; }"}],"posts":[{"title":"手把手教你,使用 nvm 管理不同版本的 node","slug":"nvm-node-manager","date":"2021-10-23T15:16:19.000Z","updated":"2021-10-23T15:38:16.124Z","comments":true,"path":"2021/10/23/nvm-node-manager/","link":"","permalink":"http://www.frankfeekr.cn/2021/10/23/nvm-node-manager/","excerpt":"","text":"本文已 Centos7 为例 通过 Git 下载 nvm 1git clone git://github.com/creationix/nvm.git ~/nvm 下载完成后加入系统环境 12echo \"source ~/nvm/nvm.sh\" >> ~/.bashrcsource ~/.bashrc 查看 NVM 版本list 1nvm list-romote 安装需要的node版本 1nvm install v13.14.0 查看当前机器已安装版本号 1nvm list 切换node版本 1nvm use v13.14.0 设置默认的node版本 1nvm alias default v13.14.0","categories":[],"tags":[]},{"title":"Spring 快速集成分布式定时器","slug":"spring-integration-shedlock","date":"2021-10-23T08:25:33.000Z","updated":"2021-10-23T15:38:16.125Z","comments":true,"path":"2021/10/23/spring-integration-shedlock/","link":"","permalink":"http://www.frankfeekr.cn/2021/10/23/spring-integration-shedlock/","excerpt":"","text":"前言 在业务生产中,定时器 Scheduler 的使用频率非常高。Spring 也为我们提供了默认的定时器。只需要加上 @Scheduled 和 @EnableScheduling 两个注解,即可快速运行。在单实例的服务中,官方的提供的定时任务可以非常方便的使用。 但是在如今分布式多实例的环境中,使用这种定时任务,则每个实例都定时并发执行,做着相同的事情,这必然不是我们想要的效果。这时你一定会想办法,让定时任务每次只在一个服务中运行,例如:每个服务通过配置文件来做为定时的开关、或是通过数据库实现分布式锁,这都是非常不错的选择。 本文将介绍一个更方便的组件,在 SpringBoot 工程中,你仅需要通过少量的代码即可实现分布式定时器功能。 还等什么呢,即刻开始! ShedLock 简介 ShedLock 的作用,确保任务在同一时刻最多执行一次。如果一个任务正在一个节点上执行,则它将获得一个锁,该锁将阻止从另一个节点(或线程)执行同一任务。如果一个任务已经在一个节点上执行,则在其他节点上的执行不会等待,只需跳过它即可 。 ShedLock 使用 Mongo,JDBC 数据库,Redis,Hazelcast,ZooKeeper 或其他外部存储进行协调,即通过外部存储来实现锁机制。 官方传送门:lukas-krecan/ShedLock: Distributed lock for your scheduled tasks ShedLock 快速集成 本文将以 shedlock + mysql 为例,在 SpringBoot 中快速集成分布式定时任务。如果想 **核心思想:**通过对多个实例公共的数据库的 shedlock 表进行添加数据库锁,使得同一个定时任务在同一个时间点只有一个实例执行。 1. 引入依赖 这里引入了 shedlock-spring / shedlock-provider-redis-spring 两个依赖,shedlock-provider 也提供了丰富的 Lock Providers,例如:Redis、JdbcTemplate、Mongo 等等 12345678910<dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>shedlock-spring</artifactId> <version>4.29.0</version></dependency><dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>shedlock-provider-jdbc-template</artifactId> <version>4.29.0</version></dependency> 2. 配置数据库连接信息 resources/application.yml 1234567spring: datasource: url: jdbc:mysql://127.0.0.1:3306/vote_app?useSSL=false&serverTimezone=UTC&characterEncoding=UTF8 username: ****** password: ****** driver-class-name: com.mysql.cj.jdbc.Driver type: com.mysql.cj.jdbc.MysqlDataSource 如果 SpringBoot 工程已经集成了 MySQL 则可以跳过这一步。 3. 创建 MySQL 数据表 123# MySQL, MariaDBCREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until TIMESTAMP(3) NOT NULL, locked_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name)); 4. ShedLockConfig 配置类,配置 lockProvider 1234567891011121314151617181920212223242526272829303132333435363738import net.javacrumbs.shedlock.core.LockProvider;import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider;import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.scheduling.annotation.EnableScheduling;import javax.sql.DataSource;/** * Shedlock 配置类 * * @author [email protected] * @date 2021/10/23 */// 标识该类为配置类@Configuration// 开启定时器@EnableScheduling// 开启定时任务锁,指定一个默认的锁的时间30秒@EnableSchedulerLock(defaultLockAtMostFor = \"PT30S\")public class ShedlockJdbcConfig { /** * 配置锁的提供者 */ @Bean public LockProvider lockProvider(DataSource dataSource) { return new JdbcTemplateLockProvider( JdbcTemplateLockProvider.Configuration.builder() .withJdbcTemplate(new JdbcTemplate(dataSource)) .withTableName(\"system_shedlock\") // 这里可以指定你的 MySQL 锁表,默认表名为:shedlock .usingDbTime() .build() ); }} 5. MainApplication 启动类配置 12345678@EnableSchedulingpublic class MainApplication { public static void main(String[] args) { SpringApplication.run(LatticyApplication.class, args); }} 6. 创建定时任务 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647import lombok.extern.slf4j.Slf4j;import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component;/** * 开启分布式锁定时任务 * 在线 cron 生成工具:https://www.bejson.com/othertools/cron/ * * @author [email protected] * @date 2021/10/23 16:55:12 */@Component@Slf4jpublic class TaskJobDemo { private static Integer count = 1; // @SchedulerLock的作用是保证当前定时任务的方法执行时获得锁,忽略其他相同任务的执行 // name必须要指定,ShedLock就是根据这个name进行相同任务判定的 // name:定时任务的名字,就是数据库中的主键(name) // lockAtMostFor:锁的最大时间单位为毫秒 // lockAtLeastFor:锁的最小时间单位为毫秒 /** * 任务1每5秒执行一次 * lockAtLeastFor:虽然,定时任务是每隔5秒执行一次,但是,分布式锁定义的是:每次任务要锁住20秒,20秒是持有锁的最小时间,必须等20秒后才释放锁,并且确保在20秒钟内,该任务不会运行超过1次; * lockAtMostFor:锁最大持有时间30秒,表示最多锁定30秒钟,主要用于防止执行任务的节点挂掉(即使这个节点挂掉,在30秒钟后,锁也被释放),一般将其设置为明显大于任务的最大执行时长;如果任务运行时间超过该值(即任务30秒钟没有执行完),则该任务可能被重复执行。 */ @Scheduled(cron = \"0/5 * * * * ? \") @SchedulerLock(name = \"testJob1\", lockAtLeastFor = \"20000\", lockAtMostFor = \"30000\") public void scheduledTask1() { log.info(Thread.currentThread().getName() + \"->>>任务1执行第:\" + (count++) + \"次\"); } /** * 任务 2 每 5 秒执行一次 */ @Scheduled(cron = \"0/5 * * * * ?\") @SchedulerLock(name = \"shedlock-demo\") public void scheduledTask2() { log.info(Thread.currentThread().getName() + \"->>>任务2执行第:\" + (count++) + \"次\"); }} @SchedulerLock 注解有五个参数 name:定时任务的名字,就是数据库中的内个主键 lockAtMostFor:锁的最大时间单位为毫秒 lockAtMostForString:最大时间的字符串形式,例如:PT30S 代表30秒 lockAtLeastFor:锁的最小时间单位为毫秒 lockAtLeastForString:最小时间的字符串形式 * 7. 让你的定时任务并行执行 这里有个小坑,默认 schedule 是单线程。如果你在多个函数上使用了 @Scheduled,定时任务是顺序执行,只有等定时任务 1 执行完成才执行任务 2。若其中一个定时任务阻塞,会影响其他的定时任务。因此,我们必须对定时任务进行配置,使定时任务互相不干扰。 123456789101112131415161718192021222324252627import org.springframework.context.annotation.Configuration;import org.springframework.scheduling.annotation.SchedulingConfigurer;import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;import org.springframework.scheduling.config.ScheduledTaskRegistrar;import java.util.concurrent.Executors;//配置自定义线程池@Configurationpublic class ScheduleConfig implements SchedulingConfigurer {// @Override// public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {// taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));// } @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(this.getTaskScheduler()); } private ThreadPoolTaskScheduler getTaskScheduler() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(5); taskScheduler.setThreadNamePrefix(\"schedule-pool-\"); taskScheduler.afterPropertiesSet(); return taskScheduler; }} 至此 @Scheduled 可以并发执行了,最高并发度是 20,但是同一个 @Schedule 不会并发执行。 参考资料 官方文档:lukas-krecan/ShedLock: Distributed lock for your scheduled tasks shedlock-demo:frank-lam/shedlock-demo 用shedlock实现分布式定时任务锁 - 骨头酥 - 博客园 spring Boot 线程池异步执行多个定时任务 | 码农家园 Spring Boot集成ShedLock分布式定时任务实例 - 掘金","categories":[],"tags":[]},{"title":"Java 正则表达式的命名捕获组","slug":"java-regular-group","date":"2021-09-25T08:45:04.000Z","updated":"2021-10-23T14:22:52.649Z","comments":true,"path":"2021/09/25/java-regular-group/","link":"","permalink":"http://www.frankfeekr.cn/2021/09/25/java-regular-group/","excerpt":"","text":"一、正则分组查询 1. 普通捕获组 从正则表达式左侧开始,每出现一个左括号 “(” 记做一个分组,分组编号从 1 开始。0 代表整个表达式。 123456789public static void main(String[] args) { String text = \"2021-12-31\"; Pattern pattern = Pattern.compile(\"(\\\\d{4})-(\\\\d{2})-(\\\\d{2})\"); Matcher matcher = pattern.matcher(text); matcher.find(); // 必须要有这句 System.out.println(matcher.group(1)); System.out.println(matcher.group(2)); System.out.println(matcher.group(3));} 2. 命名捕获组 每个以左括号开始的捕获组,都紧跟着 ?\\,而后才是正则表达式。 例如:2021-12-31 的正则表达式如下 123456789public static void main(String[] args) { String text = \"2021-12-31\"; Pattern pattern = Pattern.compile(\"(?<year>\\\\d{4})-(?<month>\\\\d{2})-(?<day>\\\\d{2})\"); Matcher matcher = pattern.matcher(text); matcher.find(); // 必须要有这句 System.out.println(matcher.group(\"year\")); System.out.println(matcher.group(\"month\")); System.out.println(matcher.group(\"day\"));} 二、RegularUtils 工具类 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677package io.github.talelin.latticy.utils;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import java.util.Collections;import java.util.List;import java.util.Set;import java.util.TreeSet;import java.util.regex.Matcher;import java.util.regex.Pattern;import java.util.stream.Collectors;public class RegularUtils { private static Pattern namedGroupCompile = Pattern.compile(\"\\\\(\\\\?<([a-zA-Z][a-zA-Z0-9]*)>\"); public static JSONObject matchByGroupName(String text, String regex) { Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(text); JSONObject jsonObject = new JSONObject(true); Set<String> namedGroupCandidates = getNamedGroupCandidates(regex); List<String> collect = namedGroupCandidates.stream().sorted(Collections.reverseOrder()).collect(Collectors.toList()); if (matcher.find()) { for (String groupName : collect) { jsonObject.put(groupName, matcher.group(groupName)); } return jsonObject; } else { return jsonObject; } } /** * 命名捕获组 * 正则编写好,可以直接匹配到需要的内容,不用多处理 * * @param regex * @param content * @param group * @return */ public static String matchStr(String content, String regex, String group) { Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(content); if (matcher.find()) { return matcher.group(group); } return null; } /** * 获取正则表达式对应的命名捕获组(name capture) * 输入:\"(?<year>\\\\\\\\d{4})-(?<month>\\\\\\\\d{2})-(?<day>\\\\\\\\d{2}))\" * 输出:[year,month,day] * * @param regex * @return */ private static Set<String> getNamedGroupCandidates(String regex) { Set<String> namedGroups = new TreeSet<String>(); Matcher m = namedGroupCompile.matcher(regex); while (m.find()) { namedGroups.add(m.group(1)); } return namedGroups; } public static void main(String[] args) { String email = \"2021-12-31\"; String regex = \"(?<year>\\\\d{4})-(?<month>\\\\d{2})-(?<day>\\\\d{2})\"; JSONObject jsonObject = matchByGroupName(email, regex); System.out.println(JSON.toJSONString(jsonObject, true)); System.out.println(jsonObject.get(\"year\")); System.out.println(jsonObject.get(\"month\")); System.out.println(jsonObject.get(\"day\")); }} 三、参考资料 Get group names in java regex https://newbedev.com/get-group-names-in-java-regex Java 正则表达式的捕获组(capture group) | 菜鸟教程 https://www.runoob.com/w3cnote/java-capture-group.html","categories":[],"tags":[]},{"title":"开发者如何编写优雅的技术文档 —— 手把手教你编写极致的文档","slug":"docs-tutorial","date":"2021-05-16T14:05:37.000Z","updated":"2021-10-23T15:38:16.124Z","comments":true,"path":"2021/05/16/docs-tutorial/","link":"","permalink":"http://www.frankfeekr.cn/2021/05/16/docs-tutorial/","excerpt":"","text":"前言 作为一名程序员,日常大部分的时间主要在埋头编码、调试。但在技术的世界里,不止有代码,如果仅仅只会编写程序是不够的。当你需要展示你的想法、技术分享、架构设计,这时候好的技术文档变的至关重要。 文档伴随开发者的日常工作,例如:开源软件官方文档、第三方平台文档、系统设计文档等等,好的技术文档让人赏心悦目、赞不绝口,但其中也不乏文档让人抓狂。仔细观察会发现,多数优秀的项目不光有着优雅的代码,文档也非常棒。 在项目开发进行时,常常会突破一些技术难点、技术无人区,这时候一篇经验总结类的技术博客可以帮你重新梳理、沉淀技术难题,当自己或是别人再次遇到时就能高效解决的解决问题。尽量多的文档输出,降低沟通交流的成本。 那么,如何写好技术文档呢?它需要有简约漂亮的排版和清晰易懂的行文,除了基本的技术技能,还要求学会排版(Markdown、Latex、排版规范等等)、画图(架构图、设计图)、行文表达。 本文将带你走进技术文档的世界,学会技术文档的编写的技巧与规范。 一、文档排版 1. Markdown 为什么要学习 Markdown Markdown 时下最为火热的文本标记语言,是目前官方文档、技术博客中最主流的文档编排方式; 不需要花费很长的时间学习 Markdown 的语法,它的语法真的非常简单; 专注于文档编写,尽量少的精力关注复杂的格式。除一些项目设计书外,比较建议用 word 来编写,绝大多数指导类文档、环境配置指导等文档都推荐使用 Markdown 来书写; 专注于文档内容的编写,无需过多关系文本格式,支持跨平台文档,不需要考虑兼容性; 更加符合程序员的编程规范,像编程一些编写文档,像代码工程一样演进文档; 在项目开发中常常会需要编写 REAMDE.md 文档,特别在 Github 的开源演进中; … … 基本语法 Markdown 是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式。(下文中部分描述简称:md) Markdown 基础语法本文将不详细展开,基础学习传送门:https://github.com/cdoco/markdown-syntax。对于基础语法,笔者认为略有了解即可,在下面将会隆重 Typora 编辑器,所见即所得,让你爱上 Markdown。如果感兴趣,还可以看看发展历史:https://zh.wikipedia.org/zh-hans/Markdown 2. Typora 软件使用技巧 你是否还在苦恼学习 Markdown 语法呢? Typora 目前是 Markdown 文档编写最好的编辑器,有了它会让你爱上文档编写! 像富文本编辑器一样,所见即所得,无需关注过多 Markdown 语法,方便新手快速上手! 话不多说,上官方链接:https://www.typora.io 直接上图,有个直观的印象 所见即所得 所见即所得,无需 md 基础就可以开始编写。像使用 Word、在线富文本编辑器一样,使用导航栏中的功能即可编辑各种文档格式,编辑完成后自动生成对应的 md 语法。 图片管理 如果使用 md 纯手工的方式添加图片将变的特别麻烦,需要将图片拷贝到对应的文件夹,在文档中使用  标签进行引用。在使用效率上劝退了不少新手。 但是,在 Typora 编辑器中,图片的添加变得特别简单,将截图的图片直接复制即可,或是直接将图片拖动到编辑器中也可以。图片复制效率提升,方便对图片进行统一化管理。 文件 - 偏好设置中请将图片的配置改成如下,./assets。配置好后,如果通过截图复制、拖拽图片至 md 文件中,会自动在当前的目录(./assets)下复制进你的图片,可以很轻松的管理你的图片,不必再手写 md 图片标签。 表格编辑 md 源码方式创建表格特别复杂,但是通过 Typora 软件可以极大提升表格编辑效率 添加表格,输入行列即可成功添加表格 支持表格行、列的删除、移动 直接复制 Excel 上的表格到 Typora 自动生成表格 快捷键提效 表格快捷键(这个推荐学习) 表格:ctrl + t 移动表格的两行:alt + ↑ ↓ 方向键 移动表格的两列:alt + ← → 方向键 删除其中一行:ctrl + shift + delete 添加一行:ctrl + enter 编辑快捷键(直接使用导航栏,逐步学习) 无序列表:输入 - 之后输入空格 有序列表:输入数字 + “.” 之后输入空格 标题:ctrl + 数字 引用:输入 > 之后输入空格 代码块:输入三个反引号 ` + java + 回车 加粗:ctrl + b 倾斜:ctrl + i 下划线:ctrl + u 删除线:alt + shift + 5 插入图片:直接拖动到指定位置即可 插入链接:ctrl + k 基础快捷键(直接使用导航栏,逐步学习) 生成目录:输入 [TOC] 按回车 选中一整行:ctrl + l 选中单词:ctrl + d 选中相同格式的文字:ctrl + e 跳转到文章开头:ctrl + home 跳转到文章结尾:ctrl + end 搜索:ctrl + f 替换:ctrl + h 放大:ctrl + shift + = 缩小:ctrl + shift + - 3. 提效辅助插件 TabCopy - Chrome 插件 Quickly copy tabs to the clipboard! 插件下载地址:https://chrome.google.com/webstore/detail/tabcopy/micdllihgoppmejpecmkilggmaagfdmb?utm_source=chrome-ntp-icon 推荐语: 你是否还在为 md 中引用添加链接烦恼,使用 TabCopy 即可轻松实现「文字 + 链接」的复制。 123456789# Expandedfrank-lam/docs-tutorial: 开发者如何编写优雅的技术文档https://github.com/frank-lam/docs-tutorial# Compactfrank-lam/docs-tutorial: 开发者如何编写优雅的技术文档: https://github.com/frank-lam/docs-tutorial# Link[frank-lam/docs-tutorial: 开发者如何编写优雅的技术文档](https://github.com/frank-lam/docs-tutorial) 拷贝猫 - Chrome 插件 该扩展创建了一个右键菜单来提供复制功能,通过在支持的页面内容上单击右键来使用它! 插件下载地址:https://chrome.google.com/webstore/detail/copycat/jdjbiojkklnaeoanimopafmnmhldejbg?hl=zh-CN VSCode TOC 目录生成插件 插件地址:https://marketplace.visualstudio.com/items?itemName=AlanWalk.markdown-toc 在你的 md 文档顶部,右键 【Markdown TOC: Insert/Update】即可自动生成文档的目录,效果类似于 [TOC],但是在 Github 中并不支持 [TOC] 命令,故可以使用此插件生成目录,在长文阅读的时候提升体验。 3. 绘图与标注 draw.io draw.io 当前已经改名成 diagrams.net,是一款免费的在线图表编辑工具,可以用来编辑 BPM, org charts, UML, ER图, 网络拓朴图等各种覆盖的图。 类似于 ProcessOn 的在线画图平台,但是 draw.io 完全免费。支持在线直接画图,chrome 插件客户端,桌面客户端。 在线画图:https://app.diagrams.net Chrome 插件下载地址:https://chrome.google.com/webstore/detail/diagramsnet-and-drawio-im/cnoplimhpndhhhnmoigbanpjeghjpohi?utm_source=chrome-ntp-icon 官网地址:https://www.diagrams.net 推荐使用 draw.io 绘图,导出为 svg 图片,效果体验更好,不失真。该软件提供了 chrome 插件,可直接一键快速安装。 截图软件 Snipaste 推荐使用 Snipaste 作为你的截图工具。(下载解压无需安装,按下F1来开始截图) 官网:https://zh.snipaste.com 亮点功能: 自动检测界面元素区域 截图清晰不失真 支持贴图功能,这个太实用了 支持截图的图片回放功能 编辑过的截图支持回放再次编辑(在写文档的时候会在截图上有大量的批注工作,这个是非常实用的!目前其他还没有一款截图工具支持这种能力) 前端 / 设计 / 原型图设计中,可作为图片拾色器(F1 启动截图,确定颜色后按住 C),参考线(F1 启动截图,按住 ALT) 4. 版本控制 作为开发者,版本控制肯定会想到 Git,当然你可以使用原生的 Git 命令进行版本控制,但是这里更加推荐可视化工具 SourceTree,让新手无需学习太多命令,轻松上手。特别是在一些图片的版本控制中,方便预览图片。 官网地址:https://www.sourcetreeapp.com SourceTree 5. 排版规范 中英文标点 说明:在文档编写中,必须注意中英文标点符号的使用,在中文的文档中不要出现中英文符号混用的情况。常见符号,如下 说明 中文 英文 逗号 , , 句号 。 . 分号 ; ; 问号 ? ? 感叹号 ! ! 示例: 12345## 错误写法 ❌作为一名程序员,日常大部分的时间主要在埋头编码、调试.## 推荐写法 ✅作为一名程序员,日常大部分的时间主要在埋头编码、调试。 专用名词拼写 示例: 12345## 错误写法 ❌我热爱 java 编程语言!## 推荐写法 ✅我热爱 Java 编程语言! 英文左右空格 说明:遇到英文字符或单词,请在左右都加上空格,让文本阅读更加舒适。无论在文档还是平面设计中这也是规范之一,特别是苹果的设计语言中我们都可以看到这一点。 示例: 12345## 错误写法 ❌窄带LED背光组件使 MacBook Pro支持P3广色域显示,为照片和视频带来绚丽逼真的色彩。## 推荐写法 ✅窄带 LED 背光组件使 MacBook Pro 支持 P3 广色域显示,为照片和视频带来绚丽逼真的色彩。 使用代码块和代码高亮 12345678910## 代码高亮先定位到你的目录,然后输入命令 `ls -al` 即可列出该目录下的所有文件。## 代码块 ```javapublic class HelloWorld { public static void main(String[] args) { System.out.println(\"我的第一个Java程序:Hello World。\"); }} 二、文档行文 文档在行文编写部分,个人认为《给程序员的中文写作指北》一文中说明的特别详尽,本节使用原文引用。 1. 句子长度 技术文章、博客文章忌用复杂的长句和重句。 主要原因是,当句子一长我们的大脑需要花太多的时间来理解这个句子的字面意义而不能专注于理解这个句子要传达的内容因而这与我们的目的完全南辕北辙。 上面这个句子读起来就非常痛苦了,换位思考一下,如果你的文章这么写读者该多痛苦。我建议,句子的长度应该控制在15个词以内,如果有长句的话,应该用标点切分为较短的句子。同时在重复率不高的情况下,尽量用简单的词汇。上面的句子可以改写为: 主要原因是,当句子一长,我们的大脑需要花太多时间,来理解这个句子的字面意义。而此时我们的大脑无法专注句子本身要传达的内容,这与我们的目的完全相悖。 2. 图文并茂 如果有条件,尽量在文章中插入一些与逻辑和表达的内容相关的图片。 图文并茂不光可以让你的读者读起来更轻松,更让你有机会用图表、gif 或其它丰富的媒体形式来表达你的观点,一举几得。同时,如果你的网站需要流量,搜索引擎对相关的图片和视频会增加权重,有利于你的搜索流量增长。 3. 起承转合 即使技术文章的主要写作文体更偏向议论文,但在写作时也需要注意起承转合。 如果你需要像本文一样,列出一系列观点,将这些观点以合理的逻辑串起来也是非常重要的。 在文章结构上,我通常会采用总、分、总的结构。 总 - 即文首先告诉读者会读到什么,如果读者不感兴趣的话可以省下来读一篇文章的时间,而如果感兴趣的话正好是个好引子。 分 - 即分散表达需要表达的观点,比如在本文中,即三个部分:关于内容、关于标点和关于行文的三块建议。 总 - 最后总结文章,你要假设读者并没有时间详读全文,但这里的总结可以让读者能在 10 秒内仍然知道你在这篇文章里表达了什么 4. 引用链接 把你引用的内容放在合适的位置,不仅是对被引用文章作者的尊重,也方便你的读者继续阅读下去。 千万不要觉得引用文章会让人觉得你只是在观点抄袭,事实上只要你有提出新鲜的观点、论据甚至只是更规范地总结了一篇文章,你的读者也会非常感激你的。 同时引用丰富的链接还可以帮助搜索引擎确定你的权威性——你不光文章写出来了,还指向了靠谱的参考文献,因此搜索引擎也会觉得你这篇文章的靠谱程度很高。 附录:主流文档协作平台 Teambition Thoughts - 面向中小企业的知识管理工具 官网:https://thoughts.teambition.com 推荐:特别适合一些产品的对外官方指导或是说明文档 石墨文档 - 企业在线协同办公系统平台,支持云端多人在线协作编辑文档和表格 官网:https://shimo.im 推荐:将文档的编写真正做到了极致,使用体验和设计风格有点像水墨屏。支持文本、表格、思维导图等等。 飞书文档 - 可多人实时编辑的在线文档。 官网:https://www.feishu.cn 推荐:飞书系列产品真是把办公体验做到了极致,特别推荐飞书文档中的思维导图,这是我目前用到过的最好用的在线思维导图,好看又好用。 语雀 - 专业的云端知识库 官网:https://www.yuque.com 推荐:类似于 Thoughts ,团队文档协作 参考引用 给程序员的中文写作指北 | 卡拉搜索 https://kalasearch.cn/blog/writing-guide-for-programmers","categories":[],"tags":[]},{"title":"一行命令实现 Github 国内镜像加速","slug":"github-speed-up","date":"2021-03-28T13:47:11.000Z","updated":"2021-10-23T14:22:52.647Z","comments":true,"path":"2021/03/28/github-speed-up/","link":"","permalink":"http://www.frankfeekr.cn/2021/03/28/github-speed-up/","excerpt":"","text":"请注意:该方法只使用于 https 协议,不适用于 SSH 协议! 一、加速镜像 克隆加速 1234567891011# 原地址git clone https://github.com/kubernetes/kubernetes.git# 改为(推荐)git clone https://github.com.cnpmjs.org/kubernetes/kubernetes.git# 或者git clone https://hub.fastgit.org/kubernetes/kubernetes.git# 或者git clone https://gitclone.com/github.com/kubernetes/kubernetes.git release下载加速 12345# 原地址wget https://github.com/goharbor/harbor/releases/download/v2.0.2/harbor-offline-installer-v2.0.2.tgz# 改为wget https://hub.fastgit.org/goharbor/harbor/releases/download/v2.0.2/harbor-offline-installer-v2.0.2.tgz raw文件下载加速 12345# 原地址wget https://raw.githubusercontent.com/kubernetes/kubernetes/master/README.md# 替换为wget https://raw.staticdn.net/kubernetes/kubernetes/master/README.md 二、一键免替换镜像地址 替换设置 可以直接在配置文件中动态替换 Github 的地址,这样不用每次克隆的时候都修改地址 1git config --global url.\"https://github.com.cnpmjs.org/\".insteadOf \"https://github.com/\" 测试 1git clone https://github.com/kubernetes/kubernetes.git 取消设置 1git config --global --unset url.https://github.com/.insteadof 查看 Git 配置信息 1git config --global --list","categories":[],"tags":[]},{"title":"使用 Certbot 为网站签发永久免费的 HTTPS 证书","slug":"let-is-encrypt-cerbot-for-https","date":"2021-03-28T11:01:10.000Z","updated":"2021-10-23T14:22:52.649Z","comments":true,"path":"2021/03/28/let-is-encrypt-cerbot-for-https/","link":"","permalink":"http://www.frankfeekr.cn/2021/03/28/let-is-encrypt-cerbot-for-https/","excerpt":"","text":"我们常常在自己的服务器上搭建 Web 服务,但是大部分都使用的 HTTP 协议。在一些特殊的场景下,例如:微信小程序或是 APP 后台服务开发,则必须使用 HTTPS 协议。众所周知,HTTPS 可以在各个云服务厂商上购买,通过都需要高昂的费用,往往劝退很多初级的个人开发者。 但是如果你使用 Nginx、Apache 等服务器来运行服务,则可以使用 Certbot 来申请免费的 SSL 证书,轻松帮你实现 HTTPS 网站。申请 Let’s Encrypt 证书不但免费,还非常简单,虽然每次只有 90 天的有效期,但可以通过脚本定期更新,配好之后一劳永逸。 Let’s Encrypt 是由互联网安全研究小组(ISRG,一个公益组织)提供的服务,主要赞助商包括电子前哨基金会,Mozilla基金会,Akamai以及思科。 ISRG于2015年4月9日与Linux基金会宣布合作。 Let’s Encrypt 最初是由 Mozilla 的两名员工于2012年发起,2015年12月3日开启公测,2016年4月12日,该项目正式对外使用。 本文将记录 Centos 7.4 + Nginx + Certbot 为 blog.frankfeekr.cn 网站安装安全的 HTTPS 证书,并且实现到期自动续期。 1. 安装 Certbot 12yum install epel-release -yyum install certbot -y 2. 申请证书 这里证书可以申请单域名或是泛域名证书。这里推荐大家申请泛域名证书申请,例如:*.frankfeekr.cn 域名证书申请后,即可用于该域名下的三级域名。即:blog.frankfeekr.cn / docs.frankfeekr.cn 等等三级域名都可以使用。 安装 python-certbot-nginx 插件 在生成证书的过程需要确保 Nginx 关闭,需要使用 80 端口执行。 在申请证书前需要先安装 certbot 的 Nginx 插件,如果执行 certbot --nginx 出现 The requested nginx plugin does not appear to be installed,则需要先安装插件。 1yum install python-certbot-nginx -y (1)单域名证书 执行命令 1certbot certonly -d blog.frankfeekr.cn --nginx 执行结果 12345678910111213IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/blog.frankfeekr.cn/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/blog.frankfeekr.cn/privkey.pem Your certificate will expire on 2021-06-26. To obtain a new or tweaked version of this certificate in the future, simply run certbot again. To non-interactively renew *all* of your certificates, run \"certbot renew\" - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le 生成的证书会放在:/etc/letsencrypt/live/blog.frankfeekr.cn 目录下,目录证书说明如下: 1234567891011121314This directory contains your keys and certificates.`[cert name]/privkey.pem` : the private key for your certificate.`[cert name]/fullchain.pem`: the certificate file used in most server software.`[cert name]/chain.pem` : used for OCSP stapling in Nginx >=1.3.7.`[cert name]/cert.pem` : will break many server configurations, and should not be used without reading further documentation (see link below).WARNING: DO NOT MOVE OR RENAME THESE FILES! Certbot expects these files to remain in this location in order to function properly!We recommend not moving these files. For more information, see the CertbotUser Guide at https://certbot.eff.org/docs/using.html#where-are-my-certificates. (2)泛域名证书(推荐) 上面我们学会了生成单个域名证书,但是这里更推荐大家申请泛域名证书,方便在多个网站服务托管的时候共同使用。 执行命令 1certbot certonly --preferred-challenges dns --manual -d *.frankfeekr.cn --server https://acme-v02.api.letsencrypt.org/directory 上面的命令执行到这里,会在命令行阻塞,这时候我们需要到域名解析的服务商添加一条 txt 的解析,如下图所示。 添加完解析后稍等几秒钟,即可回车继续,这时候就会校验记录是否有效。 至此:已经完成了泛域名的生成,生成的证书在:/etc/letsencrypt/live 目录下。相比于单域名的证书,泛域名步骤会稍微复杂一些,大家根据自己应用场景申请即可。 3. 安装 Nginx 如果已经安装了 Nginx 请直接跳过这一步 12yum install epel-release -yyum install nginx -y 4. Nginx 证书配置 在 /etc/nginx/conf.d 目录下创建 blog.frankfeekr.cn.conf(文件名以 .conf 后缀,推荐使用域名命名方便管理) 12345678910server { listen 443 ssl; server_name blog.frankfeekr.cn; ssl_certificate /etc/letsencrypt/live/frankfeekr.cn/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/frankfeekr.cn/privkey.pem; 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 /usr/share/nginx/html;} 配置完证书后重启服务 1service nginx restart 5. 自动续期 首先检查 crontab 服务是否运行 1systemctl status crond 如果没有启动,先 enable 它再 start 它 12systemctl enable crondsystemctl start crond 编辑计划任务 1crontab -e 输入下面的表达式(每天 00:00:00),让他每天都尝试一次关闭 Nginx->更新->启动 Nginx,到了最后 30 天的时候就会成功。 10 0 * * * \"service nginx stop ; /bin/certbot renew --renew-by-default; service nginx start\" 6. 访问 https 网站 点击访问:https://blog.frankfeekr.cn 证书查看:有效期是 90 天,待快到期则可以通过上述配置的定时任务来更新。 FAQ 证书重置请求超过次数,一般 3 次,子域名除外。就会出现如下错误,5 天以后才可以再次重置。 12An unexpected error occurred:There were too many requests of a given type :: Error creating new order :: too many certificates already issu for exact set of domains: blog.frankfeekr.cn: see https://letsencrypt.org/docs/rate-limits/ 速率限制 - Let’s Encrypt - 免费的SSL/TLS证书 参考链接 Let’s Encrypt - Free SSL/TLS Certificates https://letsencrypt.org/ centos7+nginx+certbot+自动续期 - mntm博客 http://www.mntm520.com/post/48/ Web开发必知必会,如何使用 Letsencrypt 为网站签发 HTTPS 证书提供安全支持_如小非的博客-CSDN博客 https://blog.csdn.net/mrmengj/article/details/112983348 轻松搞定 Let’s Encrypt 免费SSL证书 | 张衡Henry https://www.izhangheng.com/https-ssl-lets-encrypt-certbot Certbot - Centosrhel7 Nginx https://certbot.eff.org/lets-encrypt/centosrhel7-nginx","categories":[],"tags":[]},{"title":"关于校招,你必须了解的那些事","slug":"about-campus-apply","date":"2020-05-31T08:19:49.000Z","updated":"2021-10-23T14:22:52.647Z","comments":true,"path":"2020/05/31/about-campus-apply/","link":"","permalink":"http://www.frankfeekr.cn/2020/05/31/about-campus-apply/","excerpt":"","text":"分享大纲 (1)什么是校招? 什么时候开始准备校招? (2)还很迷茫吗? 带你一起了解都有哪些就业岗位? 手把手教你选择适合自己的技术栈? (3)如何准备校招面试? (4)答疑讨论环节 1. 校招初体验 1.1 什么是校招 校招(即,校园招聘),应届生通过企业的校园招聘途经进行面试选拔。 应届生由于没有工作经验,如果直接走普通的社会招聘很难进入大型企业,并且拿到期待的薪资。大型企业都有完善的校园招聘通道,把握好校招的时间点才能进入自己梦寐以求的企业和岗位。 1.2 校招时间 春招 每年 3-4 月(金三银四),主要以实习生招聘为主,和少量的正式校招岗位 秋招 每年 9-11 月(金九银十),主要以正式校园招聘岗位为主 例如:小明 2021 年 6 月毕业,则 2020 年春招(3-4月)着手准备实习生招聘,秋招(9-11 月)则要进行正式校招面试,面试通过发放 offer 直接签订三方协议。如果在秋招中没有拿到心仪的 offer,则只能到 2021 年春招再次进行面试,但是岗位和数量也都比较少。 1.3 招聘流程 关于内推: 可以找往届学长学姐、就业群、牛客网找到一些企业内部的员工进行内推。(内推成功都会有提成的,也有很大一部分内推也作为员工 KPI 的一部分) 2. 还很迷茫吗?带你一起了解都有哪些就业岗位? 手把手教你选择适合自己的技术栈? 学前端开发?学 APP 开发?学大数据开发?还是学后台开发呢? 对于 Java、C++、C#、Python、PHP 这些编程语言,又要如何选择呢? 现如今人工智能如此火热,是不是机器学习、深度学习更高级一些呢? 到底如何选择适合自己的技术岗位 2.1 有哪些技术栈 在进行技术栈选择之前,需要对当下互联网技术进行调研。先要知道都有哪些技术栈,了解技术的基本,方能找到适合自己的技术栈。 在当前互联网技术中,笔者将核心技术分为以下四个方向,它们都是互联网主要的技术方向。 1. 大前端开发 之所以称之为“大前端”,它不仅仅是网站的界面开发,从广义上讲,用户终端视觉和交互相关的部分,都属于“大前端”的范畴。在当今互联网发展的今天,多场景融合展现技术,日益复杂的界面交互变化,赋予了前端更广阔的能力。 前端技术的核心是 HTML、CSS、JavaScript,它是整个前端的灵魂。同时在前端的技术栈中 Vue、React、Angular 是最值得关注的三个开发框架。在移动端,目前有:HTML5+原生、Javascript 开发 + 原生渲染(React Native、Weex)、自绘 UI + 原生(Flutter)、增强版 Web App(PWA)是当前主流的移动跨平台解决方案。在移动端各种软件平台都相继推出小程序,构建在自身的软件上,即扫即用、用完即走,这些小程序也同样是前端的研究领域。 前端开发是用户看得见摸得着的,它注重用户的体验,是最接进产品和设计的。相比于后台服务端,端测开发可以让你构建一个丰富多彩、所见即所得的软件交互体验。 如果你对构建有趣的界面和处理各种交互逻辑感兴趣,那么前端开发或许是你的兴趣所在。 2. 后台开发 春运 12306 铁路抢票、全球双 11 购物狂欢节,之所以能够抗住巨大的流量洪峰,离不开最核心的后台系统服务。后台服务对用户不同于前端,它对用户来说是无法直接感知的,它不同于前端可见的界面。抢票时用户身份的认证、车次的查询、车票订单的查询、订单支付等等,用户数据的存储、查询、验证等等算法与逻辑,一套在系统服务端运行的程序,这就是后台服务,它是用户无法直接感受到,但是确实随时随地都在使用的。 后台开发,有时也称做后端开发。其中包含:后台编程语言(Java、Python、C++等)、数据库存储(MySQL、Postgre、Redis)、后台服务框架(如最为火热的 SpringBoot 框架)、消息中间件(Kafka)、容器化引擎(Docker、Kubernates)、服务器编程(Linux)等。总而言之,后台开发就是围绕分布式、高可用、高性能、高可靠展开的一些技术工作。 虽然后台开发不像前端一样,所见即所得。但后台开发是一个推动系统具有生命力的根,它包含了很强的架构设计与实现。一个大型的后台服务可以支撑了亿万流量的平台,是一个产品能够运营的基本。 如果你对系统设计感兴趣,热爱 Java、Python、C++、Go 等语言,想要成为一个系统架构师,那么后台开发是一个不错的选择。 3. 大数据开发 随着系统运营的时间发展,一个系统将积累了越来越多的数据,当数据达到 PB、EB 级别时,后台服务将面临了更大的挑战。这些挑战,不是后台技术栈就能够解决的,传统的关系型数据库已经无法存储这么大体量的数据。那么,这就是大数据需要解决的问题。 大数据有三个重要的特征(简称 3V 特征):大量 (Volume),高速 (Velocity),多样化 (Variety),那么面对海量数据,如何接入、如何存储与查询、如何分析?这便是大数据开发领域,需要实现的工程能力。 大数据开发,需要掌握大数据通用处理平台,如:Spark、Flink、Hadoop 等的使用;大数据编程语言,包括 Scala、Python、Java 等;分布式文件存储 HDFS;数据仓库,如:Hive、ElasticSearch;海量数据实时查询,如 Apache Druid、InfluxDB 等时序数据库;大数据计算资源调度,如 Yarn、Mesos 等;在数据接入需要了解消息队列 Kafka、RocketMQ 等;日志收集框架,如:Flume、ELK 等;此外海量数据要进行分析,也需要掌握常见的机器学习工具包,挖掘海量数据中的有用价值。 大数据开发不同于后台开发,也有别于算法。大数据开发工程师需要掌握良好的工程能力,实现从数据的接入,数据存储,数据查询,数据分析。 如果你对海量数据处理、存储、分析感兴趣,并且有一定的后台开发基础,那么大数据开发是一个适合你的选择。 4. 算法应用 你一定使用过高铁站的人脸识别,也一定听说过可爱调皮的小爱同学;你可能对自动驾驶充满向往,也可能对智能家居满怀憧憬;你可能遐想过人工智能技术在医疗领域大放异彩,也可能想象过生活在智慧城市是一种怎样便捷的感觉,但这些仅仅是人工智能领域的冰山一角。总之,人工智能技术已经在我们生活中像医疗、交通、教育、金融、生活、零售、安防、园区、环保、政务等各个方面发挥着举足轻重的作用。目前世界上主要国家都在人工智能领域进行战略布局。在就业方面,国际、国内各大厂也早已打响了人工智能人才争夺战,作为热爱算法的你是否早已跃跃欲试了呢? 算法应用领域包括的方面很多,传统的数据结构、高级算法设计等是基础,在这个人工智能技术风靡全球、飞速发展的时代,人工智能技术早已在算法应用领域占据很大的比重。机器学习是人工智能的基础,机器学习在大的层面上分为传统机器学习和深度学习,从另一个角度又可以分为非监督学习、弱监督学习和强监督学习;从应用场景角度还可以分为自然语言处理(NLP)、计算机视觉(CV),以及二者相互结合。对于机器学习从业者而言,理论层面上需要掌握像逻辑回归(LR)、感知机、最近邻算法(KNN)、决策树(DT)、支持向量机(DT)等传统算法,以及随机森林(RF)、Adaboost、GBDT、Xgboost、LightGBM 等集成学习算法,以及与特征工程相关的相关知识。在深度学习层面主要包含梯度下降反向传播算法(BP)、多层感知机(MLP)、卷机神经网络(CNN),循环神经网络(RNN),强化学习,以及包含 Dropout、Batch Normalization、正则化等优化方法。实践层面主要掌握 Scikit-Learn:,Tensorflow,Keras,Pytorch 等框架。 人工智能技术特别是深度学习技术发展之快可谓日新月异,最新技术早已远超如上所述,正等待着目前作为准技术达人的你们去挖掘探索。假如你也想在阿里云天池、Kaggle 等比赛平台上去刷榜而一展才智,或者你也想去像 CVPR、ICLR 等会议上留下浓墨重彩的一笔,或者你也想去互联网大厂与各路大神做同台竞技,那么还在等什么,算法方向就是你最佳的选择。 更多请转向,各大企业的招聘网站: 微众招聘-微众银行招聘 https://webank.cheng95.com/ 应届校招| 美团点评招聘官网 https://campus.meituan.com/campus-recruit 加入字节跳动-招聘 https://job.bytedance.com/campus 阿里巴巴集团招聘官网 https://talent.alibaba.com/campus/ 【更详细请阅读】大学四年,如何选择自己的技术栈 | Frank’s 技术世界 2.2 选择适合自己的技术 上面列举了互联网技术栈中核心的几个方向,那么如果去选择一个适合自己的技术栈呢?根据笔者多年互联网技术栈学习经验,主要归纳为下面的几点。 第一,找到自己的兴趣。在发现自己的兴趣之前,往往我们都是一张白纸,需要我们进行广泛的学习,通过一段时间的启蒙学习才会发现自己更适合做什么,从中发觉自己的兴趣。回到技术本身,作为一个技术“小白”,我们则可以从自己基本的切入点入手,选择自己的技术栈。如果对视觉、交互体验感兴趣,可以从前端开始,开发设计一个网站、一个 APP 开始。如果对后台架构设计、后台编程语言感兴趣则可以从后台开发开始,如:Python、Java、PHP 等语言开始。如果你有很好的数学功底,喜欢数据分析、概率统计,想要运用数学知识挖掘更大的价值,则可以上手机器学习、深度学习等算法的入门。如果你已经接触过了诸多技术栈,我想你应该已经知道自己的兴趣。通过启蒙学习发觉自己的兴趣,这是我们选择自己技术栈的第一步。 第二,关注一线就业方向。互联网技术日新月异,要结合自己的兴趣和当下的就业市场,不要迷失在技术的海洋中。当下火热的机器学习、Java 后台开发、C++ 开发、前端开发、APP 开发,最热门的岗位,意味着未来的前景。试想一下,一个想要入门互联网技术栈的爱好者,如果还在学习十年前的技术,老旧的技术框架,那么又怎么能迎合互联网发展的趋势呢,势必被时代所淘汰。在中国互联网技术浪潮中,技术爱好者可以关注国内一线互联网“大厂”的技术岗位,关注开发社区,瞄准就业市场、技术方向、自己的兴趣,聚力突破。 第三、洞察技术趋势与发展。技术迭代日益快速的时代下,技术人如何自处?作为一个有追求的技术的爱好者,必须紧跟技术前沿,基于自己的技术能力,关注开源技术,洞察未来的技术趋势。如当下火热的:“5G”、“人工智能”、“云计算”,5G 可以更快地传输数据,人工智能可以智能的学习分析,而云计算是为了更好地进行计算,它们必将成为未来数字经济的基础设施。例如当下的我们可以选择一些优势方向,例如:机器学习、云计算、5G 开发等等方向。洞察技术趋势与发展,才能紧跟潮流脚步。再如当下疫情的发展,对于远程办公和视频会议的需求明显激增,音视频开发的市场需求也水涨船高。 当然不仅仅只是学会一门技能,也要有自己的辅助第二技术或是第三技术。比如后台思维写前端,算法思维写后台,产品思维写算法。这些都是我们的核心竞争力,懂得更多可以让彼此之间的协作更顺畅。笔者根据自己多年的技术学习经验,从“找到自己的兴趣”、“关注一线就业方向”、“洞察技术趋势与发展”三个方面入手,一定能找到适合自己的技术栈。 3. 如何准备校招面试? 3.1 夯实基础 技术岗位的专业基础知识,必须扎实。 以【 Java 后台开发】岗位开发为例,需要掌握的知识: 在线笔试基础 Leetcode(必刷) 剑指 Offer(必刷) 内功修炼 数据结构与算法 计算机网络(应用层,传输层,网络层等相关协议) 操作系统原理 语言核心基础 语法与基础概念 面向对象与 23 种设计模式 Java 容器源码(数据结构 & 源码分析:ArrayList、Vector、LinkedList、HashMap、ConcurrentHashMap、HashSet、LinkedHashSet and LinkedHashMap) Java 并发编程(线程状态、线程机制、线程通信、J.U.C 组件、JMM、线程安全、锁优化) Java IO(磁盘操作、字节操作、字符操作、对象操作、网络操作、NIO) Java 虚拟机(运行时数据区域、垃圾收集、内存分配机制、类加载机制、性能调优监控工具) Java Web(学习 Spring + SpringMVC + MyBatis 框架和设计模式思想,学习 Servlet 和 JSP) 后台技术栈 Linux 基础 数据库(MySQL,Redis) Git 版本管理工具使用 正则表达式 高级加分项 Zookeeper(分布式协调服务) Dubbo(分布式服务治理) 分布式事务解决方案 Kafka(分布式消息通信) Nginx(反向代理) Docker(容器技术) 3.2 面试技巧 简历几要素 基本信息 姓名 / 年龄 / 联系方式 / 教育经历 / 应聘岗位 推荐大家联系方式上使用校园 edu.cn 的邮箱 校园经历 ☆☆ 参加过社团、学生会活动等;参加过校园某某平台系统开发;xxx 获奖荣誉 ☆☆ 实习经验 ☆☆ 项目经验 ☆☆☆ 面试技巧 作为技术性面试,一定要包装好一份自己的项目经历,有条件的甚至可以提早准备好自己的项目架构图。只有准备好自己的项目经验,才能在现场面试的环节中让面试官感兴趣,告诉面试官自己的亮点,从而把面试话题牵引到自己最擅长的领域。 切记不要一句话也不说,项目也不说清楚。这种面试,只会让面试官上来就考察基础,面试印象也大大折扣。 3.3 经验分享:我的校招之路 我的校招之路 | Frank’s 技术世界 https://www.frankfeekr.cn/2018/10/19/我的校招之路/ 广撒简历,重点培养。明确自己的岗位,工作地进行海投、精准面试。基本上面到 HR 面的企业都拿到了口头 Offer 或是意向书。 在面试过程中,选择往往大于努力,一定要认准自己的方向。 Offer = 40%运气 + 40%技术 + 20%表达能力 4. 答疑讨论环节 希望大家今天可以在我的分享中有一点点收货,生活也需要平衡。 ☆ 推荐一些关于校招的平台 关注公众号【全栈开发社区】,回复 “校招” 获取资料 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 https://leetcode-cn.com/ 牛客网-找工作神器|笔试题库|面试经验|实习招聘内推,求职就业一站解决_牛客网 https://www.nowcoder.com/ OfferShow ","categories":[],"tags":[]},{"title":"进击大数据,Scala 语言火速入门","slug":"scala-quick-start","date":"2020-05-19T17:32:33.000Z","updated":"2021-10-23T14:22:52.649Z","comments":true,"path":"2020/05/20/scala-quick-start/","link":"","permalink":"http://www.frankfeekr.cn/2020/05/20/scala-quick-start/","excerpt":"","text":"第1章:初识 Scala 1. Scala概述 Scala是一门多范式的编程语言,设计初衷集成面向对象编程和函数式编程的各种特性 Scala运行于Java平台(Java虚拟机),并兼容现有的Java程序 Scala combines object-oriented and functional programming in one concise, high-level language. Scala’s static types help avoid bugs in complex applications, and its JVM and JavaScript runtimes let you build high-performance systems with easy access to huge ecosystems of libraries. 2. Scala安装 Java8 123456Thpffcj:software thpffcj$ echo $JAVA_HOME/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/HomeThpffcj:software thpffcj$ java -versionjava version \"1.8.0_191\"Java(TM) SE Runtime Environment (build 1.8.0_191-b12)Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode) 下载并解压Scala 配置到系统环境变量 123456Thpffcj:software thpffcj$ vi ~/.bash_profileexport SCALA_HOME=/Users/thpffcj/Public/software/scala-2.12.8export PATH=$PATH:$SCALA_HOME/binThpffcj:software thpffcj$ source ~/.bash_profile 3. Scala使用入门及对比Java 123456789101112131415Thpffcj:bin thpffcj$ ./scalaWelcome to Scala 2.12.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_191).Type in expressions for evaluation. Or try :help.scala> 1 + 3res0: Int = 4scala> res0 * 3res1: Int = 12scala> res0 * res1res2: Int = 48scala> println(\"Hello World\")Hello World HelloWorld 12345public class HelloWorld{ public static void main(String[] args) { System.out.println(\"Hello World\"); }} Scala每行代码并不强求使用;结束,但是Java是必须的 12345object HelloWorld { def main(args : Array[String]) { println(\"Hello World\") }} 将上面代码保存为HelloWorld.scala 12345Thpffcj:data thpffcj$ scalac HelloWorld.scala Thpffcj:data thpffcj$ lsHelloWorld$.class HelloWorld.class HelloWorld.scala hello.txtThpffcj:data thpffcj$ scala HelloWorldHello World 第2章:Scala 入门 1. val vs var val:值 val 值名称:类型 = xxx 12345678910scala> val money = 100money: Int = 100scala> money =200<console>:12: error: reassignment to val money =200 ^scala> val age:Int = 20age: Int = 20 var:变量 var 值名称:类型 = xxx 12345scala> var name:String = \"zhangsan\"name: String = zhangsanscala> name = \"zhangsi\"name: String = zhangsi 2. 基本数据类型 Byte/Char Short/Int/Long/Float/Double Boolean 123456789101112131415161718192021scala> val a:Int = 10a: Int = 10scala> val b:Boolean = trueb: Boolean = truescala> val c = falsec: Boolean = falsescala> val d = 1.1d: Double = 1.1scala> val e:Float = 1.1<console>:11: error: type mismatch; found : Double(1.1) required: Float val e:Float = 1.1 ^scala> val e:Float = 1.1fe: Float = 1.1 类型转换 12345678scala> val f = 10f: Int = 10scala> val g = 10.asInstanceOf[Double]g: Double = 10.0scala> val h = 10.isInstanceOf[Int]h: Boolean = true 3. lazy在Scala中的应用 耗费计算网路的时候可以使用 lazy 12345678scala> val i = 1i: Int = 1scala> lazy val a = 1a: Int = <lazy>scala> ares0: Int = 1 我们现在想读取文件 1234567891011121314scala> import scala.io.Source._import scala.io.Source._scala> lazy val info = fromFile(\"/Users/thpffcj/Public/data/HelloWorld.scala\").mkStringinfo: String = <lazy>scala> infores3: String =\"object HelloWorld { def main(args : Array[String]) { println(\"Hello World\") }}\" 4. Scala 常用 IDE IDEA Eclipse NetBeans 5. 使用IDEA整合Maven构建应用程序 新建项目勾选 Create from archetype 并选择 scala-archetype-simple 起项目名一路 Next,选择自己安装的 Maven 地址,然后继续 Next 第一次创建项目需要下载依赖包可能会比较慢 去Plugin里下载Scala的Plugin,下载完成后重启 添加Scala-SDK 编写Hello World程序 第3章:Scala 函数 1. 方法的定义和使用 函数/方法的定义 12345def 方法名(参数名:参数类型): 返回值 类型 = { // 括号内的叫做方法体 // 方法体内的最后一行为返回值,不需要使用return} 我们去IDEA中试验一下 123456789101112131415161718object FunctionApp { def main(args: Array[String]): Unit = { println(add(2, 3)) println(three()) println(three) // 没有入参的函数,调用时括号是可以省略的 sayHello(\"Frank\") } def add(x:Int, y:Int):Int = { x+ y // 最后一行就是返回值,不需要return } def three() = 1 + 2 def sayHello(name:String): Unit = { // 不需要返回值 println(\"Say hello \" + name) }} 2. 默认参数的使用 在函数定义时,允许指定参数的默认值 123def sayName(name:String = \"Frank\"): Unit = { println(name)} 我们来看一下默认参数在Spark中的使用 1234$ ./spark-shell --help --properties-file FILE Path to a file from which to load extra properties. If not specified, this will look for conf/spark-defaults.conf. 如果你没有设置配置文件,它将会去寻找conf下的spark-defaults.conf 3. 命名函数的使用 1234567def main(args: Array[String]): Unit = { println(speed(time = 10, distance = 100))}def speed(distance:Float, time:Float):Float = { distance/time} 4. 可变参数的使用 JDK5+:可变参数 123456789101112def main(args: Array[String]): Unit = { println(sum(1, 2)) println(sum(1, 2, 3))}def sum(numbers:Int*) = { var result = 0 for (number <- numbers) { result += number } result} 5. 条件表达式 1if(a>0) true else false 6. 循环表达式 to:左闭右闭 Range:左闭右开 until:左闭右开 1234567891011121314151617181920212223scala> 1 to 10 // 左闭右闭res11: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)scala> Range(1,10) // 左闭右开res12: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)scala> 1.to(10) // 等同于 1 to 10res13: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)scala> Range(1,10,2) // 可以选择步长res14: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)scala> Range(1,10,0) // 步长不可以为0,死循环java.lang.IllegalArgumentException: step cannot be 0. at scala.collection.immutable.Range.<init>(Range.scala:86) at scala.collection.immutable.Range$.apply(Range.scala:439) ... 32 elidedscala> Range(10,0,-1) // 可以从大到小来res16: scala.collection.immutable.Range = Range(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)scala> 1 until 10 // 左闭右开 底层调用的Rangeres17: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9) for循环 123456789101112131415def main(args: Array[String]): Unit = { for (i <- 1 to 10 if i % 2 == 0) { println(i) } val courses = Array(\"Hadoop\", \"Spark\", \"Storm\") for (course <- courses) { println(course) } // course 其实就是 courses 里面的每个元素 // => 就是将左边的 course 作用上一个函数,变成另外一个结果 // println 就是作用到 course 上的一个函数 courses.foreach(course => println(course))} while循环 1234567def main(args: Array[String]): Unit = { var (num, sum) = (100, 0) while (num > 0) { sum = sum + num num = num - 1 }} 第4章:Scala 面向对象 1. 面向对象概述 封装:属性,方法封装到类中 Person private int id, String name, Date birthday… getter/setter eat sleep … 继承:父类和子类之间的关系 User extends Person 多态:父类引用指向子类对象(精髓所在 / 开发框架的基石) Person person = new Person(); User user = new User(); Person person = new User(); 2. 类的定义与使用 123456789101112131415161718192021222324252627282930313233object SimpleObjectApp { def main(args: Array[String]): Unit = { val person = new People() person.name = \"Messi\" println(person.name + \" .. \" + person.age) println(\"invoke eat method: \" + person.eat()) person.watchFootball(\"Barcelona\") person.printInfo() }}class People { // 定义属性 var name:String = _ // 占位符 val age:Int = 10 private [this] val gender = \"male\" // 定义方法 def eat():String = { name + \"eat...\" } def watchFootball(teamName:String): Unit = { println(name + \" is watching match of \" + teamName) } def printInfo(): Unit = { println(\"gender: \" + gender) }} 3. 构造器 123456789101112131415161718192021222324252627object ConstructorApp { def main(args: Array[String]): Unit = { val person = new Person(\"zhangsan\", 30) println(person.name + \":\" + person.age + \":\" + person.school) val person2 = new Person(\"lisi\", 18, \"M\") println(person2.name + \":\" + person2.age + \":\" + person2.school + \":\" + person2.gender) }}// 主构造器class Person(val name:String, val age:Int) { println(\"Person Constructor enter...\" ) val school = \"ustc\" var gender:String = _ // 附属构造器 def this(name: String, age:Int, gender:String) { this(name, age) // 附属构造器的第一行的代码必须要调用主构造器或者其他附属构造器 this.gender = gender } println(\"Person Constructor leave...\" )} 4. 继承与重写 继承 12345678910111213141516object ConstructorApp { def main(args: Array[String]): Unit = { val student = new Student(\"zhangsan\", 18, \"Math\") println(student.name + \":\" + student.major) }}// major 属性需要使用var修饰class (name:String, age:Int, var major:String) extends Person(name, age) { println(\"Student Constructor enter...\" ) println(\"Student Constructor leave...\" )} 重写 12345678910class Student(name:String, age:Int, var major:String) extends Person(name, age) { println(\"Student Constructor enter...\" ) override val school = \"peking\" override def toString: String = \"override def toString : \" + school println(\"Student Constructor leave...\" )} 5. 抽象类 12345678910111213141516171819202122232425object AbstractApp { def main(args: Array[String]): Unit = { val student = new Student2() println(student.name) student.speak }}/** * 类的一个或者多个方法没有完整的实现(只有定义,没有实现) */abstract class Person2 { def speak val name:String val age:Int}class Student2 extends Person2 { override def speak: Unit = { println(\"speak\") } override val name: String = \"Frank\" override val age: Int = 18} 6. 伴生类和伴生对象 123456789101112/** * 伴生类和伴生对象 * 如果有一个class,还有一个与class同名的object * 那么就称这个object是class的伴生对象,class是object的伴生类 */class ApplyTest {}object ApplyTest {} 7. apply 1234567891011121314151617181920212223242526272829303132333435363738394041424344object ApplyApp { def main(args: Array[String]): Unit = { for (i <- 1 to 10) { ApplyTest.incr } println(ApplyTest.count) // 10 说明object本身就是一个单例对象 val b = ApplyTest() // ==> Object.apply val c = new ApplyTest() c() // 类名() ==> Object.apply // 对象() ==> Class.apply }}class ApplyTest { def apply() = { println(\"Class ApplyTest apply...\") }}object ApplyTest { println(\"Object ApplyTest enter...\") var count = 0 def incr = { count = count + 1 } // 最佳实践:在Object的apply方法中去new Class def apply() = { println(\"Object ApplyTest apply...\") // 在object中的apply中new class new ApplyTest } println(\"Object ApplyTest leave...\")} 8. case class 123456789// 通常用在模式匹配object CaseClassApp { def main(args: Array[String]): Unit = { println(Dog(\"wangcai\").name) }}// case class 不用newcase class Dog(name:String) 9. trait Scala中的Triat是一种特殊的概念 首先我们可以将Trait作为接口来使用,此时的Triat就与Java中的接口非常类似 在triat中可以定义抽象方法,就与抽象类中的抽象方法一样,只要不给出方法的具体实现即可 类可以使用extends关键字继承trait,注意,这里不是implement,而是extends,在scala中没有implement的概念,无论继承类还是trait,统一都是extends 类继承trait后,必须实现其中的抽象方法,实现时不需要使用override关键字 scala 不支持对类进行多继承,但是支持多重继承trait,使用with关键字即可 xxx extends ATrait with BTrait 1234class SparkConf(loadDefaults: Boolean) extends Cloneable with Logging with Serializable 第5章:Scala 集合 1. 数组 定长数组 12345678910scala> val a = new Array[String](5)a: Array[String] = Array(null, null, null, null, null)scala> a.lengthres0: Int = 5scala> a(1) = \"hello\"scala> ares2: Array[String] = Array(null, hello, null, null, null) 我们再来看一种其他创建数组的方法,其实它就是用了上面我们提到的apply 1234567scala> val b = Array(\"hadoop\", \"spark\", \"storm\")b: Array[String] = Array(hadoop, spark, storm)scala> b(1) = \"flink\"scala> bres4: Array[String] = Array(hadoop, flink, storm) 我们看看数组的其他方法 1234567891011scala> val c = Array(2, 3, 4, 5, 6, 7, 8, 9)c: Array[Int] = Array(2, 3, 4, 5, 6, 7, 8, 9)scala> c.sumres5: Int = 44scala> c.minres7: Int = 2scala> c.mkString(\",\")res8: String = 2,3,4,5,6,7,8,9 变长数组 12scala> val d = scala.collection.mutable.ArrayBuffer[Int]()d: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer() 对可变数组进行操作 12345678d += 1d += 2d += (3, 4, 5)d ++= Array(6, 7, 8)d.insert(0, 0)d.remove(1)d.trimEnd(2)println(d) 我们看到最后的结果是ArrayBuffer(0, 2, 3, 4, 5, 6) 我们如何对变长数组迭代呢 1234567for (i <- (0 until d.length).reverse) { println(d(i))}for (ele <- d) { println(ele)} 可变数组可以通过toArray方法变为定长数组 12scala> d.toArrayres10: Array[Int] = Array() 2. List 1234567891011121314151617scala> val l = List(1, 2, 3, 4, 5)l: List[Int] = List(1, 2, 3, 4, 5)scala> l.headres1: Int = 1scala> l.tailres2: List[Int] = List(2, 3, 4, 5)scala> Nilres0: scala.collection.immutable.Nil.type = List()scala> val l2 = 1 :: Nill2: List[Int] = List(1)scala> val l3 = 2 :: l2l3: List[Int] = List(2, 1) 我们也可以创建变长list 1234567891011scala> val l5 = scala.collection.mutable.ListBuffer[Int]()l5: scala.collection.mutable.ListBuffer[Int] = ListBuffer()scala> l5 += (4, 5, 6, 7)res3: l5.type = ListBuffer(4, 5, 6, 7)scala> l5.toListres4: List[Int] = List(4, 5, 6, 7)scala> l5.toArrayres5: Array[Int] = Array(4, 5, 6, 7) 接下来我们看个方法,这个方法可以完成求和操作 1234567def sum(nums:Int*):Int = { if (nums.length == 0) { 0 } else { nums.head + sum(nums.tail:_*) }} 3. Set 12scala> val set = Set(1, 2, 2, 1, 4, 3)set: scala.collection.immutable.Set[Int] = Set(1, 2, 4, 3) 4. Map Map(映射)是一种可迭代的键值对(key/value)结构 所有的值都可以通过键来获取 Map 中的键都是唯一的 Map 也叫哈希表(Hash tables) Map 有两种类型,可变与不可变,区别在于可变对象可以修改它,而不可变对象不可以 默认情况下 Scala 使用不可变 Map。如果你需要使用可变集合,你需要显式的引入 import scala.collection.mutable.Map 类 12345// 空哈希表,键为字符串,值为整型var A:Map[Char,Int] = Map()// Map 键值对演示val colors = Map(\"red\" -> \"#FF0000\", \"azure\" -> \"#F0FFFF\") 定义 Map 时,需要为键值对定义类型。如果需要添加 key-value 对,可以使用 + 号 12A += ('I' -> 1)A += ('J' -> 5) Map 基本操作 keys:返回 Map 所有的键(key) values:返回 Map 所有的值(value) isEmpty:在 Map 为空时返回true 1234567891011121314object Test { def main(args: Array[String]) { val colors = Map(\"red\" -> \"#FF0000\", \"azure\" -> \"#F0FFFF\", \"peru\" -> \"#CD853F\") val nums: Map[Int, Int] = Map() println( \"colors 中的键为 : \" + colors.keys ) println( \"colors 中的值为 : \" + colors.values ) println( \"检测 colors 是否为空 : \" + colors.isEmpty ) println( \"检测 nums 是否为空 : \" + nums.isEmpty ) }} 输出结果为: 1234colors 中的键为 : Set(red, azure, peru)colors 中的值为 : MapLike(#FF0000, #F0FFFF, #CD853F)检测 colors 是否为空 : false检测 nums 是否为空 : true 你可以使用 ++ 运算符或 Map.++() 方法来连接两个 Map,Map 合并时会移除重复的 key 12345678910111213141516171819object Test { def main(args: Array[String]) { val colors1 = Map(\"red\" -> \"#FF0000\", \"azure\" -> \"#F0FFFF\", \"peru\" -> \"#CD853F\") val colors2 = Map(\"blue\" -> \"#0033FF\", \"yellow\" -> \"#FFFF00\", \"red\" -> \"#FF0000\") // ++ 作为运算符 var colors = colors1 ++ colors2 println( \"colors1 ++ colors2 : \" + colors ) // ++ 作为方法 colors = colors1.++(colors2) println( \"colors1.++(colors2)) : \" + colors ) }} 以下通过 foreach 循环输出 Map 中的 keys 和 values 1234567891011object Test { def main(args: Array[String]) { val sites = Map(\"runoob\" -> \"http://www.runoob.com\", \"baidu\" -> \"http://www.baidu.com\", \"taobao\" -> \"http://www.taobao.com\") sites.keys.foreach{ i => print( \"Key = \" + i ) println(\" Value = \" + sites(i) )} }} 5. Option & Some & None 123456789101112131415161718object OptionApp extends App { val m = Map(1 -> 2) println(m.get(1)) println(m.getOrElse(2, \"None\"))}/** * final case class Some[+A](@deprecatedName('x, \"2.12.0\") value: A) extends Option[A] { * def isEmpty = false * def get = value * } * * case object None extends Option[Nothing] { * def isEmpty = true * def get = throw new NoSuchElementException(\"None.get\") * } */ 6. Tuple 与列表一样,元组也是不可变的,但与列表不同的是元组可以包含不同类型的元素 元组的值是通过将单个的值包含在圆括号中构成的 12scala> val t = (1, 3.14, \"Fred\") t: (Int, Double, String) = (1,3.14,Fred) 访问元组的元素可以通过数字索引,我们可以使用 t._1 访问第一个元素, t._2 访问第二个元素 123456789object Test { def main(args: Array[String]) { val t = (4,3,2,1) val sum = t._1 + t._2 + t._3 + t._4 println( \"元素之和为: \" + sum ) }} 你可以使用 Tuple.productIterator() 方法来迭代输出元组的所有元素 1234567object Test { def main(args: Array[String]) { val t = (4,3,2,1) t.productIterator.foreach{ i =>println(\"Value = \" + i )} }} 第6章:Scala 模式匹配 1. 基本数据类型模式匹配 Java:对一个值进行条件判断,返回针对不同的条件进行不同的处理 123456变量 match { case value1 => 代码1 case value2 => 代码2 ...... case _ => 代码N} 具体代码实现 1234567891011121314151617181920212223object MatchApp extends App { val names = Array(\"Akiho Yoshizawa\", \"YuiHatano\", \"Aoi Sola\") val name = names(Random.nextInt(names.length)) name match { case \"Akiho Yoshizawa\" => println(\"吉老师\") case \"YuiHatano\" => println(\"波老师\") case _ => println(\"真不知道你们在说什么\") } def judgeGrade(grade:String) = { grade match { case \"A\" => println(\"Excellent\") case \"B\" => println(\"Good\") case \"C\" => println(\"Just so so\") case _ => println(\"You need work harder\") } } judgeGrade(\"A\") judgeGrade(\"C\") judgeGrade(\"D\")} 加条件的模式匹配 123456789101112def judgeGrade(name:String, grade:String) = { grade match { case \"A\" => println(\"Excellent\") case \"B\" => println(\"Good\") case \"C\" => println(\"Just so so\") case _ if (name == \"lisi\") => println(name + \", you are a good boy, but ...\") case _ => println(\"You need work harder\") }}judgeGrade(\"zhangsan\", \"D\")judgeGrade(\"lisi\", \"D\") 2. Array模式匹配 12345678910111213def greeting(array:Array[String]) = { array match { case Array(\"zhangsan\") => println(\"Hi:zhangsan\") case Array(x, y) => println(\"Hi:\" + x + \",\" + y) case Array(\"zhangsan\", _*) => println(\"Hi:zhangsan and other friend\") case _ => println(\"Hi:everybody\") }}greeting(Array(\"zhangsan\"))greeting(Array(\"lisi\", \"wangwu\"))greeting(Array(\"zhangsan\", \"lisi\", \"wangwu\"))greeting(Array(\"lisi\")) 3. List模式匹配 12345678910111213def greeting(list:List[String]) = { list match { case “zhangsan”::Nil => println(“Hi:zhangsan”) case x::y::Nil => println(“Hi:” + x + “,” + y) case “zhangsan”::tail => println(“Hi:zhangsan and other friend”) case _ => println(“Hi:everybody”) }}greeting(List(“zhangsan”))greeting(List(“lisi”, “wangwu”))greeting(List(“zhangsan”, “lisi”, “wangwu”))greeting(List(“lisi”)) 4. 类型匹配 12345678910111213def matchType(obj:Any): Unit = {obj match {case x:Int => println(“Int”)case s:String => println(“String”)case m:Map[_,_] => m.foreach(println)case _ => println(“other type”)}}matchType(1)matchType(“1”)matchType(1f)matchType(Map(“name” -> “thpffcj”)) 5. Scala异常处理 123456789101112object ExceptionApp extends App { try { val i = 10 / 0 println(i) } catch { case e:ArithmeticException => println(\"除数不能为0\") case e:Exception => println(e.getMessage) } finally { // 释放资源,一定能执行 }} 6. case class模式匹配 12345678910111213141516class Personcase class CTO(name:String, floor:String) extends Personcase class Employee(name:String, floor:String) extends Personcase class Other(name:String) extends Persondef caseclassMatch(person: Person) = { person match { case CTO(name, floor) => println(\"CTO name is: \" + name + \", floor is: \" + floor) case Employee(name, floor) => println(\"Employee name is: \" + name + \", floor is: \" + floor) case _ => println(\"other\") }}caseclassMatch(CTO(\"Thpffcj\", \"22\"))caseclassMatch(Employee(\"zhagnsan\", \"2\"))caseclassMatch(Other(\"other\")) 7. Some & None模式匹配(略) 第7章:Scala 函数高级操作 1. 字符串高级操作 12345678910111213141516object StringApp extends App { val name = \"Thpffcj\" val team = \"AC Milan\" // 插值 println(s\"Hello:$name, Welcome to $team\") val b = \"\"\" |这是一个多行字符串 |hello |world |Thpffcj \"\"\".stripMargin println(b)} 2. 匿名函数 匿名函数:函数是可以命名的,也可以不命名 (参数名:参数类型) => 函数体 12345678910scala> val m1 = (x:Int) => x+1scala> m1(10)res2: Int = scala> def add = (x:Int, y:Int) => {x+y}add: (Int, Int) => Intscala> add(2, 3)res3: Int = 5 3. curry函数 123456// 将原来接收两个参数的一个函数,转换成2个def sum(a:Int, b:Int) = {a+b}println(sum(2,3))def sum2(a:Int)(b:Int) = a + bprintln(sum2(2)(3)) 4. 高阶函数 map map:逐个去操作集合中的每个元素 1234567891011121314scala> val l = List(1, 2, 3, 4, 5, 6, 7, 8)l: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8)scala> l.map((x: Int) => x + 1)res0: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9)scala> l.map((x) => x * 2)res1: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16)scala> l.map(x => x * 2)res2: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16)scala> l.map(_ * 2)res3: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16) filter 12scala> l.map(_ * 2).filter(_ > 8)res4: List[Int] = List(10, 12, 14, 16) take 12scala> l.take(4)res5: List[Int] = List(1, 2, 3, 4) reduce scala当中的reduce可以对集合当中的元素进行归约操作。两两相加 reduce包含reduceLeft和reduceRight。reduceLeft就是从左向右归约,reduceRight就是从右向左归约。 12345678scala> l.reduce(_ + _)res6: Int = 36scala> l.reduceLeft(_ - _)res7: Int = -34scala> l.reduceRight(_ - _)res8: Int = -4 flatten 所有元素都压平 12345scala> var f = List(List(1,2),List(3,4),List(5,6))f: List[List[Int]] = List(List(1, 2), List(3, 4), List(5, 6))scala> f.flattenres0: List[Int] = List(1, 2, 3, 4, 5, 6) flatMap 12345scala> f.map(_.map((_ * 2)))res10: List[List[Int]] = List(List(2, 4), List(6, 8), List(10, 12))scala> f.flatMap(_.map(_ * 2))res11: List[Int] = List(2, 4, 6, 8, 10, 12) 5. 偏函数(略) 第8章:Scala 隐式转换 需求:为一个已存在的类添加一个新的方法 Java:动态代理 Scala:隐式转换 双刃剑 Spark/Hive/MR… 调优 12345678910111213141516171819202122232425import java.io.File//import ImplicitAspect._object ImplicitApp { // 定义隐式转换函数即可 implicit def man2superman(man:Man):Superman = new Superman(man.name) val man = new Man(\"PK\") man.fly()}class Man(val name: String) { def eat(): Unit = { println(s\"man[ $name ] eat ..... \") }}class Superman(val name: String) { def fly(): Unit = { println(s\"superman[ $name ] fly ..... \") }} 第9章:Scala 操作外部数据(略) 第10章:综合案例(略) 1. 项目概述 Spring Boot + Spring Data + Scala + Java 混编 2. 项目需求 论统一元数据管理在大数据平台中的重要性(SparkSQL/Hive) 构建大数据统一元数据管理 元数据管理:MetaStore 采集 维护 稽查 分析 3. 项目需求分析 本个项目实战: 数据库管理 id:数据库编号 name:数据库名称 location:数据库存放在HDFS/S3/OSS等文件系统上的目录 表管理:表是要属于某一个数据库 id:表编号 name:表名称 tableType:表类型 dbId:该表所属的数据库id 默认存储路径:db对应的location/tableName 4. 功能开发 5. 使用postman进行交互测试 6. 扩展 附录一:Scala 图解 在线运行地址:图解 Scala 基本语法代码片段 (来源:图解 Scala 基本语法 V2018.12.17 - PlayScala中文社区 | Scala中文技术交流社区) 引用 参考链接:Scala 学习 1 | Thpffcj的树洞 代码工程:frank-lam/scalaNocturne: scala小夜曲","categories":[],"tags":[]},{"title":"SpringBoot 打包成 war,并部署到 Tomcat","slug":"springboot-package-war","date":"2020-01-28T15:26:57.000Z","updated":"2021-10-23T14:22:52.650Z","comments":true,"path":"2020/01/28/springboot-package-war/","link":"","permalink":"http://www.frankfeekr.cn/2020/01/28/springboot-package-war/","excerpt":"","text":"一、jar 与 war 包的区别 参考来源:spring boot打jar包和打war包的区别作用 - 赖进杰的专栏 - CSDN博客 jar 包:直接通过内置 tomcat 运行,不需要额外安装 tomcat。如需修改内置 tomcat 的配置,只需要在SpringBoot 的配置文件中配置。内置 tomcat 没有自己的日志输出,全靠 jar 包应用输出日志。但是比较方便,快速,比较简单。 war 包:传统的应用交付方式,需要安装 tomcat,然后放到 webapps 目录下运行 war 包,可以灵活选择tomcat 版本,可以直接修改 tomcat 的配置,有自己的 tomcat 日志输出,可以灵活配置安全策略。相对打成 jar 包来说没那么快速方便。 二、把 SpringBoot 打成 war 包部署到 Tomcat 中 参考来源:三分钟把spring boot打成war包部署到tomcat中 - 掘金 1. 将 SpringBoot 项目打包成 war 修改pom.xml指定打包方式为 war 包: <packaging>war</packaging> 修改 pom.xml 修改 SpringBoot 内置的 tomcat 依赖,指定 scope 为 provided 12345<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope></dependency> 修改成果如下: 创建一个初始化文件初始化项目比如:MySpringBootServletInitializer.java 核心代码如下 123456public class MySpringBootServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(MywarApplication.class); }} 执行命令 mvn clean install -Dmaven.test.skip=true 将项目打成war包,执行后到target目录可以看到成功的把项目打成了war包 一些注意的点 可以修改最终的war包的名称,比如去掉文件名里的版本号:mywar.war,在pom.xml使用finalName指令可以实现 上述配置如果觉得麻烦,可以直接下载这位作者的代码包,直接下载打包体验(GitHub:neatlife/mywar) 2. 把项目的 war 放到 Tomcat 里运行 这里我们以 CentOS 7 为例,进行 Tomcat 配置和部署 下载 Tomcat9,并解压 123wget http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-9/v9.0.22/bin/apache-tomcat-9.0.22.tar.gztar -zxvf apache-tomcat-9.0.22.tar.gz 将项目打成 WAR 包放在 Tomcat 的 webapps 目录下 在 /conf 目录下找到 server.xml 的文件 在Host标签里边添加 <Context path="" docBase="myproject" reloadable="true" /> 123456<Host name=\"localhost\" appBase=\"webapps\" unpackWARs=\"true\" autoDeploy=\"true\"> <Valve className=\"org.apache.catalina.valves.AccessLogValve\" directory=\"logs\" prefix=\"localhost_access_log\" suffix=\".txt\" pattern=\"%h %l %u %t \"%r\" %s %b\" /> <Context path=\"\" docBase=\"myproject\" reloadable=\"true\" /> </Host> Context 标签内容,注意 path 填空,docBase 为项目名称 启动 Tomcat,sh bin/startup.sh 再次访问即可携带项目名称或不带都可以访问到项目。如 localhost:8080/ 或 localhost:8080/myproject 参考来源:Tomcat9部署WAR包访问不带项目名的方式 - h363659487的博客 - CSDN博客","categories":[],"tags":[]},{"title":"一文学会 SpringBoot gzip 压缩传输(请求/响应)","slug":"springboot-http-gzip-compress","date":"2020-01-13T15:36:06.000Z","updated":"2021-10-23T14:22:52.649Z","comments":true,"path":"2020/01/13/springboot-http-gzip-compress/","link":"","permalink":"http://www.frankfeekr.cn/2020/01/13/springboot-http-gzip-compress/","excerpt":"","text":"前言 经常我们都会与服务端进行大数据量的文本传输,例如 JSON 就是常见的一种格式。通过 REST API 接口进行 GET 和 POST 请求,可能会有大量的文本格式数据提交、返回。然后对于文本,它有很高的压缩率,如果在 GET/POST 请求时候对文本进行压缩会节省大量的网络带宽,减少网络时延。 HTTP 协议在相应部分支持 Content-Encoding: gzip ,浏览器请求时带上 Accept-Encoding: gzip 即可,服务端对返回的 response body 进行压缩,并在 response 头带上 Content-Encoding: gzip,浏览器会自动解析。 然而 HTTP 没有压缩 request body 的设计,因为在客户端发起请求时并不知道服务器是否支持压缩。因此没法通过 HTTP 协议来解决,只能在服务端做一些过滤器进行判断,人为约束。压缩和解压在提升网络带宽的同时,会带来 CPU 资源的损耗。 本文将手把手带你实现 SpringBoot 项目中,请求时和响应时对 body 文本进行 gzip 压缩,减小网络时延,提升传输效率。 一、请求压缩(request compress) 1. SpringBoot 整合 gzip 考虑到通用性,仿效 response 的 header Content-Encoding: gzip 方式。 客户端把压缩过的 json 作为 post-body 传输,然后增加一个 request header: Content-Encoding: gzip 来告诉服务器端是压缩的格式。 服务端增加一个 Filter,对 request 头进行检查,如果有 Content-Encoding 则解压缩后继续。这样不影响现有程序。 (1)Springboot 添加请求过滤器 增加 2 个类: ContentEncodingFilter.java 1234567891011121314151617181920212223242526272829303132package cn.frankfeekr.sample.controller;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Service;import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@Servicepublic class ContentEncodingFilter extends OncePerRequestFilter { Logger logger = LoggerFactory.getLogger(ContentEncodingFilter.class); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String conentEncoding = request.getHeader(\"Content-Encoding\"); if (conentEncoding != null && (\"gzip\".equalsIgnoreCase(conentEncoding) || \"deflate\".equalsIgnoreCase(conentEncoding))) { logger.trace(\"Content-Encoding: {}\", conentEncoding); chain.doFilter(new GZIPRequestWrapper(request), response); return; } chain.doFilter(request, response); }} GZIPRequestWrapper.java 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105package cn.frankfeekr.sample.controller;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.servlet.ReadListener;import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import java.io.IOException;import java.io.InputStream;import java.io.UnsupportedEncodingException;import java.util.zip.DeflaterInputStream;import java.util.zip.GZIPInputStream;public class GZIPRequestWrapper extends HttpServletRequestWrapper { private final static Logger logger = LoggerFactory.getLogger(GZIPRequestWrapper.class); protected HttpServletRequest request; public GZIPRequestWrapper(HttpServletRequest request) { super(request); this.request = request; } @Override public ServletInputStream getInputStream() throws IOException { ServletInputStream sis = request.getInputStream(); InputStream is = null; String conentEncoding = request.getHeader(\"Content-Encoding\"); if (\"gzip\".equalsIgnoreCase(conentEncoding)) { is = new GZIPInputStream(sis); } else if (\"deflate\".equalsIgnoreCase(conentEncoding)) { is = new DeflaterInputStream(sis); } else { throw new UnsupportedEncodingException(conentEncoding + \" is not supported.\"); } final InputStream compressInputStream = is; return new ServletInputStream() { ReadListener readListener; @Override public int read() throws IOException { int b = compressInputStream.read(); if (b == -1 && readListener != null) { readListener.onAllDataRead(); } return b; } @Override public boolean isFinished() { try { return compressInputStream.available() == 0; } catch (IOException e) { logger.error(\"error\", e); if (readListener != null) { readListener.onError(e); } return false; } } @Override public boolean isReady() { try { return compressInputStream.available() > 0; } catch (IOException e) { logger.error(\"error\", e); if (readListener != null) { readListener.onError(e); } return false; } } @Override public void setReadListener(final ReadListener readListener) { this.readListener = readListener; sis.setReadListener(new ReadListener() { @Override public void onDataAvailable() throws IOException { logger.trace(\"onDataAvailable\"); if (readListener != null) { readListener.onDataAvailable(); } } @Override public void onAllDataRead() throws IOException { logger.trace(\"onAllDataRead\"); } @Override public void onError(Throwable throwable) { logger.error(\"onError\", throwable); if (readListener != null) { readListener.onError(throwable); } } }); } }; }} (2)SpringBoot Controller Demo 1234567891011121314151617package cn.frankfeekr.sample.controller;import com.alibaba.fastjson.JSON;import org.springframework.web.bind.annotation.*;import java.util.*;@ResponseBody@RestController@RequestMapping(produces = \"application/json;charset=UTF-8\")public class HelloController { @RequestMapping(value = \"/gzip\", method = RequestMethod.POST, consumes = \"application/json\") public String post(@RequestBody Map<String, String> request) { return request.toString(); }} 2. 客户端测试 (1)gzip body 方式请求 只要在 Headers 头域带上 gzip,即可告知服务器为通过 gzip 方式来提交 12345678# 生成一个 gzip 压缩的包echo '{\"type\": \"json\", \"length\": 2020, \"name\": \"Frank\"}' | gzip > body.gz# curl 命令模拟 POST gzip 压缩请求curl --location --request POST 'http://127.0.0.1:9090/gzip' \\--header 'Content-Type: application/json' \\--header 'Content-Encoding: gzip' \\--data-binary '@body.gz' 断点调试结果如下: (2)json body 方式请求 如果不需要进行压缩,则不带上 Content-Encoding: gzip 头域配置项即可。 12345# curl 命令模拟 gzipcurl --location --request POST 'http://127.0.0.1:9090/gzip' \\--header 'Content-Type: application/json' \\--header 'Content-Encoding: gzip' \\--data-raw '{\"type\": \"json\", \"length\": 2020, \"name\": \"Frank\"}' 上述可以发现如果通过 data-raw 方式请求,则必须要去掉 --header 'Content-Encoding: gzip',否则会出现 400 Bad Request 错误。现正确请求如下: 1234# curl 命令模拟 gzipcurl --location --request POST 'http://127.0.0.1:9090/gzip' \\--header 'Content-Type: application/json' \\--data-raw '{\"type\": \"json\", \"length\": 2020, \"name\": \"Frank\"}' 二、响应压缩(response compress) 1. SpringBoot 配置 response 策略 SpringBoot 默认是不开启 gzip 压缩的,需要我们手动开启,在配置文件中添加两行 1234server: compression: enabled: true mime-types: application/json,application/xml,text/html,text/plain,text/css,application/x-javascript 注意下上面配置中的 mime-types,在 SpringBoot 2.0+ 的版本中,默认值如下,所以一般我们不需要特意添加这个配置 1234/*** Comma-separated list of MIME types that should be compressed.*/private String[] mimeTypes = new String[]{\"text/html\", \"text/xml\", \"text/plain\", \"text/css\", \"text/javascript\", \"application/javascript\", \"application/json\", \"application/xml\"}; 2. 测试 写一个测试的demo 123456789101112131415161718192021package cn.frankfeekr.sample.controller;import com.alibaba.fastjson.JSON;import org.springframework.web.bind.annotation.*;import java.util.*;@ResponseBody@RestController@RequestMapping(produces = \"application/json;charset=UTF-8\")public class HelloController { @GetMapping(\"bigReq\") public String bigReqList() { List<String> result = new ArrayList<>(2048); for (int i = 0; i < 2048; i++) { result.add(UUID.randomUUID().toString()); } return JSON.toJSONString(result); }} 测试效果,可以明显发现请求 body 被压缩,时延也变小。 参考资料 传输压缩的JSON · gexiangdong/tutorial Wiki 191120-SpringBoot系列教程Web篇之开启GZIP数据压缩 | 一灰灰Blog","categories":[],"tags":[]},{"title":"IDEA 中配置 PMD 静态代码检查插件,提升你的代码质量","slug":"idea-pmd-plugin","date":"2019-12-16T12:48:15.000Z","updated":"2021-10-23T14:22:52.648Z","comments":true,"path":"2019/12/16/idea-pmd-plugin/","link":"","permalink":"http://www.frankfeekr.cn/2019/12/16/idea-pmd-plugin/","excerpt":"","text":"什么是 PMD PMD 官网: PMD - An extensible cross-language static code analyzer. (https://pmd.github.io/) PMD是一种开源分析 Java 代码错误的工具,它是一个静态代码检测工具(所谓的静态,就是在 Java 代码不运行时就可以进行检查)。通过 PMD 的一些规则,可以检查出代码中存在的问题,例如: 可能存在的 BUG 空的 try/catch/finally/switch 语句 无效代码 未使用的局部变量、参数、私有方法等 可选的代码 String / StringBuffer 的滥用 复杂的表达式 不必须的 if 语句 可以使用 while 循环完成的 for 循环 重复的代码 复制/粘贴代码意味着复制/粘贴 BUG 总而言之,降低潜在的代码风险,减少人工审核成本,提高编码质量。此外,你也可以自己自定义规则。 PMD 插件配置 1. 插件下载 IDEA 插件下载地址:PMDPlugin - Plugins | JetBrains(或是直接在插件市场搜索安装) 2. 选择从磁盘安装 3. 重启生效 4. 添加配置模板 示例模板: 12345678910111213141516171819<?xml version=\"1.0\" encoding=\"UTF-8\"?><ruleset xmlns=\"http://pmd.sourceforge.net/ruleset/2.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" name=\"\" xsi:schemaLocation=\"http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd\"> <description> This is the PMD additional ruleset for TEAMMATES production code. </description> <exclude-pattern>.*/test/java/.*</exclude-pattern> <exclude-pattern>.*/client/java/.*</exclude-pattern> <exclude-pattern>.*.html</exclude-pattern> <exclude-pattern>.*.jsp</exclude-pattern> <exclude-pattern>.*.js</exclude-pattern> <exclude-pattern>.*.xml</exclude-pattern> <rule ref=\"category/java/bestpractices.xml/AvoidPrintStackTrace\"/> <rule ref=\"category/java/bestpractices.xml/SystemPrintln\"/> <rule ref=\"category/java/design.xml/AvoidThrowingRawExceptionTypes\"/> <rule ref=\"category/java/design.xml/SignatureDeclareThrowsException\"/></ruleset> 5. PMD本地检查扫描 每次在代码上库,或者合并分之前就可以先进行一遍静态检查,降低提交到 GitLab 后代码检查异常的可能性,从而提升开发效率。 😎 自此,配置结束,可以开始你的 PMD 使用之旅了。","categories":[],"tags":[]},{"title":"Java 字符串分割 split 函数使用知多少","slug":"in-depth-java-split-function","date":"2019-12-11T15:17:52.000Z","updated":"2021-10-23T14:22:52.648Z","comments":true,"path":"2019/12/11/in-depth-java-split-function/","link":"","permalink":"http://www.frankfeekr.cn/2019/12/11/in-depth-java-split-function/","excerpt":"","text":"最近使用 split 遇到的问题 你是否常常在使用 Java 的字符串 split() 函数,例如字符串简单的分割。 我想大部分人都跟我一样进行简单的分割,也没有发现什么大问题,示例代码如下: 1234public static void main(String[] args) { String str = \"apiVersion|uuid|timestamp|userInfo|arg1|arg2|arg3|args\"; String[] arrs = str.split(\"\\\\|\");} 但是最近在进行字符串分割的时候,发现想要对下面的字符串进行分割的时候就出现问题了 1234public static void main(String[] args) { String str = \"6.0|ff129239||frank2019|1||3|\"; String[] arrs = str.split(\"\\\\|\");} 得到的 arrs 数组如下: 我们发现,字符串最后的一个 | 之后没有元素,结尾的空字符串被丢弃了。但实际上在我的业务场景空字符也代表一个占位符,需要分割成 8 个元素的数组。 查看了 split 的源码,发现其实它还有一个重载方法 1234public String[] split(String regex, int limit) { // ... ... return Pattern.compile(regex).split(this, limit);} 如果不想让结尾的空字符串被丢弃,可以这么实现: 12345public static void main(String[] args) { String str = \"6.0|ff129239||frank2019|1||3|\"; String[] arrs = str.split(\"\\\\|\",-1); System.out.println(args);} 执行效果如下 split(regex) 和 split(regex, limit) 使用知多少 split(regex)方法 regex:分割的字符串或者正则表达式,根据字符串中的分割符,进行拆分成字符串数组 split(regex, limit)方法 regex:分割的字符串或者正则表达式 limit:作用是控制模式应用的次数 (这里的模式理解成:从左往右分割次数即可) Limit<0:模式被应用尽可能多的次数 limit =0:表示模式应用尽可能多的次数,数组可以是任意长度,并且结尾空字符串将被丢弃。 limit>0:模式将会应用 limit-1 次数组长度不会超过 limit 举例说明: split("6.0|ff129239||frank2019|1||3|",0) 是切割默认模式等同于 split("6.0|ff129239||frank2019|1||3|") 结尾分割字符为空不进行进行分割 split("6.0|ff129239||frank2019|1||3|",-1),limit 参数小于 0 结尾分割字符为空也进行分割 split("6.0|ff129239||frank2019|1||3|",3),不管字符串有多少个符合分割的分隔符,只会从左到右分成长度为 3 的数组","categories":[],"tags":[]},{"title":"大学四年,如何选择自己的技术栈","slug":"how-to-learn-my-major","date":"2019-09-01T13:37:23.000Z","updated":"2021-10-23T14:22:52.648Z","comments":true,"path":"2019/09/01/how-to-learn-my-major/","link":"","permalink":"http://www.frankfeekr.cn/2019/09/01/how-to-learn-my-major/","excerpt":"","text":"引言 你们知道程序员最熟悉,最熟练,最常用的两个快捷键是哪两个吗? 没错,就是你现在心中所想的:ctrl+c 和 ctrl+v ,俗名为:复制和粘贴。对于大部分程序员来说:复制和粘贴就是他创造伟大产品的左膀和右臂。 不知道大家有没有看过这个梗,这个梗其实相当的现实,但是你又不得不说它是说到点子上了。我前段时间听到过这样一句话:天下代码不过一个抄字。 (基础)从网上抄写程序=¥1 (入门)知道哪部分程序能抄=¥100 (高手)知道抄前后需要怎样调整=¥1000 (精英)知道怎么才能让别人看不出来你抄过=¥10000 (创业)知道怎么在抄的情况下依然让所有人认可你独特的价值=¥100000 一、技术学习方法论 为什么要讲学习方法 在学习技术这条路上并不是一帆风顺,也一直在探索一条适合自己的学习方法。从一开始的技术小白,到现在还比较上道的老鸟,在这个过程中走了太多的弯路,想在这里和大家分享一些我的经历和学习方法。 对于立志成为一个技术达人,或者对于技术有执着追求的,学习一定是伴随着终生。 技术是科学、是工具、更是理论应用于实践的途径。技术学习是无止境的,人的精力也是有限的,我们不应该一味的追求新技术。基础很重要,特别是数据结构与算法、操作系统原理、计算机网络、Linux 基础,这些在如今应用层编码的过程中或许不会被过多的关注,但是计算机的基础决定了你能走多远。从某一个技术点的深度着手,同时不断扩展自己知识的广度。项目需要什么就用什么学什么,业余时间当然可以扩展一下自己感兴趣的技术栈。 我和技术有关的日子 1. 从懵懂到想做技术 从我的个人学习经历说起吧,学习的过程挺曲折的,但是回首过去发现这些经历的都将成为你未来的谈资。从进入大学开始,个人的对于摄像与平面有浓厚的兴趣,也花了整整三年的时间投身于学生工作(主要是平面设计、视频后期、微信公众号运营相关的),一度的规划是希望成为一个设计师或是视频后期工作者。也跟随很多摄制组拍摄过广告、做过平面设计物料、微信公众号媒体运管,我认为一个好的设计一定是给人带来美的享受,对于追求完美主义的我,深深的爱上了这个角色。 与此同时,本科作为软件工程专业出身的我,同时被各种计算机的学科熏陶着,自认为技术掌握的还不错。那个时候对于 Web 特别感兴趣,特别现在的人们打开计算机,大部分时间都是和你的浏览器相处。那个时候开始学习一些 HTML/CSS/JavaScript 相关的一些东西,可以自己做一些简单的静态网页,很快不久就写了一个非常简陋的个人主页。后来,接触了动态网页写过 JSP、ASP.NET、PHP 网页,但是那时候大部分还是前后台混合渲染的方式,开发效率倒是很高但是一旦想要从新设计前端界面,就会陷入重构的泥塘。后来,慢慢接触到了前后台分离的开发方式,知道了 Vue、React 相关的一些 MVVM 框架。 在 2015 年的学生工作中,马上迎来了学代会的换届选举,在过程中发现纸质选票效率低,通常中场投票和检票也耗费大量的时间。我算是一个善于发现问题的人,两天时间泡在图书馆中,搭建了一个学代会投票系统。在后来的投票过程中,我将原来的 30 分钟,缩短到了 5 分钟,并在大屏实时滚动显示投票柱状图。这个投票系统也得到了团委老师的一致认可,在后来的 4 年中一直使用了我的投票系统,并且保持了很好的稳定性和效果。在每一年的系统中,我也不断的升级投票系统,在这几年的升级过程中也记录了自己的成长轨迹。虽然毕业了,但口碑的也在各个学院中流传开了,也正在计划将它升级为一个平台,可以让所有注册到平台上的人都可以使用我的投票平台。 在设计师和技术研发人员的角色选择中,纠结过很长一段时间。会发现,设计师可以设计出很简洁、美观、善心悦目的作品,但是只有技术研发人员才能实实在在的为它赋能,真正的实现一个具有功能性、体验性的东西。他们都是艺术家,都在各自的领域增添异彩。我从来不认为开发的是功能,每一行代码都有它的灵魂,都是我的产品,更是我的尊严。在后来的日子里,我发现这两个角色对于逐渐开始并存,为什么不能够同时应用和学习呢?做设计中最懂技术的,做技术中最懂设计的。 同时在过去三年的学生工作中,也很好的锻炼了我的团队领导能力和技术分享能力。这些软实力,也许和很多的代码能力都无关,但是在后来的项目团队协作和管理中有着潜移默化的帮助。 ★★★ 大一大二的时间还很充裕,推荐大家多参加一些社团、学生会来提升自己。 2. 我的成长与技术路线 从大三的暑假到目前的研究生阶段,开始学习前端开发技、跨平台应用、后台开发技术、了解系统架构的一些东西、机器学习算法应用、产品设计思维等等相关的东西。我开始了技术广度的挖掘,很多东西不敢说是精通,但至少一定是入门到进阶的水平,当然目前还在技术深度不断学习的路上。 我很认可一句话,“人人都是产品经理”,“人人都是架构师”。我们不应该为自己打上太多的标签,“我是一个算法工程师”、“我是一个 XX 语言开发工程师”、“我是一个产品经理”。在学习这条路上,我们应该首先抓好自己的本职工作,也就是技术深度的学习(例如:开始学会重构自己的项目代码、开始看项目源码),从而在工作之余不断的拓展自己技术的广度和思维的广度。这里说一个题外话,谷歌宣扬的 80% 的时间用来工作,20% 的时间用来学习,大名鼎鼎的 Go 语言就是三位科学家利用 20% 时间里创造的。 思考了很久,我对自己未来的规划是希望成为架构师方向,在技术达到一定高度,并有很好的想法的时候会有创业做产品的打算。于是我还是回到了自己的老本行,后台开发的方向,当然不仅仅只会关注后台相关的技术点,也会在业余时间学习一些算法和前端技术栈。逐渐的,不断在不同维度中学习技术,不断成长。 总之在技术快跑的过程中,我们不仅仅要学会一门精通的手艺,同时也要有自己的辅助第二技术或是第三技术。要学会用后台思维写前端,算法思维写后台,产品思维写算法。我们做的不仅仅是功能,要让每一行代码都具备灵魂。 个人对于产品是非常热爱的,这几年互联网的高速发展,整个中国发生了翻天覆地的变化。国民的衣食住行都发生了巨变,吃饭——美团、点评、饿了么,买衣服——淘宝、唯品会、京东,出行——哈啰单车、各种共享电动车、滴滴出行、曹操专车,住房——自如、贝壳找房、58 同城,还有改变生活方式的微信和支付宝。他们都将逐步成为我们生活中的水电煤,逐步融入人们的血液。在技术水平和思维格局到达已经层次,十分希望在未来的日子,在某个对的时刻能够创造一个影响一个时代的产品。 (图:技术路线与管理路线) 如何学习与技术快跑 上文通过自身经验举例,说了一些自己的技术成长路线。下面来谈谈技术学习的一些方法论,主要围绕以下四个部分进行讨论: 技术大厦 极限编程 学习总结 技术分享 1. 技术大厦 在学习技术前,我不知道大家是怎样的一种心态。我喜欢把技术比作一栋大厦,大部分人都会从大门进入,一步一步往上走,技术也在一点点的提高,但很多人大部分时候都会质疑自己学来到底有什么用,或是会遇到一些技术瓶颈,或是很多时候迷茫了也不知道该学习哪些东西。 技术的成长我认为分成三个层次:我不知道我不知道,我知道我不知道,我知道我知道。从技术的小白,到技术小达人,再到技术牛人。 在这栋技术大厦中,在技术小白阶段,就是按部就班的往上爬,但是当你技术学习到一定程度的时候,你必须开始了解整栋大厦的框架结构。所以在这里,必须要开始转向系统架构。这里摘抄一段《许式伟:架构设计的宏观视角》中的话,也正是我们想要表达的思想。 如同造房子有建筑工人(负责搬砖)和建筑师(负责架构设计)一样,软件系统的开发过程同样需要有程序员(负责搬“砖”)和架构师(负责架构设计)。作为架构师,我们需要的第一个能力是宏观的全局掌控能力。 如果把应用程序比作一座大厦,那么我们作为大厦的架构师,需要把大厦的结构搭建好,让程序员可以把砖填充进去,我们都知道,一个大厦的结构建得是否稳固,与地基密不可分。 所以,我们首先就需要从大厦的地基开始,熟悉这座大厦。毕竟,你对所依赖的基础架构了解得越全面,做业务架构设计就会越发从容。 技术想要成长,必须首先在思想上成长,提升自己的技术思想高度。一个不想成为建筑师的包工头不是一个好的架构师,在学习这条往上的路上,必须要学会用宏观视角来解决问题。在技术成长的过程中,有时候不需要太多的注意某个细节是怎么实现的,而是要有技术判断力,技术是否可行,技术如何选型。 这里列一些大家在大学中会学习到的课程和技能: C / C++ 第一门语言 Web 网页编程 HTML/CSS/JavaScript 多媒体技术 Java 计算机基础知识 数据结构与算法 计算机网络 操作系统 计算机组成原理 软件工程 信息安全与密码学 … 最后推荐一些阅读脚本 一张后台开发技能图谱分享给大家 整理了一些阅读清单与学习课程⎡计算机学习的“武林秘籍”⎦,欢迎大家学习 2. 极限编程 接着,来聊聊极限编程。极限,就一个字"快"! 身边也经常有很多人,常常在每次项目中,都没来得及系统学习技术栈就要开始项目开发了;大部分的时间都在复制粘贴,面向搜索引擎编程,找不到自己的技术核心点。 包括自己在内,很多时候也都希望当自己掌握了完整的一套技术栈以后再开始你的项目编码。经历过很多项目之后,我会发现其实这并不是正确的做法。同样的,这也遵循二八定律,在技术中我们只要花少量的时间学会 20% 的技术,就可以开始项目开发,剩下的 80% 大部分都应该在项目实践中去学习的。 在很多的项目中,根本来不及有太多的时间让我们学习。很可能一个新的技术,通过短短 5-7 天的学习,就必须要应用到项目中去,很难有太多的时间让你系统学习。特别在互联网的应用场景中,时间就是金钱,一个好的想法,必须在最短的时间内上线。极限编程正是这样一种敏捷快速的开发方式,要求团队成员拥有很高的技术素养,在很短时间内就可以学会并且应用。 在这里,我们从个人的经验,分享我在极限编程学习过程中的路线: 先看技术效果 学会检索一些牛逼的项目,看看通过这个技术可以达到什么样的效果,判断是否满足自己的需求。 结果导向的学习,更加能提升我们的学习兴趣。 快速视频学习入门(10小时以内) 特别像我一样学习不太喜欢看书,感觉到很枯燥的同学,我推荐直接找视频快速入门,视频不宜太长,10小时左右(2.0x 速度看)。快速的了解技术的状况,掌握基本的核心技术,会的部分直接跳过。 这里推荐一些好的学习网站:慕课网、极客时间、哔哩哔哩、极客学院、实验楼、学堂在线。 开源小项目 在视频学习入门之后我们可能还不具备一个项目的编码能力,这时候我们可以找一些开源项目来跑一跑(学会使用 GitHub),项目不宜太复杂,一个简单的小项目即可。 配合文档同步学习 技术文档不像小说,一定更不要从头到尾的读,选择有必要的部分尝试通读,但是大部分的文档应该作为字典一样的查询工具书。 开始投入项目生产 终于可以正式开始项目实践了,选择好自己的项目框架,配合文档、视频,不会的部分当然还得靠搜索引擎。 项目复盘与重构 重新审视自己的代码,有条件的应该让大神给你看看,指点一下代码,以便在未来升级改进。 技术进阶,重新回到书本(或是项目文档) 无论你在搜索引擎找到的答案,都是比较碎片化的知识。当你想要深入到底层或是原理相关的部分,你应该选择一本大众认可的书籍进行深入阅读,大部分的书才是一个最系统和深入的学习。当然要看技术点的感兴趣和必要成分,不是所有的技术都需要深入了解,但是在自己的技术领域应该还是需要不断精通和升华。 3. 学习总结 在技术学习的过程中,我们可能会遇到很多的技术难点,还会遇到很多碎片化的检索工作,很多技术点我们肯定是不可能都记住甚至背下来。经常,我们会遇到一些常见遇到一些常见的东西,每次见到我们都要重新谷歌检索一遍,浪费了大量的时间。所以,学习总结是非常有必要。 都说好记性不如烂笔头,定期的学习和整理必然对学习巩固有所帮助,这里通过索引的方式对技术做一个系统分类,方便随时巩固和学习,当然还有面试。在学习这条路上难免会有很多盲点和学不完的知识。有道无术,术尚可求,掌握好思维能力才能应对千变万化的技术。不要把大脑当成硬盘,也不要做高速运转的 CPU,而修行自己的大脑成为一个搜索引擎,学会分析解决问题。 这里盘点一些学习总结的方式: 技术博客 & 微信订阅号 首先技术博客是一个非常好的途径,可以通过掘金、CSDN、知乎专栏、知识星球等等途径编写自己的技术博客。 稍微极客一点的同学可以自己搭建一个自己的博客,例如:WordPress、Hexo、Hugo 这样的博客。(推荐使用 Hexo + GitHub + Typora + MD 来写自己的技术博客)。欢迎来我的博客逛逛:https://www.frankfeekr.cn/ 在技术博客的编写过程中,首先对你知识的回顾是很有帮助的,其次也将成为你的个人检索宝库,把自己常见的一些问题都记录在你的博客中,再下一次遇到的时候就可以信手捏来。 很多著名的技术书籍早期都是通过博客来积累自己的素材,当你的技术和文字达到一定功底,我想出书也是非常自然而然的事情。 此外还可以申请个人的微信订阅号来编写自己的技术博客,这也是一个很好的传播途径,慢慢的积累自己的粉丝哈哈。欢迎关注我的微信公众号,“全栈开发社区”。 GitHub 开源项目 GitHub 不仅仅可以分享代码性质的项目,当然在上面你还可以开源自己博客项目,你可以通过 .md 来记录自己的技术博客。 很多开源的书籍、文档翻译项目都可以通过 GitHub 来托管 通过 GitHub 你还获取到了版本控制的文本和图床,这相比自己搭建服务器来构建博客更加的方便和安全,并且永不丢失。 欢迎看看我在 GitHub 上的技术项目:https://github.com/frank-lam/fullstack-tutorial 其他 此外你还可以通过诸如:为知笔记、有道云笔记等方式进行学习总结 最后建议大家不要再用你的 txt 或是 word 来记录你的笔记 4. 分享技术 学习金字塔是美国缅因州的国家训练实验室研究成果,它用数字形式形象显示了:采用不同的学习方式,学习者在两周以后还能记住内容(平均学习保持率)的多少。 在金字塔基座位置的学习方式,是“教别人”或者“马上应用”,可以记住 90% 的学习内容。通过学习金字塔,我们会发现学习最好的方式应该是主动学习并且学会分享教授别人如何学习。在教授别人的过程中,我们在输出知识,也在不断的输入别人的疑惑,看似单向学习的过程中实际上也是双向的学习,不断的巩固自己的知识。 那么在技术学习中呢?没错我们不仅要学会倾听别人的技术,很多时候我们要学会分享技术。要知道一个好的技术管理者,必须要学会根据团队成员的不同情况,制定不一样的培养方案,这也是很重要的一个能力。通过分享你的技术,同时你也会不断的巩固自己的逻辑能力和技术表达能力。 (图:学习金字塔,图片来源网络) 最后几句话 技术学习的路是很长的,需要我们掌握好高效的学习方式,终身学习。 你要相信自己,可以通过 20 小时学会任何技能,也能通过 10000 小时成为某个领域的资深专家。在立足于本身技术深度学习的同时,也记得拓展自己思维的广度。 只有一个好的技术学习方法 ,才能让你在如今不断动荡的互联网江湖中,屹立不倒、处变不惊。 二、计算机学科技术栈 技能云 为了更好的学习技术,找到自己的方向和目标。我们应该先了解,计算机学科都有哪些技术栈。 技能树","categories":[],"tags":[]},{"title":"IntelliJ IDEA 2019 开发工具和谐","slug":"intellij-idea-2019-crack","date":"2019-08-31T07:48:05.000Z","updated":"2021-10-23T14:22:52.649Z","comments":true,"path":"2019/08/31/intellij-idea-2019-crack/","link":"","permalink":"http://www.frankfeekr.cn/2019/08/31/intellij-idea-2019-crack/","excerpt":"","text":"在一个下着雨,美妙的下午,撸着代码。突然,IDEA 提示到期,瞬间让人泪奔。 查找了很多资料,终于完美的和谐了 IDEA。 所谓永久,就是让你撸码到头秃,撸码到虚脱送 ICU。 环境 win10 IntelliJ IDEA 2019.1.3 x64 测试时间:2019/08/31 和谐步骤 1. 修改 Hosts 目录 在 windows 的 hosts 目录:C:\\Windows\\System32\\drivers\\etc 追加如下记录 10.0.0.0 https://account.jetbrains.com:443 注意:不需要 0.0.0.0 www.jetbrains.com 整体写入文件, 不然会影响某些功能 2. 刷新 DSN 缓存 打开 cmd 终端,输入 ipconfig/flushdns,然后回车刷新 DNS 缓存 3. 获取激活码 打开 http://idea.lanyus.com 点击“获得激活码”,并复制 4. 马上激活 打开激活界面,选择 Activation code,复制上面的激活码,点击 active,即可激活 至此,已经完成了全部步骤,可以开始撸代码了。","categories":[],"tags":[{"name":"IDEA","slug":"IDEA","permalink":"http://www.frankfeekr.cn/tags/IDEA/"}]},{"title":"CentOS 环境下 Open JDK 替换为 Oracle JDK","slug":"centos-openjdk-2-oraclejdk","date":"2019-08-29T03:34:00.000Z","updated":"2021-10-23T14:22:52.647Z","comments":true,"path":"2019/08/29/centos-openjdk-2-oraclejdk/","link":"","permalink":"http://www.frankfeekr.cn/2019/08/29/centos-openjdk-2-oraclejdk/","excerpt":"","text":"环境 CentOS 7.4 1234[root@localhost ~]# java -versionopenjdk version \"1.8.0_212\"OpenJDK Runtime Environment (build 1.8.0_212-b04)OpenJDK 64-Bit Server VM (build 25.212-b04, mixed mode 下载 Oracle JDK 首先从 Oracle 网站 下载所需的JDK。 替换为 Oracle JDK 1234567891011121314151617181920212223[root@localhost ~]# rpm -ivh jdk-8u144-linux-x64.rpm准备中... ################################# [100%]正在升级/安装... 1:jdk1.8.0_144-2000:1.8.0_144-fcs ################################# [100%]Unpacking JAR files... tools.jar... plugin.jar... javaws.jar... deploy.jar... rt.jar... jsse.jar... charsets.jar... localedata.jar...[root@localhost ~]# update-alternatives --config java共有 2 个提供“java”的程序。 选项 命令-----------------------------------------------*+ 1 java-1.8.0-openjdk.x86_64 (/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.212.b04-0.el7_6.x86_64/jre/bin/java) 2 /usr/java/jdk1.8.0_144/jre/bin/java按 Enter 保留当前选项[+],或者键入选项编号:2 验证 1234[root@localhost ~]# java -versionjava version \"1.8.0_144\"Java(TM) SE Runtime Environment (build 1.8.0_144-b01)Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode) 扩展 rpm:rpm命令_Linux rpm 命令用法详解:RPM软件包的管理工具 update-alternatives:【Linux】使用update-alternatives命令进行版本的切换","categories":[],"tags":[{"name":"Linux","slug":"Linux","permalink":"http://www.frankfeekr.cn/tags/Linux/"}]},{"title":"玩转时序数据库 InfluxDB(一)初体验","slug":"influxdb-tutorial-start","date":"2019-07-24T02:31:37.000Z","updated":"2021-10-23T14:22:52.648Z","comments":true,"path":"2019/07/24/influxdb-tutorial-start/","link":"","permalink":"http://www.frankfeekr.cn/2019/07/24/influxdb-tutorial-start/","excerpt":"","text":"申明:本文为学习过程中的笔记,在现有的资料基础之上做了学习和整理,非完全原创的。引用资料见文末,参考资料。 官方文档:InfluxDB 1.7 documentation | InfluxData Documentation 一、启程 1. 介绍 InfluxDB 是使用 GO 编写的基于时间序列的数据库,用于存储大量带有时间戳的数据,报错 DevOps 监控,日志数据,应用程序的指标、数据分析数据等等。通过 InfluxDB 自动保存数据,你不需要删除和清理,只需要定义一段时间 DB 会帮你自动清理。 InfluxDB 提供三种操作方式: 客户端命令行方式 HTTP API 接口 各语言 API 库 2. 关键概念 基本概念 InfluxDB 和传统数据库(如:MySQL)的一些区别 InfluxDB 传统数据库中的概念 database 数据库 measurement 数据库中的表 points 表里面的一行数据 特有概念 tag–标签,在 InfluxDB 中,tag 是一个非常重要的部分,表名+tag 一起作为数据库的索引,是“key-value”的形式 field–数据,field 主要是用来存放数据的部分,也是“key-value”的形式 timestamp–时间戳,作为时序型数据库,时间戳是 InfluxDB 中最重要的部分,在插入数据时可以自己指定也可留空让系统指定 说明:在插入新数据时,tag、field 和 timestamp 之间用空格分隔 series–序列,所有在数据库中的数据,都需要通过图表来展示,而这个 series 表示这个表里面的数据,可以在图表上画成几条线。具体可以通过 SHOW SERIES FROM "表名" 进行查询 Retention policy–数据保留策略,可以定义数据保留的时长,每个数据库可以有多个数据保留策略,但只能有一个默认策略 Point–点,表示每个表里某个时刻的某个条件下的一个 field 的数据,因为体现在图表上就是一个点,于是将其称为 point。Point 由时间戳(time)、数据(field)、标签(tags)组成 Point 属性 传统数据库中的概念 time 每个数据记录时间,是数据库中的主索引 (会自动生成) fields 表中的列(没有索引的属性)也就是记录的值:温度, 湿度 tags 表中的索引:地区,海拔 3. 端口服务 8083:Web admin 管理服务的端口, http://localhost:8083 8086:HTTP API 的端口 8088:集群端口 (目前还不是很清楚, 配置在全局的 bind-address,默认不配置就是开启的) 二、安装 1. 基于 Linux CentOS 安装 12wget https://dl.influxdata.com/influxdb/releases/influxdb-0.13.0.x86_64.rpmsudo yum localinstall influxdb-0.13.0.x86_64.rpm 2. 基于 Docker 安装 1docker run -d -p 8083:8083 -p8086:8086 --expose 8090 --expose 8099 --name influxDbService influxdb -d:容器在后台运行 -p:将容器内端口映射到宿主机端口,格式为 宿主机端口:容器内端口;8083 是 influxdb 的 web 管理工具端口,8086 是 influxdb 的 HTTP API 端口 –expose:可以让容器接受外部传入的数据 –name:容器名称 此处 influxDbService 则是启动后的容器名 最后是镜像名称 influxdb,镜像名可以通过 docker images 查看; 通过 tag 区分启镜像版本 若不加 tag 则启动的是最新版本 latest 三、可视化客户端安装 客户端为绿色版,下载解压打开即可。下载地址:Releases · CymaticLabs/InfluxDBStudio 这里也提供 InfluxDB Studio 的使用说明,供大家参考:windows 下 influxDB 操作工具 InfluxDBStudio 四、客户端命令操作(基本语法) 1. 数据库操作 创建 CREATE DATABASE {NAME}; 12345678> create database frank> show databasesname: databasesname----_internaltelegraffrank 这时候我们发现数据库有一个表“_internal”,其实这个表是 influxdb 数据库的一些指标存储库。有点类似 mysql 数据库的 mysql 库。 删除 DROP DATABASE {NAME}; 1> drop database frank 使用 DROP {DB}; 12> use frankUsing database frank 2. 数据表和数据操作 建库的操作可以发现非常类似于 MySQL 下的操作。而在 InfluxDB 下没有细分的表的概念,InfluxDB 下的表在插入数据库的时候自动会创建。可以通过 show measurements 命令查看所有的表,这个类似于 MySQL 下的show tables 显示所有表 123456> show measurementsname: measurementsname----cputemperature 新建表(写数据) 标准格式,注意在写数据的时候如果不添加时间戳,系统会默认添加一个时间。InfluxDB 中没有显式的新建表的语句,只能通过 insert 数据的方式来建立新表。 语法格式 1insert <measurement>[,<tag-key>=<tag-value>...] <field-key>=<field-value>[,<field2-key>=<field2-value>...] [unix-nano-timestamp] 示例 123> INSERT cpu,host=serverA,region=us_west value=0.64> INSERT temperature,machine=unit42,type=assembly external=25,internal=37 1434067467000000000 删除表 123456> drop measurement disk_free> show measurementsname: measurements------------------nameweather 读数据 查询语句与 SQL 一样,不用过多的学习 查询数据 12345> SELECT \"host\", \"region\", \"value\" FROM \"cpu\"name: cputime host region value---- ---- ------ -----1563895618490964877 serverA us_west 0.64 每个表输出一行(支持 Go 语言的正则表达式、支持类似于 MySQL 中的 limit 语句) 12345678910> SELECT * FROM /.*/ LIMIT 1name: cputime external host internal machine region type value---- -------- ---- -------- ------- ------ ---- -----1563895618490964877 serverA us_west 0.64name: temperaturetime external host internal machine region type value---- -------- ---- -------- ------- ------ ---- -----1434067467000000000 25 37 unit42 assembly 修改和删除数据 InfluxDB 属于时序数据库,没有提供修改和删除数据的方法。 但是删除可以通过 InfluxDB 的数据保存策略(Retention Policies)来实现 update 更新语句没有,不过有 alter 命令,在 influxdb 中,删除操作用和更新基本不用到 。在针对数据保存策略方面,有一个特殊的删除方式,这个后面再提。 3. series 操作 series 表示这个表里面的数据,可以在图表上画成几条线,series 主要通过 tags 排列组合算出来。 我们可以查询表的 series,如下所示: 1234> show series from memkeymem,host=ResourcePool-0246-billing07mem,host=billing07 4. 用户操作 12345678# 显示用户SHOW USERS# 创建用户CREATE USER \"username\" WITH PASSWORD 'password'# 创建管理员权限的用户CREATE USER \"username\" WITH PASSWORD 'password' WITH ALL PRIVILEGES# 删除用户DROP USER \"username\" influxdb 的权限设置比较简单,只有读、写、ALL 几种。 五、HTTP API 操作 接口地址 接口路径 描述 /debug/pprof debug 排查问题使用 /debug/requests 使用这个请求监听最近是否有请求 /debug/vars 查询 influxdb 收集到静态信息 /ping 检测 influxdb 状态 /query 查询数据接口(同时可以创建 ku) /write 写入数据接口(一个已存在数据库) 状态码 2xx:服务请求正常 4xx:代表请求语法有问题 5xx:服务端出问题,导致超时等故障 1. 创建数据库 12345678910curl -i -XPOST http://localhost:8086/query --data-urlencode \"q=CREATE DATABASE mydb\"HTTP/1.1 200 OKContent-Type: application/jsonRequest-Id: 5edd88a8-ef90-11e8-83cd-a0999b0f94e3X-Influxdb-Build: OSSX-Influxdb-Version: 1.7.0~n201811230800X-Request-Id: 5edd88a8-ef90-11e8-83cd-a0999b0f94e3Date: Sat, 24 Nov 2018 02:26:38 GMTTransfer-Encoding: chunked{\"results\":[{\"statement_id\":0}]} 2. 写入数据 12345678curl -i -XPOST 'http://localhost:8086/write?db=mydb' --data-binary 'cpu_load_short,host=server01,region=us-west value=0.65 1434055564000000000'HTTP/1.1 204 No ContentContent-Type: application/jsonRequest-Id: 1ae386c4-ef91-11e8-83d8-a0999b0f94e3X-Influxdb-Build: OSSX-Influxdb-Version: 1.7.0~n201811230800X-Request-Id: 1ae386c4-ef91-11e8-83d8-a0999b0f94e3Date: Sat, 24 Nov 2018 02:31:53 GMT 3. 写入多个数据点 1curl -i -XPOST 'http://localhost:8086/write?db=mydb' --data-binary 'cpu_load_short,host=server02 value=0.67 cpu_load_short,host=server02,region=us-west value=0.55 1422568543702900257 cpu_load_short,direction=in,host=server01,region=us-west value=2.0 1422568543702900257’ HTTP/1.1 204 No Content Content-Type: application/json Request-Id: 574f52a0-ef91-11e8-83d9-a0999b0f94e3 X-Influxdb-Build: OSS X-Influxdb-Version: 1.7.0~n201811230800 X-Request-Id: 574f52a0-ef91-11e8-83d9-a0999b0f94e3 Date: Sat, 24 Nov 2018 02:33:34 GMT 4. 从文件导入数据库 从文件导入时候建议不要超过 5000 条,如果超过请对文件进行切割,因为 http api 的接口 5s 会超时,请求数据过多会导致数据无法确认是否成功。 文件 cpu_data.txt 内容如下: 1234cpu_load_short,host=server02 value=111cpu_load_short,host=server02,region=us-west value=0.222 1543027130702900257cpu_load_short,direction=in,host=server01,region=us-west value=111.222 1543027129702900257curl -i -XPOST 'http://localhost:8086/write?db=mydb' --data-binary @cpu_data.txt HTTP/1.1 204 No Content Content-Type: application/json Request-Id: 4b2ed710-ef92-11e8-83e3-a0999b0f94e3 X-Influxdb-Build: OSS X-Influxdb-Version: 1.7.0~n201811230800 X-Request-Id: 4b2ed710-ef92-11e8-83e3-a0999b0f94e3 Date: Sat, 24 Nov 2018 02:40:24 GMT 六、数据保存策略(Retention Policies) InfluxDB 每秒可以处理成千上万条数据,要将这些数据全部保存下来会占用大量的存储空间,有时我们可能并不需要将所有历史数据进行存储。InfluxDB 没有提供直接删除 Points 的方法,但是它提供了 Retention Policies,用来让我们自定义数据的保留时间。 1. 查看 1SHOW RETENTION POLICIES ON \"testDB\" 2. 创建 1CREATE RETENTION POLICY \"rp_name\" ON \"db_name\" DURATION 30d REPLICATION 1 DEFAULT 其中: rp_name:策略名 db_name:具体的数据库名 30d:保存 30 天,30 天之前的数据将被删除 它具有各种时间参数,比如:h(小时),w(星期) REPLICATION 1:副本个数,这里填 1 就可以了 DEFAULT 设为默认的策略 3. 修改 1ALTER RETENTION POLICY \"rp_name\" ON db_name\" DURATION 3w DEFAULT 4. 删除 1DROP RETENTION POLICY \"rp_name\" ON \"db_name\" 七、常用函数 InfluxDB 提供了很多的有用的函数,这里列举了常用的三个维度函数,Use InfluxQL functions to aggregate, select, and transform data. Aggregations Selectors Transformations COUNT() BOTTOM() CEILING() DISTINCT() FIRST() DERIVATIVE() INTEGRAL() LAST() DIFFERENCE() MEAN() MAX() ELAPSED() MEDIAN() MIN() FLOOR() SPREAD() PERCENTILE() HISTOGRAM() SUM() TOP() MOVING_AVERAGE() NON_NEGATIVE_DERIVATIVE() STDDEV() 1. 聚合类函数 1)COUNT() 函数 返回一个(field)字段中的非空值的数量。 语法: 1SELECT COUNT(<field_key>) FROM <measurement_name> [WHERE <stuff>] [GROUP BY <stuff>] 示例: 12345> SELECT COUNT(water_level) FROM h2o_feetname: h2o_feet--------------time count1970-01-01T00:00:00Z 15258 说明 water_level 这个字段在 h2o_feet 表中共有 15258 条数据。 注意:InfluxDB 中的函数如果没有指定时间的话,会默认以 epoch 0 (1970-01-01T00:00:00Z) 作为时间。 可以在 where 中加入时间条件,如下: 12345678910111213> SELECT COUNT(water_level) FROM h2o_feet WHERE time >= '2015-08-18T00:00:00Z' AND time < '2015-09-18T17:00:00Z' GROUP BY time(4d)name: h2o_feet--------------time count2015-08-17T00:00:00Z 14402015-08-21T00:00:00Z 19202015-08-25T00:00:00Z 19202015-08-29T00:00:00Z 19202015-09-02T00:00:00Z 19152015-09-06T00:00:00Z 19202015-09-10T00:00:00Z 19202015-09-14T00:00:00Z 19202015-09-18T00:00:00Z 335 2)DISTINCT() 函数 返回一个字段(field)的唯一值。 语法: 1SELECT DISTINCT(<field_key>) FROM <measurement_name> [WHERE <stuff>] [GROUP BY <stuff>] 使用示例 12345678> SELECT DISTINCT(\"level description\") FROM h2o_feetname: h2o_feet--------------time distinct1970-01-01T00:00:00Z between 6 and 9 feet1970-01-01T00:00:00Z below 3 feet1970-01-01T00:00:00Z between 3 and 6 feet1970-01-01T00:00:00Z at or greater than 9 feet 这个例子显示 level description 这个字段共有四个值,然后将其显示了出来,时间为默认时间。 3)MEAN() 函数 返回一个字段(field)中的值的算术平均值(平均值)。字段类型必须是长整型或 float64。 语法格式: 1SELECT MEAN(<field_key>) FROM <measurement_name> [WHERE <stuff>] [GROUP BY <stuff>] 使用示例 12345> SELECT MEAN(water_level) FROM h2o_feetname: h2o_feet--------------time mean1970-01-01T00:00:00Z 4.286791371454075 说明 water_level 字段的平均值为4.286791371454075,时间为默认时间,当然,你也可以加入 where 条件。 4)MEDIAN() 函数 从单个字段(field)中的排序值返回中间值(中位数)。字段值的类型必须是长整型或 float64 格式。 语法: 1SELECT MEDIAN(<field_key>) FROM <measurement_name> [WHERE <stuff>] [GROUP BY <stuff>] 使用示例 12345> SELECT MEDIAN(water_level) from h2o_feetname: h2o_feet--------------time median1970-01-01T00:00:00Z 4.124 说明表中 water_level 字段的中位数是 4.124 5)SPREAD() 函数 返回字段的最小值和最大值之间的差值。数据的类型必须是长整型或 float64。 语法: 1SELECT SPREAD(<field_key>) FROM <measurement_name> [WHERE <stuff>] [GROUP BY <stuff>] 使用示例 12345> SELECT SPREAD(water_level) FROM h2o_feetname: h2o_feet--------------time spread1970-01-01T00:00:00Z 10.574 6)SUM() 函数 返回一个字段中的所有值的和。字段的类型必须是长整型或 float64。 语法: 1SELECT SUM(<field_key>) FROM <measurement_name> [WHERE <stuff>] [GROUP BY <stuff>] 使用示例: 12345> SELECT SUM(water_level) FROM h2o_feetname: h2o_feet--------------time sum1970-01-01T00:00:00Z 67777.66900000002 此语句计算出了 h2o_feet 表中 所有 water_level 字段的和。 2. 选择类函数 1)TOP() 函数 作用:返回一个字段中最大的 N 个值,字段类型必须是长整型或 float64 类型。 语法: 1SELECT TOP( <field_key>[,<tag_key(s)>],<N> )[,<tag_key(s)>|<field_key(s)>] [INTO_clause] FROM_clause [WHERE_clause] [GROUP_BY_clause] [ORDER_BY_clause] [LIMIT_clause] [OFFSET_clause] [SLIMIT_clause] [SOFFSET_clause] 使用示例 12345678> SELECT TOP(\"water_level\",3) FROM \"h2o_feet\"name: h2o_feettime top---- ---2015-08-29T07:18:00Z 9.9572015-08-29T07:24:00Z 9.9642015-08-29T07:30:00Z 9.954 这个例子返回表中 water_level 字段中最大的三个值。 2)BOTTOM() 函数 作用:返回一个字段中最小的 N 个值。字段类型必须是长整型或 float64 类型。 语法: 1SELECT BOTTOM(<field_key>[,<tag_keys>],<N>)[,<tag_keys>] FROM <measurement_name> [WHERE <stuff>] [GROUP BY <stuff>] 使用示例 1234567> SELECT BOTTOM(water_level,3) FROM h2o_feetname: h2o_feet--------------time bottom2015-08-29T14:30:00Z -0.612015-08-29T14:36:00Z -0.5912015-08-30T15:18:00Z -0.594 这个例子返回表中 water_level 字段中最小的三个值。 也可将关联 tag 放在一起查询,但如果 tag 值少于 N 的值,则返回的值的个数只会取 tag 中字段值少的那个。 如下所示: 123456> SELECT BOTTOM(water_level,location,3) FROM h2o_feetname: h2o_feet--------------time bottom location2015-08-29T10:36:00Z -0.243 santa_monica2015-08-29T14:30:00Z -0.61 coyote_creek 语句取最小的三个值,然而结果只返回了 2 个值,因为 location 这个 tag 只有 两个取值。 3)FIRST() 函数 作用:返回一个字段中最老的取值。 语法: 1SELECT FIRST(<field_key>)[,<tag_key(s)>] FROM <measurement_name> [WHERE <stuff>] [GROUP BY <stuff>] 示例: 12345> SELECT FIRST(water_level) FROM h2o_feet WHERE location = 'santa_monica'name: h2o_feet--------------time first2015-08-18T00:00:00Z 2.064 这个语句返回了 在 location 为 santa_monica 条件下,最旧的那个 water_level 字段的取值和时间。 4)LAST() 函数 作用:返回一个字段中最新的取值。 语法: 1SELECT LAST(<field_key>)[,<tag_key(s)>] FROM <measurement_name> [WHERE <stuff>] [GROUP BY <stuff>] 示例: 12345> SELECT LAST(water_level),location FROM h2o_feet WHERE time >= '2015-08-18T00:42:00Z' and time <= '2015-08-18T00:54:00Z'name: h2o_feet--------------time last location2015-08-18T00:54:00Z 6.982 coyote_creek 5)MAX() 函数 作用:返回一个字段中的最大值。该字段类型必须是长整型,float64,或布尔类型。 语法: 1SELECT MAX(<field_key>)[,<tag_key(s)>] FROM <measurement_name> [WHERE <stuff>] [GROUP BY <stuff>] 示例: 12345> SELECT MAX(water_level),location FROM h2o_feetname: h2o_feet--------------time max location2015-08-29T07:24:00Z 9.964 coyote_creek 6)MIN() 函数 作用:返回一个字段中的最小值。该字段类型必须是长整型,float64,或布尔类型。 语法: 1SELECT MIN(<field_key>)[,<tag_key(s)>] FROM <measurement_name> [WHERE <stuff>] [GROUP BY <stuff>] 示例: 12345> SELECT MIN(water_level),location FROM h2o_feetname: h2o_feet--------------time min location2015-08-29T14:30:00Z -0.61 coyote_creek 7)PERCENTILE() 函数 作用:返回排序值排位为 N 的百分值。字段的类型必须是长整型或 float64。 百分值是介于 100 到 0 之间的整数或浮点数,包括 100。 语法: 1SELECT PERCENTILE(<field_key>, <N>)[,<tag_key(s)>] FROM <measurement_name> [WHERE <stuff>] [GROUP BY <stuff>] 示例: 12345> SELECT PERCENTILE(water_level,5),location FROM h2o_feetname: h2o_feet--------------time percentile location2015-08-28T12:06:00Z 1.122 santa_monica 就是将 water_level 字段按照不同的 location 求百分比,然后取第五位数据。 3. 变换类函数 1)DERIVATIVE() 函数 作用:返回一个字段在一个 series 中的变化率。 InfluxDB 会计算按照时间进行排序的字段值之间的差异,并将这些结果转化为单位变化率。其中,单位可以指定,默认为 1s。 语法: 1SELECT DERIVATIVE(<field_key>, [<unit>]) FROM <measurement_name> [WHERE <stuff>] 其中,unit取值可以为以下几种: 123456u --microsecondss --secondsm --minutesh --hoursd --daysw --weeks DERIVATIVE() 函数还可以在 GROUP BY time() 的条件下与聚合函数嵌套使用,格式如下: 1SELECT DERIVATIVE(AGGREGATION_FUNCTION(<field_key>),[<unit>]) FROM <measurement_name> WHERE <stuff> GROUP BY time(<aggregation_interval>) 示例: 假设 location = santa_monica 条件下数据有以下几条: 123456789name: h2o_feet--------------time water_level2015-08-18T00:00:00Z 2.0642015-08-18T00:06:00Z 2.1162015-08-18T00:12:00Z 2.0282015-08-18T00:18:00Z 2.1262015-08-18T00:24:00Z 2.0412015-08-18T00:30:00Z 2.051 计算每一秒的变化率: 123456789> SELECT DERIVATIVE(water_level) FROM h2o_feet WHERE location = 'santa_monica' LIMIT 5name: h2o_feet--------------time derivative2015-08-18T00:06:00Z 0.000144444444444444572015-08-18T00:12:00Z -0.000244444444444444652015-08-18T00:18:00Z 0.00027222222222222182015-08-18T00:24:00Z -0.0002361111111111112015-08-18T00:30:00Z 2.777777777777842e-05 第一行数据的计算公式为(2.116 - 2.064) / (360s / 1s) 计算每六分钟的变化率 123456789> SELECT DERIVATIVE(water_level,6m) FROM h2o_feet WHERE location = 'santa_monica' LIMIT 5name: h2o_feet--------------time derivative2015-08-18T00:06:00Z 0.0520000000000000462015-08-18T00:12:00Z -0.088000000000000082015-08-18T00:18:00Z 0.097999999999999862015-08-18T00:24:00Z -0.084999999999999962015-08-18T00:30:00Z 0.010000000000000231 第一行数据的计算过程如下:(2.116 - 2.064) / (6m / 6m) 计算每 12 分钟的变化率: 123456789> SELECT DERIVATIVE(water_level,12m) FROM h2o_feet WHERE location = 'santa_monica' LIMIT 5name: h2o_feet--------------time derivative2015-08-18T00:06:00Z 0.104000000000000092015-08-18T00:12:00Z -0.176000000000000162015-08-18T00:18:00Z 0.195999999999999732015-08-18T00:24:00Z -0.169999999999999932015-08-18T00:30:00Z 0.020000000000000462 第一行数据计算过程为:(2.116 - 2.064 / (6m / 12m) 计算每 12 分钟最大值的变化率 123456> SELECT DERIVATIVE(MAX(water_level)) FROM h2o_feet WHERE location = 'santa_monica' AND time >= '2015-08-18T00:00:00Z' AND time < '2015-08-18T00:36:00Z' GROUP BY time(12m)name: h2o_feet--------------time derivative2015-08-18T00:12:00Z 0.0099999999999997872015-08-18T00:24:00Z -0.07499999999999973 这个函数功能非常多,也非常复杂,更多对于此功能的详细解释请看官网:https://docs.influxdata.com/influxdb/v0.13/query_language/functions/#derivative 2)DIFFERENCE() 函数 作用:返回一个字段中连续的时间值之间的差异。字段类型必须是长整型或 float64。 最基本的语法: 1SELECT DIFFERENCE(<field_key>) FROM <measurement_name> [WHERE <stuff>] 与 GROUP BY time() 以及其他嵌套函数一起使用的语法格式: 1SELECT DIFFERENCE(<function>(<field_key>)) FROM <measurement_name> WHERE <stuff> GROUP BY time(<time_interval>) 其中,函数可以包含以下几个: 1COUNT(), MEAN(), MEDIAN(), SUM(), FIRST(), LAST(), MIN(), MAX(), 和 PERCENTILE() 使用示例 例子中使用的源数据如下所示: 1234567891011> SELECT water_level FROM h2o_feet WHERE location='santa_monica' AND time >= '2015-08-18T00:00:00Z' and time <= '2015-08-18T00:36:00Z'name: h2o_feet--------------time water_level2015-08-18T00:00:00Z 2.0642015-08-18T00:06:00Z 2.1162015-08-18T00:12:00Z 2.0282015-08-18T00:18:00Z 2.1262015-08-18T00:24:00Z 2.0412015-08-18T00:30:00Z 2.0512015-08-18T00:36:00Z 2.067 计算water_level间的差异: 12345678910> SELECT DIFFERENCE(water_level) FROM h2o_feet WHERE location='santa_monica' AND time >= '2015-08-18T00:00:00Z' and time <= '2015-08-18T00:36:00Z'name: h2o_feet--------------time difference2015-08-18T00:06:00Z 0.0520000000000000462015-08-18T00:12:00Z -0.088000000000000082015-08-18T00:18:00Z 0.097999999999999862015-08-18T00:24:00Z -0.084999999999999962015-08-18T00:30:00Z 0.0100000000000002312015-08-18T00:36:00Z 0.016000000000000014 数据类型都为 float 类型。 3)ELAPSED() 函数 作用:返回一个字段在连续的时间间隔间的差异,间隔单位可选,默认为 1 纳秒。 单位可选项如下: Units Meaning ns nanoseconds (1 billionth of a second) u or µ microseconds (1 millionth of a second) ms milliseconds (1 thousandth of a second) s second m minute h hour d day w week 语法: 1SELECT ELAPSED(<field_key>, <unit>) FROM <measurement_name> [WHERE <stuff>] 示例: 计算 h2o_feet 字段在纳秒间隔下的差异。 12345678> SELECT ELAPSED(water_level) FROM h2o_feet WHERE location = 'santa_monica' AND time >= '2015-08-18T00:00:00Z' and time <= '2015-08-18T00:24:00Z'name: h2o_feet--------------time elapsed2015-08-18T00:06:00Z 3600000000002015-08-18T00:12:00Z 3600000000002015-08-18T00:18:00Z 3600000000002015-08-18T00:24:00Z 360000000000 在一分钟间隔下的差异率: 12345678> SELECT ELAPSED(water_level,1m) FROM h2o_feet WHERE location = 'santa_monica' AND time >= '2015-08-18T00:00:00Z' and time <= '2015-08-18T00:24:00Z'name: h2o_feet--------------time elapsed2015-08-18T00:06:00Z 62015-08-18T00:12:00Z 62015-08-18T00:18:00Z 62015-08-18T00:24:00Z 6 注意:如果设置的时间间隔比字段数据间的时间间隔更大时,则函数会返回 0,如下所示: 12345678> SELECT ELAPSED(water_level,1h) FROM h2o_feet WHERE location = 'santa_monica' AND time >= '2015-08-18T00:00:00Z' and time <= '2015-08-18T00:24:00Z'name: h2o_feet--------------time elapsed2015-08-18T00:06:00Z 02015-08-18T00:12:00Z 02015-08-18T00:18:00Z 02015-08-18T00:24:00Z 0 4)MOVING_AVERAGE() 函数 作用:返回一个连续字段值的移动平均值,字段类型必须是长整形或者 float64 类型。 语法: 基本语法 1SELECT MOVING_AVERAGE(<field_key>,<window>) FROM <measurement_name> [WHERE <stuff>] 与其他函数和 GROUP BY time() 语句一起使用时的语法 1SELECT MOVING_AVERAGE(<function>(<field_key>),<window>) FROM <measurement_name> WHERE <stuff> GROUP BY time(<time_interval>) 此函数可以和以下函数一起使用: 1COUNT(), MEAN(),MEDIAN(), SUM(), FIRST(), LAST(), MIN(), MAX(), and PERCENTILE(). 示例: 1234567891011> SELECT water_level FROM h2o_feet WHERE location = 'santa_monica' AND time >= '2015-08-18T00:00:00Z' and time <= '2015-08-18T00:36:00Z'name: h2o_feet--------------time water_level2015-08-18T00:00:00Z 2.0642015-08-18T00:06:00Z 2.1162015-08-18T00:12:00Z 2.0282015-08-18T00:18:00Z 2.1262015-08-18T00:24:00Z 2.0412015-08-18T00:30:00Z 2.0512015-08-18T00:36:00Z 2.067 5)NON_NEGATIVE_DERIVATIVE() 函数 作用:返回在一个 series 中的一个字段中值的变化的非负速率。 语法: 1SELECT NON_NEGATIVE_DERIVATIVE(<field_key>, [<unit>]) FROM <measurement_name> [WHERE <stuff>] 其中 unit 取值可以为以下几个: Valid time specifications for unit are: u microseconds s seconds m minutes h hours d days w weeks 与聚合类函数放在一起使用时的语法如下所示: 1SELECT NON_NEGATIVE_DERIVATIVE(AGGREGATION_FUNCTION(<field_key>),[<unit>]) FROM <measurement_name> WHERE <stuff> GROUP BY time(<aggregation_interval>) 此函数示例请参阅:DERIVATIVE()``函数 6)STDDEV() 函数 作用:返回一个字段中的值的标准偏差。值的类型必须是长整型或 float64 类型。 语法: 1SELECT STDDEV(<field_key>) FROM <measurement_name> [WHERE <stuff>] [GROUP BY <stuff>] 示例: 12345> SELECT STDDEV(water_level) FROM h2o_feetname: h2o_feet--------------time stddev1970-01-01T00:00:00Z 2.279144584196145 示例 2: 12345678910111213141516171819202122> SELECT STDDEV(water_level) FROM h2o_feet WHERE time >= '2015-08-18T00:00:00Z' and time < '2015-09-18T12:06:00Z' GROUP BY time(1w), locationname: h2o_feettags: location = coyote_creektime stddev---- ------2015-08-13T00:00:00Z 2.24372630801939852015-08-20T00:00:00Z 2.1212761501447192015-08-27T00:00:00Z 3.04161221707862152015-09-03T00:00:00Z 2.53480650254352072015-09-10T00:00:00Z 2.5840039548826732015-09-17T00:00:00Z 2.2587514836274414name: h2o_feettags: location = santa_monicatime stddev---- ------2015-08-13T00:00:00Z 1.111563445875532015-08-20T00:00:00Z 1.09098492790823662015-08-27T00:00:00Z 1.98701161800969622015-09-03T00:00:00Z 1.35167784509020672015-09-10T00:00:00Z 1.49605738115005882015-09-17T00:00:00Z 1.075701669442093 八、连续查询 定义 InfluxDB 的连续查询是在数据库中自动定时启动的一组语句,语句中必须包含 SELECT关键词和GROUP BY time()关键词。 InfluxDB 会将查询结果放在指定的数据表中。 目的 使用连续查询是最优的降低采样率的方式,连续查询和存储策略搭配使用将会大大降低 InfluxDB 的系统占用量。 而且使用连续查询后,数据会存放到指定的数据表中,这样就为以后统计不同精度的数据提供了方便。 操作 只有管理员用户可以操作连续查询。 1)新建连续查询 新建连续查询的语法如下所示: 12345CREATE CONTINUOUS QUERY <cq_name> ON <database_name> [RESAMPLE [EVERY <interval>] [FOR <interval>]] BEGIN SELECT <function>(<stuff>)[,<function>(<stuff>)] INTO <different_measurement> FROM <current_measurement> [WHERE <stuff>] GROUP BY time(<interval>)[,<stuff>] END 查询部分被 CREATE CONTINUOUS QUERY […] BEGIN 和 END 所包含,主要的逻辑代码也是在这一部分。 使用示例: 12345678910111213> CREATE CONTINUOUS QUERY cq_30m ON telegraf BEGIN SELECT mean(used) INTO mem_used_30m FROM mem GROUP BY time(30m) END> SHOW CONTINUOUS QUERIESname: telegraf--------------name querycq_30m CREATE CONTINUOUS QUERY cq_30m ON telegraf BEGIN SELECT mean(used) INTO telegraf.\"default\".mem_used_30m FROM telegraf.\"default\".mem GROUP BY time(30m) ENDname: _internal---------------name query 示例在 telegraf 库中新建了一个名为 cq_30m 的连续查询,每三十分钟取一个 used 字段的平均值,加入 mem_used_30m 表中。使用的数据保留策略都是 default。 2)显示所有已存在的连续查询 查询所有连续查询可以使用如下语句: 123456789101112> SHOW CONTINUOUS QUERIESname: telegraf--------------name querycq_30m CREATE CONTINUOUS QUERY cq_30m ON telegraf BEGIN SELECT mean(used) INTO telegraf.\"default\".mem_used_30m FROM telegraf.\"default\".mem GROUP BY time(30m) ENDname: _internal---------------name query 可以看到其连续查询的名称以及 语句等信息。 3)删除 Continuous Queries 删除连续查询的语句如下: 1DROP CONTINUOUS QUERY <cq_name> ON <database_name> 其他说明 在 InfluxDB 中,将连续查询与数据存储策略一起使用会达到最好的效果。 比如,将精度高的表的存储策略定为一个周,然后将精度底的表存储策略定的时间久一点,这要就可以实现高低搭配,以满足不同的工作需要。 九、再谈连续查询 连续查询语法 连续查询的语法如下: 1CREATE CONTINUOUS QUERY <cq_name> ON <database_name> [RESAMPLE [EVERY <interval>] [FOR <interval>]] BEGIN SELECT <function>(<stuff>)[,<function>(<stuff>)] INTO <different_measurement> FROM <current_measurement> [WHERE <stuff>] GROUP BY time(<interval>)[,<stuff>] END 指定连续查询的时间范围 可以使用 RESAMPLE FOR 关键词来指定连续查询的时间范围,比如,每次执行都对 1 小时内的数据进行连续查询: 1CREATE CONTINUOUS QUERY vampires_1 ON transylvania RESAMPLE FOR 60m BEGIN SELECT count(dracula) INTO vampire_populations_1 FROM raw_vampires GROUP BY time(30m) END 这个语句每次会将 1 小时的数据执行连续查询,也就是说,每次执行时,会将 now() 到 now()-30m 和 now()-30m 到 now()-60m 分别做连续查询,这样我们就可以手动指定连续查询的时间范围了。 指定连续查询的执行频次 可以使用 RESAMPLE EVERY 关键词来指定连续查询的执行频次,比如,指定连续查询的执行频次为每 15m 执行一次: 1CREATE CONTINUOUS QUERY vampires ON transylvania RESAMPLE EVERY 15m BEGIN SELECT count(dracula) INTO vampire_populations FROM raw_vampires GROUP BY time(30m) END 这样,连续查询会每隔 15m 执行一次。 同时指定连续查询的范围和频次 将 RESAMPLE FOR 和 EVERY 关键词同时使用,可以同时指定连续查询的范围和频次,如下: 1CREATE CONTINUOUS QUERY vampires_2 ON transylvania RESAMPLE EVERY 15m FOR 60m BEGIN SELECT count(dracula) INTO vampire_populations_2 FROM raw_vampires GROUP BY time(30m) END 这个语句指定连续查询每 15m 执行一次,每次执行的范围为 60m。 参考资料 基础教程文档:InfluxDB 系列教程 | Linux 大学 00-InfluxDB 入门介绍-我的运维历程-51CTO 博客 influxdb 的简单使用 - 运维之路 influxdb 语法 - 个人文章 - SegmentFault 思否","categories":[],"tags":[{"name":"influxdb","slug":"influxdb","permalink":"http://www.frankfeekr.cn/tags/influxdb/"}]},{"title":"基于 SpringBoot & IDEA & JRebel 玩转远程热部署与远程调试","slug":"springboot-idea-jrebel-hotswap","date":"2019-07-17T12:37:24.000Z","updated":"2021-10-23T14:22:52.650Z","comments":true,"path":"2019/07/17/springboot-idea-jrebel-hotswap/","link":"","permalink":"http://www.frankfeekr.cn/2019/07/17/springboot-idea-jrebel-hotswap/","excerpt":"","text":"前言 在 SpringBoot 开发过程中,当我们在 Debug 我们的工程时,随便修改一段代码逻辑、修改接口路由、新增一个工具类等等情况下,都需要我们重新启动工程。反复的修改逻辑,反复的重启,这是相当痛苦的过程。当工程越来越大的时候,效率将变得特别低下,大部分的时间就是在不断的重启项目、编译包、部署包。 近期在研究基于 SpringBoot + IDEA 远程调试功能,可以通过 remote-debug 方式对测试环境中的 JVM 中的代码进行调试。所谓的远程调试就是,在本地代码可以调试服务器上的代码,模拟真实环境的请求(前提是本地的代码必须和远程服务器运行的代码一致)。在这种调试环境下,只能修改少量基本的业务代码,例如新增一个函数则都无法生效。 这里强力推荐 JRebel 插件,JRebel 是一款热部署插件。当项目在 Debug 的时候,修改某一个 Java文件时,JRebel 就可以解决在项目运行状态 debug 状态下任意修改 Java文件并动态反馈到运行的项目中。JRebel 有两个非常酷的特性,(一)支持热部署,(二)支持远程热部署。 查阅大量资料,总算对配置非常清晰,决定详细整理以供备忘,也系统可以帮助小白更快的实现远程部署与远程调试! 配置环境说明 这里以我的配置环境为例,进行 框架:SpringBoot 2.1.6 工具:IntelliJ IDEA 2019.1.3 x64 服务器:CentOS Linux release 7.6.1810 JDK:Java 1.8 JRebel 官网:JRebel, XRebel & QRebel By ZeroTurnaround | JRebel.com 文档:JRebel — JRebel 2019.x documentation 一、远程调试配置 1. 右上角点击,Edit Configurations,点击 + 号,创建 Remote 应用 2. 填写远程服务器信息 Name:应用名 Host:服务器地址 Port:5005 3. 复制 Command line arguments for remote JVM 下的命令 1-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 说明:自此,已经获得了启动远程服务的参数,命令将在启动的时候来使用 4. 启动服务 我们知道 SpringBoot 通过 Maven 打包后会生成 jar 包,服务端我们通过 jar -jar 的方式启动。通过上一步我们获得了服务端启动的配置参数命令,我们可以执行一下命令启动我的服务。 1java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 webapp.jar 我们可以用如下命令进行验证是否监听成功 1netstat -anp | grep 5005 5. 远程调试 选择我们配置的远程调试项,点击 Debug 即可进行远程调试。 启动完成,对需要 Debug 的代码打上断点,剩下的操作步骤就是访问远程服务器对应的业务请求,本地就会同步Debug。 😎 自此,可以尽情的开始远程调试工作了! 但是在使用远程调试的过程中,如果进行了断点,会出现服务阻塞的情况。如何解决这样的问题,将在下面进行描述。 二、热部署 这里将引入 JRebel 插件,可以实现对本地的服务和远程的服务进行热部署。本节将主要针对本地热部署进行配置。 1. IDEA 中安装 Jrebel 插件 File -> Settings -> Plugins -> Search plugins in marketplace 搜索 JRebel for IntelliJ 安装即可,离线安装包下载地址:JRebel for IntelliJ - Plugins | JetBrains 安装完成后,重启 IDEA 生效。此时在 Settings 面板中会出现 JRebel 的选项卡 2. 激活 JRebel 插件 (一)官方 Active Code 方式激活(只可使用10天) 进入官网 Evaluate JRebel,填写完整信息 点击 Register 获取激活码 此时即可获取你的激活码,复制保存到本地,后续将会用到。 JRebel 插件激活 点击 help -> JRebel -> Activaction -> Activation code,将我们网页上获取到的 Activation code 粘贴后,点击 Activate JRebel 即可激活。 激活成功后在 JRebel 配置面板中,会显示 VALID 标志,即说明激活成功了。 此时,你已经获取到了试用版的 JRebel 的使用权限,即可实现本地热部署的功能,可以进行第 3 步配置。(其他激活方式请参考:Activation — JRebel 2019.x documentation,若想要永久破解,请往下阅读。) (二)永久和谐方式 本人用户正版软件,推荐尽可能使用正版,如果想要 cracked(和谐)可以参考以下配置说明 在这里我们将通过 JRebel 的 Team URL(connect to online licensing service) 方式进行和谐(cracked)。 由于网络稳定性考虑,这里我们将搭建自己的 license server for JRebel 具体的搭建过程可以参考 Gitee 上的这位作者的仓库配置说明:JrebelLicenseServerforJava: A license server for Jrebel。当然同时,这里我也将提供一个打包完成的 jar 包,直接上传至服务器运行即可。 JrebelBrainsLicenseServerforJava-1.0-SNAPSHOT-jar-with-dependencies 12345# 服务启动(阻塞)java -jar JrebelBrainsLicenseServerforJava-1.0-SNAPSHOT-jar-with-dependencies.jar -port 8081# 服务启动(后台),推荐使用这种方式nohup java -jar JrebelBrainsLicenseServerforJava-1.0-SNAPSHOT-jar-with-dependencies.jar -port 8081 & 浏览器中打开链接 http://192.168.72.131:8081 ,出现如下界面则服务端配置成功。 如果是团队协作推荐搭建在一台内部都能够访问的服务器。 这里我使用的 JRebel 是 JRebel for IntelliJ v2019.1.4 版本的,我们可以通过如下 URL 进行注册 http://host:8081/{guid},其中 GUID 可以通过在线工具自己生成一个,用于区分不同的用户,而 GUID 基本上是不会出现重复的情况。生成GUID - 程序员在线工具 在此处,我可以通过 http://192.168.72.131:8081/bc1fdd38-9be0-4251-a619-e14a4a6c21b9 和 任意一个邮箱 进行和谐(这里我们先准备好这两个参数即可) 在 Team URL 中填入以上两个参数,即可实现激活 在 file->settings->plugins->JRebel 中查看是否已显示激活,激活后显示 valid,图标为绿色。此时可以点击 Work offline 实现离线的验证,离线下有效期为半年时间,半年后可以重新认证即可。若是 online 则实时会往服务端进行匹配验证。推荐使用 Work offline 方式! 补充:这里我们主要使用自己搭建 Server 的方式进行验证,这里也提供一些网上第三方的服务平台。在网络可达的情况下,免去搭建的烦恼。 beer_tools for jrebel 在和谐的过程中,参考的一些资料: JRebel永久破解激活方法 - songfei_dream的专栏 idea离线使用jrebel(亲测可用) - lzt099的博客 - CSDN博客 Jrebel在Idea中使用 - 鹰眼eagle-故障主动识别 - 3MS知识管理社区 JrebelLicenseServerforJava: A license server for Jrebel & JetBrains products, it also support JRebel for Android and XRebel. IDEA+Jrebel实现热部署和远程调试 - ManageOne云服务保障 - 3ms知识管理社区 3. 设置 IDEA 为自动编译 由于 JRebel 是实时监控 class 文件的变化来实现热部署的,所以在 IDEA 环境下需要打开自动编译功能才能实现随时修改,随时生效。 按住 Ctrl + Alt + Shift + / 弹出,选择 Registry 后勾选 4. Debug 启动服务 选择 SpringBoot 的入口类,右键选择 Debug with JRebel,等待启动完成即可 5. 本地热部署 此时可以修改或者增加代码,通过 Ctrl + Shift + F9 即可实现重新编译热部署。此时再也不需要每增加一个函数体、类都需要进行重启的操作,大大提升了 SpringBoot 开发调试的效率。 🤩 自此,我们已经学会了本地热部署的方式,需要远程调试与热部署方式进行配合调试请往下阅读。 三、远程热部署 在本节,将针对远程热部署与调试进行说明,搭建一个高效的开发测试环境。 配置远程热部署服务,主要步骤如下: 在服务器安装 JRebel 配置本地 JRebel 在服务器用 JRebel 启动专案 本地新增远端服务器进行热部署 1. 配置服务器 JRebel 官方配置文档:JRebel remote server support in IntelliJ IDEA — JRebel 2019.x documentation 下载安装包,确保容器启动用户有权限访问该路径 1curl -O http://dl.zeroturnaround.com/jrebel-stable-nosetup.zip 由于,使用 SpringBoot 2.x,必须要下载安装最新版的 JRebel 也可以通过官网地址:JRebel Release Archive | JRebel.com,进行下载 下载好后可以通过 FTP/SFTP 工具上传至服务器,在 CentOS 下使用 unzip 进行解压,得到 jrebel 文件夹 1unzip jrebel-stable-nosetup.zip 设置密码(进入到 jrebel 目录) 1234java -jar jrebel.jar -set-remote-password <password># 例如,设置密码为 12341234java -jar jrebel.jar -set-remote-password 12341234 激活服务端(进入到 jrebel/bin 目录,执行脚本) 在激活服务端中,JRebel 也提供了 3 种激活方式,详细地址:Activation — JRebel 2019.x documentation 在这里我们通过 With license server URL as an argument 方式,具体脚本如下 1./activate.sh http://192.168.72.131:8081/bc1fdd38-9be0-4251-a619-e14a4a6c21b9 [email protected] With evaluation license activation code as an argument 1./activate.sh <activation-code> 2. 配置本地 JRebel Startup 初始化配置,点击确认 JRebel Panel 窗口,将需要热部署的项目打包,生成 rebel.xml 和 rebel-remote.xml,如下图: 通过 maven package 重新打包生成 jar 包,上传至服务器 服务器端启动服务 启动参数 1-agentpath:[/path/to/JRebel Agent] -Drebel.remoting_plugin=true 热部署启动 12345678# 远程热部署 启动java -agentpath:/root/commom/jrebel/lib/libjrebel64.so -Drebel.remoting_plugin=true -jar webapp.jar# 远程热部署+远程调试 启动java -agentpath:/root/commom/jrebel/lib/libjrebel64.so -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Drebel.remoting_plugin=true -jar webapp.jarjava -agentpath:/var/www/jrebel/lib/libjrebel64.so -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Drebel.remoting_plugin=true -jar webapp.jar -port 8083 详细官网配置说明:Server configuration — JRebel 2019.x documentation 启动日志如下,即说明启动成功 Listening for transport dt_socket at address: 5005 说明启动了远程调试 JRebel started in remote server mode. 说明启动了远程热部署 123456789101112131415Listening for transport dt_socket at address: 50052019-07-16 09:15:20 JRebel: Starting logging to file: /root/.jrebel/jrebel.log2019-07-16 09:15:20 JRebel:2019-07-16 09:15:20 JRebel: #############################################################2019-07-16 09:15:20 JRebel:2019-07-16 09:15:20 JRebel: JRebel Agent 2019.1.4 (201907051008)2019-07-16 09:15:20 JRebel: (c) Copyright 2007-2019 Rogue Wave Software, Inc.2019-07-16 09:15:20 JRebel:2019-07-16 09:15:20 JRebel: Over the last 1 days JRebel prevented2019-07-16 09:15:20 JRebel: at least 0 redeploys/restarts saving you about 0 hours.2019-07-16 09:15:20 JRebel:2019-07-16 09:15:20 JRebel: JRebel started in remote server mode.2019-07-16 09:15:20 JRebel:2019-07-16 09:15:20 JRebel:2019-07-16 09:15:20 JRebel: ############################################################# 本地 JRebel -> Remote Servers 添加远程服务器 点击 Test Connection,点击 OK 确认 此时即可点击右上角 “远程热部署” 按钮后,通过 Ctrl + Shift + F9 即可实现重新编译热远程部署 可以先远程热部署后,再点击远程调试 Debug,即可实现先热部署再调试,大大加快开发效率。 😄 自此,所有的配置过程都结束了!即可开始尽情享受,JRebel 远程热部署和远程调试~ 实现真正的热部署,无论是改了代码片段还是配置文件,都可以做到不用重新启动就生效! TODO 文档还有很多不足的地方,这是接下来会更新的部分 [ ] 基于 Docker 远程调试与部署 [ ] 远程调试,服务阻塞怎么办 参考资料 Intellij IDEA基于Springboot的远程调试 - 程序新视界 - CSDN博客 IDEA + Spring Boot + JRebel 热部署不能自动编译的解决方案和启动的几种方式 - github.com/duanluan - CSDN博客 IntelliJ IDEA热部署插件JRebel免费激活图文教程(持续更新)_心得技巧_积微成著 JRebel遠端熱部署springboot教程 - IT閱讀 微服务开发神器–JRebel 插件破解和实现本地/远程热部署教程(IntelliJ IDEA版) - 掘金 JRebel远程热部署springboot教程 - whz的博客 - CSDN博客","categories":[],"tags":[]},{"title":"Linux 磁盘分区管理","slug":"Linux-磁盘分区管理","date":"2019-06-12T09:21:24.000Z","updated":"2021-10-23T14:22:52.646Z","comments":true,"path":"2019/06/12/Linux-磁盘分区管理/","link":"","permalink":"http://www.frankfeekr.cn/2019/06/12/Linux-磁盘分区管理/","excerpt":"","text":"查看磁盘信息 df 列出文件系统的整体磁盘使用量 1df -h du 评估文件系统的磁盘使用量(常用在估计目录所占磁盘容量) 12du -shdu -sh * fdisk 输出设备的所有分区内容和操作分区(删除,新增) 1fdisk -l GPT 分区 进入 parted 分区 1parted 选择你需要操作的分区 1select /dev/sda 设定使用的分区类型 1mklabel gpt 说明:如果要用 MBR 分区,输入 msdos 即可 添加一个分区 1mkpart (1) 输入分区格式,默认 ext2,回车。(此处可以随意选,之后重新格式化写入文件系统) (2) 输入分区的开始位置,输入 1,从第 1Mb 开始(最好不要从 0 开始,这里有一个所谓的 4k 对齐得问题,有兴趣的可以了解:xxx) (3) 输入分区从第几Mb结束,输入2000,第 2000Mb 结束(如果想要把剩余的全部挂载,请输入 -1) (4) 此时已经分区成功,输入print查看目前分区情况 或者可以通过一句命令直接创建 1mkpart webapp 4000 10000 可以设置单位 1unit GB 删除分区 1rm [Number] 例子 123mkpart docker 1 400000mkpart webapp 400000 1000000mkpart home 1000000 -1 格式化 1mkfs.ext4 /dev/sda1 磁盘挂载 mount 将 /dev/hda1 挂在 /mnt 之下 1mount /dev/hda1 /mnt umount 下面两条命令分别通过设备名和挂载点卸载文件系统,同时输出详细信息: 1234567# 通过设备名卸载# umount -v /dev/sda1/dev/sda1 umounted# 通过挂载点卸载# umount -v /mnt/mymount//tmp/diskboot.img umounted 分区永久挂载 修改分区文件 /etc/fstab,加一行 1/dev/sdb1 /var/www ext4 defaults 0 1 Docker 修改默认存储位置 关闭 Docker 1service docker stop 先创建挂载点,然后将 docker 得文件移动到目标路径 1mv /var/lib/docker /var/lib/docker_backup 拷贝 docker 文件到 dockerdata 1cp -rf /var/lib/docker_backup/* /var/lib/dockerdata 创建软连接,此时确保 /var/lib/docker 文件夹不存在 1ln -s /var/lib/dockerdata /var/lib/docker Docker 重启 1service docker restart 删除软链接注意事项 软链接创建好了,我们来看看怎么删除它 正确的删除方式(删除软链接,但不删除实际数据) 1rm -rf ./dockerdata 错误的删除方式 1rm -rf ./dockerdata/ (这样就会把原来test_chk下的内容删除) 现在看到区别了吧,所以说,以后大家在用rm -rf 删除软链接时,注意了!!!! 参考资料 四个修改Docker默认存储位置的方法-一生志在千里也知似水流年-51CTO博客 Ubuntu 16.04开机自动挂载硬盘分区(转) Linux umount命令 | 菜鸟教程 使用userdel命令删除Linux用户 - Zidane_Zhang - 博客园","categories":[],"tags":[]},{"title":"搜索引擎使用的小贴士","slug":"search-engine-tips","date":"2019-05-31T17:01:34.000Z","updated":"2021-10-23T14:22:52.649Z","comments":true,"path":"2019/06/01/search-engine-tips/","link":"","permalink":"http://www.frankfeekr.cn/2019/06/01/search-engine-tips/","excerpt":"","text":"必须掌握的十个搜索技巧 1、准确搜索 2、排除关键词 3、用「Either OR」(或)逻辑进行搜索 4、同义词搜索 有时候对不太确切的关键词进行搜索反而会显得更加合适。在未能准确判断关键词的情况下,你可以通过同义词进行搜索。 如果你在搜索引擎输入「plumbing ~university」,你所得到的反馈结果会包含「plumbing universities」和「plumbing colleges」等相似条目。 5、在站内进行搜索 6、善用星号 例如,如果你在搜索引擎中输入「architect*」,你所得到的反馈结果将会是所有包含 architect、architectural、architecture、architected、architecting 以及其他所有以「architect」作为开头的词汇的条目。 7、在两个数值之间进行搜索 在寻找问题的答案时,一个很好的方法是在一定范围内寻找和关键词相关的资讯。例如想要找出 1920 至 1950 年间的英国首相,直接在搜索引擎中输入「英国首相 1920… 1950」即可得出想要的结果。 记住,数值之间的符号是两个英文句号加一个空格键。 8、在网页标题、链接和主体中搜索关键词 有时你或许会遇上找出所有和关键词相关的所有网页标题、链接和网页主体的需求,在这个时候你需要使用的是限定词「inurl:」(供在 url 链接中搜索使用)、「intext:」(供在网页主体中搜索使用)以及「intitle:」(供在网页标题中搜索使用)。 例如,在搜索引擎中输入「intitle: 后台登录」会得到所有和关键词「后台登录」相关的网页标题。 9、搜索相关网站 相关的限定词可用于搜索相关网站时使用。例如,你仅需在搜索引擎中输入「related:whu.edu.cn」即可得到所有和「whu.edu.cn」相关的网站反馈结果。 10、搜索技能的组合使用 你可以对上述所有搜索技能进行组合运用,以便按照自己的意愿缩小或者扩展搜索范围。尽管有些技能或许并不常用,但准确搜索和站内搜索这些技能的使用范围还是相当广泛的。 参考资料 程序员应该掌握的10个搜索技巧_慕课手记 How to use search like a pro: 10 tips and tricks for Google and beyond | Technology | The Guardian .search-btn { cursor: pointer; width: 102px; height: 38px; line-height: 38px; padding: 0; border: 0; background: none; background-color: #38f; font-size: 16px; color: white; box-shadow: none; font-weight: normal; text-align:center; height:31px; }","categories":[],"tags":[]},{"title":"策划了11期的技术沙龙,顺利结束啦!纪念一下","slug":"luojia-talk-show-s1","date":"2019-05-30T15:59:59.000Z","updated":"2021-10-23T14:22:52.649Z","comments":true,"path":"2019/05/30/luojia-talk-show-s1/","link":"","permalink":"http://www.frankfeekr.cn/2019/05/30/luojia-talk-show-s1/","excerpt":"","text":"一些感悟和心得 在过去策划了 11 期的技术沙龙活动中,确实没想到坚持下来了!我想,三人行必有我师,在和大家交流的过程中也是对自己技术升华的一个过程。 某种程度上来说,策划这样的技术沙龙,主要是想要提升大家的技术视野,另一方面想要调解每周紧张的工作学习氛围。 坚持是一件太难得事情了,感谢自己的坚持,还有大家的支持。希望第二季更加精彩~ EDN 2019年05月30日 珞珈小讲坛 - 技术沙龙 第一季回顾 第1期 《customvision》柳家胜 《linux命令行小工具z jump》廖浩男 《如何写一首歌》张健 《博士生活是怎样的体验》吴光生 《python代码优化案例》谢老师 第2期 《关于 MySQL 你不知道的》张健 《游戏外挂怪圈》陈子豪 《打开 Latex 的大门》陈哲 第3期 《你还在关linux防火墙吗?》张健 《竞赛分享》李宇翔 《我曾经的英语学习之道》花春兵 《线性回归与正则化》李光耀 第4期 《五笔输入法入门》张健 《实验室深度学习工作站管理》柳家胜 《手把手教你,搭建 VPS 翻墙服务》林立城 《带你打开 5G 大门》陈子豪 《从<东邪西毒>初窥王家卫的光影世界》胡鹏 第5期 《绿皮书》电影沙龙 第6期 《CrossEntropy你了解多少?》柳家胜 《手把手教你,搭建 VPS 翻墙服务》林立城 《普通的密码学》李卓彧 《医学小常识(上篇)》张健 第7期 《模仿游戏》电影沙龙 第8期 《免费获取 VPS 10个月主机,搭建 SS 翻墙服务》廖浩南 《适老化设计+DigitalOcean云服务》廖浩南 《Git Ftp》胡鹏 《一句代码也不写,教你优雅搭建免费的博客》林立城 第9期 《心电图识别的简易医学知识》张健 《贸易战》陈子豪 《常见深度学习微信公众号你知道多少(#^.^#)》柳家胜 《前端知识体系,和一些你不知道的奇技淫巧》林立城 《博弈论知多少》花春兵 第10期 《浅谈深度学习多GPU模型训练》柳家胜 《谈谈技术学习的一些方法论》林立城 第11期 - 完结篇 《Search Engine Tips》胡鹏 《表白神器:荧光反应》张健 《Git 版本控制工具实战》林立城 《zsh,让你的爱上终端》马浩原 Talker 分享主题 张健(7) 《如何写一首歌》 《关于 MySQL 你不知道的》 《你还在关linux防火墙吗?》 《五笔输入法入门》 《心电图识别的简易医学知识》 《医学小常识(上篇)》 《表白神器:荧光反应》 柳家胜(6) 《customvision》 《实验室深度学习工作站管理》 《CrossEntropy你了解多少?》 《实验室深度学习工作站管理》 《常见深度学习微信公众号你知道多少(#^.^#)》 《浅谈深度学习多GPU模型训练》 林立城(4) 《手把手教你,搭建 VPS 翻墙服务》 《一句代码也不写,教你优雅搭建免费的博客》 《前端知识体系,和一些你不知道的奇技淫巧》 《谈谈技术学习的一些方法论》 《Git 版本控制工具实战》 廖浩男(3) 《linux命令行小工具z jump》 《免费获取 VPS 10个月主机,搭建 SS 翻墙服务》 《适老化设计+DigitalOcean云服务》 陈子豪(3) 《游戏外挂怪圈》 《带你打开 5G 大门》 《中美贸易战》 花春兵(2) 《我曾经的英语学习之道》 《博弈论知多少》 胡鹏(3) 《从<东邪西毒>初窥王家卫的光影世界》 《Git Ftp》 《Search Engine Tips》 陈哲(1) 《打开 Latex 的大门》 吴光生(1) 《博士生活是怎样的体验》 李光耀(1) 《线性回归与正则化》 李卓彧(1) 《普通的密码学》 李宇翔(1) 《竞赛分享》 马浩原(1) 《zsh,让你的爱上终端》 谢老师(1) 《python代码优化案例》 照片合辑","categories":[],"tags":[]},{"title":"如何选择自己的技术栈","slug":"如何选择自己的技术栈","date":"2019-05-27T03:06:34.000Z","updated":"2021-10-23T14:22:52.651Z","comments":true,"path":"2019/05/27/如何选择自己的技术栈/","link":"","permalink":"http://www.frankfeekr.cn/2019/05/27/如何选择自己的技术栈/","excerpt":"","text":"有这样一个梗 你们知道程序员最熟悉,最熟练,最常用的两个快捷键是哪两个吗?没错,就是你现在心中所想的:ctrl+c 和 ctrl+v ,俗名为:复制和粘贴。对于大部分程序员来说:复制和粘贴就是他创造伟大产品的左膀和右臂。 不知道大家有没有看过这个梗,这个梗其实相当的现实,但是你又不得不说它是说到点子上了。我前段时间听到过这样一句话:天下代码不过一个抄字。 (基础)从网上抄写程序=¥1 (入门)知道哪部分程序能抄=¥100 (高手)知道抄前后需要怎样调整=¥1000 (精英)知道怎么才能让别人看不出来你抄过=¥10000 (创业)知道怎么在抄的情况下依然让所有人认可你独特的价值=¥100000 技能云 在编程的世界里,该如何选择自己的技术栈呢。学前端?学 APP 开发?对于 Java、C++、C#、Python、PHP 又如何选择呢?人工智能现如今这么火,是不是机器学习、深度学习更高级一些呢?那么程序员又如何修炼内功呢? 技能树 几点建议 从基础到入门 夯实基础:知其然,知其所以然 项目实战:结合项目,拓展到自己专注的技术 MD笔记:学会用 markdown 写文档! 从入门到高手 代码积累:学会积累自己写过的优质代码 博客与GitHub:坚持写博客,学会使用 Git,GitHub 是你最好得名片","categories":[],"tags":[]},{"title":"Edge Computing and Edge Intelligence","slug":"Edge-Computing-and-Edge-Intelligence","date":"2019-05-24T06:06:11.000Z","updated":"2021-10-23T15:38:16.123Z","comments":true,"path":"2019/05/24/Edge-Computing-and-Edge-Intelligence/","link":"","permalink":"http://www.frankfeekr.cn/2019/05/24/Edge-Computing-and-Edge-Intelligence/","excerpt":"","text":"参考文献 《Edge Computing: Vision and Challenges》 IEEE INTERNET OF THINGS JOURNAL, VOL. 3, NO. 5, OCTOBER 2016 边缘云计算技术及标准化白皮书(2018) - 中国电子技术标准化研究 阿里云计算有限公司 中国电子技术标准化研究院 等 2018 年 12 月 12 日联合发布 **《基于个人计算机的智能家居边缘计算系统》**计算机学报,上海交通大学 计算机科学与工程系 一、引言 1. IT 3.0 时代会发生什么? 很久之前,电力 1.0 时代,想要用电,需要自己买发电机 不久之前,电力 2.0 时代,有了电网,想要用电,直接从电网购买即可,用多少买多少 当今,电力 3.0 时代,在很多发达国家,不少用户家里安装了太阳能板,有些可以电力自给自足,有些甚至有富裕,可以把电反哺电网,赚取收入 如果你混迹于 IT 圈子,你会发现类似的现象: 不久之前,IT 1.0 时代,每个公司想要处理数据,要自备机房,自己买服务器 现如今,IT 2.0 时代,有了云计算,公司直接购买云服务器和云计算力即可,用多少买多少 即将到来的 IT 3.0 时代,你觉得会发生什么? 闲散的计算力收集和再利用,形成雾计算! 云计算是可配置计算机资源和更高级别服务的共享池,可以通过最少的管理工作快速配置(一般是用互联网); 其实,云计算你可以理解为资源共享池。举个例子就是,我有很多东西,家里放不下了,放到一个特定的地方存着,随时提取,别人碰不了,保质保量。 华为《GIV2025 打开智能世界产业版图》白皮书 2. Cloud, Fog and Edge Computing – What’s the Difference? 实践场景 智慧城市 如果将智能家居的应用场景放大到一个社区或城市,将在公共安全、健康数据、公共设施、交通运输等众多细分领域产生极大的应用价值。这里应用边缘计算的初衷更多是出于成本和效率考虑,在一个八百万人口规模的城市,每小时产生的数据量可能达到 100PB,采用传统云计算处理方式将给网络带宽带来极大的压力,城市各角落的边缘设备实时处理和收集数据将带来效率上的极大提升。 自动驾驶技术与智能交通 在自动驾驶领域,边缘计算至关重要,因为**自动驾驶汽车上数百个传感器每小时将产生 40TB 的数据量。**从安全性的角度而不是从成本的角度考虑,数据的处理必须实时完成,当遇到紧急情况时,比如汽车前方突然出现踢球玩耍的小孩,这时自动驾驶系统必须依赖实时高效的边缘计算给予决策支持,并作出应急处理:刹车! 云计算(Cloud Computing) 云计算(英语:cloud computing),是一种基于互联网的计算方式,通过这种方式,共享的软硬件资源和信息可以按需求提供给计算机各种终端和其他设备。 边缘计算(Edge Computing) Edge computing—also known as just “edge”—brings processing close to the data source, and it does not need to be sent to a remote cloud or other centralized systems for processing. By eliminating the distance and time it takes to send data to centralized sources, we can improve the speed and performance of data transport, as well as devices and applications on the edge. 在科技飞速发展的今天,物联网已经成为公共云上运行的关键工作负载之一。**虽然现在云端物联网价值很大,但数据延迟和宽带消耗时它面临的最大问题。**毕竟现在这个时代,时间既金钱。为了解决这个问题,边缘计算应运而生。当有了边缘设备来充当“端”和“云”之间的中介,即可通过云的物联网来控制平面,从而进行更加集中化的管理。 举一个不太恰当,但能帮助理解的例子。把云端比作班主任,班主任很厉害吧。但一个班四五十个人,如果事无巨细的由班主任来决定,浪费时间还效率不高。这时各个班委出现了。选择一些有独立处理班级事务能力的同学,来分摊全班的事务。效率提升了不说,也减轻了班主任的负担。边缘计算就好比这些班委。帮云计算分担那些边缘的问题,从而起到省时省力的作用。 用人体神经举个例子,云计算就是身体所有反应都需要经过大脑处理后作出。而边缘计算就是由神经条件反射。 雾计算(Fog Computing) Fog computing is a standard that defines how edge computing should work, and it facilitates the operation of compute, storage and networking services between end devices and cloud computing data centers. Additionally, many use fog as a jumping-off point for edge computing. Fog computing, a term created by Cisco, also refers to extending computing to the edge of the network. Cisco introduced its fog computing in January 2014 as a way to bring cloud computing capabilities to the edge of the network. 3. 云计算是如何服务的 说到这里不得不说一下云计算的服务和交付方式,就是大家常见的 IaaS、PaaS、SaaS。 What is IaaS IaaS 其实是 “Infrastructure as a service” 的英文缩写,翻译过来是 “基础设施作即服务”,为了方便使用就成为了 “IaaS”。 IaaS 就是用户通过购买/租用云虚拟服务器来使用云服务的交付方式,现阶段的企业用户使用较少。 What is PaaS PaaS “Platform as a Service” 平台即服务。云计算公司把应用服务的运行和开发环境作为服务提供给用户,用户可以在这个平台上开发自己的云。国内像阿里、华为、腾讯、金山云等公司都会提供这样的服务。 What is SaaS SaaS“Soft as a Service” 软件即服务,云服务开发商通常将软件定位在云上,用户通过购买软件来使用云的一种交互方式。我们经常用到的钉钉、WPS 云办公等企业软件就是 SaaS 的一种表现形式。 4. 私有云、公有云、混合云 私有云 私有云从字面意思上了解就是私有的云,用户自己开发或者是云计算公司为用户单独使用而开发的云就是私有云。私有云的开发成本很高,需具备一定的硬件和软件基础,但是安全性方面是三云中最高的。 共有云 公有云一般指大家都能用的云,一般来说会收取用户相对较少的费用或者免费使用。我们日常生活中,最常用的百度云盘、微信云等都是公有云,当然三大通讯运营商所使用的云也是公有云。方便是公有云最大的特点,安全性是公有云的不足。 混合云 混合云融合公有云和私有云,是未来云计算市场的发展趋势。企业客户既希望于将数据放在私有云上保持独立性和安全性,又希望借助公有云的平台实现资源的共享。混合云匹配私有云和公有云,既保证了安全也达到了节省成本,提高效率的目的,目前已经应用在医疗领域。 二、再谈边缘计算 1. 引言 在聊边缘计算之前,我们先聊聊这个星球上最魔性的生物之一——章鱼。 具体来讲,边缘计算将数据的处理、应用程序的运行甚至一些功能服务的实现,由网络中心下放到网络边缘的节点上。 一直以来,公共和企业设施的监测和维护消耗着大量的人力、物力成本;电力、制造等行业数字化转型中对海量数据的实时、智能处理也有着强烈需求。 如果用常规模式构建物联网,随着设备的迅速增加,网络边缘侧所产生的数据量级将非常巨大。这些数据如果都交由云端的管理平台来处理,将会: 2. 什么是边缘计算 在网络边缘产生的数据正在逐步增加,如果我们能够在网络的边缘结点去处理、分析数据,那么这种计算模型会更高效。许多新的计算模型正在不断的提出,因为我们发现随着物联网的发展,云计算并不总是那么高效的。接下来文章中将会列出一些原因来证明为什么边缘计算能够比云计算更高效,更优秀。 3. 为什么需要边缘计算 云服务的推动:云中心具有强大的处理性能,能够处理海量的数据。但是,将海量的数据传送到云中心成了一个难题。云计算模型的系统性能瓶颈在于网络带宽的有限性,传送海量数据需要一定的时间,云中心处理数据也需要一定的时间,这就会加大请求响应时间,用户体验极差。 物联网的推动:现在几乎所有的电子设备都可以连接到互联网,这些电子设备会后产生海量的数据。传统的云计算模型并不能及时有效的处理这些数据,在边缘结点处理这些数据将会带来极小的响应时间、减轻网络负载、保证用户数据的私密性。 终端设备的角色转变:终端设备大部分时间都在扮演数据消费者的角色,比如使用智能手机观看爱奇艺、刷抖音等。然而,现在智能手机让终端设备也有了生产数据的能力,比如在淘宝购买东西,在百度里搜索内容这些都是终端节点产生的数据。 图 1 是传统云计算模型下,最左侧是服务提供者来提供数据,上传到云中心,终端客户发送请求到云中心,云中心响应相关请求并发送数据给终端客户。终端客户始终是消费者的角色。 图 图1 云计算模型 图2 边缘计算模型 4. 边缘计算的优点 在人脸识别领域,响应时间由 900ms 减少为 169ms 把部分计算任务从云端卸载到边缘之后,整个系统对能源的消耗减少了 30%-40%。 数据在整合、迁移等方面可以减少 20 倍的时间。 TensorFlow.js | TensorFlow https://www.tensorflow.org/js/demos 三、边缘智能:基于边缘计算的深度学习模型推断加速方法 生态 产业三分天下,拥有终端、算法、算力者通吃 目前,边缘智能产业生态架构已形成,主要有三类玩家: 第一类:算法玩家。从算法切入,如提供计算机视觉算法、NLP算法等。 第二类:终端玩家。从硬件切入,如提供手机、PC等智能硬件。 第三类:算力玩家。从终端芯片切入,如开发用于边缘计算的AI芯片等。 研究背景 作为人工智能领域的当红炸子鸡,深度学习技术近年来得到了学术界与产业界的大力追捧。目前,深度学习技术已在计算机视觉、自然语言处理以及语音识别等领域大放异彩,相关产品正如雨后春笋般涌现。**由于深度学习模型需要进行大量的计算,因此基于深度学习的智能通常只存在于具有强大计算能力的云计算数据中心。**考虑到当下移动终端设备的高度普及,如何将深度学习模型高效地部署在资源受限的终端设备,从而使得智能更加贴近用户这一问题以及引起了学术界与工业界的高度关注。针对这一难题,边缘智能(Edge Intelligence)技术通过协同终端设备与边缘服务器,来整合二者的计算本地性与强计算能力的互补性优势,从而达到显著降低深度学习模型推理的延迟与能耗的目的。 图1. 基于终端设备与边缘服务器协同的深度学习推断 边缘智能的核心研究问题在于如何在资源受限的边缘端高效部署深度学习模型,其中包括边缘设备深度学习模型优化,深度学习计算迁移,边缘服务器与终端设备间的协同调度等问题。 研究问题 对于常见的常见的深度学习模型,如深度卷积神经网络 CNN,是由多层神经网络相互叠加而成。由于不同网络层的计算资源需求以及输出数据量都具有显著的差异性,那么一个直观的想法是将整个深度学习模型切分成两部分,其中计算量大的一部分卸载到边缘端服务器进行计算,而计算量小的一部分则保留在终端设备本地计算,如图 2 所示。显然,上述终端设备与边缘服务器协同推断的方法能有效降低深度学习模型的推断时延。然而,选择不同的模型切分点降导致不同的计算时间,我们需要选择最佳的模型切分点从而最大化发挥终端与边缘协同的优势。 图2. 边缘服务器与终端设备协同推理示例 除了对模型进行切分(DNN partitioning),加速深度学习模型推断的另一手段为**模型精简(DNN right-sizing),即选择完成时间更快的“小模型”,而非对资源需求更高的“大模型”。**如图 3 所示,对于任意深度学习任务,我们可以离线训练具有多个退出点的分支网络,其中,退出点越靠后,模型越“大”, 准确率也越高但相应地推断时延越大。因此,当深度学习任务的完成时间比较紧迫时,我们可以选择适当地牺牲模型的精确度来换取更优的性能(即时延)。值得注意的是,此时我们需要谨慎权衡性能与精度之间的折衷关系(tradeoff)。 图3. 具有多个退出点的深度学习分支网络 综合运用上述模型切分和模型精简等两种调节深度学习模型推断时间的优化手段,并小心权衡由此引发的性能与精度之间的折衷关系,本文定义如下研究问题:对于给定时延需求的深度学习任务,如何联合优化模型切分和模型精简这两个决策,从而使得在不违反时延需求的同时最大化深度学习模型的精确度。 研究结果 针对上述问题,我们提出了基于边缘与终端协同的深度学习模型运行时优化框架Edgent。如图4所示,Edgent 的优化逻辑分为三个阶段:离线训练阶段,在线优化阶段以及协同推断阶段。 首先,在离线训练阶段,Edgent训练深度学习任务对应的分支网络,并生成回归模型来预测分支网络中不同网络层在边缘服务器以及在终端设备上的计算时间。其次,在在线优化阶段,Edgent实时测量当前移动终端与边缘服务器之间链路的网络带宽,以便于估算移动终端与边缘服务器间的数据传输时延。紧接着,Edgent沿着尺寸从大到小的网络分支(如图3中从右至左的5个网络分支),依次遍历每个网络分支上不同的切分点,并基于当前网络带宽和不同网络层计算时间估算所选网络分支与切分点对应的端到端延迟与模型精确度。在遍历完所有的分支网络与切分点后,Edgent输出满足时延需求的所有网络分支与切分点组合中具有最大精确度的一个组合。最后,在协同推断阶段,根据上述在线优化阶段所输出的最优网络分支与切分点组合,边缘服务器与移动终端对深度学习模型进行协同推断。 图4. 基于边缘与终端协同的深度学习模型运行时优化框架 Edgent 参考资料 什么是云?3分钟带你走进云计算世界 Cloud Computing - Fog Computing & Edge Computing: What’s the Difference? 边缘计算与雾计算:定义和企业用途 - 思科 边缘智能:基于边缘计算的深度学习模型推断加速方法 - 云+社区 - 腾讯云 华为《GIV2025打开智能世界产业版图》白皮书","categories":[],"tags":[]},{"title":"虚拟化技术的变迁","slug":"虚拟化技术变迁","date":"2019-05-24T02:34:42.000Z","updated":"2021-10-23T14:22:52.652Z","comments":true,"path":"2019/05/24/虚拟化技术变迁/","link":"","permalink":"http://www.frankfeekr.cn/2019/05/24/虚拟化技术变迁/","excerpt":"","text":"Reference Containers and Cloud: From LXC to Docker to Kubernetes - IEEE Journals & Magazine PDF download Abstract It’s focuses on container technology and how it’s emerging as an important part of the cloud computing infrastructure. It looks at Docker, an open source project that automates the faster deployment of Linux applications, and Kubernetes, an open source cluster manager for Docker containers. Content 服务部署的模式变迁 服务如何部署 这么多的服务如何管理编排 容器化编排的技术 搭建高可用、负载均衡服务 应用部署运行模式变迁 简单的说,Kubernetes 是管理 Container 的工具,OpenStack 是管理 VM 的工具。 OpenStack = 计算资源管理(主要最成熟的是虚机) + 存储资源管理 + 网络资源管理 Kubernetes = 容器资源管理 + 集群编排 所以采用 OpenStack+Kubernetes 是目前相对完整的云应用解决方案栈 什么是 Docker 容器化引擎 特性 容器 虚拟机 启动 秒级 分钟级 硬盘使用 一般为 MB 一般为 GB 性能 接近原生 弱于 系统支持量 单机支持上千个容器 一般几十个 优点: 轻量级 隔离机制,不依赖系统环境 秒级启动 应用迁移,一次构建到处运行 快速使用 启动方式 1: 1nvidia-docker run -it -v /home/biod/notebooks:/notebooks -e PASSWORD=\"biodwhu\" -p 5050:8888 -d --restart=always tensorflow/tensorflow:latest-gpu 启动方式 2: 1docker run -it -v /home/biod/notebooks:/notebooks --runtime=nvidia -e PASSWORD=\"biodwhu\" -p 5050:8888 -d --restart=always tensorflow/tensorflow:latest-gpu 启动方式 3:docker-compose.yml 123456789101112version: '3.1'services: supermicro: image: tensorflow/tensorflow:latest-gpu container_name: supermicro environment: - PASSWORD=\"biodwhu\" restart: always ports: - 5050:8888 volumes: - /home/biod/notebooks:/notebooks kubernetes 什么是 kubernetes(k8s) Kubernetes 是 Google 2014 年创建管理的,是 Google 10 多年大规模容器管理技术 Borg 的开源版本。 Kubernetes 是容器集群管理系统,是一个开源的平台,可以实现容器集群的自动化部署、自动扩缩容、维护等功能。使用 Kubernetes 我们可以: 快速部署应用 快速扩展应用 无缝对接新的应用功能 节省资源,优化硬件资源的使用 Kubernetes 的目标是促进完善组件和工具的生态系统,以减轻应用程序在公有云或私有云中运行的负担。 特点 可移植: 支持公有云,私有云,混合云,多重云(多个公共云) 可扩展: 模块化,插件化,可挂载,可组合 自动化: 自动部署,自动重启,自动复制,自动伸缩/扩展 为什么需要 Kubernetes 可以在物理或虚拟机的 Kubernetes 集群上运行容器化应用,Kubernetes 能提供一个以 “容器为中心的基础架构”,满足在生产环境中运行应用的一些常见需求,如: 多个进程协同工作 存储系统挂载 应用健康检查 应用实例的复制 自动伸缩/扩展 注册与发现 负载均衡 滚动更新 资源监控 日志访问 调试应用程序 提供认证和授权 检查 Nodes 状态 1234567kubectl get nodes# 输出如下,STATUS 为 Ready 即为正常状态NAME STATUS ROLES AGE VERSIONkubernetes-master Ready master 44h v1.14.1kubernetes-slave1 Ready <none> 3h38m v1.14.1kubernetes-slave2 Ready <none> 3h37m v1.14.1 运行第一个容器实例 123456# 使用 kubectl 命令创建两个监听 80 端口的 Nginx Pod(Kubernetes 运行容器的最小单元)kubectl run nginx --image=nginx --replicas=2 --port=80# 输出如下kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.deployment.apps/nginx created","categories":[],"tags":[]},{"title":"谈谈技术学习的一些方法论","slug":"谈谈技术学习的一些方法论","date":"2019-05-09T15:54:17.000Z","updated":"2021-10-23T14:22:52.652Z","comments":true,"path":"2019/05/09/谈谈技术学习的一些方法论/","link":"","permalink":"http://www.frankfeekr.cn/2019/05/09/谈谈技术学习的一些方法论/","excerpt":"","text":"引言 一直想谈谈学习方法的一些东西,终于耗费了一周时间写完了。本文篇幅较长,如果耐心读完我想一定会对你多少有一定帮助。 个人的知识水平受限,理解一定是具有局限性的。希望我的一些经历可以帮助到大家,也是自我的一次反思和总结。学习是一个不断的过程,若是若干时间后有更多的学习感悟,我也会一直更新。 有不对的地方欢迎留言指正 🙏🏼 一、为什么要讲学习方法 在学习技术这条路上并不是一帆风顺,也一直在探索一条适合自己的学习方法。从一开始的技术小白,到现在还比较上道的老鸟,在这个过程中走了太多的弯路,想在这里和大家分享一些我的经历和学习方法。 对于立志成为一个技术达人,或者对于技术有执着追求的,学习一定是伴随着终生。所谓的互联网还是国企养老的说法更是根本不存在的,一旦进入所谓的舒适区,很可能下一个末位淘汰的就是你。我不认为从业者过度的关注什么互联网寒冬的说法,应该从提升技术素养出发,找到自己感兴趣的方向。要相信大部分金子都可以发光发热的,当然伯乐很重要。 技术是科学、是工具、更是理论应用于实践的途径。技术学习是无止境的,人的精力也是有限的,我们不应该一味的追求新技术。基础很重要,特别是数据结构与算法、操作系统原理、计算机网络、Linux 基础,这些在如今应用层编码的过程中或许不会被过多的关注,但是计算机的基础决定了你能走多远。从某一个技术点的深度着手,同时不断扩展自己知识的广度。项目需要什么就用什么学什么,业余时间当然可以扩展一下自己感兴趣的技术栈。 在本文想和大家分享一些我的学习方法和经验,希望对大家有所启发。 二、和技术有关的日子 1. 从懵懂到想做技术 从我的个人学习经历说起吧,学习的过程挺曲折的,但是回首过去发现这些经历的都将成为你未来的谈资。从进入大学开始,个人的对于摄像与平面有浓厚的兴趣,也花了整整三年的时间投身于学生工作(主要是平面设计、视频后期、微信公众号运营相关的),一度的规划是希望成为一个设计师或是视频后期工作者。也跟随很多摄制组拍摄过广告、做过平面设计物料、微信公众号媒体运管,我认为一个好的设计一定是给人带来美的享受,对于追求完美主义的我,深深的爱上了这个角色。 与此同时,本科作为软件工程专业出身的我,同时被各种计算机的学科熏陶着,自认为技术掌握的还不错。那个时候对于 Web 特别感兴趣,特别现在的人们打开计算机,大部分时间都是和你的浏览器相处。那个时候开始学习一些 HTML/CSS/JavaScript 相关的一些东西,可以自己做一些简单的静态网页,很快不久就写了一个非常简陋的个人主页。后来,接触了动态网页写过 JSP、ASP.NET、PHP 网页,但是那时候大部分还是前后台混合渲染的方式,开发效率倒是很高但是一旦想要从新设计前端界面,就会陷入重构的泥塘。后来,慢慢接触到了前后台分离的开发方式,知道了 Vue、React 相关的一些 MVVM 框架。 在 2015 年的学生工作中,马上迎来了学代会的换届选举,在过程中发现纸质选票效率低,通常中场投票和检票也耗费大量的时间。我算是一个善于发现问题的人,两天时间泡在图书馆中,搭建了一个学代会投票系统。在后来的投票过程中,我将原来的 30 分钟,缩短到了 5 分钟,并在大屏实时滚动显示投票柱状图。这个投票系统也得到了团委老师的一致认可,在后来的 4 年中一直使用了我的投票系统,并且保持了很好的稳定性和效果。在每一年的系统中,我也不断的升级投票系统,在这几年的升级过程中也记录了自己的成长轨迹。虽然毕业了,但口碑的也在各个学院中流传开了,也正在计划将它升级为一个平台,可以让所有注册到平台上的人都可以使用我的投票平台。 在设计师和技术研发人员的角色选择中,纠结过很长一段时间。会发现,设计师可以设计出很简洁、美观、善心悦目的作品,但是只有技术研发人员才能实实在在的为它赋能,真正的实现一个具有功能性、体验性的东西。他们都是艺术家,都在各自的领域增添异彩。我从来不认为开发的是功能,每一行代码都有它的灵魂,都是我的产品,更是我的尊严。在后来的日子里,我发现这两个角色对于逐渐开始并存,为什么不能够同时应用和学习呢?做设计中最懂技术的,做技术中最懂设计的。 同时在过去三年的学生工作中,也很好的锻炼了我的团队领导能力和技术分享能力。这些软实力,也许和很多的代码能力都无关,但是在后来的项目团队协作和管理中有着潜移默化的帮助。 2. 我的成长与技术路线 从大三的暑假到目前的研究生阶段,开始学习前端开发技、跨平台应用、后台开发技术、了解系统架构的一些东西、机器学习算法应用、产品设计思维等等相关的东西。我开始了技术广度的挖掘,很多东西不敢说是精通,但至少一定是入门到进阶的水平,当然目前还在技术深度不断学习的路上。 我很认可一句话,“人人都是产品经理”,“人人都是架构师”。我们不应该为自己打上太多的标签,“我是一个算法工程师”、“我是一个 XX 语言开发工程师”、“我是一个产品经理”。在学习这条路上,我们应该首先抓好自己的本职工作,也就是技术深度的学习(例如:开始学会重构自己的项目代码、开始看项目源码),从而在工作之余不断的拓展自己技术的广度和思维的广度。这里说一个题外话,谷歌宣扬的 80% 的时间用来工作,20% 的时间用来学习,大名鼎鼎的 Go 语言就是三位科学家利用 20% 时间里创造的。 一个追求用户极致体验的你,为产品而创造的你,未来也可能成为乔布斯一样优秀的产品经理。执着追求技术,那么未来你可能成为某个领域的技术专家或是某个维度的系统架构师。在追求技术的同时,有长远的战略眼光,有可能成为一个技术总监。有广阔胸怀的,赏识人才,能有调理的管理好一批人,有市场洞察力,能将技术变现,那未来离财务自由不远了。 后来的我读研了,由于是专硕,两年的读研时间显的好短暂,才短暂接触了几个月的机器学习算法,都才刚刚入门了 Python 和会使用 Tensorflow 和 Scikit-learn 框架进行一些简单的机器学习分类工作,马上又要着手于校招,让我措手不及。在机器学习算法等相关学科,对于数学功底要求比较高,对于快速就业让我十分的无助。由于之前也写过几年的 PHP 后台开发相关的工作,大概在很长的一段时间内一直在纠结究竟是找机器学习岗位或是后台开发方向。 思考了很久,我对自己未来的规划是希望成为架构师方向,在技术达到一定高度,并有很好的想法的时候会有创业做产品的打算。于是我还是回到了自己的老本行,后台开发的方向,当然不仅仅只会关注后台相关的技术点,也会在业余时间学习一些算法和前端技术栈。逐渐的,不断在不同维度中学习技术,不断成长。 总之在技术快跑的过程中,我们不仅仅要学会一门精通的手艺,同时也要有自己的辅助第二技术或是第三技术。要学会用后台思维写前端,算法思维写后台,产品思维写算法。我们做的不仅仅是功能,要让每一行代码都具备灵魂。 个人对于产品是非常热爱的,这几年互联网的高速发展,整个中国发生了翻天覆地的变化。国民的衣食住行都发生了巨变,吃饭——美团、点评、饿了么,买衣服——淘宝、唯品会、京东,出行——哈啰单车、各种共享电动车、滴滴出行、曹操专车,住房——自如、贝壳找房、58 同城,还有改变生活方式的微信和支付宝。他们都将逐步成为我们生活中的水电煤,逐步融入人们的血液。在技术水平和思维格局到达已经层次,十分希望在未来的日子,在某个对的时刻能够创造一个影响一个时代的产品。 (图:技术路线与管理路线) 三、如何学习与技术快跑 上文通过自身经验举例,说了一些自己的技术成长路线。下面来谈谈技术学习的一些方法论,主要围绕以下四个部分进行讨论: 技术架构 极限编程 学习总结 技术分享 1. 技术架构 在学习技术前,我不知道大家是怎样的一种心态。我喜欢把技术比作一栋大厦,大部分人都会从大门进入,一步一步往上走,技术也在一点点的提高,但很多人大部分时候都会质疑自己学来到底有什么用,或是会遇到一些技术瓶颈,或是很多时候迷茫了也不知道该学习哪些东西。 技术的成长我认为分成三个层次:我不知道我不知道,我知道我不知道,我知道我知道。从技术的小白,到技术小达人,再到技术牛人。 在这栋技术大厦中,在技术小白阶段,就是按部就班的往上爬,但是当你技术学习到一定程度的时候,你必须开始了解整栋大厦的框架结构。所以在这里,必须要开始转向系统架构。这里摘抄一段《许式伟:架构设计的宏观视角》中的话,也正是我们想要表达的思想。 如同造房子有建筑工人(负责搬砖)和建筑师(负责架构设计)一样,软件系统的开发过程同样需要有程序员(负责搬“砖”)和架构师(负责架构设计)。作为架构师,我们需要的第一个能力是宏观的全局掌控能力。 如果把应用程序比作一座大厦,那么我们作为大厦的架构师,需要把大厦的结构搭建好,让程序员可以把砖填充进去,我们都知道,一个大厦的结构建得是否稳固,与地基密不可分。 所以,我们首先就需要从大厦的地基开始,熟悉这座大厦。毕竟,你对所依赖的基础架构了解得越全面,做业务架构设计就会越发从容。 技术想要成长,必须首先在思想上成长,提升自己的技术思想高度。一个不想成为建筑师的包工头不是一个好的架构师,在学习这条往上的路上,必须要学会用宏观视角来解决问题。在技术成长的过程中,有时候不需要太多的注意某个细节是怎么实现的,而是要有技术判断力,技术是否可行,技术如何选型。 下面给大家列一下,后台开发工程师需要达到的一个高度吧,做一个简单的感知。 数据结构与算法实战 Leetcode 剑指 Offer 内功修炼 数据结构与算法 海量数据处理方法 Linux 基础与命令 计算机网络(应用层,传输层,网络层等相关协议) Web 网络和 HTTP/HTTPS 协议 数据库(MySQL,Redis) 操作系统原理 Git 版本管理工具使用 正则表达式 Java 核心技术 语法与基础概念 面向对象与 23 种设计模式 Java 容器源码(数据结构 & 源码分析:ArrayList、Vector、LinkedList、HashMap、ConcurrentHashMap、HashSet、LinkedHashSet and LinkedHashMap) Java 并发编程(线程状态、线程机制、线程通信、J.U.C 组件、JMM、线程安全、锁优化) Java IO(磁盘操作、字节操作、字符操作、对象操作、网络操作、NIO) Java 虚拟机(运行时数据区域、垃圾收集、内存分配机制、类加载机制、性能调优监控工具) Java Web(学习 SSM + SpringBoot 框架和设计模式思想) 分布式解决方案 SpringCloud Zookeeper(分布式协调服务) Dubbo(分布式服务治理) 分布式事务解决方案 ActiveMQ、Kafka、RabbitMQ(分布式消息通信) Redis(分布式缓存与集群搭建) mycat(数据库路由) Nginx(反向代理) Docker(容器技术) Tomcat 最后推荐一些阅读脚本 一张后台开发技能图谱分享给大家 整理了一些阅读清单与学习课程⎡计算机学习的“武林秘籍”⎦,欢迎大家学习 2. 极限编程 接着,来聊聊极限编程。极限,就一个字"快"! 身边也经常有很多人,常常在每次项目中,都没来得及系统学习技术栈就要开始项目开发了;大部分的时间都在复制粘贴,面向搜索引擎编程,找不到自己的技术核心点。 包括自己在内,很多时候也都希望当自己掌握了完整的一套技术栈以后再开始你的项目编码。经历过很多项目之后,我会发现其实这并不是正确的做法。同样的,这也遵循二八定律,在技术中我们只要花少量的时间学会 20% 的技术,就可以开始项目开发,剩下的 80% 大部分都应该在项目实践中去学习的。 在很多的项目中,根本来不及有太多的时间让我们学习。很可能一个新的技术,通过短短 5-7 天的学习,就必须要应用到项目中去,很难有太多的时间让你系统学习。特别在互联网的应用场景中,时间就是金钱,一个好的想法,必须在最短的时间内上线。极限编程正是这样一种敏捷快速的开发方式,要求团队成员拥有很高的技术素养,在很短时间内就可以学会并且应用。 在这里,我们从个人的经验,分享我在极限编程学习过程中的路线: 先看技术效果 学会检索一些牛逼的项目,看看通过这个技术可以达到什么样的效果,判断是否满足自己的需求。 结果导向的学习,更加能提升我们的学习兴趣。 快速视频学习入门(10小时以内) 特别像我一样学习不太喜欢看书,感觉到很枯燥的同学,我推荐直接找视频快速入门,视频不宜太长,10小时左右(2.0x 速度看)。快速的了解技术的状况,掌握基本的核心技术,会的部分直接跳过。 这里推荐一些好的学习网站:慕课网、极客时间、哔哩哔哩、极客学院、实验楼、学堂在线。 开源小项目 在视频学习入门之后我们可能还不具备一个项目的编码能力,这时候我们可以找一些开源项目来跑一跑(学会使用 GitHub),项目不宜太复杂,一个简单的小项目即可。 配合文档同步学习 技术文档不像小说,一定更不要从头到尾的读,选择有必要的部分尝试通读,但是大部分的文档应该作为字典一样的查询工具书。 开始投入项目生产 终于可以正式开始项目实践了,选择好自己的项目框架,配合文档、视频,不会的部分当然还得靠搜索引擎。 项目复盘与重构 重新审视自己的代码,有条件的应该让大神给你看看,指点一下代码,以便在未来升级改进。 技术进阶,重新回到书本(或是项目文档) 无论你在搜索引擎找到的答案,都是比较碎片化的知识。当你想要深入到底层或是原理相关的部分,你应该选择一本大众认可的书籍进行深入阅读,大部分的书才是一个最系统和深入的学习。当然要看技术点的感兴趣和必要成分,不是所有的技术都需要深入了解,但是在自己的技术领域应该还是需要不断精通和升华。 3. 学习总结 在技术学习的过程中,我们可能会遇到很多的技术难点,还会遇到很多碎片化的检索工作,很多技术点我们肯定是不可能都记住甚至背下来。经常,我们会遇到一些常见遇到一些常见的东西,每次见到我们都要重新谷歌检索一遍,浪费了大量的时间。所以,学习总结是非常有必要。 都说好记性不如烂笔头,定期的学习和整理必然对学习巩固有所帮助,这里通过索引的方式对技术做一个系统分类,方便随时巩固和学习,当然还有面试。在学习这条路上难免会有很多盲点和学不完的知识。有道无术,术尚可求,掌握好思维能力才能应对千变万化的技术。不要把大脑当成硬盘,也不要做高速运转的 CPU,而修行自己的大脑成为一个搜索引擎,学会分析解决问题。 个人也是因为 MarkDown 而爱上的写作,这里盘点一些学习总结的方式: 技术博客 & 微信订阅号 首先技术博客是一个非常好的途径,可以通过掘金、CSDN、知乎专栏、知识星球等等途径编写自己的技术博客。 稍微极客一点的同学可以自己搭建一个自己的博客,例如:WordPress、Hexo、Hugo 这样的博客。(推荐使用 Hexo + GitHub + Typora + MD 来写自己的技术博客)。欢迎来我的博客逛逛:https://www.frankfeekr.cn/ 在技术博客的编写过程中,首先对你知识的回顾是很有帮助的,其次也将成为你的个人检索宝库,把自己常见的一些问题都记录在你的博客中,再下一次遇到的时候就可以信手捏来。 很多著名的技术书籍早期都是通过博客来积累自己的素材,当你的技术和文字达到一定功底,我想出书也是非常自然而然的事情。 此外还可以申请个人的微信订阅号来编写自己的技术博客,这也是一个很好的传播途径,慢慢的积累自己的粉丝哈哈。欢迎关注我的微信公众号,“全栈开发社区”。 GitHub 开源项目 GitHub 不仅仅可以分享代码性质的项目,当然在上面你还可以开源自己博客项目,你可以通过 .md 来记录自己的技术博客。 很多开源的书籍、文档翻译项目都可以通过 GitHub 来托管 通过 GitHub 你还获取到了版本控制的文本和图床,这相比自己搭建服务器来构建博客更加的方便和安全,并且永不丢失。 欢迎看看我在 GitHub 上的技术项目:https://github.com/frank-lam/fullstack-tutorial 其他 此外你还可以通过诸如:为知笔记、有道云笔记等方式进行学习总结 最后建议大家不要再用你的 txt 或是 word 来记录你的笔记 4. 分享技术 学习金字塔是美国缅因州的国家训练实验室研究成果,它用数字形式形象显示了:采用不同的学习方式,学习者在两周以后还能记住内容(平均学习保持率)的多少。 在金字塔基座位置的学习方式,是“教别人”或者“马上应用”,可以记住 90% 的学习内容。通过学习金字塔,我们会发现学习最好的方式应该是主动学习并且学会分享教授别人如何学习。在教授别人的过程中,我们在输出知识,也在不断的输入别人的疑惑,看似单向学习的过程中实际上也是双向的学习,不断的巩固自己的知识。 那么在技术学习中呢?没错我们不仅要学会倾听别人的技术,很多时候我们要学会分享技术。要知道一个好的技术管理者,必须要学会根据团队成员的不同情况,制定不一样的培养方案,这也是很重要的一个能力。通过分享你的技术,同时你也会不断的巩固自己的逻辑能力和技术表达能力。 (图:学习金字塔,图片来源网络) 四、最后几句话 技术学习的路是很长的,需要我们掌握好高效的学习方式,终身学习。 你要相信自己,可以通过 20 小时学会任何技能,也能通过 10000 小时成为某个领域的资深专家。在立足于本身技术深度学习的同时,也记得拓展自己思维的广度。 只有一个好的技术学习方法 ,才能让你在如今不断动荡的互联网江湖中,屹立不倒、处变不惊。 感谢你耐心的阅读自此,这是对我的莫大鼓励。个人知识水平受限,在未来的日子,若有新的想法和感悟,我也将在本文中更新修改。","categories":[],"tags":[{"name":"技术世界","slug":"技术世界","permalink":"http://www.frankfeekr.cn/tags/技术世界/"}]},{"title":"技术学习清单⎡计算机学习的“武林秘籍”⎦","slug":"cs-learning-list","date":"2019-04-19T12:20:07.000Z","updated":"2021-10-23T14:22:52.647Z","comments":true,"path":"2019/04/19/cs-learning-list/","link":"","permalink":"http://www.frankfeekr.cn/2019/04/19/cs-learning-list/","excerpt":"","text":"在这里,为你推荐了技术学习路上优质的学习书籍、学习课程。在推荐清单中将附带官网链接,图书则附京东和豆瓣链接。如果你有更好的学习资料,可以在下方留言,采纳后我会更新在文章中。 对于文中涉及到的书籍和课程,都将以 PDF 或是视频的方式归档在百度网盘。可以关注我的公众号 “全栈开发社区” 回复 “技术学习清单” 即可获取下载链接。 一、数据结构与算法 包含数据结构与算法两部分 📖阅读清单 书名 推荐 京东 豆瓣 《算法4》 算法圣经,比《算法导论》更浅显易懂 JD douban 《剑指Offer》 校招,面试算法必刷,配合牛客网刷题 JD douban 《程序员面试指南》 左神,视频课一般,书是还是可以的 JD douban 📺学习课程 课程 推荐 【慕课网】刘宇波:玩转数据结构,从入门到进阶 数据结构从底层到实现,浅显易懂 【慕课网】刘宇波:程序员的内功修炼,学好算法与数据结构 程序员的内功修炼,强烈推荐 【慕课网】刘宇波:玩转算法面试 leetcode题库分门别类详细解析 Leetcode 刷题入门,强烈推荐 【极客时间】覃超:算法面试通关40讲 市面上比较新的课程,推荐 【慕课网】刘宇波:看得见的算法 7个经典应用诠释算法精髓 通过7款经典好玩游戏,真正将算法用于实际开发 二、计算机基础 📖阅读清单 书名 推荐 京东 在线 📺学习课程 课程 推荐 二、操作系统 📖阅读清单 书名 推荐 京东 在线 《Linux+C 程序设计大全》 快绝版的书了吧,非常经典,现在好多市面上的书都是这里抄来的 当当 / 《快乐的 Linux 命令行》 最好得 Linux Tutorial,适合通读的书籍,并不是鸟哥这种类型的工具书,建议读一遍 JD 📖 《深入理解计算机系统》 程序员的内功修炼,必读必读必读! JD / 《UNIX环境高级编程》 还没开始读… JD / 《Linux+高性能服务器编程》 已经绝版的书啦,按需印刷 JD / 《计算机程序的构造和解释》 学习函数式编程 JD 📺学习课程 课程 推荐 【慕课网】快速上手Linux 玩转典型应用 / 【慕课在线】Linux达人养成计划 I-Linux的入门级课程! 入门最佳指南 【慕课在线】Linux 达人养成计划 II VIM+磁盘管理+用户权限! 入门最佳指南 【小甲鱼】零基础入门学习汇编语言 幽默不失内涵,非常值得学习 【哔哩哔哩】操作系统_清华大学(向勇、陈渝) 操作系统的书本看的乏味,不妨可以看看清华大学的这个课程 三、计算机网络 📖阅读清单 书名 推荐 京东 《计算机网络原理创新教程》韩立刚主编 全网最通俗易懂的网络课程,还有配套课程 JD 《图解HTTP》 后台开发工程师,必须要会的 HTTP 协议 JD 📺学习课程 课程 推荐 【51CTO】韩老师-计算机网络原理-156讲 教科书级别,比谢希仁版的计算机网络更浅显易懂,有配套视频理论和实战,老师的讲课风格非常棒,是全网入门计算机网络最佳书籍与课程适合面试和考研! 【慕课网】HTTP协议原理+实践 Web开发工程师必学 如果看不进书,可以推荐看这个,不算是非常优秀,但是适合快速入门 四、面向对象与设计模式 📖阅读清单 书名 推荐 京东 《设计模式之禅》 极具趣味,容易理解,但讲解又极为严谨和透彻 JD 《研磨设计模式》 程序员的武林秘籍,必读刊物 JD 《重构_改善既有代码的设计》 程序员的武林秘籍,必读刊物,Java 语言编写 JD 《设计模式的艺术》 结合大量应用实例分析,通俗易懂,豆瓣网友评价“一本被埋没的好书”。所有内容均可在作者博客史上最全设计模式导学目录获取。🌸 推荐来自:@Angus-Liu 📺学习课程 课程 推荐 【极客学院】极客学院 23 种设计模式 大概是目前看到的最好得设计模式入门课程 【慕课网】Java 设计模式精讲 Debug 方式+内存分析 / 五、Java 包含 Java 核心知识和 Java Web 框架。 📖阅读清单 书名 推荐 京东 《Java 核心技术 卷Ⅰ / Ⅱ》 Java 爱好者经典必读,需要的时候翻一番 JD 《Java 编程思想》 Java 爱好者经典必读,需要的时候翻一番 JD 《Java 并发编程实战》 并发编程经典读物 JD 《阿里巴巴 Java 开发手册》 Java 程序员人手一本的读物 JD 《Java 程序员面试笔试宝典》 很好的面试宝典,但是有点老了,本仓库的很多内容也参考了本书。建议直接阅读本仓库 JD 《深入理解 Java 虚拟机》周志明 值得通读几遍的 JVM 刊物 JD 《Java 网络编程》 买了好久,我也还没开始翻 JD 📺学习课程 课程 推荐 【网易云课堂】Java 开发工程师(Web方向)翁凯 浙大老师,幽默风趣不失内涵,Java 语言启蒙导师。建议只看翁恺部分 【廖雪峰】Java 教程 廖雪峰就是质量的保证 【慕课网】Java 零基础入门 理论视频讲解 + 编码实战,还行吧 【龙果学院】Java 并发编程原理与实战 说不出来什么,但是还是不错的 【龙果学院】深入理解 Java 虚拟机(jvm性能调优+内存模型+虚拟机原理) 在虚拟机课程中看过最好得了! 【尚学堂】白鹤翔_jvm虚拟机优化 JVM 浅显入门,可以在 B 站上看 六、Java web 📖阅读清单 书名 推荐 京东 《Spring 实战》 深入 Spring 必读 JD 《深入分析Java Web技术内幕》 阿里开源的 Java web 技术丛书 JD 《Spring MVC+MyBatis开发从入门到项目实战》 / JD 📺学习课程 课程 推荐 【网易云课堂】thinkphp5开发restful-api接口 了解 RESTful 设计最短的入门课程 【黑马程序员】SSH框架_王泽 SSH 入门必学,全网最好得课程 【黑马程序员】SpringMVC+Mybatis SSM 入门必学,全网最好得课程 【牛客网】初中高Python+Java项目实战_叶神 / 【慕课网】Spring Cloud微服务实战_廖师兄 / 七、数据库 📖阅读清单 书名 推荐 京东 《高性能 MySQL》 MySQL 工具书 JD 《Redis 实战》 Redis 入门与实战最佳读物 JD 📺学习课程 课程 推荐 【慕课网】MySQL 性能管理及架构设计 / 【慕课网】Redis 从入门到高可用,分布式实践 / 【慕课网】MySQL 面试指南 / 八、大数据技术栈 📖阅读清单 书名 推荐 京东 【极客时间专栏】Kafka核心技术与实战 📺学习课程 课程 推荐 ElasticSearch入门 九、微服务与云原生 📖阅读清单 书名 推荐 京东 在线 《微服务:从设计到部署》中文版 翻译国外的书籍,暂未初版,推荐在 GitHub 上直接阅读,或是下载 PDF JD 📖 《Docker技术入门与实战(第3版)》 浅显易懂,理论与实战,推荐在 Gitbook 阅读 JD 📖 《深入理解 Nginx》陶辉 教科书级 Nginx 指南,可配合极客时间视频课程 JD / 📺学习课程 课程 推荐 【咕泡学院】架构师系列课程 部分课程值得学习,特别是 James 的讲课风格超级棒,带你深入底层 【慕课网】系统学习 Docker 践行 DevOps 理念 / 【慕课在线】Kubernetes基础:开启云原生之门 2 小时不到让你快速入门,k8s,非常值得推荐! 【慕课网】Nginx 从入门到实践 / 【极客时间】Nginx 核心知识100讲 适合配套课本一起学习 十、面试 📺学习课程 课程 推荐 【慕课网】360 大牛全面解读 PHP 面试 / 【慕课网】Google 面试官亲授 升级 Java 面试 / 十一、机器学习与深度学习 📖阅读清单 书名 推荐 京东 《机器学习实战》图灵出版社 机器学习入门必读刊物 JD 《百面机器学习》 算法工程师少有的面试读物,还是彩色的,推荐给大家。包含机器学习与深度学习的核心知识 JD 📺学习课程 课程 推荐 【慕课网】刘宇波:Python3入门机器学习 经典算法与应用 看过的最好的机器学习入门视频,比吴恩达的实战更多,更浅显易懂 【网易云课堂】吴恩达:机器学习 全球机器学习爱好者都在看的学习视频 【慕课网】刘宇波:专为程序员设计的线性代数课程 程序员的内功修炼 【莫烦Python】机器学习系列 非常棒的机器学习入门,我的入门也来自于这里 十二、工具 📺学习课程 课程 推荐 【表严肃】讲正则表达式 表严肃的风格幽默风趣,轻松就把技术学到啦 【表严肃】讲 Git 表严肃的风格幽默风趣,轻松就把技术学到啦 【极客时间】Git 三剑客,携程代码平台-苏玲 目前看过的最好得 Git 学习课程 【慕课在线】IntelliJ IDEA神器使用技巧 学习 Java 前,先把工具用熟练,磨刀不误砍材工 十三、Golang 📌学习指南 无论是后台开发、前端开发、算法应用,我想 Python、Go、Shell 都将成为一门必须掌握的脚本语言。 特别在运维、高并发、爬虫等等的项目中,我想 Go 有着独特的魅力。包括 Docker、Kubernetes 都是基于 Go 语言构建的开源项目。 在互联网高速发展的当下,诸多互联网都将 Go 作为第一技术选型的语言。例如哔哩哔哩、知乎、饿了么已经将 Go 作为自己的主要开发语言。 📺学习课程 课程 推荐 【极客时间】Go语言从入门到实战 前亚马逊首席架构师,这个课听着非常有味道啊。 【慕课网实战】Google资深工程师深度讲解Go语言 慕课网比较热门的一门课,最后有分布式爬虫实战,但是更推荐上面极客时间的课程 📖阅读清单 书名 推荐 京东 文档 - Go 编程语言 Go 语言入门中文文档 / Documentation - The Go Programming Language Go 语言入门英文文档 / 《The Way to Go》中文译本中文正式名《Go 入门指南》 Go 入门指南,GitHub阅读 | LearnKu阅读 / ⛱开源项目 项目 推荐 go-colly | Scraping Framework for Golang Fast and Elegant Scraping Framework for Gophers Gin Web Framework Go 语言中比较热门的 Web 框架","categories":[],"tags":[{"name":"学习宝典","slug":"学习宝典","permalink":"http://www.frankfeekr.cn/tags/学习宝典/"}]},{"title":"【话题讨论】后台思维写前端,算法思维写后台,产品思维写算法","slug":"后台思维写前端-算法思维写后台-产品思维写算法","date":"2019-04-19T03:00:10.000Z","updated":"2021-10-23T14:22:52.650Z","comments":true,"path":"2019/04/19/后台思维写前端-算法思维写后台-产品思维写算法/","link":"","permalink":"http://www.frankfeekr.cn/2019/04/19/后台思维写前端-算法思维写后台-产品思维写算法/","excerpt":"","text":"轻松时刻,突然想写这么一个话题。 认识太多的技术达人了,但是他们大多数都有很多的共性,这里举几个例子。 有些大佬们对于深度学习各种神经网络研究颇深,机器学习信手捏来 但是居然对很多数据结构与算法竟然一知半解,一个快速排序的三种写法都不了解,红黑树不了解 Linux 甚至有些都没有学会怎么用 操作系统底层的一些概念更是没有深入了解 还认识一些后台非常有经验的人,拿到一些大厂 Offer 的人 居然连 HTTP 的基本协议都不了解 对前端简单的 HTML、CSS、JS 都没接触过 如果你有更好的例子,欢迎大家留言补充 … 其实技术就是工具,根据不同的应用场景,使用不同的技术栈。个人比较推崇人人都是产品经理,人人都架构师的思维理念。在每个项目中或许我们只是一个角色的存在,只有更好的掌握不同方向的技能,才能在自己产出的同时给到别人最好的交互体验。 前端不懂后台,怎么和后台更好的交付,怎么能提出更好的接口设计需求? 后台不懂前端,怎么才能根据不同的技术选型,为前端提供最好的接口交互设计? 算法不懂前端,不懂后台,如何才能将我们通过数学逻辑输出的有效数据更好的通过前端、后台的方式展示? 产品不懂技术,如何才能有理有据,判断项目和技术的可行性?如何才能更好的让项目落地?一个好的产品经理必须具备一定的技术水准。 作为技术而言,我深信做的不是功能,是产品。 最后,这里说说 “懂” 的理解:懂意味着了解,写过实际的项目,但并不意味着一定需要深入。 个人知识水平能力有限,希望技术达人留言交流。本文将根据大家的智慧持续更新…","categories":[],"tags":[{"name":"技术世界","slug":"技术世界","permalink":"http://www.frankfeekr.cn/tags/技术世界/"}]},{"title":"前端知识体系","slug":"前端知识体系","date":"2019-04-17T11:43:05.000Z","updated":"2021-10-23T14:22:52.650Z","comments":true,"path":"2019/04/17/前端知识体系/","link":"","permalink":"http://www.frankfeekr.cn/2019/04/17/前端知识体系/","excerpt":"","text":"前端三要素 HTML(结构):超文本标记语言(Hyper Text Markup Language),决定网页的结构和内容 CSS(表现):层叠样式表(Cascading Style Sheets),设定网页的表现样式 JavaScript(行为):是一种弱类型脚本语言,其源代码不需经过编译,而是由浏览器解释运行,用于控制网页的行为 结构层(HTML) 略 表现层(CSS) CSS 层叠样式表是一门标记语言,并不是编程语言,因此不可以自定义变量,不可以引用等,换句话说就是不具备任何语法支持,它主要缺陷如下: 语法不够强大,比如无法嵌套书写,导致模块化开发中需要书写很多重复的选择器; 没有变量和合理的样式复用机制,使得逻辑上相关的属性值必须以字面量的形式重复输出,导致难以维护; 这就导致了我们在工作中无端增加了许多工作量。为了解决这个问题,前端开发人员会使用一种称之为 【CSS 预处理器】 的工具,提供 CSS 缺失的样式层复用机制、减少冗余代码,提高样式代码的可维护性。大大提高了前端在样式上的开发效率。 什么是 CSS 预处理器 CSS 预处理器定义了一种新的语言,其基本思想是,用一种专门的编程语言,为 CSS 增加了一些编程的特性,将 CSS 作为目标生成文件,然后开发者就只要使用这种语言进行 CSS 的编码工作。转化成通俗易懂的话来说就是“用一种专门的编程语言,进行 Web 页面样式设计,再通过编译器转化为正常的 CSS 文件,以供项目使用”。 常用的 CSS 预处理器有哪些 SASS:基于 Ruby,通过服务端处理,功能强大。解析效率高。需要学习 Ruby 语言,上手难度高于 LESS。 LESS:基于 NodeJS,通过客户端处理,使用简单。功能比 SASS 简单,解析效率也低于 SASS,但在实际开发中足够了,所以我们后台人员如果需要的话,建议使用 LESS。 行为层(JavaScript) JavaScript 一门弱类型脚本语言,其源代码在发往客户端运行之前不需经过编译,而是将文本格式的字符代码发送给浏览器由浏览器解释运行。 Native 原生 JS 开发 原生 JS 开发,也就是让我们按照 【ECMAScript】 标准的开发方式,简称是 ES,特点是所有浏览器都支持。截止到当前博客发布时间(2018 年 12 月 04 日),ES 标准已发布如下版本: ES3 ES4(内部,未正式发布) ES5(全浏览器支持) ES6(常用,当前主流版本) ES7 ES8 ES9(草案阶段) 区别就是逐步增加新特性。 TypeScript 微软的标准 TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程。由安德斯·海尔斯伯格(C#、Delphi、TypeScript 之父;.NET 创立者)主导。 该语言的特点就是除了具备 ES 的特性之外还纳入了许多不在标准范围内的新特性,所以会导致很多浏览器不能直接支持 TypeScript 语法,需要编译后(编译成 JS)才能被浏览器正确执行。 JavaScript 框架 jQuery:大家熟知的 JavaScript 框架,优点是简化了 DOM 操作,缺点是 DOM 操作太频繁,影响前端性能;在前端眼里使用它仅仅是为了兼容 IE6、7、8; Angular:Google 收购的前端框架,由一群 Java 程序员开发,其特点是将后台的 MVC 模式搬到了前端并增加了模块化开发的理念,与微软合作,采用 TypeScript 语法开发;对后台程序员友好,对前端程序员不太友好;最大的缺点是版本迭代不合理(如:1代 -> 2代,除了名字,基本就是两个东西;截止发表博客时已推出了 Angular6) React:Facebook 出品,一款高性能的 JS 前端框架;特点是提出了新概念 【虚拟 DOM】 用于减少真实 DOM 操作,在内存中模拟 DOM 操作,有效的提升了前端渲染效率;缺点是使用复杂,因为需要额外学习一门 【JSX】 语言; Vue:一款渐进式 JavaScript 框架,所谓渐进式就是逐步实现新特性的意思,如实现模块化开发、路由、状态管理等新特性。其特点是综合了 Angular(模块化) 和 React(虚拟 DOM) 的优点; Axios:前端通信框架;因为 Vue 的边界很明确,就是为了处理 DOM,所以并不具备通信能力,此时就需要额外使用一个通信框架与服务器交互;当然也可以直接选择使用 jQuery 提供的 AJAX 通信功能; UI 框架 Ant-Design:阿里巴巴出品,基于 React 的 UI 框架 ElementUI:饿了么出品,基于 Vue 的 UI 框架 Bootstrap:Twitter 推出的一个用于前端开发的开源工具包 AmazeUI:又叫“妹子 UI”,一款 HTML5 跨屏前端框架 JavaScript 构建工具 Babel:JS 编译工具,主要用于浏览器不支持的 ES 新特性,比如用于编译 TypeScript WebPack:模块打包器,主要作用是打包、压缩、合并及按序加载 注:以上知识点已将 WebApp 开发所需技能全部梳理完毕 三端统一 混合开发(Hybrid App) 主要目的是实现一套代码三端统一(PC、Android、iOS)并能够调用到设备底层硬件(如:传感器、GPS、摄像头等),打包方式主要有以下两种: 云打包:HBuild -> HBuildX,DCloud 出品;API Cloud 本地打包: Cordova(前身是 PhoneGap) 微信小程序 详见微信官网,这里就是介绍一个方便微信小程序 UI 开发的框架:WeUI 后端技术 前端人员为了方便开发也需要掌握一定的后端技术,但我们 Java 后台人员知道后台知识体系极其庞大复杂,所以为了方便前端人员开发后台应用,就出现了 NodeJS 这样的技术。 NodeJS 的作者已经声称放弃 NodeJS(说是架构做的不好再加上笨重的 node_modules,可能让作者不爽了吧),开始开发全新架构的 Deno 既然是后台技术,那肯定也需要框架和项目管理工具,NodeJS 框架及项目管理工具如下: Express:NodeJS 框架 Koa:Express 简化版 NPM:项目综合管理工具,类似于 Maven YARN:NPM 的替代方案,类似于 Maven 和 Gradle 的关系 附:当前主流前端框架 Vue.js iView iview 是一个强大的基于 Vue 的 UI 库,有很多实用的基础组件比 elementui 的组件更丰富,主要服务于 PC 界面的中后台产品。使用单文件的 Vue 组件化开发模式 基于 npm + webpack + babel 开发,支持 ES2015 高质量、功能丰富 友好的 API ,自由灵活地使用空间。 官网地址 Github iview-admin 备注:属于前端主流框架,选型时可考虑使用,主要特点是移动端支持较多 ElementUI Element 是饿了么前端开源维护的 Vue UI 组件库,组件齐全,基本涵盖后台所需的所有组件,文档讲解详细,例子也很丰富。主要用于开发 PC 端的页面,是一个质量比较高的 Vue UI 组件库。 官网地址 Github vue-element-admin 备注:属于前端主流框架,选型时可考虑使用,主要特点是桌面端支持较多 ICE 飞冰是阿里巴巴团队基于 React/Angular/Vue 的中后台应用解决方案,在阿里巴巴内部,已经有 270 多个来自几乎所有 BU 的项目在使用。飞冰包含了一条从设计端到开发端的完整链路,帮助用户快速搭建属于自己的中后台应用。 官网地址 Github 备注:主要组件还是以 React 为主,截止 2019 年 02 月 17 日更新博客前对 Vue 的支持还不太完善,目前尚处于观望阶段 VantUI Vant UI 是有赞前端团队基于有赞统一的规范实现的 Vue 组件库,提供了一整套 UI 基础组件和业务组件。通过 Vant,可以快速搭建出风格统一的页面,提升开发效率。 官网地址 Github AtUI at-ui 是一款基于 Vue 2.x 的前端 UI 组件库,主要用于快速开发 PC 网站产品。 它提供了一套 npm + webpack + babel 前端开发工作流程,CSS 样式独立,即使采用不同的框架实现都能保持统一的 UI 风格。 官网地址 Github CubeUI cube-ui 是滴滴团队开发的基于 Vue.js 实现的精致移动端组件库。支持按需引入和后编译,轻量灵活;扩展性强,可以方便地基于现有组件实现二次开发。 官网地址 Github 混合开发 Flutter Flutter 是谷歌的移动端 UI 框架,可在极短的时间内构建 Android 和 iOS 上高质量的原生级应用。Flutter 可与现有代码一起工作, 它被世界各地的开发者和组织使用, 并且 Flutter 是免费和开源的。 官网地址 Github 备注:Google 出品,主要特点是快速构建原生 APP 应用程序,如做混合应用该框架为必选框架 Ionic Ionic 既是一个 CSS 框架也是一个 Javascript UI 库,Ionic 是目前最有潜力的一款 HTML5 手机应用开发框架。通过 SASS 构建应用程序,它提供了很多 UI 组件来帮助开发者开发强大的应用。它使用 JavaScript MVVM 框架和 AngularJS/Vue 来增强应用。提供数据的双向绑定,使用它成为 Web 和移动开发者的共同选择。 官网地址 官网文档 Github 微信小程序 mpvue mpvue 是美团开发的一个使用 Vue.js 开发小程序的前端框架,目前支持 微信小程序、百度智能小程序,头条小程序 和 支付宝小程序。 框架基于 Vue.js,修改了的运行时框架 runtime 和代码编译器 compiler 实现,使其可运行在小程序环境中,从而为小程序开发引入了 Vue.js 开发体验。 官网地址 Github 备注:完备的 Vue 开发体验,并且支持多平台的小程序开发,推荐使用 WeUI WeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页和微信小程序量身设计,令用户的使用感知更加统一。包含 button、cell、dialog、toast、article、icon 等各式元素。 官网地址 Github","categories":[],"tags":[{"name":"前端","slug":"前端","permalink":"http://www.frankfeekr.cn/tags/前端/"}]},{"title":"RESTful 架构详解","slug":"RESTful-架构详解","date":"2019-04-17T11:07:45.000Z","updated":"2021-10-23T14:22:52.646Z","comments":true,"path":"2019/04/17/RESTful-架构详解/","link":"","permalink":"http://www.frankfeekr.cn/2019/04/17/RESTful-架构详解/","excerpt":"","text":"1. 什么是REST REST (Representational State Transfer),中文意思是:表述性状态转移。 一组架构约束条件和原则 如果一个架构符合 REST 的约束条件和原则,我们就称它为 RESTful 架构 2. RESTful的基本概念 在 REST 中,一切的内容都被认为是一种资源 每个资源都由 URI 唯一标识 使用统一的接口处理资源请求(POST/GET/PUT/DELETE/HEAD) 无状态(每次请求之前是无关联,没有 session ) 3. REST和RESTful API区别 3.1 REST REST:全称应该为:Resource Representational State Transfer(表现层状态转移),通俗来讲即资源在网络中以某种表现形式进行状态转移。 REST可以理解为 client 和 server 间的一种交互方式,即 client 发出 http 请求,server 端的资源发生状态转移,比如更新、删除等。 3.2 RESTful API 可以理解为 server 端提供的具有 REST 风格的接口 4. 理解RESTful 要理解RESTful架构,需要理解Representational State Transfer这个词组到底是什么意思,它的每一个词都有些什么涵义。 下面我们结合REST原则,围绕资源展开讨论,从资源的定义、获取、表述、关联、状态变迁等角度,列举一些关键概念并加以解释。 资源与URI 统一资源接口 资源的表述 资源的链接 状态的转移 4.1 资源和URI 使用 / 来表示资源的层级关系 使用 ? 用来过滤资源 使用 _ 或者 - 让URI的可读性更好 , 或 ; 可以用来表示同级资源的关系 4.2 统一资源接口 请求方法 描述 GET 获取某个资源。 幂等(取多少次结果都没有变化) POST 创建一个新的资源 PUT 替换某个已有的资源(更新操作) , 幂等(更新多次只保存一个结果) DELETE 删除某个资源 HEAD 主要用于确认 URL 的有效性以及资源更新的日期时间等 PATCH 新引入的,对PUT方法的补充,用来对已知资源进行局部更新 4.3 资源表述 上面提到,客户端通过 HTTP 方法可以获取资源,是吧? 不,确切的说,客户端获取的只是资源的表述而已。资源在外界的具体呈现,可以有多种表述(或成为表现、表示)形式,在客户端和服务端之间传送的也是资源的表述,而不是资源本身。 例如文本资源可以采用 html、xml、json 等格式,图片可以使用 PNG 或 JPG 展现出来。 资源的表述包括数据和描述数据的元数据,例如,HTTP 头 “Content-Type” 就是这样一个元数据属性。 那么客户端如何知道服务端提供哪种表述形式呢? 答案是可以通过 HTTP 内容协商,客户端可以通过 Accept 头请求一种特定格式的表述,服务端则通过 Content-Type 告诉客户端资源的表述形式。 MIME 类型 accept: text/xml html文件 Content-Type告诉客户端资源的表述形式 4.4 资源的链接 超媒体即应用状态引擎(可以做多层链接) https://api.github.com/repos/github 1234{ \"message\": \"Not Found\", \"documentation_url\": \"https://developer.github.com/v3\"} 4.5 状态转移 服务器端不应该保存客户端状态。 应用状态 -> 服务器端不保存应用状态 访问订单 根据接口去查询 访问商品 查询 5. RESTful的最佳设计 以豆瓣网为例 应该尽量将 API 部署在专用域名之下 http://api.douban.com/v2/user/1000001?apikey=XXX 应该将 API 的版本号放入 URL http://api.douban.com/v2/user/1000001?apikey=XXX 在 RESTful 架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的 ”集合”(collection),所以 API 中的名词也应该使用复数。 http://api.douban.com/v2/book/:id (获取图书信息) http://api.douban.com/v2/movie/subject/:id (电影条目信息) http://api.douban.com/v2/music/:id (获取音乐信息) http://api.douban.com/v2/event/:id (获取同城活动) 对于资源的具体操作类型,由 HTTP 动词表示。常用的 HTTP 动词有下面四个(对应 增/删/改/查 )。 GET(select):从服务器取出资源(一项或多项)。 eg. 获取图书信息 【GET】 http://api.douban.com/v2/book/:id POST(create):在服务器新建一个资源。 eg. 用户收藏某本图书 【POST】 http://api.douban.com/v2/book/:id/collection PUT(update):在服务器更新资源(客户端提供改变后的完整资源)。 eg. 用户修改对某本图书的收藏 【PUT】 http://api.douban.com/v2/book/:id/collection DELETE(delete):从服务器删除资源。 eg. 用户删除某篇笔记 【DELETE】 http://api.douban.com/v2/book/annotation/:id 如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果 ?limit=10:指定返回记录的数量 eg. 获取图书信息 【GET】 http://api.douban.com/v2/book/:id?limit=10 服务器向用户返回的状态码和提示信息 每个状态码代表不同意思, 就像代号一样 HTTP状态码 1系 信息 2系 正常返回 3系 重定向 4系 数据异常 5系 服务器异常 业务状态码 参考资料 RESTful 架构详解 | 菜鸟教程 RESTful API 设计指南 - 阮一峰的网络日志","categories":[],"tags":[{"name":"HTTP","slug":"HTTP","permalink":"http://www.frankfeekr.cn/tags/HTTP/"}]},{"title":"后台开发技能图谱","slug":"后台开发技能图谱","date":"2019-04-17T05:53:20.000Z","updated":"2021-10-23T14:22:52.650Z","comments":true,"path":"2019/04/17/后台开发技能图谱/","link":"","permalink":"http://www.frankfeekr.cn/2019/04/17/后台开发技能图谱/","excerpt":"","text":"欢迎在 issue#25 中留言,持续更新","categories":[],"tags":[{"name":"skill-tree","slug":"skill-tree","permalink":"http://www.frankfeekr.cn/tags/skill-tree/"}]},{"title":"Git 命令速查","slug":"Git命令速查","date":"2019-04-17T05:42:44.000Z","updated":"2021-10-23T14:22:52.646Z","comments":true,"path":"2019/04/17/Git命令速查/","link":"","permalink":"http://www.frankfeekr.cn/2019/04/17/Git命令速查/","excerpt":"","text":"本文参考极客时间苏玲老师的《玩转Git三剑客》 添加配置 12git config [--local | --global | --system] user.name 'Your name'git config [--local | --global | --system] user.email 'Your email' 查看配置 1git config --list [--local | --global | --system] 区别 123local:区域为本仓库global: 当前用户的所有仓库system: 本系统的所有用户 git add . 和 git add -u区别 12git add . :将工作空间新增和被修改的文件添加的暂存区git add -u :将工作空间被修改和被删除的文件添加到暂存区(不包含没有纳入Git管理的新增文件) 创建仓库 123456git init [project folder name] 初始化 git 仓库git add [fileName] 把文件从工作目录添加到暂存区git commit -m'some information' 用于提交暂存区的文件git commit -am'Some information' 用于提交跟踪过的文件git log 查看历史git status 查看状态 额外 git add -u 可以添加所有已经被 git 控制的文件到暂存区 以前删除文件夹只会用 「-rf」,今天学到了 「-r」,并得知它们两个区别:「-r」 有时候会提示是否确认删除。 给文件重命名的简便方法 12git mv [old file name] [new file name]git commit -m 'some information' 通过git log查看版本演变历史 123456git log --all 查看所有分支的历史git log --all --graph 查看图形化的 log 地址git log --oneline 查看单行的简洁历史。git log --oneline -n4 查看最近的4条简洁历史。git log --oneline --all -n4 --graph 查看所有分支最近4条单行的图形化历史。git help --web log 跳转到git log 的帮助文档网页 1git branch -v 查看本地有多少分支 通过图形界面工具来查看版本历史 1gitk 探密.git目录 查看.git文件夹下的内容: 1ls .git/ -al 如下: 1234567891011121314drwxr-xr-x 1 Andy 197609 0 12月 17 22:38 ./drwxr-xr-x 1 Andy 197609 0 12月 17 21:50 ../-rw-r--r-- 1 Andy 197609 7 12月 17 22:38 COMMIT_EDITMSG-rw-r--r-- 1 Andy 197609 301 12月 12 22:55 config-rw-r--r-- 1 Andy 197609 73 12月 12 22:55 description-rw-r--r-- 1 Andy 197609 96 12月 19 00:00 FETCH_HEAD-rw-r--r-- 1 Andy 197609 23 12月 12 22:55 HEADdrwxr-xr-x 1 Andy 197609 0 12月 12 22:55 hooks/-rw-r--r-- 1 Andy 197609 249 12月 17 22:38 indexdrwxr-xr-x 1 Andy 197609 0 12月 12 22:55 info/drwxr-xr-x 1 Andy 197609 0 12月 12 22:55 logs/drwxr-xr-x 1 Andy 197609 0 12月 17 22:38 objects/-rw-r--r-- 1 Andy 197609 114 12月 12 22:55 packed-refsdrwxr-xr-x 1 Andy 197609 0 12月 12 22:55 refs/ 123456cat命令主要用来查看文件内容,创建文件,文件合并,追加文件内容等功能。cat HEAD 查看HEAD文件的内容git cat-file 命令 显示版本库对象的内容、类型及大小信息。git cat-file -t b44dd71d62a5a8ed3 显示版本库对象的类型git cat-file -s b44dd71d62a5a8ed3 显示版本库对象的大小git cat-file -p b44dd71d62a5a8ed3 显示版本库对象的内容 .git里几个常用的如下: 123456HEAD:指向当前的工作路径config:存放本地仓库(local)相关的配置信息。refs/heads: 存放分支refs/heads/master/: 指向master分支最后一次commitrefs/tags: 存放tag,又叫里程牌 (当这次commit是具有里程碑意义的 比如项目1.0的时候 就可以打tag)objects:核心文件,存储文件 .git/objects/ 存放所有的 git 对象,对象哈希值前 2 位作为文件夹名称,后 38 位作为对象文件名, 可通过 git cat-file -p 命令,拼接文件夹名称+文件名查看。 commit、tree和blob三个对象之间的关系 123commit: 提交时的镜像tree: 文件夹blob: 文件 【同学问题】 每次commit,git 都会将当前项目的所有文件夹及文件快照保存到objects目录,如果项目文件比较大,不断迭代,commit无数次后,objects目录中文件大小是不是会变得无限大? 【老师解答】 Git对于内容相同的文件只会存一个blob,不同的commit的区别是commit、tree和有差异的blob,多数未变更的文件对应的blob都是相同的,这么设计对于版本管理系统来说可以省很多存储空间。其次,Git还有增量存储的机制,我估计是对于差异很小的blob设计的吧。 分离头指针情况下的注意事项 detached HEAD 进一步理解HEAD和branch 12345git checkout -b new_branch [具体分支 或 commit] 创建新分支并切换到新分支git diff HEAD HEAD~1 比较最近两次提交git diff HEAD HEAD~2 比较最近和倒数第三次提交git diff HEAD HEAD^ 比较最近两次提交git diff HEAD HEAD^^ 比较最近和倒数第三次提交 怎么删除不需要的分支? 查看分支: 1git branch -av 删除分支命令: 12git branch -d [branch name] 删除git branch -D [branch name] 强制删除 怎么修改最新 commit 的 message? 1git commit --amend 对最近一次的commit信息进行修改 怎么修改老旧 commit 的 message? 1git rebase -i [要更改的commit的上一级commit] 接下来就是一个交互过程… 这期间会产生一个detached HEAD,然后将改好的commit指向该detached HEAD,如下图所示: git rebase工作的过程中,就是用了分离头指针。rebase意味着基于新base的commit来变更部分commits。它处理的时候,把HEAD指向base的commit,此时如果该commit没有对应branch,就处于分离头指针的状态,然后重新一个一个生成新的commit,当rebase创建完最后一个commit后,结束分离头状态,Git让变完基的分支名指向HEAD。 怎样把连续的多个commit整理成1个? 1git rebase -i [要更改的commit的上一级commit] 123456789101112131415161718192021222324252627282930$ git log --graph* commit 7d3386842a2168ae630b65f687364243139c893c (HEAD -> master, origin/master, origin/HEAD)| Author: aimuch <[email protected]>| Date: Thu Dec 20 23:34:18 2018 +0800|| update|* commit 9eb3188bbc63cae1bfed5f9dfc1593019e360a6a| Author: aimuch <[email protected]>| Date: Wed Dec 19 20:30:14 2018 +0800|| update|* commit bbe6d53e2b477f2d2aa402af7f315ecdfc63459e| Author: aimuch <[email protected]>| Date: Wed Dec 19 20:12:29 2018 +0800|| update|* commit 7735d66ded7f98adeca93d96fb7be12ffb67c76a| Author: aimuch <[email protected]>| Date: Wed Dec 19 00:27:00 2018 +0800|| update|* commit d9f9d115fab425b5654f8ccfec6a996aef35b76b| Author: aimuch <[email protected]>| Date: Wed Dec 19 00:23:36 2018 +0800|| update 123456789101112131415161718192021pick 7735d66 update #合并到该commit上squash bbe6d53 updatesquash 9eb3188 updatesquash 7d33868 update# Rebase d9f9d11..7d33868 onto d9f9d11 (4 commands)## Commands:# p, pick <commit> = use commit# r, reword <commit> = use commit, but edit the commit message# e, edit <commit> = use commit, but stop for amending# s, squash <commit> = use commit, but meld into previous commit# f, fixup <commit> = like \"squash\", but discard this commit's log message# x, exec <command> = run command (the rest of the line) using shell# b, break = stop here (continue rebase later with 'git rebase --continue')# d, drop <commit> = remove commit# l, label <label> = label current HEAD with a name# t, reset <label> = reset HEAD to a label# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]# . create a merge commit using the original merge commit's# . message (or the oneline, if no original merge commit was# . specified). Use -c <commit> to reword the commit message. 12345678910111213141516# This is a combination of 4 commits.# This is the 1st commit message:update# This is the commit message #2:update# This is the commit message #3:update# This is the commit message #4:update git 修改.gitignore后生效 1234git rm -r --cached . #清除缓存git add . #重新trace filegit commit -m \"update .gitignore\" #提交和注释git push origin master #可选,如果需要同步到remote上的话 怎么比较暂存区和HEAD所含文件的差异? 1git diff --cached 或者 1git diff --staged 怎么比较工作区和暂存区所含文件的差异? 1git diff 1git diff -- [filename/pathname] #比较具体的文件或者路径 如何让暂存区恢复成和HEAD的一样? 1git reset HEAD 1234git reset 有三个参数--soft 这个只是把 HEAD 指向的 commit 恢复到你指定的 commit,暂存区 工作区不变--hard 这个是 把 HEAD, 暂存区, 工作区 都修改为 你指定的 commit 的时候的文件状态--mixed 这个是不加时候的默认参数,把 HEAD,暂存区 修改为 你指定的 commit 的时候的文件状态,工作区保持不变 如何让工作区的文件恢复为和暂存区一样? 1git checkout -- <file>... 恢复工作区用checkout,恢复暂存区用reset。 怎样取消暂存区部分文件的更改? 1git reset HEAD -- <file>... 看看不同提交的指定文件的差异 1git diff commit-id1 commit-id2 -- <file>... 正确删除文件的方法 1git rm <file> 开发中临时加塞了紧急任务怎么处理? 12git stash list #查看stash中存放的信息git stash #将当前工作区内容存放到\"堆栈\"中 1git stash apply #把\"堆栈\"里面的内容弹出到工作区中,同时\"堆栈\"中信息还在 1git stash pop #把\"堆栈\"里面的内容弹出到工作区中,同时丢弃\"堆栈\"中最新的信息 如何指定不需要Git管理的文件? 1.gitignore 【同学提问】 如果提交commit后,想再忽略一些已经提交的文件,怎么处理。 【老师回答】 The problem is that .gitignore ignores just files that weren’t tracked before (by git add). Run git reset name_of_file to unstage the file and keep it. In case you want to also remove given file from the repository (after pushing), use git rm --cached name_of_file. 把想忽略的文件添加到 .gitignore ;然后通过 git rm – cached name_of_file 的方式删除掉git仓库里面无需跟踪的文件。 添加远程仓库 1git remote add [shortname] [url] 配置公私钥 1、 检查是否已存在相应的ssh key: 打开终端, 输入: 1ls -al ~/.ssh 核对列出来的ssh key是否有已存在的,假如你没有看到列出的公私钥对,或是不想再用之前的公私钥对,你可以选择下面的步骤生成新的公私钥对. 2、生成新的ssh key,并添加至ssh-agent: 打开终端, 使用ssh key生成命令: 1ssh-keygen -t rsa -b 4096 -C \"[email protected]\" 注意 :后面的邮箱对应相应账号的邮箱,假如是github的账号,且注册账号的邮箱为[email protected],则命令行为:ssh-keygen -t rsa -b 4096 -C "[email protected]。 3、接下来会提示你保存的ssh key的名称以及路径。默认路径是(/Users/you/.ssh/id_rsa) (you为用户个人目录)。这一步很重要,如果你使用默认的,且下一个账号也是使用默认的路径和文件名,那么之前的ssh key就会被后来生成的ssh key重写,从而导致之前的账号不可用。因此,正确的做法是给它命名,最后以应用名进行命名,因为更容易区分。以下是我个人配的: 1/Users/andy/.ssh/github_rsa 4、接下来会提示设置ssh安全密码。这一步可以使用默认的(即不设置密码),直接按回车即可。倘若想了解更多关于ssh key密码设置的细节,可访问: “Working with SSH key passphrases” 。 5、 ssh key生成后,接下来需要为ssh key添加代理,这是为了让请求自动对应相应的账号。网上很多文章写到需要另外配置config文件,经本人亲测,其实是不需要的,在生成了ssh key后,通过为生成的ssh key添加代理即可,为ssh key添加代理命令:ssh-add ~/.ssh/xxx_rsa,xxx_rsa是你生成的ssh key的私钥名。 6、连接测试 接下来我们测试是否配置成功,打开终端,输入: 1ssh -T [email protected] Git Pull 避免用户名和密码方法 “如果你使用的是 SSH 方式连接远端,并且设置了一个没有口令的密钥,这样就可以在不输入用户名和密码的情况下安全地传输数据。 然而,这对 HTTP 协议来说是不可能的 —— 每一个连接都是需要用户名和密码的。 这在使用双重认证的情况下会更麻烦,因为你需要输入一个随机生成并且毫无规律的 token 作为密码。” —— 《官方文档说明》 每次使用命令 git pull 从服务器获得最新代码时,都需要输入用户名和密码,这样浪费了大量的时间和热情,这里的几行命令将会对你的代码更新有一定帮助。 1git config --global credential.helper store 执行完后查看 %HOME% 目录下的 .gitconfig 文件,会多了一项。 12[credential] helper = store 此时,通过 git pull 即可无需账号密码就可以更新代码 参考资料:Git Pull 避免用户名和密码方法 - 王信平 - 博客园 怎么快速淘到感兴趣的开源项目? UI界面高级搜索: https://github.com/search/advanced 命令高级搜索: 1git 最好 学习 资料 in:readme stars:>1000 language:c 上述命令的意思是搜索reademe中包含git、最好、学习、资料”且star大于1000的,用C语言编写的仓库。","categories":[],"tags":[{"name":"Git","slug":"Git","permalink":"http://www.frankfeekr.cn/tags/Git/"}]},{"title":"深度学习初识","slug":"深度学习初识","date":"2019-04-16T13:30:48.000Z","updated":"2021-10-23T14:22:52.652Z","comments":true,"path":"2019/04/16/深度学习初识/","link":"","permalink":"http://www.frankfeekr.cn/2019/04/16/深度学习初识/","excerpt":"","text":"一、入门基本概念 机器学习简介 机器学习:无序数据转化为价值的方法 机器学习价值:从数据中抽取规律,并预测未来 机器学习应用举例 分类问题:图像识别、垃圾邮件识别 回归问题:股价预测、房价预测 排序问题:点击率预估、推荐 生成问题:图像生成、图像风格转换、图像文字描述生成 机器学习应用流程 机器学习岗位职责 数据处理(采集+去噪) 模型训练(特征+模型) 模型评估与优化(MSE、F1-score、AUC+调参) 模型应用(A/B测试) 深度学习简介 人工智能、机器学习、深度学习之间的关系 1人工智能(AI)> 机器学习(Machine Learning)> 深度学习(Deep learning) 深度学习与机器学习关系 机器学习是实现人工智能的方法 深度学习是实现机器学习算法的技术 深度学习算法集合 卷积神经网络(Convolutional Neural Network, CNN) CV 领域使用较多 循环神经网络(Recurrent Neural Networks,RNNs) NLP 领域使用较多 处理不定长数据 自动编码器 稀疏编码 深度信念网络 深度学习 + 强化学习 = 深度强化学习 AlphaGo AlphaZero 深度学习进展 图像分类 ImageNet: http://image-net.org/ 机器翻译 图像生成 Implementing Neural Artistic Style Transfer | L2Program 字体生成 AlphaGo 二、神经网络 人体神经元模型 神经网络模型是模仿人类的大脑神经构造而构造的。先来看人体神经元模型。 神经元的可以分为四个区域: 接收区(receptive zone):树突接收到输入信息。 触发区(trigger zone):位于轴突和细胞体交接的地方,决定是否产生神经冲动。 传导区(conducting zone):由轴突进行神经冲动的传递。 输出区(output zone):神经冲动的目的就是要让神经末梢,突触的神经递质或电力释出,才能影响下一个接受的细胞(神经元、肌肉细胞或是腺体细胞),此称为突触传递。 人工神经网络 人工神经网络(ANN:Artificial Neural Network),简称神经网络(NN:Neural Network)。迄今为止,人工神经网络尚无统一定义, 其实一种模拟了人体神经元构成的数学模型,依靠系统的复杂程度,通过调整内部大量节点之间相互连接的关系,从而达到处理信息的目的。 上图显示了人工神经网络是一个分层模型,逻辑上可以分为三层: 输入层:输入层接收特征向量 x 。 输出层:输出层产出最终的预测 h 。 隐含层:隐含层介于输入层与输出层之间,之所以称之为隐含层,是因为当中产生的值并不像输入层使用的样本矩阵 X 或者输出层用到的标签矩阵 y 那样直接可见。 1. 神经元 2. 逻辑回归模型 1神经元 -> 激活函数sigmoid -> 二元类逻辑斯蒂回归模型 神经元 -> 多输出 W从向量扩展为矩阵 输出W*x则变成向量 多输出神经元 -> softmax -> 多分类逻辑斯蒂回归模型 目标函数 也称为损失函数,衡量对数据的拟合程度 梯度下降 下山算法 找方向 走一步 梯度下降算法 三、Tensorflow基础 Tensorflow简介 Google Brain 第二代机器学习框架 计算图模型 命令式变成 声明式变成 TensorFlow 安装 TensorFlow 官方文档 TensorFlow 安装方法 基于 VirtualEnv 的安装 原生 pip Docker 从源代码安装 参考资料 机器学习入门(五) – 神经网络 深度学习-初识 - Corwien - SegmentFault 思否","categories":[],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"http://www.frankfeekr.cn/tags/深度学习/"}]},{"title":"手把手教你,搭建搬瓦工 SS 代理","slug":"手把手教你,搭建搬瓦工-SS-代理","date":"2019-04-16T11:19:40.000Z","updated":"2021-10-23T14:22:52.651Z","comments":true,"path":"2019/04/16/手把手教你,搭建搬瓦工-SS-代理/","link":"","permalink":"http://www.frankfeekr.cn/2019/04/16/手把手教你,搭建搬瓦工-SS-代理/","excerpt":"","text":"一、必须搞懂的几个名词 1. 什么是 VPS 虚拟专用服务器(英语:Virtual private server,缩写为 VPS),是将一台服务器分割成多个虚拟专享服务器的服务。 2. 什么是 VPN VPN 在很多人心目中就是用来翻墙的工具,其实不是。VPN 最主要的功能,并不是用来翻墙,只是它可以达到翻墙的目的。 虚拟专用网络(英语:Virtual Private Network,缩写为VPN),它的功能是:在公用网络上建立专用网络,进行加密通讯。在企业网络和高校的网络中应用很广泛。你接入 VPN,其实就是接入了一个专有网络,你的网络访问都从这个出口出去,你和 VPN 之间的通信是否加密,取决于你连接 VPN 的方式或者协议。 3. 什么是 ss / ssr ss ss作者是 GitHub:clowwindy,大约两年前,他自己为了翻墙写了shadowsocks,简称 ss 或者叫影梭,后来他觉得这个东西非常好用,速度快,而且不会被封锁,他就把源码共享在了 GitHub 上,然后就火了,但是后来作者被请去喝茶,删了代码,并且保证不再参与维护更新。 ssr 在ss作者被喝茶之后,GitHub 上出现了一个叫 breakwa11(破娃)的帐号,声称 ss 容易被防火墙检测到,所以在混淆和协议方面做了改进,更加不容易被检测到,而且兼容 ss,改进后的项目叫 shadowsocks-R,简称 ssr,然后 ss 用户和 ssr 用户自然分成了两个派别,互相撕逼,后来,破娃被人肉出来,无奈之下删除了 ssr 的代码,并且解散了所有相关群组。 4. vpn和ss/ssr的区别和优缺点 通过上面的介绍,其实基本已经能看出 vpn 和 ss/ssr 的区别了,那么他们到底孰优孰劣。 因为 VPN 是走的专用通道,它是用来给企业传输加密数据用的,所以 VPN 的流量特征很明显,以 openvpn 为例,更详细的在这里不说了,流量特征明显,防火墙直接分析你的流量,如果特征匹配,直接封掉。 ss/ssr 的目的就是用来翻墙的,而 VPN 的目的是用来加密企业数据的,对于 VPN 来说安全是第一位的,而对于 ss/ssr 来说穿透防火墙是第一位,抗干扰性强,而且对流量做了混淆,所有流量在通过防火墙的时候,基本上都被识别为普通流量,也就是说你翻墙了,但是政府是检测不到你在翻墙的。两者的出发点和着重点就不同,ss/ssr 更注重流量的混淆加密。如果要安全匿名上网,可以用 vpn+tor 或者 ss/ssr+tor。 而安全性方面还要补充的一点就是,国内 VPN 服务商,政府是很容易拿到他们的服务器日志的,如果他们真的这样做了,你翻墙做了什么,一览无余。 5. 什么是 GFW 防火长城(英语:Great Firewall,常用简称:GFW,中文也称中国国家防火墙,中国大陆民众俗称墙、防火墙、功夫网等等),是对中华人民共和国政府在其互联网边界审查系统(包括相关行政审查系统)的统称。 此系统起步于 1998 年,其英文名称得自于 2002年5月17日 Charles R. 二、翻墙的原理 ss 和 ssr 它的原理都是一样的,就是 socks5 代理。socks 代理只是简单的传递数据包,而不必关心是何种协议,所以 socks 代理比其他应用层代理要快的多。socks5 代理是把你的网络数据请求通过一条连接你和代理服务器之间的通道,由服务器转发到目的地,这个过程中你是没有通过一条专用通道的,只是数据包的发出,然后被代理服务器收到,整个过程并没有额外的处理。通俗的说,现在你有一个代理服务器在香港,比如你现在想要访问google,你的电脑发出请求,流量通过 socks5 连接发到你在香港的服务器上,然后再由你在香港的服务器去访问 google,再把访问结果传回你的电脑,这样就实现了翻墙。 直连模式就是流量不走代理 ,PAC 模式简单说就是国内地址不走代理,国外走代理,全局模式就是不管国内国外,所有流量通过代理服务器访问 下载 ss 或者 ssr 客户端:Shadowsocks - A secure socks5 proxy 三、软硬件环境 这里以搬瓦工为例 一台搬瓦工服务器,或者一台国外的服务器。 搬瓦工服务器配置 系统:CentOS 7 x86_64 bbr 一台可以上网的电脑 搬瓦工服务器 常用链接 搬瓦工用户登录 https://bwh88.net/clientarea.php 搬瓦工免费更换IP的方法 https://www.banwago.com/2058.html 登录搬瓦工KiwiVM后台管理面板,在新窗口打开以下链接进行IP检测: https://kiwivm.64clouds.com/main-exec.php?mode=blacklistcheck 购买方式 2019 最新搬瓦工购买教程 & 支付宝支付教程-Bandwagonhost中文网 恢复搬瓦工账号 解决方法很简单。我们进入 KiwiVM 后台之后,让 VPS 处于开机状态(running),然后点击左侧菜单的 Root shell – basic,然后输入如下命令: 1echo \"root:mypassword\" | chpasswd 命令很简单,我们就自己打一下吧。注意空格,其中“root:mypassword”之间的冒号是英文冒号,且中间没有空格。“root”就是用户名,不需要改动,“mypassword”就是你要设置的密码,根据自己情况进行填写。 参考资料:搬瓦工 VPS 已关机但是无法修改 root 密码的解决办法-Bandwagonhost中文网 Quick Start 1. 服务器配置 前置环境 Python zip Python 与 pip 安装 官网下载 pip 18.0 这里提供 18.0 的安装版本 1wget https://files.pythonhosted.org/packages/69/81/52b68d0a4de760a2f1979b0931ba7889202f302072cc7a0d614211bc7579/pip-18.0.tar.gz 移动到 /usr/local 目录,并进入目录解压 12345mv pip-18.0.tar.gz /usr/local/cd /usr/local/#解压到当前目录,保留原文件tar -zxvf pip-18.0.tar.gz 进入解压后的 pip-18.0 目录,使用 Python 安装 12cd pip-18.0python setup.py install 发现没安装setuptools。同样的方法安装setuptools。在/usr/local目录下使用wget命令下载setuptools-40.2.0.zip,例如: 1wget https://files.pythonhosted.org/packages/ef/1d/201c13e353956a1c840f5d0fbf0461bd45bbd678ea4843ebf25924e8984c/setuptools-40.2.0.zip 1234567yum install -y unzip zipunzip setuptools-40.2.0.zipcd setuptools-40.2.0# 安装 setuptoolspython setup.py installcd ../pip-18.0python setup.py install 使用 pip show pip 查看 pip,发现 pip 终于成功安装。 安装 Shadowsocks server 使用 pip 命令安装 ss server: 1pip install shadowsocks 配置 Shodowsocks server 使用配置文件设置 ss server 可以方便后面修改。创建一个 json 文件 /etc/shadowsocks.json,配置如下: 12345678910{ \"server\": \"修改1:你的服务器IP地址\", \"server_port\":443, \"local_address\": \"127.0.0.1\", \"local_port\":1080, \"password\":\"修改2:这里设置你的密码\", \"timeout\":300, \"method\":\"aes-256-cfb\", \"fast_open\": false} 可以针对不同的用户配置不同的用户名和密码,方法如下: 1234567891011{ \"server\": \"0.0.0.0\", \"port_password\": { \"8381\": \"foobar1\", \"8382\": \"foobar2\", \"8383\": \"foobar3\", \"8384\": \"foobar4\" }, \"timeout\": 300, \"method\": \"aes-256-cfb\"} 在前台运行ss server配置文件: 1ssserver -c /etc/shadowsocks.json 在后台运行ss server配置文件: 12ssserver -c /etc/shadowsocks.json -d startssserver -c /etc/shadowsocks.json -d stop 2. 客户端配置 Windows 一键下载:Shadowsocks-4.1.5.zip 历史版本:shadowsocks-win Mac OS X 一键下载:ShadowsocksX-NG.1.8.1.zip 历史版本:ShadowsocksX-NG 推荐一些翻墙代理 V2SS SSD VPS Servers, Cloud Servers and Cloud Hosting by Vultr - Vultr.com 蓝灯 - 秒杀VPN … …","categories":[],"tags":[{"name":"ss","slug":"ss","permalink":"http://www.frankfeekr.cn/tags/ss/"}]},{"title":"打开 Docker 大门","slug":"打开Docker大门","date":"2019-01-03T16:36:55.000Z","updated":"2021-10-23T14:22:52.651Z","comments":true,"path":"2019/01/04/打开Docker大门/","link":"","permalink":"http://www.frankfeekr.cn/2019/01/04/打开Docker大门/","excerpt":"","text":"一、Docker 快速入门 A simple, interactive and fun playground to learn Docker:Play with Docker 初探 Docker 什么是 Docker Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 GitHub 上进行维护。Docker 项目后来还加入了 Linux 基金会,并成立推动 开放容器联盟(OCI)。 Docker 自开源后受到广泛的关注和讨论,至今其 GitHub 项目已经超过 4 万 6 千个星标和一万多个 fork。甚至由于 Docker 项目的火爆,在 2013 年底,dotCloud 公司决定改名为 Docker。Docker 最初是在 Ubuntu 12.04 上开发实现的;Red Hat 则从 RHEL 6.5 开始对 Docker 进行支持;Google 也在其 PaaS 产品中广泛应用 Docker。 Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于 操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于 LXC,从 0.7 版本以后开始去除 LXC,转而使用自行开发的 libcontainer,从 1.11 开始,则进一步演进为使用 runC 和 containerd。 Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。 下面的图片比较了 Docker 和传统虚拟化方式的不同之处。传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。 为什么要使用 Docker 作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。 更高效的利用系统资源 由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。 更快速的启动时间 传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。 一致的运行环境 开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 「这段代码在我机器上没问题啊」 这类问题。 持续交付和部署 对开发和运维(DevOps)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。 使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成(Continuous Integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署(Continuous Delivery/Deployment) 系统进行自动部署。 而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。 更轻松的迁移 由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。 更轻松的维护和扩展 Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。 对比传统虚拟机总结 特性 容器 虚拟机 启动 秒级 分钟级 硬盘使用 一般为 MB 一般为 GB 性能 接近原生 弱于 系统支持量 单机支持上千个容器 一般几十个 CentOS 7 安装 Docker 先更新 yum 软件管理器,然后再安装 Docker 12[root@localhost /] yum -y update[root@localhost /] yum install -y docker 说明:上述 -y 代表选择程序安装中的 yes 选项 或是,直接安装 1yum install docker 验证安装,查看 Docker 版本信息 123[root@localhost /] docker -vDocker version 1.13.1, build 8633870/1.13.1You have new mail in /var/spool/mail/root 启动 / 重启 / 关闭 Docker 123[root@localhost /] docker start[root@localhost /] docker restart[root@localhost /] docker stop 【意外情况】service docker start 无法启动问题 centos 安装 docker 显示 No package docker available,原因是 yum 没有找到 docker 的包,需要 epel 第三方软件库,运行下面的命令 sudo yum install epel-release 【亲测有效】Centos安装完成docker后启动docker报错docker 配置yum源 vim /etc/yum.repos.d/docker.repo 123456[dockerrepo]name=Docker Repositorybaseurl=https://yum.dockerproject.org/repo/main/centos/$releasever/enabled=1gpgcheck=1gpgkey=https://yum.dockerproject.org/gpg 通过yum安装 123yum install docker-engineservice docker startservice docker status 日志 1vim /var/log/docker 在 Ubuntu 16.04 LTS 上 离线安装 Docker / Docker-compose - TonyZhang24 - 博客园https://www.cnblogs.com/atuotuo/p/9272368.html Docker 镜像加速器 加速器服务 DaoCloud 加速器,一行命令,镜像万千 阿里云 - 开发者平台 - 容器 hub 配置 Docker 加速器 以 Linux 为例 1curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://f1361db2.m.daocloud.io 该脚本可以将 --registry-mirror 加入到你的 Docker 配置文件 /etc/docker/daemon.json 中。适用于 Ubuntu14.04、Debian、CentOS6 、CentOS7、Fedora、Arch Linux、openSUSE Leap 42.1,其他版本可能有细微不同。更多详情请访问文档。 删除 /etc/docker/daemon.json 中最后一个逗号,重启 Docker 服务即可 Docker 常用命令 1. 启动、停止、重启服务 123456[root@localhost ~]## service docker restartRedirecting to /bin/systemctl restart docker.service[root@localhost ~]## service docker stopRedirecting to /bin/systemctl stop docker.service[root@localhost ~]## service docker startRedirecting to /bin/systemctl start docker.service 2. 拉取一个镜像,启动容器 12[root@localhost ~]## docker pull centos[root@localhost ~]## docker run -it -v /centos_dir:/docker_dir --name biodwhu-1 centos -i:允许我们对容器内的 (STDIN) 进行交互 -t:在新容器内指定一个伪终端或终端 -v:是挂在宿机目录, /centos_dir 是宿机目录,/docker_dir 是当前 Docker 容器的目录,宿机目录必须是绝对的。 -p:端口映射 –name:是给容器起一个名字,可省略,省略的话 docker 会随机产生一个名字 –restart:always 3. 启动的容器列表 1[root@localhost ~]## docker ps 4. 查看所有的容器 1[root@localhost ~]## docker ps -a 5. 启动、停止、重启某个容器 123456[root@localhost ~]## docker start biodwhu-1biodwhu-1[root@localhost ~]## docker stop biodwhu-2biodwhu-2[root@localhost ~]## docker restart biodwhu-3biodwhu-3 6. 查看指定容器的日志记录 1[root@localhost ~]## docker logs -f biodwhu-1 7. 删除某个容器,若正在运行,需要先停止 123456[root@localhost ~]## docker rm biodwhu-1Error response from daemon: You cannot remove a running container 2d48fc5b7c17b01e6247cbc012013306faf1e54f24651d5e16d6db4e15f92d33. Stop the container before attempting removal or use -f[root@localhost ~]## docker stop biodwhu-1biodwhu-1[root@localhost ~]## docker rm biodwhu-1biodwhu-1 8. 删除容器 123456## 删除某个容器[root@localhost ~]## docker rm f3b346204a39## 删除所有容器[root@localhost ~]## docker stop $(docker ps -a -q)[root@localhost ~]## docker rm $(docker ps -a -q) 9. 删除镜像 12345678## 删除某个镜像[root@localhost ~]## docker rmi docker.io/mysql:5.6## 删除所有镜像[root@localhost ~]## docker rmi $(docker images -q)## 强制删除所有镜像[root@localhost ~]## docker rmi -f $(docker images -q) 10. 删除虚悬镜像 我们在 build 镜像的过程中,可能会产生一些临时的不具有名称也没有作用的镜像他们的名称一般都是 <none> ,我们可以执行下面的命令将其清除掉: 123[root@localhost ~]## docker rmi $(docker images -f \"dangling=true\" -q)## 或者[root@localhost ~]## docker image prune -a -f 11. 镜像导入与导出 保存镜像 1[root@localhost ~]## docker save a46c2a2722b9 > /var/docker/images_save/mysql.tar.gz 加载镜像 1[root@localhost ~]## docker load -i /var/docker/images_save/mysql.tar.gz 12. 如何查看镜像和容器的占用空间 docker system df 命令,类似于 Linux 上的 df 命令,用于查看 Docker 的磁盘使用情况: 12345[root@localhost containers]# docker system dfTYPE TOTAL ACTIVE SIZE RECLAIMABLEImages 46 18 7.903 GB 6.735 GB (85%)Containers 18 13 16.94 MB 2.642 MB (15%)Local Volumes 56 2 521.9 MB 517.7 MB (99%) 13. 清理容器和镜像 docker system prune 命令可以用于清理磁盘,删除关闭的容器、无用的数据卷和网络,以及虚悬镜像(即无tag的镜像)。docker system prune -a 命令清理得更加彻底,可以将没有容器使用 Docker 镜像都删掉。注意,这两个命令会把你暂时关闭的容器,以及暂时没有用到的 Docker 镜像都删掉了…所以使用之前一定要想清楚吶。 二、Docker 环境下服务器的磁盘如何分区 三、Docker File 镜像构建 四、Docker Compose docker-compose 命令安装(方法一) 推荐使用这种方式来安装 docker-compose 更加简单。 可以通过修改 URL 中的版本,自定义您需要的版本。 Github源 12sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-composesudo chmod +x /usr/local/bin/docker-compose Daocloud镜像 12curl -L https://get.daocloud.io/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-composechmod +x /usr/local/bin/docker-compose 卸载 1sudo rm /usr/local/bin/docker-compose 参考资料:docker 及 docker-compose 的快速安装和简单使用 - 易墨 - 博客园 docker-compose 命令安装(方法二) Docker-Compose 是一个部署多个容器的简单但是非常必要的工具. 安装 Docker-Compose 之前,请先安装 python-pip 1. 安装 python-pip 首先检查 Linux 有没有安装 python-pip 包,终端执行 pip -v 12[root@localhost ~]## pip -V-bash: pip: command not found 没有 python-pip 包就执行命令 1[root@localhost ~]## yum -y install epel-release 执行成功之后,再次执行 1[root@localhost ~]## yum -y install python-pip 对安装好的 pip 进行升级 1[root@localhost ~]## pip install --upgrade pip 2. 安装 Docker-Compose 终端执行 1[root@localhost ~]## pip install docker-compose 检查 docker-compose 安装 1[root@localhost ~]## docker-compose -version 参考资料: CentOS7下安装Docker-Compose - YatHo - 博客园 docker-compose.yml 规范 五、Docker 实战 实战1:快速搭建 MySQL 官方镜像仓库 https://hub.docker.com/_/mysql/ docker-compose.yml 1234567891011121314151617181920version: '3.1'services: mysql: restart: always image: mysql:5.6 container_name: mysql ports: - 3306:3306 environment: TZ: Asia/Shanghai MYSQL_ROOT_PASSWORD: 123456 command: --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci --explicit_defaults_for_timestamp=true --lower_case_table_names=1 --max_allowed_packet=128M --sql-mode=\"STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO\" volumes: - /usr/local/docker/mysql/mysql-data:/var/lib/mysql 实战2:快速搭建 phpMyAdmin 官方镜像仓库 phpmyadmin/phpmyadmin docker-compose.yml 12345678910111213141516version: '3.1'services: phpmyadmin: image: phpmyadmin/phpmyadmin container_name: phpmyadmin environment: - PMA_ARBITRARY=1 - PMA_HOST=120.92.17.12 ## - PMA_PORT=3306 ## - PMA_USER=xxx ## - PMA_PASSWORD=xxx restart: always ports: - 6060:80 volumes: - /sessions 实战3:快速搭建 GitLab GitLab 使用163邮箱发送邮件 - 刘锐群的笔记 - CSDN博客 https://blog.csdn.net/liuruiqun/article/details/50000213 gitlab服务器邮箱配置 - weifengCorp - 博客园 https://www.cnblogs.com/weifeng1463/p/8489563.html twang2218/gitlab-ce-zh - Docker Hub https://hub.docker.com/r/twang2218/gitlab-ce-zh/ 123456789101112131415161718192021version: '3'services: web: image: 'twang2218/gitlab-ce-zh:10.5' restart: always hostname: '120.131.11.187' environment: TZ: 'Asia/Shanghai' GITLAB_OMNIBUS_CONFIG: | external_url 'http://120.131.11.187:3000' gitlab_rails['gitlab_shell_ssh_port'] = 2222 unicorn['port'] = 8888 nginx['listen_port'] = 3000 ports: - '3000:3000' - '8443:443' - '2222:22' volumes: - /usr/local/docker/gitlab/config:/etc/gitlab - /usr/local/docker/gitlab/data:/var/opt/gitlab - /usr/local/docker/gitlab/logs:/var/log/gitlab 参考资料 docker前后分离笔记 - 小翼的前端天地","categories":[],"tags":[]},{"title":"手把手教你,搭建内网穿透服务","slug":"手把手教你,搭建内网穿透服务","date":"2018-12-17T08:49:11.000Z","updated":"2021-10-23T14:22:52.651Z","comments":true,"path":"2018/12/17/手把手教你,搭建内网穿透服务/","link":"","permalink":"http://www.frankfeekr.cn/2018/12/17/手把手教你,搭建内网穿透服务/","excerpt":"","text":"在很多场景下内网穿透都是我们常常遇到的需求,之前也用过花生壳、ngrok、FRP 等等一些工具,但是由于限速、收费、安全各方面因素只好放弃了。 目前所在实验室主要从事深度学习和机器学习相关工作,有一台高配 GPU 计算型服务器,大家使用 Jupyter Notebook 在网页上进行编码工作,但是只能在学院内网才能进行使用,在外面就无法使用让人比较抓狂。如果购买阿里云等深度学习服务,价格昂贵,只好决定通过内网穿透实现公网工作。 近期无意间看到 「传送门:lanproxy」 这款开源工具,正好实验室购置了一台公网服务器,正好可以实现内网穿透,决定入坑折腾一番。对于有后台开发基础的同学还是挺简单的,不过好记性不如烂笔头,来吧,这里手把手深入探索一番。 一、概述 1.1 什么是内网穿透 好吧,先上一段百度百科的定义 内网穿透,即NAT穿透,网络连接时术语,计算机是局域网内时,外网与内网的计算机节点需要连接通信,有时就会出现不支持内网穿透。 反正简单来说,就是能通过公网访问你的内网服务,把你的内网通过一台公网服务器,穿透出去。 1.2 什么是 lanproxy lanproxy 是一个将局域网个人电脑、服务器代理到公网的内网穿透工具,目前仅支持 tcp 流量转发,可支持任何 tcp 上层协议(访问内网网站、本地支付接口调试、ssh 访问、远程桌面…)。目前市面上提供类似服务的有花生壳、TeamView、GoToMyCloud 等等,但要使用第三方的公网服务器就必须为第三方付费,并且这些服务都有各种各样的限制,此外,由于数据包会流经第三方,因此对数据安全也是一大隐患。https://lanproxy.io2c.com 1.3 原理 内网穿透的原理如下图所示: 用户访问我们的服务器,这个服务器是有公网IP的,所以用户可以无压力访问 服务器与本地电脑保持长链接,当有请求的时候,服务器将请求转发到我们的本地电脑 本地电脑将响应回复给服务器 服务器将响应回复给用户 要搭建内网穿透,我们得完成两个任务 在公网能访问的服务器上运行我们的内网穿透服务; 在本地电脑上面运行内网穿透客户端。 当然,你可以自己根据原理实现一套,不过我们有现成的三方开源工具,可以帮我们实现这一套功能。这个就是我们今天的主角 lanproxy。 二、快速指南 这里我将手把手带你配置 lanproxy 穿透服务,这里配置主要分成了 公网服务器配置(请按照 2.2 中的说明进行安装) 和 内网电脑配置(推荐通过 4.1 中的说明安装) 。 在内网电脑配置中分为:Java 客户端和 GO 客户端 硬件与环境要求 这里以我的环境为例 一台公网服务器(Centos 7.4,当然这不重要,反正都 docker 啦) docker(简化更多的配置,这里采用 docker 容器进行搭建) Nginx 环境 一台内网电脑(Ubuntu 16.04) 这里提供了四种方式进行客户端启动 Docker容器部署 Java > 1.7 的 JDK环境 + 客户端下载 Java > 1.7 的 JDK环境 + maven + 源码下载 Go 客户端下载启动 docker 启动服务,tensorflow(jupyter notebook) 已备案的域名(可选) 此步骤也可省略,但端口太多通过 Nginx 配合域名进行转发,使用更加优雅。故这里我也将会配置两个公网域名 提供安装方式 安装服务 说明 推荐 安装方式 服务端 一台公网服务器 ⭐⭐⭐ 方式1:Docker容器部署 客户端 一台内网电脑 ⭐⭐⭐ 方式1:Docker容器部署 ⭐⭐ 方式2:Java > 1.7 的 JDK环境 + 客户端下载 ⭐ 方式3:Java > 1.7 的 JDK环境 + maven + 源码下载 ⭐ 方式4:Go 客户端下载启动 服务端与客户端 服务 下载地址 lanproxy client for java GitHub lanproxy server for java GitHub Docker Hub 镜像 镜像 下载地址 franklin5/lanproxy-server Docker Hub franklin5/lanproxy-client Docker Hub 三、公网服务器配置(Docker 一键启动) 3.1 基础环境安装 安装 docker 服务,以 CentOS 7.4 为例 123456789101112131415161718192021221、安装依赖包$ sudo yum install -y yum-utils device-mapper-persistent-data lvm22、设置稳定版仓库$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo3、安装//安装最新版本$ sudo yum install docker-ce//或者安装指定版本$ yum list docker-ce --showduplicates | sort -r //显示有以下版本 docker-ce.x86_64 18.03.0.ce-1.el7.centos docker-ce-stable//指定一个版本进行安装$ sudo yum install docker-ce-<VERSION STRING>4、启动doker$ sudo systemctl start docker5、通过运行hello-world镜像验证安装是否成功$ sudo docker run hello-world 安装 Nginx 服务,以 CentOS 7.4 为例 1234567891.添加Nginx到YUM源$ sudo rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm2.安装Nginx$ sudo yum install -y nginxNginx将完成安装在你的CentOS 7 服务器中。3.启动Nginx$ service nginx start 更多配置请参考:CentOS 7 yum 安装 Nginx - 菜鸟路上的小白 - CSDN博客 3.2 Docker 启动服务端程序 ⭐⭐⭐ 方式一:docker run 通过 Docker,启动 lanproxy 服务,service docker start 启动 Docker 后运行一下代码 1234567891011docker run -d \\ --name lanproxy-server \\ -p 8090:8090 \\ -p 4900:4900 \\ -p 4993:4993 \\ -p 9000-9100:9000-9100 \\ --restart=always \\ -e LANPROXY_USERNAME=\"admin\" \\ -e LANPROXY_PASSWORD=\"admin\" \\ -v - /usr/local/docker/lanproxy-server/config-data:/root/.lanproxy \\ franklin5/lanproxy-server 参数: LANPROXY_USERNAME:配置后台管理账号,默认admin LANPROXY_PASSWORD:配置后台管理密码,默认admin 可选:我也为你创建了上述的下载脚本,方便每次的启动 1wget https://raw.githubusercontent.com/frank-lam/lanproxy-nat/master/docker-server/docker-run.sh 输入你的公网服务器 IP:8090,例如:http://120.92.10.120:8090,即可看到后台管理 Web 页面。好啦,到这里 lanproxy 的基础环境已经搭建成功,是不是很快,这就是 docker 的魅力。如果不用 docker 启动,请参考 lanproxy 的官方文档。 这里的 9000-9100 即为需要映射的端口地址,可以自定义设置。 方式二:docker compose 创建 docker-compose.yml 文件 12345678910111213141516171819version: '3.1'services: lanproxy-client: image: franklin5/lanproxy-server container_name: lanproxy-server environment: # 配置后台管理账号,默认admin - LANPROXY_USERNAME=admin # 配置后台管理密码,默认admin - LANPROXY_PASSWORD=admin volumes: # 用于保存创建的配置文件,避免重启服务后配置消失 - /usr/local/docker/lanproxy-server/config-data:/root/.lanproxy ports: - 8090:8090 - 4900:4900 - 4993:4993 - 9000-9100:9000-9100 restart: always 可选:你可以手工复制创建,当然这里也为你提供方便下载的 docker-compose.yml 1wget https://raw.githubusercontent.com/frank-lam/lanproxy-nat/master/docker-server/docker-compose.yml 启动服务 1docker-compose up -d 停止服务 1docker-compose down 😎 至此你的 lanproxy 服务端已经配置成功,账号密码:即为 docker 配置中你配置的账号密码 3.3 Nginx 反向代理配置域名 在上一步,我们通过 docker 启动了一个 lanproxy 环境,但是通过 IP 和端口号组合的方式并不优雅。这里我将解析两个域名通过 Nginx 进行端口转发。 1234567# 两个域名都解析到你的公网上去# 这个域名由于访问 lanproxy 管理后台lanproxy.frankfeekr.cn => 120.92.10.120 | Nginx to => 127.0.0.1:8090# 这个域名由于访问你的内网电脑服务,9000 端口可自由的在 lanproxy 管理后台进行配置jupyter.frankfeekr.cn => 120.92.10.120 | Nginx to => 127.0.0.1:9000 好啦,上门就是对域名进行了一些简单的解释,现在开始 Nginx 的配置 进入 /etc/nginx/conf.d Nginx 的配置目录,在这里创建配置文件 创建 lanproxy.frankfeekr.cn.conf 配置文件 12345678910111213141516server { listen 80; # 这里使用自己的域名 server_name lanproxy.frankfeekr.cn; charset utf-8; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $http_host; # 这里根据你的 lanproxy 配置,改成 config.server.port的值 proxy_pass http://127.0.0.1:8090; client_max_body_size 35m; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection \"upgrade\"; }} 创建 jupyter.frankfeekr.cn.conf 配置文件 12345678910111213141516server { listen 80; # 这里使用自己的域名 server_name jupyter.frankfeekr.cn; charset utf-8; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $http_host; # 这里根据你的lanproxy配置,改成 外网接口 的值,在lanproxy后台网页上配置,后面配置 proxy_pass http://127.0.0.1:9000; client_max_body_size 35m; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection \"upgrade\"; }} 重启 Nginx 服务 1$ service nginx restart 😚 至此,你可以通过域名 http://lanproxy.frankfeekr.cn 进行访问 lanproxy 网页后台配置 3.4 继续配置 lanproxy 后台服务 添加一个客户端 添加配置 如果准备使用 Docker 客户端启动服务的请注意 🔥🔥🔥 如果通过 Java 客户端直接在主机启动客户端服务则 IP 地址为:127.0.0.1:port ; 如果通过 Docker 方式启动客户端,则 IP 地址为:172.17.0.1:port,这是 Docker 宿主机默认网卡 IP,如果自己手动修改过了 IP 地址,则填写你的 IP。可以通过命令 ip a | grep docker 查看你的宿主机 IP 地址。 😜 至此,服务端的配置就完成了。下面开始内网电脑的配置。 四、内网电脑客户端配置 4.1 强烈推荐:开箱即用的 Docker 版客户端 ⭐⭐⭐ 由于在不同的主机上配置客户端,发现非常消耗时间,同时也缺乏稳定性。于是决定来定制一款开箱即用的 docker 容器版客户端,这里你将不需要安装任何客户端环境,甚至只需要几行优雅的命令即可启动一个客户端,速度和服务端配置一样飞起来。 lanproxy-client docker images:franklin5/lanproxy-client - Docker Hub 运行 lanproxy client 服务 方式一:docker run 一键启动客户端 123456docker run -d \\ --name lanproxy-client \\ -e LANPROXY_KEY=\"input_your_key\" \\ -e LANPROXY_HOST=\"input_your_host\" \\ --restart=always \\ franklin5/lanproxy-client 参数说明 input_your_key:这里是在 lanproxy 后台配置的密钥 input_your_host:服务器的 ip,支持域名 可选:为了这里也为你提供了执行的 docker-run.sh (保存成 shell 修改和运行更方便) 1wget https://raw.githubusercontent.com/frank-lam/lanproxy-nat/master/docker-client/docker-run.sh 停止容器 1docker stop lanproxy-client 删除容器 1docker rm lanproxy-client 强制删除容器 1docker rm -f lanproxy-client 重启容器 1docker restart lanproxy-client 方式二:docker-compose 在你的项目目录下创建:docker-compose.yml 1234567891011version: '3.1'services: lanproxy-client: image: franklin5/lanproxy-client container_name: lanproxy-client environment: # 这里是在lanproxy后台配置的密钥 - LANPROXY_KEY=input_your_key # 服务器的ip,支持域名 - LANPROXY_HOST=input_your_host restart: always 可选:你可以手工复制创建,当然这里也为你提供方便下载的 docker-compose.yml 1wget https://raw.githubusercontent.com/frank-lam/lanproxy-nat/master/docker-client/docker-compose.yml 后台启动容器 1docker-compose up -d 🚩使用 docker 方式运行客户端,请务必阅读 以上两种运行方式,选择一种运行即可一键启动 docker 客户端容器。但使用 docker 直接运行容器和在宿主机上运行时有所不同。在 docker 服务启动后,docker 会为宿主机和容器各自分配一个 docker 网卡,而宿主机上会分配默认的 IP 地址,即:172.17.0.1,故容器中可以 ping 通宿主机上 172.17.0.1 的任何 TCP 端口。 所以我们在后面的后台网页上的端口配置,不再是 172.0.0.1,必须是 172.17.0.1:port。 话不多说,请进入下一个步奏慢慢体会。 lanproxy 网页后台服务查看 客户端启动服务后,后台可查看状态(在线 / 离线) 流量统计 4.2 推荐:开箱即用客户端,仅需安装 Java 环境 ⭐⭐ 如果你的本地已经有了 Java 环境(无论你是编译安装,还是 yum/apt-get 安装,都 ok),最低环境 JDK 1.7 以上。那么我推荐你是用本节中的配置说明,可以不用配置 maven 环境,直接拉取客户端一键运行,更加方便。 Java JDK 1.8 安装 验证你的本地是否有 Java 环境,如果已经存在 Java 1.7 以上的环境,即可调到下一个步骤 1java -version 安装 Java 1.8(Ubuntu/apt-get) 123456# 首先,更新包索引apt-get update# 安装Java运行时环境(JRE)apt-get install default-jre -y# 验证安装是否成功java -version 详细可参考:在Ubuntu 16.04如何安装Java使用apt-get的 - 一只宅男的自我修养 - 博客园 安装 Java 1.8(Centos/yum) 123456# 首先,更新包索引yum update# 安装 Java 1.8yum install java-1.8.0-openjdk.x86_64 -y# 验证安装是否成功java -version 详细可参考:centos7通过yum安装JDK1.8 - 来吧 单刷各种经典 - CSDN博客 下载 Java 客户端(开箱即用) 为了免去 mvn 安装中出现的意外,我已经为你打造了一款开箱即用的客户端 1git clone https://github.com/frank-lam/lanproxy-client.git 客户端目录 1234567891011drwxr-xr-x 7 root root 4096 Jan 2 08:17 ./drwxr-xr-x 1 root root 4096 Jan 2 08:17 ../drwxr-xr-x 8 root root 4096 Jan 2 08:17 .git/drwxr-xr-x 3 root root 4096 Jan 2 08:17 distribution/drwxr-xr-x 4 root root 4096 Jan 2 08:17 proxy-client/drwxr-xr-x 4 root root 4096 Jan 2 08:17 proxy-common/drwxr-xr-x 4 root root 4096 Jan 2 08:17 proxy-protocol/-rw-r--r-- 1 root root 606 Jan 2 08:17 restart.sh-rw-r--r-- 1 root root 570 Jan 2 08:17 start.sh-rw-r--r-- 1 root root 375 Jan 2 08:17 status.sh-rw-r--r-- 1 root root 588 Jan 2 08:17 stop.sh 修改配置文件信息 打包完成之后,客户端文件会出现在 distribution/proxy-client-0.1 目录下,打开之后有是个文件夹:bin、conf、lib和log,配置信息在 conf/config.properties 文件内,根据前面服务端的配置信息修改一下。 1234567891011121314# 这里是在lanproxy后台配置的密钥client.key=e6a49a19b8024652ab4ff2210abf2c6a# 配置ssl信息,根据服务端的配置填写(enable = false 就不需要配置)ssl.enable=falsessl.jksPath=test.jksssl.keyStorePassword=123456# 服务器的ip,也支持域名server.host=120.92.10.120#proxy-server ssl默认端口4993,默认普通端口4900#ssl.enable=true时这里填写ssl端口,ssl.enable=false时这里填写普通端口server.port=4900 运行 lanproxy client 服务 启动客户端(官方脚本) 客户端信息配置完成之后就可以启动客户端了 1234# mac/linux使用这个$ bash bin/startup.sh# Windows 直接运行 bin/startup.bat 当然这里我也为你准备了一些开箱即用的脚本(推荐使用) 12345678910# 切换到项目根目录# 重启sh restart.sh# 启动sh start.sh# 停止客户端sh stop.sh# 当前客户端运行状态sh status.sh lanproxy 网页后台服务查看 客户端启动服务后,后台可查看状态(在线 / 离线) 流量统计 4.3 Java 客户端,通过 Maven 方式安装依赖 ⭐ 这里我的内网是一台 Ubuntu 16.04 的服务器,以下我都将以此为例 Java 1.8 安装 下载 JDK1.8(lanproxy 支持 JDK1.7 以上环境) 1$ wget https://download.oracle.com/otn-pub/java/jdk/8u191-b12/2787e4a523244c269598db4e85c51e0c/jdk-8u191-linux-x64.tar.gz 解压安装包,移动文件夹到 /user/lib 目录 12$ tar -zxvf jdk-8u191-linux-x64.tar.gz$ mv jdk1.8.0_191 /usr/lib/jdk/jdk1.8.0_191 环境变量配置 这里是将环境变量配置在 etc/profile,即为所有用户配置 JDK 环境。 1$ vim + etc/profile 环境设置 12345#set java envexport JAVA_HOME=/usr/lib/jdk/jdk1.8.0_191export JRE_HOME=${JAVA_HOME}/jre export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib export PATH=${JAVA_HOME}/bin:$PATH 执行命令使修改立即生效 1$ source /etc/profile 配置软连接 软连接相当于windows系统中的快捷键,部分软件可能会从/usr/bin目录下查找Java,因此添加该软连接防止其他软件查找不到的情况。 12$ sudo update-alternatives --install /usr/bin/java java /usr/lib/jdk/jdk1.8.0_191/java 300 $ sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jdk/jdk1.8.0_191/bin/javac 300 测试安装是否成功 12# 在终端输入,出现版本号则表示安装成功$ java -version 更详细文档: ubuntu16.04搭建jdk1.8运行环境 - 朝花夕拾 - CSDN博客 maven 安装 maven 是个项目管理工具,在编程领域应用广泛。 官网 下载maven。 1$ wget https://www-eu.apache.org/dist/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.tar.gz 解压到 /opt/maven 目录 创建 manve 目录。 1$ sudo mkdir /opt/maven 解压到 /opt/maven 目录下。 1$ sudo tar zxvf apache-maven-3.6.0-bin.tar.gz -C /opt/maven 配置 maven 环境 1$ vim + /etc/profile 在文件内容后面添加以下内容: 1234# set maven envexport M2_HOME=/opt/maven/apache-maven-3.6.0export CLASSPATH=$CLASSPATH:$M2_HOME/libexport PATH=$PATH:$M2_HOME/bin 保存,输入以下命令使配置文件生效。 1$ source /etc/profile 验证是否安装成功。 1$ mvn -v 修改 maven 源为阿里云,以及仓库默认存放路径。这样 maven 下载 jar 包的速度会快很多。 打开 maven 的配置文件 1$ vim /opt/maven/apache-maven-3.6.0/conf/settings.xml 在 /home 目录下生成 maven/repository 文件夹 1<localRepository>maven/reposity</localRepository> 修改源 12345678<mirrors> <mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <mirrorOf>central</mirrorOf> </mirror></mirrors> 😚 至此,Maven 安装成功 参考资料: ubuntu16.04安装maven - Rocky的编程随记 - CSDN博客 启动内网服务 在前面我们将 jupyter.frankfeekr.cn 反向代理(公网服务器 9000 服务,内网 5050) 这里我们需要确保在内网电脑上,浏览器能够访问到 127.0.0.1:5050 这个服务。以我的 jupyter notebook 为例: 这里,我的内网服务已经启动。接下来只需要启动 lanproxy 客户端即可。 运行 lanproxy client 服务 官方提供了两种方式运行客户端,一种方式是使用官方提供的 GO,另一种通过 Java 环境运行。本文采用主流的 Java 方式运行 克隆 lanproxy 代码到本地电脑 1$ git clone https://github.com/ffay/lanproxy.git lanproxy 打包 lanproxy 打包之前需要确保你安装了 maven 12$ cd lanproxy$ mvn package 修改配置文件信息 打包完成之后,客户端文件会出现在 distribution/proxy-client-0.1 目录下,打开之后有是个文件夹:bin、conf、lib和log,配置信息在 conf/config.properties 文件内,根据前面服务端的配置信息修改一下。 1234567891011121314# 这里是在lanproxy后台配置的密钥client.key=e6a49a19b8024652ab4ff2210abf2c6a# 配置ssl信息,根据服务端的配置填写(enable = false 就不需要配置)ssl.enable=falsessl.jksPath=test.jksssl.keyStorePassword=123456# 服务器的ipserver.host=120.92.10.120#proxy-server ssl默认端口4993,默认普通端口4900#ssl.enable=true时这里填写ssl端口,ssl.enable=false时这里填写普通端口server.port=4900 启动客户端 客户端信息配置完成之后就可以启动客户端了 1234# mac/linux使用这个$ bash bin/startup.sh# Windows 直接运行 bin/startup.bat 访问测试 😚 至此,即可通过公网域名 http://jupyter.frankfeekr.cn 访问内网服务,实现内网穿透 当然你也可以通过公网 IP 和域名访问,例如:http://120.92.10.120:9000 这种方式访问! 停止客户端 1$ bash bin/stop.sh lanproxy 网页后台服务查看 客户端启动服务后,后台可查看状态(在线 / 离线) 流量统计 4.4 Go 客户端 GitHub 客户端主页:lanproxy-go-client 不想安装 Java 环境的可以选择 Go 安装 Go 环境安装 1. buntu、Debian或Linux Mint安装Go语言 基于 Debian的 Linux 发行版本都可以使用 apt-get 命令来进行安装: 1sudo apt-get install golang 要查看当前系统安装的 Go 语言版本可以使用如下命令: 1go version 由于 Go 代码必需保存在 workspace(工作区)中,所以我们必需在 Home 目录(例如 ~/workspace)创建一个workspace 目录并定义 GOPATH 环境变量指向该目录,这个目录将被 Go 工具用于保存和编辑二进制文件。 123mkdir ~/workspaceecho 'export GOPATH=\"$HOME/workspace\"' >> ~/.bashrcsource ~/.bashrc 根据不同的需要,我们可以使用 apt-get 安装 Go tools: 1sudo apt-cache search golang 2. Fedora、CentOS或RHEL安装Go语言 基于 Red Hat 的 Linux 发行版本都可以使用 yum 命令来进行安装: 12sudo yum updatesudo yum install golang 要查看当前系统安装的 Go 语言版本可以使用如下命令: 1go version 接下来还是在 Home 目录(例如 ~/workspace)创建一个 workspace 目录并定义 GOPATH 环境变量指向该目录,这个目录将被 Go 工具用于保存和编辑二进制文件。 123mkdir ~/workspaceecho 'export GOPATH=\"$HOME/workspace\"' >> ~/.bashrcsource ~/.bashrc 根据不同的需要,我们可以使用 yum 安装 Go tools: 1yum search golang 参考资料:如何为Linux安装Go语言 - Go语言中文网 - Golang中文社区 拉取 ffay/lanproxy-go-client 代码 1git clone https://github.com/ffay/lanproxy-go-client.git 安装客户端依赖包 12cd lanproxy-go-clientsh build-release.sh 安装后,这时候在目录下会出现文件。 启动客户端 普通端口连接 12345678# mac 64位nohup ./client_darwin_amd64 -s SERVER_IP -p SERVER_PORT -k CLIENT_KEY &# linux 64位nohup ./client_linux_amd64 -s SERVER_IP -p SERVER_PORT -k CLIENT_KEY &# windows 64 位./client_windows_amd64.exe -s SERVER_IP -p SERVER_PORT -k CLIENT_KEY SSL端口连接 12345678# mac 64位nohup ./client_darwin_amd64 -s SERVER_IP -p SERVER_SSL_PORT -k CLIENT_KEY -ssl true &# linux 64位nohup ./client_linux_amd64 -s SERVER_IP -p SERVER_SSL_PORT -k CLIENT_KEY -ssl true &# windows 64 位./client_windows_amd64.exe -s SERVER_IP -p SERVER_SSL_PORT -k CLIENT_KEY -ssl true 例如: 1nohup ./client_linux_amd64 -s lp.thingsglobal.org -p 4900 -k 01c1e176d6ee466c8db717a8 & 命令参数: 12345678GLOBAL OPTIONS: -k value client key -s value proxy server host -p value proxy server port (default: 4900) --ssl value enable ssl (default: \"false\", -p value should be server ssl port) --cer value ssl cert path, default skip verify certificate --help, -h show help --version, -v print the version lanproxy 网页后台服务查看 客户端启动服务后,后台可查看状态(在线 / 离线) 流量统计 五、总结 通过以上的配置,只要有一台公网电脑,即可实现内网穿透功能。摆脱花生壳的域名端口限制,流量限制,带宽限制。笔者的公网服务器为 6M 带宽,通过公网映射,文件下载测试大概在 800K/s - 1.5M/s 范围,如果仅是普通的网站服务完全可以完美穿透。 一般的应用场景:用来 SSH/MSTSC 远程连接,站点对外访问,是完全绰绰有余。 Todo List 为了方便客户端批量安装,我将会编写更为方便的 shell 脚本,做到一键自动部署 [ ] 一键 shell 脚本安装 [ ] 交互式 shell 脚本安装 参考资料 基于docker搭建lanproxy内网穿透服务 - 奉强的个人博客 lanproxy GitHub 官方主页 linux设置开机自启动 - ssooking - 博客园 关于作者 :boy: 在颠覆世界的同时,也要好好关照自己。 from zero to hero.","categories":[],"tags":[{"name":"内网穿透","slug":"内网穿透","permalink":"http://www.frankfeekr.cn/tags/内网穿透/"}]},{"title":"我的校招之路","slug":"我的校招之路","date":"2018-10-19T13:50:40.000Z","updated":"2021-10-23T14:22:52.651Z","comments":true,"path":"2018/10/19/我的校招之路/","link":"","permalink":"http://www.frankfeekr.cn/2018/10/19/我的校招之路/","excerpt":"","text":"秋招研磨 经过了大半年的学习和努力。2018 年 10 月 17 日,我的秋招终于落下帷幕。 讲讲我的秋招之路吧。大概是 2018 年 2 月底,春节都还没结束,学校的就业群里也开始了阿里内推,2018 年暑期实习生招聘的序幕拉开了。 由于是专硕,两年的读研时间显的好短暂,才短暂接触了几个月的机器学习算法,都才刚刚入门了 Python 和会使用 Tensorflow 和 Scikit-learn 框架进行一些简单的机器学习分类工作,马上又要着手于校招,让我措手不及。在机器学习算法等相关学科,对于数学功底要求比较高,对于快速就业让我十分的无助。由于之前也写过两年的 PHP 后台开发相关的工作,大概在很长的一段时间内一直在纠结究竟是找机器学习岗位或是后台开发方向。 思考了很久,我对自己未来的规划是希望成为架构师方向,在技术达到一定高度,并有很好的想法的时候会有创业做产品的打算。于是我准备往后台开发方向,但是问题又来了! 究竟是找 Python web、PHP web、Java web,哪个方向呢?Python 和 PHP 算是自己擅长的方向,想找一份工作,固话一下相关的基础知识准没问题。当做了一段时间算法后,也对各个岗位的就业分析了一下,主要还是 Java 占据了半壁江山,其次是 C++,然后是 Python、PHP、GO。在小型公司,快速的项目迭代更多的使用了 PHP 或是 Python 进行快速开发,但当项目成长到一定的体量都被 Java 给一统江湖了。其中,特别是拼多多和小红书这两家企业,从 PHP 和 Python 替换到了 Java 后台架构。PHP/Python:do fast,Java:think big。 出于这样一个打算,我选择了 Java ,也期待未来从事更多的分布式系统架构相关工作。在后台开发的技术栈上,尤其是 Java 和 C++ 这样的语言入门和学习的门槛太高。还没有一个特别好的学习指南能够帮助我好好的学习 Java 这样一门语言,其实在本科也学过一年多 Java,但是更多的是停留在应用层面。 大概三月初吧,开始准备全心转向 Java 后台开发方向。得益于之前对于 PHP 的后台开发语言的学习,我的 Java 学习之路还是比较顺利和深入的。查看了很多知乎大神对于学习路径的指导,我也根据自己以往的学习经验摸索出了对于整个技术栈的认知。在学习过程中,你会看书、看视频教程、看博客等等,一开始也用 OneNote 做一些笔记,但是发现很不方便。在 Github 上看到了很多优秀的开源笔记项目,后来用到了 Typora 这 Markdown 编辑器,开始爱上了记录,于是乎创建了 frank-lam/fullstack-tutorial 这个仓库。此后开始了我正式的,艰苦的学习之路。(具体的技术栈我不一一展开,感兴趣的同学欢迎关注我的仓库,截止目前已有 800+ star 和 200+ fork) 下面给大家列一下,后台开发工程师需要达到的一个高度吧。 在线笔试基础 Leetcode 必刷 剑指 Offer 必刷 内功修炼 数据结构与算法 海量数据处理方法 Linux 基础与命令 计算机网络(应用层,传输层,网络层等相关协议) Web 网络和 HTTP/HTTPS 协议 数据库(MySQL,Redis,SQLServer) 操作系统原理 Git 版本管理工具使用 正则表达式 Java 核心技术 语法与基础概念 面向对象与 23 种设计模式 Java 容器源码(数据结构 & 源码分析:ArrayList、Vector、LinkedList、HashMap、ConcurrentHashMap、HashSet、LinkedHashSet and LinkedHashMap) Java 并发编程(线程状态、线程机制、线程通信、J.U.C 组件、JMM、线程安全、锁优化) Java IO(磁盘操作、字节操作、字符操作、对象操作、网络操作、NIO) Java 虚拟机(运行时数据区域、垃圾收集、内存分配机制、类加载机制、性能调优监控工具) Java Web(学习 Spring + SpringMVC + MyBatis 框架和设计模式思想,学习 Servlet 和 JSP) 高级加分项 Zookeeper(分布式协调服务) Dubbo(分布式服务治理) 分布式事务解决方案 ActiveMQ、Kafka、RabbitMQ(分布式消息通信) Redis(分布式缓存与集群搭建) mycat(数据库路由) Nginx(反向代理) Docker(容器技术) Tomcat 或许很多人看到我罗列的这些东西已经开始焦虑,并且感到不适了。是的,要知道一个道理:面试造火箭,工作拧螺丝。如果想通往大厂那么请好好静下心来,至少需要一年的时间学习来学习我所列的知识清单。也感谢校招的这段时间,逼着推动我,让我对技术的认识有了一个全新的认识。 再回到秋招这件事,暑期实习生招聘的几个月中,参加了无数的校园宣讲会也面试了几家公司增长了一些经验,但是很不幸最后都挂了。虽然没有能成功实习,对自己的能力有了一个很好的评估,深知到自己基础知识的薄弱,于是静下心来开始了漫长夯基的日子。这里推荐很多即使不能出去实习的同学,也好好的把握好实习招聘,做一些经验上的积累。像很多公司也推出了 mini 短期实习的一些政策,或许是一个很好的机会。 都说金九银十,是的九月、十月是秋招最重要的两个月。但是学霸们六七月份已经开始了他们的提前批内推(也称学霸批),如果你想加入阿里(7.15开始)、大疆(6.30截止)等等大厂需要早早准备了。并且内推免笔试呢,增加面试经验。 老实说那段日子真的很难熬,印象最深刻大概还停留在六、七、八月份紧张的学习,那段时间如同考研冲刺般的激励自己,无数次在心中暗示自己我一定行的。那几个月大概对于技术的学习超过了以往任何一段时间,比以往任何一段时间更明确自己的方向,我认为超过了过去两年学习的水平。翻阅了无数本计算机相关的图书,为了快速入门看了五十多门的视频课程,其中有无数优秀的书籍和课程,这里我将在后期一起整理分享。大概七八月份两个月没有休息过,每天八点到实验室开始看书,晚上大概十一点左右打卡回去。那段时间虽然艰苦,但是对基础的学习和后来的面试起到了关键性的作用,否则我想将无法支撑我的面试。 很快的九月来了,秋招正式的打响了。大概九月份每天都在频繁的笔试、面试、宣讲会,印象最深刻的是同一天 4 场笔试(从早晨 9 点一直笔试持续到晚上 11点才结束),记录最高的一天跑了五场面试,跑遍了华科和武大周边的五星级酒店。 秋招还是喜出望外,拿到一些 Offer,海康威视、华为、贝贝网、苏宁易购、腾讯、58 同城(58 的口头 Offer,北京的岗位还是拒绝了)。虽然一心想去阿里和网易都没有很顺利,都在两轮面试后挂了(面试的太早了,都没有让我好好准备),但是最后还是拿到了华为。 转眼回头再看,每一个无眠的深夜,每一个纠结的过往,从此都云淡风轻。 如何准备秋招 夯实基础,把我在上文列举的知识清单掌握 写一份优秀的简历 包装一个你最熟悉的项目 掌握好面试话术 多看面经,每次面试后写下面经 简历即是你的名片,把你会的东西都写到简历上,谨慎使用(熟悉,良好,了解)等表述性词汇,一定要对自己的简历上每个字负责。提到的技术栈,一定要有深入研究,否则切记写上。详细描述一个你最熟悉的项目,包括(1)项目背景和你的工作;(2)项目核心技术点和难点;(3)项目优化改进。 最后在面试过程中要衣着整洁,语言吐字清晰,切记说不会,一定要思考后再回答。面试是一个交互性的交流,不要觉得低人一等,更不要被面试官牵着走,所以这里再次强调简历的重要性,大部分面试都会根据你的简历来提问。甚至我在面试中单独附上了我项目的整体架构图,有了架构图更好的对项目进行深化,也是牵引面试官的一个好办法。如果你的简历没有一个好的项目,那么请做好疯狂被怼基础知识的可能性吧! 调整好心态 在秋招的过程中面试了 20 家企业,也一路挂过来,一定要有一颗平和的心。广撒简历,重点培养。明确自己的岗位,工作地进行海投、精准面试。基本上面到 HR 面的企业都拿到了口头 Offer 或是意向书。 在面试过程中,选择往往大于努力,一定要认准自己的方向。 Offer = 40%运气 + 40%技术 + 20%表达能力 后记 秋招之路不易,写下一些记忆,希望能够帮助更多的人。","categories":[],"tags":[{"name":"校招","slug":"校招","permalink":"http://www.frankfeekr.cn/tags/校招/"}]},{"title":"深入理解快速排序(随机快排、双路快排、三路快排)","slug":"深入理解快速排序(随机快排、双路快排、三路快排)","date":"2018-07-01T04:50:11.000Z","updated":"2021-10-23T14:22:52.652Z","comments":true,"path":"2018/07/01/深入理解快速排序(随机快排、双路快排、三路快排)/","link":"","permalink":"http://www.frankfeekr.cn/2018/07/01/深入理解快速排序(随机快排、双路快排、三路快排)/","excerpt":"","text":"快速排序可以说是20世纪最伟大的算法之一了。相信都有所耳闻,它的速度也正如它的名字那样,是一个非常快的算法了。当然它也后期经过了不断的改进和优化,才被公认为是一个值得信任的非常优秀的算法。 本文将结合快速排序的三方面进行比较和深入解析。 快速排序 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152public class QuickSort { // 递归使用快速排序,对arr[l...r]的范围进行排序 public static void QuickSort(int[] arr,int l,int r){ if(l>=r) return; int p = partition(arr,l,r); QuickSort(arr,l,p-1); QuickSort(arr,p+1,r); } // 将数组通过p分割成两部分 // 对arr[l...r]部分进行partition操作 // 返回p, 使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p] public static int partition(int[] arr, int l, int r) { swap(arr, l, (int) (Math.random() * (r - l + 1)) + l); // 随机快速排序 int v = arr[l]; int j = l; for(int i = j +1;i<=r;i++){ if(arr[i] < v){ j++; swap(arr,i,j); } } swap(arr,l,j); return j; } public static void swap(int[] arr,int i,int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } // 打印arr数组的所有内容 public static void printArray(int[] arr) { for (int i = 0; i < arr.length; i++){ System.out.print( arr[i] ); System.out.print( ' ' ); } System.out.println(); return; } public static void main(String[] args){ int[] arr = {4,3,12,12}; QuickSort(arr,0,arr.length-1); printArray(arr); }} 双路快速排序 若果数组中含有大量重复的元素,则partition很可能把数组划分成两个及其不平衡的两部分,时间复杂度退化成O(n²)。这时候应该把小于v和大于v放在数组两端 实际上把等于的部分分散到了数组两端 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283public class QuickSort2Ways { // 双路快速排序的partition // 返回p, 使得arr[l...p-1] < arr[p] ; arr[p+1...r] > arr[p] private static int partition(int[] arr, int l, int r) { // 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot swap(arr, l, (int) (Math.random() * (r - l + 1)) + l); int v = arr[l]; // arr[l+1...i) <= v; arr(j...r] >= v int i = l + 1, j = r; while (true) { // 注意这里的边界, arr[i] < 0, 不能是arr[i] <= v // 思考一下为什么? while (i <= r && arr[i] < v) i++; // 注意这里的边界, arr[j] > v, 不能是arr[j] >= v // 思考一下为什么? while (j >= l + 1 && arr[j] > v) j--; // 对于上面的两个边界的设定, 有的同学在课程的问答区有很好的回答:) // 大家可以参考: http://coding.imooc.com/learn/questiondetail/4920.html // 答案:多了个等号的判断也会造成两棵子树不平衡 if (i > j) break; swap(arr, i, j); i++; j--; } swap(arr, l, j); return j; } // 递归使用快速排序,对arr[l...r]的范围进行排序 private static void QuickSort2Ways(int[] arr, int l, int r) { // 对于小规模数组, 使用插入排序 // if( r - l <= 15 ){ // InsertionSort.sort(arr, l, r); // return; // } int p = partition(arr, l, r); QuickSort(arr, l, p - 1); QuickSort(arr, p + 1, r); } private static void swap(int[] arr, int i, int j) { int t = arr[i]; arr[i] = arr[j]; arr[j] = t; } // 打印arr数组的所有内容 public static void printArray(int[] arr) { for (int i = 0; i < arr.length; i++) { System.out.print(arr[i]); System.out.print(' '); } System.out.println(); return; } // 测试 QuickSort public static void main(String[] args) { //双路快速排序算法也是一个O(nlogn)复杂度的算法 // 可以在1秒之内轻松处理100万数量级的数据 int[] arr = {4, 3, 12, 12}; QuickSort2Ways(arr, 0, arr.length - 1); printArray(arr); }} 三路快速排序 数组分成三个部分,大于v 等于v 小于v 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061public class QuickSort3Ways { // 递归使用快速排序,对arr[l...r]的范围进行排序 private static void QuickSort3Ways(int[] arr, int l, int r){ // 随机在arr[l...r]的范围中, 选择一个数值作为标定点pivot swap( arr, l, (int)(Math.random()*(r-l+1)) + l ); int v = arr[l]; int lt = l; // arr[l+1...lt] < v int gt = r + 1; // arr[gt...r] > v int i = l+1; // arr[lt+1...i) == v while( i < gt ){ if( arr[i] < v){ swap( arr, i, lt+1); i ++; lt ++; } else if( arr[i] > v ){ swap( arr, i, gt-1); gt --; } else{ // arr[i] == v i ++; } } swap( arr, l, lt ); QuickSort3Ways(arr, l, lt-1); QuickSort3Ways(arr, gt, r); } private static void swap(int[] arr, int i, int j) { int t = arr[i]; arr[i] = arr[j]; arr[j] = t; } // 打印arr数组的所有内容 public static void printArray(int[] arr) { for (int i = 0; i < arr.length; i++) { System.out.print(arr[i]); System.out.print(' '); } System.out.println(); return; } // 测试 QuickSort public static void main(String[] args) { // 三路快速排序算法也是一个O(nlogn)复杂度的算法 // 可以在1秒之内轻松处理100万数量级的数据 int[] arr = {4, 3, 12, 12}; QuickSort3Ways(arr, 0, arr.length - 1); printArray(arr); }}","categories":[],"tags":[{"name":"数据库结构与算法","slug":"数据库结构与算法","permalink":"http://www.frankfeekr.cn/tags/数据库结构与算法/"}]},{"title":"Web API文档生成工具apidoc使用","slug":"Web-API文档生成工具apidoc使用","date":"2018-06-27T08:06:37.000Z","updated":"2021-10-23T14:22:52.647Z","comments":true,"path":"2018/06/27/Web-API文档生成工具apidoc使用/","link":"","permalink":"http://www.frankfeekr.cn/2018/06/27/Web-API文档生成工具apidoc使用/","excerpt":"","text":"apidoc可以根据代码注释生成web api文档,支持大部分主流语言,相对而言,web接口的注释维护起来更加方便,不需要额外再维护一份文档。 apidoc从注释生成静态html网页文档,不仅支持项目版本号,还支持api版本号。 官网:Inline Documentation for RESTful web APIs 环境安装(windows系统为例) node环境 Node.js官网 (下载最新的node.js msi安装包即可,按提示安装成功后,打开cmd命令行) 12node -v 回车#出现版本号则说明node安装成功 Centos上安装nodejs 通过yum安装 1yum install -y nodejs 安装成功后 1234567[root@chengchi mango_server]# node -vv6.14.2[root@chengchi mango_server]# npm -v3.10.10# 如果安装完nodejs没有安装上npm命令,请通过yum安装npm[root@chengchi mango_server]# yum install npm 安装cnpm(淘宝镜像) 安装完msi版本的node,已经默认安装好npm工具。因为国外官方npm太慢,故使用淘宝cnpm(这是一个完整 npm 镜像,你可以用此代替官方版本(只读),同步频率目前为 10分钟 一次以保证尽量与官方服务同步。) 1234# 安装cnpm淘宝镜像npm install -g cnpm --registry=https://registry.npm.taobao.org# cnpm -v 回车(查看是否安装成功) 安装apidoc命令工具 1cnpm install apidoc -g 好了到这里,我们的环境安装工作到此结束,大功告成,现在开始写我们的注释然后生成文档吧。 注释的书写规则 和 生成 apidoc命令: 1apidoc -i D:\\att\\att_2017\\Application\\Check -o D:\\att\\att_2017\\apidoc Web API文档生成工具apidoc","categories":[],"tags":[]},{"title":"用别名(alias)创建你自己的命令","slug":"用别名(alias)创建你自己的命令","date":"2018-06-06T02:00:54.000Z","updated":"2021-10-23T14:22:52.652Z","comments":true,"path":"2018/06/06/用别名(alias)创建你自己的命令/","link":"","permalink":"http://www.frankfeekr.cn/2018/06/06/用别名(alias)创建你自己的命令/","excerpt":"","text":"一、alias初体验 现在是时候,感受第一次编程经历了!我们将用 alias 命令创建我们自己的命令。但在 开始之前,我们需要展示一个命令行小技巧。可以把多个命令放在同一行上,命令之间 用”;”分开。它像这样工作 1command1; command2; command3... 我们会用到下面的例子: 1234[me@linuxbox ~]$ cd /usr; ls; cd -bin games kerberos lib64 local share tmp...[me@linuxbox ~]$ 正如我们看到的,我们在一行上联合了三个命令。首先更改目录到/usr,然后列出目录 内容,最后回到原始目录(用命令”cd -“),结束在开始的地方。 现在,通过 alias 命令 把这一串命令转变为一个命令。 我们要做的第一件事就是为我们的新命令构想一个名字。 比方说”test”。在使用”test”之前,查明是否”test”命令名已经存在系统中,是个很不错 的主意。 为了查清此事,可以使用 type 命令: 12[me@linuxbox ~]$ type testtest is a shell builtin 哦!”test”名字已经被使用了。试一下”foo”: 12[me@linuxbox ~]$ type foobash: type: foo: not found 太棒了!”foo”还没被占用。创建命令别名: 1[me@linuxbox ~]$ alias foo='cd /usr; ls; cd -' 注意命令结构: 1alias name='string' 在命令”alias”之后,输入“name”,紧接着(没有空格)是一个等号,等号之后是 一串用引号引起的字符串,字符串的内容要赋值给 name。我们定义了别名之后, 这个命令别名可以使用在任何地方。试一下: 1234[me@linuxbox ~]$ foobin games kerberos lib64 local share tmp...[me@linuxbox ~]$ 我们也可以使用 type 命令来查看我们的别名: 12[me@linuxbox ~]$ type foofoo is aliased to `cd /usr; ls ; cd -' 删除别名,使用 unalias 命令,像这样: 123[me@linuxbox ~]$ unalias foo[me@linuxbox ~]$ type foobash: type: foo: not found 虽然我们有意避免使用已经存在的命令名来命名我们的别名,但这是常做的事情。通常, 会把一个普遍用到的选项加到一个经常使用的命令后面。例如,之前见到的 ls 命令,会 带有色彩支持: 12[me@linuxbox ~]$ type lsls is aliased to 'ls --color=tty' 要查看所有定义在系统环境中的别名,使用不带参数的 alias 命令。下面在 Fedora 系统中 默认定义的别名。试着弄明白,它们是做什么的: 123[me@linuxbox ~]$ aliasalias l.='ls -d .* --color=tty'... 在命令行中定义别名有点儿小问题。当你的 shell 会话结束时,它们会消失。 二、接如何设置永久有效的alias命令 1.打开.bashrc文件 当系统重启之后就会失效,所以要实现永久有效,则需要 修改用户目录下的一个文件 .bashrc 目录为 ~/.bashrc 1vim ~/.bashrc 2.自定义命令行 alias cls=’clear’这行,并且加一个注释# User specific aliases and functions方便我们日后的查阅 修改后如下: 12345678910111213#.bashrc# User specific aliases and functionsalias cls='clear'# Source global definitionsif [ -f /etc/bashrc ]; then . /etc/bashrcfi# Uncomment the following line if you don't like systemctl's auto-paging feature:# export SYSTEMD_PAGER=# User specific aliases and functions12345678910111213 3.保存退出 1:wq 4.使用命令生效更改 1source ~/.bashrc 5.验证 重启后尝试 关闭ssh重新连接 输入alias可以看到所有的别名","categories":[],"tags":[{"name":"Linux","slug":"Linux","permalink":"http://www.frankfeekr.cn/tags/Linux/"}]},{"title":"滴滴出行 Java 实习生面经","slug":"滴滴出行Java实习生面经","date":"2018-05-29T15:06:33.000Z","updated":"2021-10-23T14:22:52.652Z","comments":true,"path":"2018/05/29/滴滴出行Java实习生面经/","link":"","permalink":"http://www.frankfeekr.cn/2018/05/29/滴滴出行Java实习生面经/","excerpt":"","text":"时间:20180529 19:30(1hours10mins) 自我介绍 项目介绍 网络基础 1.OSI七层模型,TCP/IP四层模型 2.TCP和UDP区别 3.TCP三次握手和四次挥手,一定要三次挥手不行吗? 4.网络请求的过程 5.介绍一下http和https,https原理是什么 6.http头信息是怎么样的 Java基础 1.Java怎么来实现线程安全的,i++,sychronized,内部类… 2.hashmap是怎么实现的,和ConcurrentHashMap有什么区别? 3.线程生命周期 4.== 和 equal() 有什么区别 5.防SQL注入的原理是怎么样的 操作系统 1.乐观锁和悲观锁 2.线程和进程,线程怎么通信,进程间怎么通信 3.IO类型有哪些?同步阻塞,异步阻塞… 数据结构 1.介绍一下都有哪些排序算法 2.快速排序和归并排序时间复杂度都一样,为什么要用快速排序 3.abcdef…英文字母排序,要怎么做 4.流排序和计数排序","categories":[],"tags":[{"name":"校招","slug":"校招","permalink":"http://www.frankfeekr.cn/tags/校招/"}]},{"title":"阿里巴巴实习生初面面经","slug":"阿里巴巴实习生初面面经","date":"2018-05-28T08:44:38.000Z","updated":"2021-10-23T14:22:52.652Z","comments":true,"path":"2018/05/28/阿里巴巴实习生初面面经/","link":"","permalink":"http://www.frankfeekr.cn/2018/05/28/阿里巴巴实习生初面面经/","excerpt":"","text":"自我介绍 Java基础 1.Java的基本数据类型有哪些,包装类有哪些?知道自动装箱和拆箱吗? 4类8种基本数据类型。4整数型,2浮点型,1字符型,1布尔型 数据类型 存储需求 取值范围 对应包装类 byte 8位 最大存储数据量是255,存放的数据范围是-128~127之间 Byte short 16位 最大数据存储量是65536,数据范围是-32768~32767之间 Short int 32位 最大数据存储容量是2的32次方减1,数据范围是负的2的31次方到正的2的31次方减1 Integer long 64位 最大数据存储容量是2的64次方减1,数据范围为负的2的63次方到正的2的63次方减1 Long float 32位 数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F Float double 64位 数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加 Double boolean 只有true和false两个取值 Boolean char 16位 存储Unicode码,用单引号赋值 Character 引用数据类型 类(class)、接口(interface)、数组 自动装箱和拆箱 基本数据类型和它对应的封装类型之间可以相互转换。自动拆装箱是jdk5.0提供的新特特性,它可以自动实现类型的转换 装箱:从基本数据类型到封装类型叫做装箱 拆箱:从封装类型到基本数据类型叫拆箱 1234567jdk 1.5public class TestDemo { public static void main(String[] args) { Integer m =10; int i=m; }} 上面的代码在jdk1.4以后的版本都不会报错,它实现了自动拆装箱的功能,如果是jdk1.4,就得这样写了 1234567jdk 1.4public class TestDemo { public static void main(String[] args) { Integer b = new Integer(210); int c = b.intValue(); }} 2.5个节点的二叉树有几种形态? 5种(纸上画一下就ok了) 3.死锁的四个必要条件 互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用。 请求与保持条件(Hold and wait):已经得到资源的进程可以再次申请新的资源。 非剥夺条件(No pre-emption):已经分配的资源不能从相应的进程中被强制地剥夺。 循环等待条件(Circular wait):系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源。 4.GC垃圾回收机制(待完善) 待完善 5.ArrayList和LinkedList的区别 1、ArrayList和LinkedList可想从名字分析,它们一个是Array(动态数组)的数据结构,一个是Link(链表)的数据结构,此外,它们两个都是对List接口的实现。前者是数组队列,相当于动态数组;后者为双向链表结构,也可当作堆栈、队列、双端队列 2、当随机访问List时(get和set操作),ArrayList比LinkedList的效率更高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找。 3、当对数据进行增加和删除的操作时(add和remove操作),LinkedList比ArrayList的效率更高,因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动。 4、从利用效率来看,ArrayList自由性较低,因为它需要手动的设置固定大小的容量,但是它的使用比较方便,只需要创建,然后添加数据,通过调用下标进行使用;而LinkedList自由性较高,能够动态的随数据量的变化而变化,但是它不便于使用。 5、ArrayList主要控件开销在于需要在lList列表预留一定空间;而LinkList主要控件开销在于需要存储结点信息以及结点指针信息。 6.一个无序的数组,递归方式,不用排序算法如何排序?(待完善) 7.重载和重写 重载和重写,如何确定调用哪个函数 重载:重载发生在同一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载。 重写:重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。根据不同的子类对象确定调用的那个方法。 8.final和static final 1. 数据 声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。 对于基本类型,final 使数值不变; 对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。 1234final int x = 1;// x = 2; // cannot assign value to final variable 'x'final A y = new A();y.a = 1; 2. 方法 声明方法不能被子类覆盖。 private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是覆盖基类方法,而是在子类中定义了一个新的方法。 3. 类 声明类不允许被继承。 static 1. 静态变量 静态变量在内存中只存在一份,只在类初始化时赋值一次。 静态变量:类所有的实例都共享静态变量,可以直接通过类名来访问它; 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。 1234public class A { private int x; // 实例变量 public static int y; // 静态变量} 2. 静态方法 静态方法在类加载的时候就存在了,它不依赖于任何实例,所以静态方法必须有实现,也就是说它不能是抽象方法(abstract)。 3. 静态语句块 静态语句块在类初始化时运行一次。 4. 静态内部类 内部类的一种,静态内部类不依赖外部类,且不能访问外部类的非静态的变量和方法。 5. 静态导包 1import static com.xxx.ClassName.* 在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。 6. 变量赋值顺序 静态变量的赋值和静态语句块的运行优先于实例变量的赋值和普通语句块的运行,静态变量的赋值和静态语句块的运行哪个先执行取决于它们在代码中的顺序。 1public static String staticField = \"静态变量\"; 123static { System.out.println(\"静态语句块\");} 1public String field = \"实例变量\"; 123{ System.out.println(\"普通语句块\");} 最后才运行构造函数 123public InitialOrderTest() { System.out.println(\"构造函数\");} 存在继承的情况下,初始化顺序为: 父类(静态变量、静态语句块) 子类(静态变量、静态语句块) 父类(实例变量、普通语句块) 父类(构造函数) 子类(实例变量、普通语句块) 子类(构造函数) 9.平时怎么调试程序,Gstack了解吗?(待完善) 10.&&与&,||与|的区别 (1)&&和&都是表示与,区别是&&只要第一个条件不满足,后面条件就不再判断。而&要对所有的条件都进行判断。 12345678910// 例如:public static void main(String[] args) { if((23!=23)&&(100/0==0)){ System.out.println(\"运算没有问题。\"); }else{ System.out.println(\"没有报错\"); } } // 输出的是“没有报错”。而将&&改为&就会如下错误:// Exception in thread \"main\" java.lang.ArithmeticException: / by zero 原因: &&时判断第一个条件为false,后面的100/0==0这个条件就没有进行判断。 &时要对所有的条件进行判断,所以会对后面的条件进行判断,所以会报错。 (2)||和|都是表示“或”,区别是||只要满足第一个条件,后面的条件就不再判断,而|要对所有的条件进行判断。 看下面的程序: 12345678public static void main(String[] args) { if((23==23)||(100/0==0)){ System.out.println(\"运算没有问题。\"); }else{ System.out.println(\"没有报错\"); } }// 此时输出“运算没有问题”。若将||改为|则会报错。 原因 ||判断第一个条件为true,后面的条件就没有进行判断就执行了括号中的代码 而|要对所有的条件进行判断, 所以会报错 11.Java中的引用和C++中的指针有什么区别 类型:引用是地址的数据元素,可以转换成字符串查看,不用关心它的长度;指针是一个装地址的变量,它的长度是固定的,一般是一个计算机字长 所占内存:引用申明时没有实体,不占用空间;指针申明后用到才会赋值,用不到不会分配内存 类型转换:引用类型转换可能不成功,运行时会抛出异常或者编译不通过;指针只是个内存地址,但可能所指的地址不是程序想要的 初始值:引用初始值是null;指针是int类型,如果不初始化指针,它的值就会不固定,这样很危险 计算:引用是不能进行计算的;指针可以,如自加或自减,可以代替数组下标 控制:引用不可以计算,所以它只存在自己程序中,可以被控制;指针是内存地址,可以计算,所以它有可能指向一个不属于自己程序使用的内存地址,对于其他程序是很危险的,对自己而言也是不容易控制的 内存泄漏:引用不会引起内存泄漏;指针容易产生内存泄漏,所以要及时回收 作为参数:函数内交换两个引用是没有意义的;指针可以作为参数给函数使用","categories":[],"tags":[{"name":"校招","slug":"校招","permalink":"http://www.frankfeekr.cn/tags/校招/"}]},{"title":"Navicat Premium 12数据库管理工具破解说明","slug":"Navicat-Premium-12数据库管理工具破解说明","date":"2018-05-24T03:03:32.000Z","updated":"2021-10-23T14:22:52.646Z","comments":true,"path":"2018/05/24/Navicat-Premium-12数据库管理工具破解说明/","link":"","permalink":"http://www.frankfeekr.cn/2018/05/24/Navicat-Premium-12数据库管理工具破解说明/","excerpt":"","text":"下载地址:Navicat Premium 12安装包(附破解工具) 注意: PatchNavicat.exe可能报毒,但是安全的破解文件,介意的小伙伴请慎重下载! 破解方式: 打开PatchNavicat.exe 浏览,选定3次,“C:\\Program Files\\PremiumSoft\\Navicat Premium 12\\navicat.exe” 即可破解成功! so easy… 请不要下载官网的安装包,否则无法破解成功。","categories":[],"tags":[{"name":"开发工具","slug":"开发工具","permalink":"http://www.frankfeekr.cn/tags/开发工具/"}]},{"title":"浅谈 Java Web 开发与 Python Web 开发的区别","slug":"浅谈Java-Web开发与Python-Web开发的区别","date":"2018-04-29T14:41:58.000Z","updated":"2021-10-23T14:22:52.652Z","comments":true,"path":"2018/04/29/浅谈Java-Web开发与Python-Web开发的区别/","link":"","permalink":"http://www.frankfeekr.cn/2018/04/29/浅谈Java-Web开发与Python-Web开发的区别/","excerpt":"","text":"转载自:浅谈 Java Web 开发与 Python Web 开发的区别 – 金丝燕网 今天这篇文章谈一谈 Java Web 开发和 Python Web 开发的区别。在这里我并不是鼓励大家从 Java Web 转向 Python Web 开发,我只是想说一下自己的感觉而已,不一定适合每一位情况,仅仅供大家参考。另外,我也建议搞 Java Web 的人可以了解一下 Python Web 的开发情况,从另外一个角度看 Java Web 开发肯定大有收获。 我使用 Java 的时间很久,而且 Java 也给我带来了很多的收获。之前一直觉得 Java 非常重要,从内心是把它当做人生的一技之长,可随着年龄和阅历的增长,也随着职位和职责的改变,早年的观念也在逐渐发生变化。 我做 Web 开发有三年多,这个时间段里对 Java 有了很多深入的研究,后来转向了大数据开发,初步对 Python 有了些了解,最后换了一家公司,转身成为一个技术领头人,而这里的网站是以 Python 开发的。经过最近一段时间对 Python Web 的了解,我越发感觉有些话想说出来。 我感觉,Java Web 过于复杂,导致人们花费了很多的精力去了解其中的细节,最终眼里只看到树木,而不能看到森林。这种情况对开发人员来说,是不利的会导致走很多的弯路。之前我看过《Struts 技术内幕》,花费了很多的精力去研究其源码,等真正对它比较熟悉的时候,业内逐渐向 Spring MVC 过渡了,然后赶紧买了几本书来看,有 Spring MVC 学习指南,也有 Spring MVC 源码解析等,后来大家又开始转向微服务,然后紧跟脚步学习新的知识。可是后来我发现这种发展道路并不能建立起一个全局的 Web 开发观,很多时候纠结在知识的细节上面。反观 Python Web 开发,薄薄的一本书,从虚拟化的环境配置到框架介绍,从模板的使用到信号机制,从系统管理到消息中间件,一本书就能囊括很多整个 Web 开发系统,让人对 Web 开发有一个全局观。一个开发者如果能有一个全局观,那么他就能快速的切入到重点,找到更适合自己发展的职业道路。 转载自: java 和 python 开发 web 优缺点 Java 属于高大上,适合 12306 这种有钱的金主,同样的项目要是用 java 做的,就能唬来成倍的钱,没钱搞 java,只能晚上加班到10来点,在 eclipse 吭哧吭哧地编译完项目以后,在七八屏的堆栈信息里,不停上翻下翻象捡芝麻一样 看到底哪里出错了,python 属于小而美,适合做一些内聚性很强的工具,用来当锤子,榔头使唤,但象 web 开发这种的到处是杂七杂八的零散的文件,象餐厅服务员一样上菜端盘子这种苦力活最适合 php 这种 “最好的语言”, 因为 web 开发大部分要快速迭代,码农经常听的是 “需求又变了”,只有 php 码农的键盘才能跟得上老板思维变化的节奏,左屏 notepad++ 敲代码,右屏 f5 刷浏览器就搞定了,什么编译,重启服务器,数空格的时间都不用了。php 之所以能号称最好的语言,还有一个原因是性价比,大部分公司只要不是中石化中石油都是从小公司开始创业的,象掏包这样的网站一开始就招 php,小老板最关心啥,钱啊,花1分钱要赚1块钱,,你想想,一个 php, 策划,开发,测试,稍带服务器全端了,而且还能加班加点,随叫随到,老板心里就一个字, 值!小老板招 php 的心情,就跟屌丝买小米手机的心情是一样一样的,就差抢购了。 Java 框架多数是扯的比较多,属于害怕 java 性能过好,抢其他语言饭碗,所以自己给自己 JJ 来一刀的东西。语言在泛型方面不够完整。但是比较靠谱。 python 简单直接,开发效率高,不过生产系统不建议用 python 直接堆 web 页面给用户用,做原型很不错,做服务器端日志迁移分析工具也挺好,用 python 做管理监控等脚本开发也不错。 上面就是小编为大家整理的关于 java 和 python 开发 web 的文章,希望对大家有帮助。在实际的操作过程中大家可以根据实际情况进行灵活的调整。","categories":[],"tags":[{"name":"杂文","slug":"杂文","permalink":"http://www.frankfeekr.cn/tags/杂文/"}]},{"title":"腾讯校园实习招聘面经","slug":"腾讯校园实习招聘面经","date":"2018-04-15T12:44:12.000Z","updated":"2021-10-23T14:22:52.652Z","comments":true,"path":"2018/04/15/腾讯校园实习招聘面经/","link":"","permalink":"http://www.frankfeekr.cn/2018/04/15/腾讯校园实习招聘面经/","excerpt":"","text":"时间:2018年4月15日 下午 岗位:腾讯云-运营开发 下午很荣幸的参加了腾讯在武汉专场面试,大概面试在一个小时的时间。大公司真的不一样,一套完整的流程下来感觉特别舒服。 到达面试的酒店,先是微信扫一扫签到,然后到达会议室候场,轮到自己的时候会有微信通知+短信通知。基本上面试的时间和约好的相差不大,只会晚不会早,所以如果比较晚面试的也没必要提早太久过去。 好了话不多说啦,我就回忆一下面试中遇到的问题: 开始前 自我介绍一下 为什么要考研 Linux篇 知道Linux内存占用的命令吗? Linux定时任务的命令? crontab -e,那具体的配置参数:分 时 日 月 周 进程和线程的区别? Linux下proc目录都存放了哪些东西? 硬链接与软链接 数据库篇 知道哪些存储引擎? InnoDB 和 MyISAM有什么区别? 为什么要索引? 一般哪些字段需要我们建立索引? Redis有哪些数据结构? drop 和 delete的区别?truncate呢? 数据结构 什么是堆和栈?说一下堆栈都存储哪些数据? 知道哪些排序? 说一下冒泡排序和快速排序?说一下他们的时间复杂度? 数组和链表有什么区别? 计算机网络 说一下OSI七层模型? TCP/IP五层模型? TCP和UDP的区别? TCP三次握手?那四次挥手呢? 说一下503和403都代表了什么意思? Python语法 list和tuple有什么区别? python执行shell命令具体是哪个包的哪个函数? 字符串转JSON字符串的函数是什么? web开发 介绍一下RESTful API是什么,详细介绍一下? 用过哪些框架?(前端+后端) vue.js和jquery.js有什么区别? HTTP中403和500码代表什么含义? 知道HTTP哪些请求方式,GET和POST区别?我还顺便说了Delete和Update 机器学习 知道哪些机器学习算法? 说一下项目用到的机器学习算法?(主要聊了KNN和SVM算法) 假设有10W台服务器,服务器有可能一年内会有一次宕机的可能性,如何通过预测减少这些意外?(通过深度学习,如何选取特征与数据,模型…) 项目类 最近做的是什么项目? 还有很多现在没法一一回想起来了,后续再更新和完成问题的解答。 最后 最近在看什么技术书籍? 平时都看什么技术网站啊?(我:Github,stack overflow、CSDN、SegmentFault…) 你有什么想问我的吗? 我问了:(1)事研发岗位和机器学习等算法领域结合?(2)如何成为全栈开发工程师?(3)如果加入你们运营开发岗位具体是做哪方面的工作?","categories":[],"tags":[{"name":"校招","slug":"校招","permalink":"http://www.frankfeekr.cn/tags/校招/"}]},{"title":"PHP 编程中 @ 符号的作用","slug":"PHP-编程中-符号的作用","date":"2018-04-10T01:35:18.000Z","updated":"2021-10-23T14:22:52.646Z","comments":true,"path":"2018/04/10/PHP-编程中-符号的作用/","link":"","permalink":"http://www.frankfeekr.cn/2018/04/10/PHP-编程中-符号的作用/","excerpt":"","text":"在编程过程中我们经常会遇到@这个符号,例如(C#)中:通常在路径字符串中我们会使用@表示强制不转义。@"C:\\Program Files\\TTPlayer\\TTPlayer.exe "。 在PHP编程中我们也经常会看到@符号,之前也很少关注这个符号,只知道是屏蔽错误的。例如下列的代码中,明明程序的结果都出来了,但是还出现这样的warning,这并不是 php 版本问题,而是 php 配置问题。 一般是由于PHP版升级的原因,PHP 5.4 以上的版本一般会报这个错误。 12345Warning: Creating default object from empty value in D:\\work\\cmisTEST\\Alfresco_CMIS_API.php on line 441Warning: Creating default object from empty value in D:\\work\\cmisTEST\\Alfresco_CMIS_API.php on line 442Warning: Creating default object from empty value in D:\\work\\cmisTEST\\Alfresco_CMIS_API.php on line 443Warning: Creating default object from empty value in D:\\work\\cmisTEST\\Alfresco_CMIS_API.php on line 444==================================== Contained objects: Data Dictionary (cmis:document) Guest Home (cmis:document) User Homes (cmis:document) Imap Attachments (cmis:document) Sites (cmis:document) DemoFolder (cmis:document) ACCT (cmis:document) dev (cmis:document) 1508725606009.jpg (cmis:document) 1508725628733.jpg (cmis:document) 1508725869696.jpg (cmis:document) USER (cmis:document) 测试文本.txt (cmis:document) 只需要修改php程序即可,如下: 1234@$this->containedObjects[$x]->objUrl=(string)$objUrl;@$this->containedObjects[$x]->author=(string)$ent->author->name;@$this->containedObjects[$x]->title=(string)$ent->title;@$this->containedObjects[$x]->type=$objType; 或者在修改php.ini文件,或是在程序顶部增加ini_set( 'display_errors', 'off' );代码块即可。 总结 @是可以屏蔽函数执行过程中遇到问题而产生的一些错误、警告信息,这样用户就看不到程序的出错信息。这样除了用户界面会友好一些外,更重要的是安全性,因为屏蔽了出错文件的路径等信息。","categories":[],"tags":[{"name":"PHP","slug":"PHP","permalink":"http://www.frankfeekr.cn/tags/PHP/"}]},{"title":"全栈开发神兵利器","slug":"全栈开发神兵利器","date":"2018-03-25T14:30:31.000Z","updated":"2021-10-23T14:22:52.650Z","comments":true,"path":"2018/03/25/全栈开发神兵利器/","link":"","permalink":"http://www.frankfeekr.cn/2018/03/25/全栈开发神兵利器/","excerpt":"","text":"工欲善其事,必先利其器。这里我将推荐开发过程中的提效工具、开发利器、协作工具、文档工具等等。欢迎在 issues#21 中补充你用到过的神兵利器,我将把留言中的工具更新到本文。 一、团队协作 团队协作 Teambition:团队协作工具创导者 有道云协作:企业知识管理与协作平台 tower:深受用户喜爱的团队协作工具 远程 TeamViewer:安全远程访问和支持 TeamViewer 基于最广泛的平台和技术,连接全世界的人、地区和事物。 向日葵:简单好用的远程控制软件 mstsc:运行 win+r,输入 mstsc。不要忽略 windows 自带的强大远程桌面连接工具 注意:真的不要再用 QQ 远程了,真的很卡! 笔记备忘 印象笔记:工作必备效率应用 有道云笔记:网易出品,获得 5000 万用户青睐的笔记软件。提供了 PC 端、移动端、网页端等多端应用,用户可以随时随地对线上资料进行编辑、分享以及协同。 日事清:怕工作进度延误 就用日事清 滴答清单:一个帮你高效完成任务和规划时间的应用 二、图形与设计 思维导图与原型设计 XMind:思维导图,框架图等等,非常推荐。收费软件,部分功能可用 MindManager:让思考、计划和沟通变得更容易 百度脑图:在线免费脑图,推荐 Mockplus:更快、更简单的原型设计 Axure RP:是一款专业的快速原型设计工具 绘图工具 Visio:微软绘图工具,以直观的方式工作,轻松绘制图表 亿图:国产综合图形图表设计软件。类似 visio 的绘图工具(完美破解版) ProcessOn:支持流程图、思维导图、原型图、UML、网络拓扑图、组织结构图等 draw.io:free online diagram software for making flowcharts, process diagrams, org charts, UML, ER and network diagrams StarUML:A sophisticated software modeler for agile and concise modeling(总之 UML 绘图神器) 平面与视频设计 只会写代码,设计都不会?本人从事过多年平面设计和视频相关的工作,这里也给大家推荐一些平时做设计的时的一些软件。 Adobe Photoshop:图像编辑和合成,这个就不用我介绍了吧 Adobe Premiere Pro:视频制作和编辑(业余爱好者可使用绘声绘影) After Effects:电影视觉效果和动态图形 After Illustrator:矢量图形和插图 Corel DRAW:和 AI 齐名的矢量图制作工具 三、版本控制 SVN Subversion (SVN) 是一个开源的版本控制系統, 也就是说 Subversion 管理着随时间改变的数据。 这些数据放置在一个中央资料档案库 (repository) 中。 这个档案库很像一个普通的文件服务器, 不过它会记住每一次文件的变动。 这样你就可以把档案恢复到旧的版本, 或是浏览文件的变动历史 工具下载:tortoiseSVN 学习资源 文档:菜鸟教程 SVN教程 视频:版本管理工具介绍—SVN篇 Git Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目 工具下载: SourceTree(推荐★★★) tortoiseGit GitHub Desktop 学习资源 文档:菜鸟教程 Git教程 视频:版本管理工具介绍—Git篇 Git 工作流 集中式工作流,功能分支工作流, GitFlow 工作流,Forking 工作流,Pull Requests Git 托管平台 Github:全球最大的程序员社交网站(同性交友网站) 码云:国内比较大的 Git 托管平台。码云专为开发者提供稳定、高效、安全的云端软件开发协作平台。无论是个人、团队、或是企业,都能够用码云实现代码托管、项目管理、协作开发 CODING:国内 Git 托管平台,Coding,让开发更简单 自主搭建代码托管平台 GitLab:可以使用 GitLab 官方的服务,也提供了开源社区版供团队搭建使用。(推荐使用 Docker 可实现一键自动化搭建) Gogs:一款极易搭建的自助 Git 服务,通过 Go 语言写的,适合在 Linux 服务器上搭建 VisualSVN:VisualSVN Server allows you to easily install and manage a fully-functional Subversion server on the Windows platform. iF.SVNAdmin:The iF.SVNAdmin application is a web based GUI to your Subversion authorization file. It is based on PHP 5.3 and requires a web server (Apache) to be installed. (通过 PHP 在 Linux 上搭建 SVN 平台,并且有 web 管理页面) 四、全栈开发 数据库管理(以Mysql为例) Navicat Premium:可以连接所有数据库,配套Navicat也针对不同的数据库有不同的版本,请点击进入官网自行查看,收费软件,需要百度自行破解。 SQLyog:Administrate MySQL Databases With Ease Using a Graphical Interface,免费 SSH 连接工具 MobaXterm(超级推荐,太极客了!而且是免费的) Xshell 5 SecureCRT 6.6 GitKraken 推荐:以上三款工具我都使用过,目前已经弃用 Xshell 和 SecureCRT,推荐使用 MobaXterm SHELL Oh My Zsh - a delightful & open source framework for Z-Shell 接口调试工具 抓包工具1 | Fiddler:The free web debugging proxy(很优秀的抓包工具,目前似乎只支持 windows 用户) 抓包工具2 | charles:Charles is an HTTP proxy / HTTP monitor / Reverse Proxy that enables a developer to view all of the HTTP and SSL / HTTPS traffic between their machine and the Internet.(MacOS的必备抓包工具) 接口调试 | postman:Developers use Postman to build modern software for the API-first world. 轻量级开发工具 Sublime Text:A sophisticated text editor for code, markup and prose VS Code:Free. Open source. Runs everywhere.(非常推荐,后起之秀,有丰富的社区插件,超级推荐使用,推荐安装 One Dark Pro Theme) Atom:A hackable text editor for the 21st Century brackets:A modern, open source text editor that understands web design.(前端神奇) 三者比较请移步知乎:Atom、Sublime Text、VSCode 三者比较,各有哪些优势和弱势? 容器化技术 Docker:秒级启动虚拟机容器技术。真正一次编写,到处运行。(一定要学!) 五、文档技术 在团队协作中必须会涉及到文档交互部分,这里推荐以下几个文档平台和开源项目 文档平台 看云:专注于文档在线创作、协作和托管(极力推荐,每个文档只有50Mb的免费空间,超过需要收费) 自动文档生成工具 ApiDoc:Inline Documentation for RESTful web APIs,可以通过命令行将代码中的注释生成在线可调试的文档,开发者的福音啊 Swagger:The Best APIs are Built with Swagger Tools,在 Java web 项目中用的比较多 开源框架 ShowDoc:一个非常适合IT团队的在线 API 文档、技术文档工具。使用 PHP 开发的文档框架 MinDoc:MinDoc 是一款针对IT团队开发的简单好用的文档管理系统 vuepress:vue 官方团队的文档解决方案,适合于静态博客或是文档 docsify:类似 gitbook 和 vuepress 的文档解决方案 hexo:markdown 编写,自动生成静态博客","categories":[],"tags":[{"name":"开发工具","slug":"开发工具","permalink":"http://www.frankfeekr.cn/tags/开发工具/"}]},{"title":"互联网的一代人","slug":"互联网的一代人","date":"2017-12-04T04:37:07.000Z","updated":"2021-10-23T14:22:52.650Z","comments":true,"path":"2017/12/04/互联网的一代人/","link":"","permalink":"http://www.frankfeekr.cn/2017/12/04/互联网的一代人/","excerpt":"","text":"本来只是想发一条微博的,硬是写成了博客。 想想09年以前啊,那时候还有上网、下线的概念,现在和别人说起来“我下线了”都会觉得好好笑。而如今人和人之间真正实现了移动互联的contact,而还有少数人还会通过电波声音短信传递。看如今路由器已经覆盖家家户户了吧,想起08年那时候还好奇猫和路由器的区别,现在看来算是显得老土了。而移动互联高度普及的现在,最值得我敬佩的是:移动互联竟然可以这么彻底的改变父母这60、70后的观念,并彻底让他们欲罢不能。 而现在的人啊,有时候说自己工作忙学习忙,但是他们依然停不下来的都是忙里偷闲的刷微博和朋友圈,耗费大量时间在手机社交应用中有必要和没必要的聊天上,而上淘宝也变成一种减压的方式。前些日子听一朋友说,现在啊我们都已经成为了住在别人手机里的朋友,现在的人谈个恋爱比从前累多了,每天都需要和微信那头的他聊的“天昏地暗”(此处夸张),而这种聊天方式太即时,更像是在对方的身边安插一个信使,若是没有及时的回复又会惹得对方的百般猜想。说起这些聊天工具吧,就因为太即时和免费,我们太随意的就会发出太多“无意义”的信息,同时也无形中绑架我们的时间,而一大堆的表情包也在不断的拉长人与人之间的距离。回顾一下自己以前大部分靠发短信联系的日子,发信息的时候肯定不会问诸如“在吗?在么?”之类的问句,会直接切入话题。而短信,它才是对方及时能看到的信息传递媒介,发短信的时候我们会过滤很多不必要的言语,有时候会为了因为短信的字数而不断的re-summary&re-edit…使之言简意赅,原因是短信是收费的(非常不错的是在谷歌的whatsapp中每年都需要6块钱的费用)。简单的做了几个情侣之间的测试,通过短信和微信两种途径给对方发消息,若是在多个钟头之后回复自己,相比微信就会变得坐立不安,再发第二条的概率远远比短信来的高。“对方没有回复自己的时候又觉得对方会不会发生什么事情了?”但是又能发生多大的事情会不打电话告诉你呢?总之微信的过程伴随了太多的心理活动,而短信次之,电话则不会。 网络和手机盛行的如今,大部分的人也将更能接受“网恋”,90和00年代认为网络是虚拟的,而如今的10年代确实彻底的颠覆了这一观念。曾经有一句经典的话,“我在网络上和别人聊天,都不知道对方是人还是狗?”。有调查也显示,10年以后,在大学生的群体中,网恋几乎成为了80%大学生的交往途径,同时移动互联的普及也增大了婚外情的发生概率。 如果说80后见证了互联网和电脑的普及和发展,那么90后是真真切切的看到手机的兴起,从操作系统的手机,到Symbian OS、BlackBerry OS,再到现在主流的Android OS和iOS,并看到无线网络WIFI、3G、4G从未有过的普及率。想想微信、淘宝、支付宝对于我们生活方式的革新这是从未有过的。说个自己发生的例子:早在08年的时候,那时候第一次上淘宝网买东西,突然有一天想要查看快递物流的意识,那时候塞班系统也不存在淘宝客户端,通过2g的流量,也只能用wap网页查看物流,给用户的体验是相当差的。然而在同年9月,在GoogleI/O大会上,谷歌正式发布了Android 1.0系统,这也是Android系统最早的版本。后来不断的看到了安卓机的出现,大屏手机的出现,现在通过app大大便捷了我们的生活。当年有同学说安卓必将成为未来的主流趋势,诺基亚也会告别这个时代。我笑着回答,诺基亚全球具有这么高的销售额,手机行业的老大哥,怎么可能?而现在国内的手机厂商基于安卓原生的定制系统也都层出不穷,最知名的莫过于flyme和MIUI定制系统(此处不做深究便简单略过)。总之互联网的发展每一天都充满了可能性,谁又可能说不会有新的产物代替现在的东西呢。 说起来父母这辈的中年人,即便是没有受到电脑发展影响的他们,一定能感受到手机对他们生活革命性变化。这点会从他们的朋友圈和他们日益增长的微信好友数量所深刻体会,更滋生了一种“父母式的朋友圈”。想起来12年以前他们总是抱怨我们为什么总喜欢抱着手机玩,睡前和睡醒就看手机,而现在的他们也变得口是心非,并正式融入手机控这一大家族。 身处移动互联网时代,智能手机弃之不用显然不可能,但手机真是毁掉了一代人的眼。写到这里似乎会感觉到消极的味道,但我们也要感谢时代和科技的发展带给我们的便捷,以包容或者接纳的心态对待变化的世界是一种美妙的体验。","categories":[],"tags":[{"name":"杂文","slug":"杂文","permalink":"http://www.frankfeekr.cn/tags/杂文/"}]},{"title":"围墙外的世界 | 网站推荐","slug":"围墙外的世界-网站推荐","date":"2017-11-28T08:03:23.000Z","updated":"2021-10-23T14:22:52.650Z","comments":true,"path":"2017/11/28/围墙外的世界-网站推荐/","link":"","permalink":"http://www.frankfeekr.cn/2017/11/28/围墙外的世界-网站推荐/","excerpt":"","text":"之前偶尔为了查阅文献也会翻墙,最近准备正式开始接触墙外的世界,我想作为一名计算机技术人员翻墙是第一步,顺便这篇博文也成为了我在CSDN上的博客启程。 如果你也想看看墙外的世界,这篇文章相信会对你有帮助: 好啦上面的都是铺垫,下面才是正题:手把手教你,搭建搬瓦工 SS 代理 | Frank’s 技术世界 Google搜索 程序猿、攻城狮必备的搜索引擎 https://www.google.com Google学术搜索 学术论文搜索必备,很多时候在百度学术上找不到想要的信息时,谷歌学术我想会给你带来惊喜 https://scholar.google.com.hk/schhp?hl=zh-CN&as_sdt=0,5 Facebook 全球最大的社交网站,嘿嘿,朋友圈都不在上面,可能微博玩的会更多一些 https://www.facebook.com/ Instagram Instagram(照片墙)是一款运行在移动端上的社交应用,以一种快速、美妙和有趣的方式将你随时抓拍下的图片彼此分享。爱分享生活和照片的同学可以在上面分享你的生活瞬间。 https://www.instagram.com/ Twitter —— 聚焦当下 是一个社交网络及微博客服务。用户可以经由SMS、实时通信、电邮、Twitter网站或Twitter第三方应用发布更新(称为tweets),输入最多140字的更新,允许用户将自己的最新动态和想法以短信息的形式发送给手机和个性化网站群,而不仅仅是发送给个人,你可以在别人跟帖,别人也可以跟你的贴,对所有人都是开放的。 https://twitter.com/ YouTube 视频分享网站,很多国内禁的视频都可以在上面看到 https://www.youtube.com/ Quora 国外版知乎,问答类型的社交平台,个人觉得对于学习英文还是蛮有帮助的 https://www.quora.com/ 维基百科,自由的百科全书 百科词库,国内类似的就是百度百科了,不敢说维基要比百度准确多少,都可以借鉴,要做学术理论以书本和文献期刊为主 https://zh.wikipedia.org/wiki/Wikipedia:%E9%A6%96%E9%A1%B5 Yahoo 美国著名的互联网门户网站,也是20世纪末互联网奇迹的创造者之一。 https://www.yahoo.com/ 本文转发自我的CSDN博客:Frank的技术世界 - CSDN博客","categories":[],"tags":[]}]}