30岁是一道坎。
从年龄上来讲,又是一个十年。人生就这样过完了三分之一,不由得心生感慨!感慨时光如梭,感慨事业无成,感概没有了上一个十年时的锐气,就连眼神也没有了那时的干净清澈。从十年前的白面书生变成了中年油腻男,时间真的是一把杀猪刀!
从工作与生活上来讲,三十而立。但很多人在这个时候面对着上有老下有有小往往却力不从心。需要咬紧牙关坚持下去,翻过这道坎。
就在前几天,我也进入了而立之年。生日当天,那一整天仿佛都充满了仪式感,仿佛过完了那一天就进入到了一个新的人生阶段。当时灵感爆棚(也可能是一时冲动,人很多时候会把冲动当作灵感),希望能记下点什么,但什么都没记下……
今年的315入职新公司,在三十岁之前从传统行业转到互联网行业。之所以转,是因为觉得在传统行业中已经到了天花板,进入了舒适区,不想温水煮青蛙,想换一种环境看看。从目前的情况来看,似乎还不错,在新的环境里有很多事情值得做,值得拼一下。似乎又回到了刚入职场的状态,这是一个之前一直期望的状态,很满意。
30岁不仅仅是一道坎,跨过这道坎实际上是一趟新的征程。希望在这趟新的旅程中能一直坚挺下去!
工作流产品模块化设计构想
之前做过几年工作流产品的产品研发,最近正在的做的知识管理产品,需要新增文章审批功能,实际上就是一个简单的工作流。做完这个事情后,发现市面上单纯的开源工作流引擎居多,还没有一款全功能的开源工作流产品。顿时有了自己抽空余时间折腾一个的想法,权当把之前的工作经验与想法归纳总结一下。
在我的构思中,为了最大限度的复用,这款工作流产品应当是模块化可拆分的。你差一个流程引擎那么你用引擎部分即可,差权限控制那么你把权限控制模块拿走即可,就是说能做到各个部分松耦合,可灵活替换。
目前,这款工作流产品会包含如下几个部分:
- 流程核心引擎
- 流程可视化模块
- 组织与人员模块
- 权限模块
- 消息模块
- 菜单模块
- 基础的表单开发组件
- 流程流转统计分析模块
默认会把这些模块组装成一个能够RUN起来的系统,用户喜欢那一部分可以单出抽出来用哪一部分,同样的,不喜欢哪一部分可以去掉自己开发。
2018年就找这么一件事来丰富业余生活了。
读《恶意》有感
实际上读完这本书已经有一段时间了,有几点感想需要尽快的记录下来,因为深知越往后拖就越没有记录下来的动力。
关于写作手法
文章的写作手法是第一个让我惊奇的地方。全书并不是常见的从头到尾讲故事的叙述文,而是由犯罪嫌疑人和警察的笔记,以及心理活动穿插构成。笔记与心理活动相穿插,让故事与案件逻辑层层递进,不断的推进故事的发展。使得读者更容易的进入到故事当中,把笔记当作案件的记录与线索,把心理活动当作对案件的分析与不断的剥离出事实真相。这是我第一次看到这种写作手法,不由得赞叹:小说居然还能这么写!~
关于先入为主
在故事的一开始,野野口修透露日高邦彦杀猫的情节给人一股深深的寒意,让读者一下子就觉得日高邦彦不会是什么好人。但是,事实却恰恰相反,这是野野口修故意设下的圈套。当得知最终的真相后,由于先入为主的观念,一个微不足道的小情节居然能带来如此巨大的影响,值得深思。
关于”恶意”
“也没有什么特别的原因,就是看他不爽!”这就是野野口修杀害日高邦彦的犯罪动机。这么一句毫无逻辑,毫无道理的话,让人毛骨悚然,不寒而栗。
回归到现实世界,在日常的生活与工作中,这样毫无缘由的恶意有时候也会毫无缘由的出现在你面前,当你感受到别人对你的这股恶意时,你会觉得很无力也很可怕。
希望自己永远不要成为这样的人。
专研精神是技术人员成长的源动力
前几天,项目中的开发人员向研发组提交了这样一个问题,发现在PC中使用搜狗输入法时,输入完成敲击回车按钮无法触发输入框的查询事件。同样,另外一个组件在IOS系统中,使用IOS原生的输入法也存在该问题。
针对该问题我跟踪了一下,发现当使用搜狗输入法时,光标虽然在input框里,但是输入的内容却在输入法提供的浮动窗口中,输入结束,敲击空格或回车,此时才把输入的内容放入input框中。
查看了该组件的实现代码,发现该组件是监听的keyup事件,以及监听了input的回车事件来实现输入完成后的自动查询。经过分析,使用搜狗输入法输入时,由于输入法有自己的弹出框,所有事件均在这个弹出框上,输入完成敲击回车或空格时同样也是,所以无法触发组件的input框事件。对于此种情况,考虑给input框绑定onchange事件来解决该问题,但是onchange事件必须要input框失去焦点才会触发。由此看起来,似乎已经没有什么好的解决办法了,于是乎就考虑到能否在input的输入框里加一个小的查询按钮来解决该问题。
我把该分析过程以及产品组这边暂定的解决方案告诉了相关技术人员,让他们等候产品组这边的组件更新。本来想着这件事就告一段落,他们只需要等着产品组这边的更新即可了。但是,到了第二天下午,我收到一个amazing的消息,发现该问题的开发人员告诉我说已经自行解决了该问题,并且在产品组提供的组件上修改完成并做了有效性验证!让我这边再去看一下那样修改是否合适!
最终是解决方案是使用HTML5的oninput
与IE专属的onpropertychange
来解决该问题。这篇帖子已经阐述得很详细了,这里就不在详细描述了。在这里,截图如下:
对于这件事有如下几点感想:
- 技术人员的技术成长,不仅取决与多看书多看资料与文档,还取决于碰到问题不放弃的专研精神。同时,这股力量还会鞭策你多去了解你不懂的东西,弄懂之后发现更多的不懂,形成良好的正向循环。所以,毫不夸张的说专研精神是技术人员成长的源动力。
- 懈怠、满于现有解决方案而不更深入的去探求更完美的方案,是我这边在这个问题上没有走得更远的根本原因。
- 团队中需要更多这样的技术人员,思想的碰撞才能产生更多的火花。同时技术人员之间需要更多的技术分享与沟通,独立的思想注定会是孤独的。
作者公众号:
人生中唯一的2017
今天是2017的最后一天,转眼就将来到2018,很快就会迎来自己的30岁,三十而立。
重要事件
今年是十分特别的一年,在今年升级做了父亲。在那一刻,除了激动还有感激,感激老婆怀胎十月的辛苦,生小孩儿的不容易。当升级做父亲的那一刻,突然间从心理上就感觉不一样了,这种不一样在于很多复杂的因素,能想到的词有:责任、家、顶梁柱、成熟等等。
收获的
- 读完了《禅与摩托车维修指南》《暗时间》《人类简史》《遥远的救世主》《恶意》,技术方面《算法》第四版、《Think in Java》。茅盾文学奖获奖作品《无字》看到第四章,文字絮絮叨叨的实在是没有动力看下去了。
- 单词由百词斩换到了Anki,麦克米伦7000还剩下80%,效果很不错,润物细无声。
- 学会了如何带娃……
留下的
- 完成用户中心产品的开发。
- 今年基础开发框架产品差不多又升级了10来个版本,更加的稳定,同时也更加的陈旧。
- 年中的时候,充分利用空挡,实现了知识管理产品的从0到1以及更多。年底公司内部正式上线,算是开始产生价值了。
- 今年总共分享了37篇博客。博客的浏览量新增5W+。
2018
- 好好带娃
- 见缝插针多读书
- 技术这条路坚持走下去
- 满30之前搞定房子的装修
Tomcat启用HTTPS配置
获取证书
可使用jdk自带的keytool开生成证书,此种方式与向第三方权威机构购买的证书的区别为,第一次请求时需要选择信任站点并继续访问,在浏览器的地址框里会显示不安全的红色提醒。
使用keytool -genkey -alias tomcat -validity 3650 -keyalg RSA -keystore D://.keystore
即可在D盘生成.keystore
文件,如下所示
1 | PS C:\Users\YiYing\Desktop> keytool -genkey -alias tomcat -keyalg RSA -keystore D://.keystore |
说明:
- -alias为别名
- -validity 3650有效期为10年
- -keystore为生成的文件路径
1 | Option Defaults |
文档地址:
https://docs.oracle.com/javase/6/docs/technotes/tools/solaris/keytool.html
Tomcat配置
需要修改tomcat的server.xml文件,如下所示:
1 | <!-- Define a SSL HTTP/1.1 Connector on port 8443 |
本来这一段是注释掉的,去掉注释并增加keystoreFile
和keystorePass
参数即可。
最后,启动服务器,使用https://localhost:8443/
即可访问服务器。
文档地址:
字符解码时加号解码为空格问题探究
问题
最近发现一个问题,后台Java代码使用new URL(url)
方式发起一个POST请求,模拟表单提交操作,从后台提交数据到服务器端。当客户端的数据中带加号时,服务器端的Servlet使用request.getParameter('param')
得到的数据加号被变为了空格。比如:客户端发送的数据为aa+aa
,服务器接收到的数据变为了aa aa
。
简单写了几句代码验证一下:
1 | String str = "aaa+aaa aaa%2B"; |
运行结果如下:
1 | ENCODE=aaa%2Baaa+aaa%252B |
原因
通过上面的代码可以看到,编码时,空格被编码成了+号,+号变成了%2b
。当未经过编码的带+号的字符串解码时,+号直接转变为了空格。
翻了一下文档是这么说的(地址):
接着到application/x-www-form-urlencoded MIME format的说明处(地址):
再翻了一下servlet中request.getParameter('param')
方法说明,如下:
接着看getParameter到底如何取数据的:
从上图可以知道,当在servlet中使用getParameter
方法来接收POST数据时,默认是会根据请求头的类型来解码的。这就证实了如果通过后端new URL(url)
的方式模拟表单的POST,如果在POST前不对数据进行编码且数据中带有+号,那么服务器端收到的数据必然会是错误的(+号会被自动解码为空格)。
验证
常规的表单提交content-type有两种:application/x-www-form-urlencoded
和multipart/form-data
,如果表单提交时不设置任何类型,默认以第一种方式提交数据;第二种属于带附件的表单提交,当表单中有附件时,必须设置表单的enctype
为multipart/form-data
.
很多时候,我们用 Ajax 提交数据时,也是使用这种方式。例如 JQuery的 Ajax,Content-Type 默认值为application/x-www-form-urlencoded;charset=utf-8
。
前端代码
1 | $.ajax({ |
HTTP请求情况
由于在chrome的控制台中所显示的数据被优化显示了,所以在Form Date
部分,需要点击后面的view source
才能看到浏览器发送数据的真实格式是怎么样的。默认优化后的显示应当为yiying:aaa+aa a
,
从上图中可以看到,浏览器实际上的使用Content-Type:application/x-www-form-urlencoded; charset=UTF-8
的编码规则,实际发送的数据为yiying=aaa%2Baa+a
,其中+号变为了%2b,空格被编码为+号。
后端情况
1 |
|
从后端的输出值可以看到,后端的servlet自动解码为原始数据。
关于后端中文乱码问题的特别说明:
后端服务器(tomcat 的server.xml)如果没有设置编码规则(URIEncoding=”utf-8”),Servlet默认会以
ISO-8859-1
来解码中文字符(Servlet规范定义),而前端标识为utg-8,此种情况就会产生乱码,可以用如下3种办法来解决:
- 在server.xml设置编码规则为utf-8
- 在调用getParameter之前使用代码设置编码规则,
request.setCharacterEncoding("utf-8");
- 定点处理,
String name = new String(request.getParameter("name").getBytes("iso-8859-1"),"utf-8");
结论
显而易见,如果需要在后端使用new java.net.URL(url)
的方式POST提交数据,需要像浏览器那样先给数据编码处理,再发送数据。
作者公众号:
JSONP原理探究
介绍
JSONP是一直种解决跨域问题的方案,实现的原理来自于页面中的<script>
标签能够跨域请求资源。要通过JSONP实现跨域,需要服务器端做额外支持。
前端代码
前端部分核心在于通过script标签的src告诉服务器端约定好的回调方法名
。代码如下
1 | var callbackName = 'callbackFunc'; |
后端代码
服务端部分主要为接收前端发送过来的请求参数,核心在于约定好的方法名
。代码如下
1 | package com.demo; |
结果
下图为验证结果:
其它
1.前台部分jquery对jsonp的支持,只需设置dataType:"jsonp"
,样例如下
1 | $.ajax({ |
2.后台Java部分可使用fastjson来实现返回数据
1 | import com.alibaba.fastjson.JSONPObject; |
3.跨域情况下一般都牵涉到权限问题,可通过SSO或者服务器端开放匿名访问权限解决,或者token也行
4.使用Nginx的代理功能来解决跨域问题也是很好的一种办法
MySql启动报 write error: No space left on device问题解决
启动MySQL数据库时报如下错误:
1 | [root@localhost redmine-3.1.1-1]# ./ctlscript.sh start |
其中,引人注目的就是No space left on device
。
使用df -h
命令查看磁盘使用情况如下:
1 | [root@localhost ~]# df -h |
可以看到磁盘空间已经被完全占满。
接着,使用du -sh *
命令查看/dev/sda2
中的磁盘占用具体情况,结果如下:
1 | [root@localhost /]# du -sh * |
逐级往下找,发现是nexus的日志文件已经有30多G了,删掉,再次启动mysql问题得到解决。
HTTP协商缓存实践
从浏览器的缓存类型上来讲分为强缓存和协商缓存,之前写过一篇文章《HTTP缓存详解》做了详细的整理。
目前做的知识管理产品,为了首页能够更快的加载,需要对首页请求的资源做缓存整理。通过整理分析,决定对css、js做强缓存处理;对个人头像的请求做协商缓存处理;
为什么要头像不做强缓存处理呢?原因为目前的头像都是通过人员的ID来获取,而不是通过头像ID来获取。这样做的好处为在后台获取相关数据时,比如排行榜以人员ID作为主键,如果要带出该人员的头像信息,还得去头像表里面联查一下来获取头像ID。因此,由于后端部分的设计造成头像做协商缓存才是最优解。
目前的逻辑为,当用户第一次进入知识管理系统,请求个人头像的URL会返回200并从服务器下载个人头像,此时服务器会在response里面设置ETAG值为头像的最后修改日期;当刷新页面时,服务器会判断请求头中的If-None-Match
是否与当前的最后更新日期匹配,如果匹配则直接返回304(不返回数据),如果不匹配则从磁盘中取出头像。
相关代码如下:
1 | /** |