作者 |?李辉责编 | 郭芮
大蒜,牛油果,西葫芦,包菜,鸡蛋,西红柿,鸡腿,三文鱼,土豆,洋葱,黄瓜,杏仁干……这是我买菜前在手机里列出的购物清单。我觉得我已经比很多相信自己脑子记得住、却常常丢三落四买了酱油忘了醋的理工男靠谱了。毕竟我买东西都是一样不少买回去的,但是每次回家还总是被老婆数落,这个不对那个不满意的。
有一天我终于忍不住爆发了,说那你给我个购物清单吧,到底要买什么?!然后她的清单发来了:
大蒜一挂,要 bio(有机)的;
西葫芦一根,注意不要破损的,指甲印太多的;
鸡蛋一盒6个装,黄壳,不要白壳;
西红柿bio 650g 8个装那种,买前捏一下,软的不能要;
两个鸡腿,500g左右盒装,盒子上要标“非转基因”;
三文鱼200g,不要冷冻,要冰鲜的,去鲜鱼柜台买;
洋葱一包,白皮圆形大个的,不要红皮;
小脆皮黄瓜,9cm左右的买6根;
杏仁干,250g一袋,碳水含量低于4%;
……
ps: 所有上述食品都要看保质期。
你可能要问:土豆不就一种吗,不就是马铃薯,洋芋?那你太天真了,德国是个有200多种土豆、能把土豆吃出花来的国度。
我在超市里盯着这个采购清单,突然无比后悔刚才的冲动。本来作为直男,采购的速度是以光速计算的,只要找到货架位置,刷刷刷把东西扔进购物车就行。现在我必须一个个按规定检查,在一排排同类但不同生产商的货中挑出符合规定的东西,还要看保质期。采购速度直接翻了五倍,本来一刻钟左右我就应该出现在收银台的,现在快一小时了我还在干果区找碳水含量低于4%的杏仁干,最后抓了一个路过的店员一起找了半天才在零食区发现。
好不容易采购完毕回家后,心想这次总该不会不满意了吧。结果棋差一着,还是被抱怨了:那盒鸡蛋有一个蛋破了。我按习惯是打开盒子,扫视一下外壳的,但我万万没想到,那个蛋底部破了。所以今后清单上又多了个标注:鸡蛋检查盒子底部,看有没有蛋液渗出。
回归正题
男人的思维方式,大多数情况下是直线式、收敛式、简单式的,他们天生排斥“不必要”的修饰和麻烦。我敢打赌,99.999%程序猿的购物单和我一样。那些各种各样的“细节”和“形式”,在女人看来无比重要的细枝末节,在多数男人看来,只是浪费时间和吹毛求疵的表现。
这和软件开发有什么关系?如果把这份购物清单看做客户发给你的产品需求,或者一份软件设计文档呢?
产品需求分析
因为工作领域关系,经常有朋友找我咨询一些软件项目的问题,比如:
开发一个教育类app多少钱?做一个网店多少钱?我想做一套在地图上实时显示数据并且分析的可视化平台,开发周期一般多久?我需要一套采购物流系统,开发周期一般多久?
我的回答一般都是以这句话反问:
我问一下,你所在城市房子多少钱一套呀?
别人一般回答:
这要看房子大小,朝向,哪个区买的,差别很大啊。
我会接着说:
做软件和买房子一样的,按软件实际的需求和开发构架的差别,开发周期和费用也是有很大的变化区间的。
然后再一步步细问对方的实际需求,最终可以得出一个大概的项目开发预算和时间预估。
需求设计文档
我平时习惯在谈项目初期,先做业务和构架需求分析,但还是时不时一个方面没考虑到,就入了坑。曾经有一个做产品经理的朋友找我合作一个项目:客户要做一个电商系统,项目第一版的要求很简单,只要能运行正常购物流程即可:
电脑和手机可以访问;用户可以邮件注册,也能微信注册;有产品列表展示,购物车,订单,用户扫二维码支付即可;用户界面自己决定,以后可以换模板;管理员后台能管理产品,批量上传订单,上传上传图片,查看客户订单。
我自己手头有一套完整的基于spring的cms开发框架,包括前端api模块、后端管理模块、用户权限模块、数据持久化模块等等,所以上面这些按估算很容易搞定。第3点里的产品,购物车、订单分成三个小模块,做个二维码支付也不是大问题。而且第一版只要基本功能,不用考虑任何的分布式,大流量,并发抢购等今后做大了要考虑的问题,无需过度设计。
我这个朋友也是个资深程序猿出身,所以我也很放心他作为客户传话筒,所有需求遵循kiss原则,但保留基本的模块扩展性。讨论过后,这第一版的构架图简单如下图所示,我也写了个相应的需求列表。
我们这边技术层面一切ok,客户那边看起来也很佛系,对于我们写的第一版的基本需求列表没有异议,事情似乎向着阳光的方向发展。我想到了所有技术和构架上能想到的点,却单单漏了一点:
朋友是个男性产品经理。
所有该谈的事情谈定了后,最终朋友从客户那里得到了一个产品需求细节文档:
用户分级别,享受不同折扣率;
产品含品牌属性;
产品归于相应的一级和二级目录;
产品由不同供货商提供;
产品含颜色和大小等属性;
产品分官方价格和折扣价格,订单扣费按当天折扣;
支付和交易需要后台管理;
产品按重量和大小分不同的物流渠道以及关税申报登记;
订单最终价格是折扣后价格,加上动态计算的物流价格;
订单表含发票和发货模块,同时扩展物流模块;
发票模块需要调用一个第三方发票在线生成系统;
支付模块并不和支付宝直接对接,而是和一个境外第三方支付机构;
特价,打折模块;
官方blog模块;
若干其它模块......
如果真正做过电商系统的朋友,看到第5点可能会心里一颤,这个就是要做一个完整的sku系统啊!sku是大型电商系统的基础,复杂度不言而喻。按之前的开发预算和时间预估,这么多关联模块根本不可能完成。
把朋友电话里暴揍一顿后,按客户的产品需求,产品构架思维导图变成了这样。
我得到的教训就是:
在没有客户的详细需求设计文档之前,绝对不要做任何项目估算,更别提动手开发!
看到这,各位是不是很容易联想到你们的男性产品经理或是项目经理,和客户开个会后,就急冲冲地列出一份新的feature所需大致功能,然后马上发邮件或者口头传述给开发团队,程序员直接就开始开发。
这里可没有性别歧视,我工作中遇到的男性产品经理或程序猿在做功能设计文档时,常常没有女性产品经理那么细心和耐心。这种文档不明确就开发的做法,看似敏捷快速迭代,但做出来的功能往往和客户所需相差甚远,最后返工付出的代价难以计算。
分析细节标准
我们公司有个客服系统,客户在使用产品遇到问题时,会邮件或者电话给客服,客服录入系统后会分配给相应研发团队。这是一个常见的一问一答流程,我们的反馈会通过客服发给客户。
客户:我app突然登陆不了。
研发:请问您用的系统版本是什么?app的版本是什么?登陆时发生什么错误?
客户:我的系统版本是xx,app版本是xxx。输入用户民和密码后,登陆界面跳转到浏览器,然后就没反应了。
研发:请问浏览器里显示什么,有没有错误代码,是什么时候登陆的?
客户:浏览器里显示“request error”,时间是xxxx。
研发:请尝试清空app的缓存,以及浏览器的缓存。步骤如下……….
…….
有没有感觉这样的沟通成本其实很高,客户提供的信息模糊,研发问得也不尽清晰。这里和产品需求文档类似,到底细节的标准在哪?
我建议我们程序猿在做需求分析时,先向你的女朋友或妻子学习如何分析细节,比如分辨下面这些:
呃,好吧,当我没说……
我的建议是需求也业务拆分尽可能详细,考虑周全,最好能覆盖今后绝大部分的测试用例。
比如需要开发一个用户注册登录的功能,从某个男性产品经理嘴里说出来可能就一句话:给我开发一个用户系统,能注册,能登录,三天够不够?
从女性角度,她们习惯把一件简单的事情变的复杂化——如果这里的简单并没有你想象的那么简单呢?
如果你足够细心,就需要和客户做仔细沟通后达成共识,最终交付给研发团队的应该是如下一份需求kickoff列表(只是举例,考虑并非100%全面):
用email注册还是用户名注册?
如果是用户名,最短几位,最长几位,允许特殊字符吗,密码最短几位,最长几位?
如果email注册,是密码一起填写注册,还是有验证步骤?
点击注册前,是否展示用户协议。不勾选显示什么错误?
如果有验证步骤,需要手机验证吗?如果要,要对接哪个api,验证码长短,过期时间?
手机验证码发送时间间隔,以及发送频率限制;
如果只需邮箱验证,发送给用户的激活链接有效期多久,过期了怎么办?
验证这个业务要不要持久化?
发送激活邮件的模板,html还是txt,谁来设计?
激活链接过期后,显示什么错误,然后跳转到验证邮件重发界面?
激活链接激活后,显示什么信息,自动跳转到哪个界面,比如设置密码界面?
密码设置允许特殊字符吗,最短几位,最长几位?
密码入库前采用何种加密算法,以哪个属性作为salt?
用户密码设置成功后,跳转哪个界面,显示什么消息?
用户后期允不允许改用户名或注册邮箱吗?
用户后期修改密码是否需要邮件或手机验证?
允不允许单用户多地点登录,如果不允许,显示什么错误自动登出?
支不支持单点登录sso?
要不要开发移动端app,需不需要提供注册和登录api?
api需不需要支持oauth2,或是token?
登录构架是单体还是需要考虑分布式session?
用户权限系统设计,多role或是单role设计?
鉴权系统设计是无状态还是有状态?
用户自己或者管理员关于权限系统的操作流程是什么?
有没有第三方sns接口注册接入,如果接入,需不需要同步头像,需不需要同时必须新建本地账号。
第三方sns接入的流程,回调接口,以及界面设计;
相关前端和app界面设计图,错误对话框ui;
......
需求文档越详细,后期开发与测试遇到的误解就越少,试错成本也越低。
该文档必须保持在线实时更新,有任何变更,研发团队必须第一时间得知。不要通过短信或者email更改需求,这会使团队和产品经理陷入无尽的扯皮炼狱之中。
细节的力量
如果说女性太注重于细节,是期待对方给足自己安全感。那么我们在做项目时注重细节,其实也是给自己一种安全感:减少返工。一个口碑好的软件,肯定是从用户需求分析开始就研究细节,设计时重视细节。
大部分程序猿都是非常看重技术,但从男性视角介入软件产品设计时,很容易陷入工程师思维:先考虑怎么做,再考虑做什么,最后考虑为什么做?一看到新项目,脑海里浮现的立刻是各类框架、各类中间件、高并发,恨不得立刻开机撸码。
而从女性角度介入软件产品设计时,她们往往会带有与生俱来的消费者直觉,会从第三方角度看事情。
我这里不是简单地贴职场标签,而是提醒不管是程序猿还是程序媛们,思考一下自己在设计产品时思维的盲区。
最后,女人天生都是产品经理,只不过:
作者简介:李辉,德国硕士毕业后,在软件咨询业工作多年,涉猎前后端及移动开发构架。现在德国博世智能家居部门任高级软件工程师。
声明:本文为作者独家投稿,作者独立观点,不代表 csdn 立场。
?热 文?推 荐?
??windows 7 时代即将终结!
??王欣“马桶 mt”改名“mt”;任正非点赞苹果;酷派 27 岁总裁上任 | 极客头条
??java 这一年都经历了什么?
?“对不起,你的离职是我的错!”
??gartner的预言:通向混合it之旅
??阿里“菜鸟”ai?
??刚刚!华为又被美国盯上了!
??心疼!能为程序员男友做些什么吗?
print_r('点个好看吧!');var_dump('点个好看吧!');nslog(@"点个好看吧!");system.out.println("点个好看吧!");console.log("点个好看吧!");print("点个好看吧!");printf("点个好看吧!");cout?<
点击“阅读原文”,打开 csdn app 阅读更贴心!
喜欢就点击“好看”吧
模糊查询有以下三种方法(convert转成string)(between and?)(datediff()函数)
字段 ?createtime ?
查询??2018-12-24
1.convert转成string,在用like查询。第一种方法应该适用与任何数据类型 ; ?
select top 10 * from table where convert(varchar,createtime,120) like '2018-12-24'
2.between ??第二种方法适用string外的类型 ?
select * from table1 where yourtime between '2017-06-30 0:00:00' and '2018-12-24 24:59:59'";
3 datediff()函数 ? ?第三种方法则是为date类型定制的比较实用快捷的方法。
select * from table1???where datediff(day,yourtime,'2018-12-24')=0?
sql语句格式化日期 去掉毫秒
--从第一位开始取到第十六位结束
select substring( convert(varchar,getdate(),120),1,16) from a
?
问题
我的简书爱游戏官网登录入口主页
gc超阈值
在之前的5分钟内,垃圾收集花费的平均时间为每分钟36.4秒。临界阈值60% node_manager_gc_duration has become bad: avgrage time spent in garbage collection was 36.4 seconds(s) per minute over the prevoies 5 minute(s). critical threshold: 60% the healthy test result for yarn_resourcemanagers_health has become concerning
排查
查看nodemanage日志
日志中每隔几秒就会发生gc且老年代回收( cms )的时间比较久 爱游戏平台官网问题查询 关键字: node_manager_gc_duration 从社区的答复来看, 大多数的建议是调整nodemanager的堆大小和监控值, 但是并没说到问题的原因, 接着继续排查吧
点击图标库, 查看进程使用情况
发现最近时间段堆内存一直接近最大值, 且每次回收掉的内存非常少; 查看7天数据gc和堆内存的占用处于持续上升的状态, 初步定位是内存泄漏 查看30天发现29号的时候内存突降, 之后又开始持续上升至峰值徘徊, 其实29号的前几天也是不正常的
查看日志和进程信息, 29号的9点确有异常, 触发了yarn节点重启机制 , 发生内存溢出生成了dump文件
内存泄漏
yarn默认没有开启gc的打印, 需要自己在cdh进行配置, 搜索java_gc_args,在对应位置填上-xloggc:gc.log -xx: printgctimestamps -xx: printgcdetails, 配置生效需要重启该节点( 可选项 )
1. 查看gc详情
jstat -gcutil 25678 3000 100 从图中来看明显是有问题的;ygc和fgc代表新生代和老年代gc的次数, 每隔几秒次数就会递增, 且老年代的空间一直没有回收掉
2. 分析在堆中对象的数量和占用字节
执行su -s /bin/bash -c ''/usr/java/jdkl.8.0_202-amd64/bin/jmap -histo:live 20886" yarn | more
如果提示jdk版本的报错, jmap前加上运行参数的全路径;如果报unable to open socket file: target process not responding or hotsport vm not loaded, 则切换到进程的用户;如果切换时报this account is currently not available, 则可用root用户su -s指定用户去执行命令
从命令结果来看, 占用最多的是字符串和byte数组, 但是不知道里面的内容是什么, 所以需要mat去分析堆中对象的详情
3. 将dump文件导入到mat中
su -s /bin/bash -c '/usr/java/jdkl.8.0_202-amd64/bin/jmap -dump:format=b,file=/tmp/nnm.dump 20886' yarn, 如果已经发生了内存溢出生成了dump文件, 则不需要执行这一步
点击histogram->右键对象list objects->with outgoing references
发现缓存大量spark job的shuffle索引文件路径, 但实际上appcache下并没有文件, 所以推断这个是shuffle的临时文件, 查阅源码证明推断正确, externalshuffleservice的executorremoved()和applicationremoved()方法; 为什么这类文件会这么多, 在哪个地方引用的呢?
解决思路
去spark的web界面去查看该job执行时,是否发生大量的shuffle操作, 如果是可尝试去调整代码上或job运行参数, 以来减少数据的混洗
通过分析, 该job( application_1588204334324_0246 )其实早已经执行完了, 却还是一直触发gc的告警, hdfs上本地的shuffle文件也不存在了, 进一步推断是yarn缓存管理的问题
寻找spark yarn中控制shuffle缓存的配置并修改 ###排查spark源码 根据调用堆栈搜索shuffleindexcache,shuffleindexinformation,cachebuilder, 在源码中找到关键类externalshuffleblockresolver
string indexcachesize = conf.get("spark.shuffle.service.index.cache.size", "100m");
cacheloader
new cacheloader
public shuffleindexinformation load(file file) throws ioexception {
return new shuffleindexinformation(file);
}
};
shuffleindexcache = cachebuilder.newbuilder()
.maximumweight(javautils.bytestringasbytes(indexcachesize))
.weigher(new weigher
public int weigh(file file, shuffleindexinformation indexinfo) {
return indexinfo.getsize();
}
}).build(indexcacheloader);
查看源码得知缓存的统计是根据索引文件大小叠加的, 即shuffleindexinformation的size相加; 根据上面的缓存键值分布图做空间推测
shuffleindexinformation的size平均为152字节, 则100mb可存放的对象数为 689852 个 = 100(mb) * 1024 * 1024 / 152key是file类型的, 每个对象占用920字节, 则缓存中key占用的空间为 605 mb = 689852 * 920 / 1024 / 1024
结合mat的总览图, 缓存对象占用817mb, 那么和推断的值705mb = 605 100基本是吻合的; spark管理这一块的缓存只累加了value的文件容量, 而忽略了key占用的内存, 个人感觉这一块会有隐患, 如果shuffleindexinformation的size值很小, 那么key的数量会非常的多, 如果内存配置不够大那么发生内存溢出的概率比较大
更改源码
在github上查看这个类的提交记录没有找到相关的修复, 提下issue或pr看官方有什么答复; 有个要注意的小点, 源码中缓存使用的是localcache.localloadingcache, 调用get方法的时候会同时load进缓存中, 且externalshuffleblockresolver中有提供清理过期文件的操作
方案1: cacheloader构造时重写weigher()方法中加入key的容量统计方案2: 执行清理操作时同时清理缓存 30672 pr
查看hadoop的源码
堆中存在着大量路径信息, 所以可以尝试去搜索关键字如 appcache, 结合调用堆栈可找到相关类resourcelocalizationservice,yarnconfiguration,localcachecleaner,查看源码得知yarn提供资源缓存的清理, 默认值10分钟执行一次,清理的阈值为10g
解决
调整堆内存, 调整shuffle index大小或更改源码( 不推荐 )
参考
apche贡献指南 hadoop源码 spark源码 yarn resourcemanager内存泄漏 大数据运维日常-yarn 运维、巡检、监控、调优、排障 cdh文档
还没有评论,来说两句吧...