Repository navigation
Expand file tree
/
Copy pathatom.xml
More file actions
1328 lines (965 loc) · 76.4 KB
/
Copy pathatom.xml
File metadata and controls
1328 lines (965 loc) · 76.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[jenwang的随想]]></title>
<link href="http://jenwang.me/atom.xml" rel="self"/>
<link href="http://jenwang.me/"/>
<updated>2020-08-07T15:09:40+08:00</updated>
<id>http://jenwang.me/</id>
<author>
<name><![CDATA[]]></name>
</author>
<generator uri="http://jenwang.me/">jenwang</generator>
<entry>
<title type="html"><![CDATA[Flowable怎么通过委托表达式获取到Micronaut的bean]]></title>
<link href="http://jenwang.me/15967788014795.html"/>
<updated>2020-08-07T13:40:01+08:00</updated>
<id>http://jenwang.me/15967788014795.html</id>
<content type="html"><![CDATA[
<h2 id="toc_0">flowable Unknown property used in expression: ${testListener}</h2>
<p>众所周知在spring中工作流引擎activiti/flowable是可以通过委托表达式获取到spring bean的。但在micronaut中会报这个异常,因为activiti/flowable官方已经集成好了spring,而micronaut并没有人帮你集成过,获取不到容器里的bean是理所当然的。</p>
<p>由于micronaut是个新框架,网上翻了一圈都没找到对路的文章,以及在micronaut中怎么解决。</p>
<p>无奈之下自能自己出手了,翻了翻flowable的源码,找到跟spring集成部分的代码,理解了一下,模仿着写了一个针对micronaut的简单集成,打通flowable和micronaut容器,经过一番调试之后问题解决。</p>
<hr/>
<p>在给任务节点设置监听器,运行到这个节点时会出现这个异常<code>flowable Unknown property used in expression: ${testListener}</code>,监听器的设置如下图所示,设置了委托表达式delegateExpression,本意是想获取一个bean作为监听器。<br/>
<img src="media/15967788014795/15967792583768.jpg" alt=""/></p>
<p>对应的xml为:</p>
<pre><code><userTask id="task1" name="task1" flowable:formFieldValidation="true">
<extensionElements>
<flowable:taskListener event="complete" delegateExpression="${billGenerateListener}">
</flowable:taskListener>
</extensionElements>
</userTask>
</code></pre>
<p>监听器代码如下:</p>
<pre><code>@Context
@Named("billGenerateListener")//监听器名称,跟委托表达式里的设置对应
public class BillGenerateTaskListener implements TaskListener {
@Override
public void notify(final DelegateTask delegateTask) {
delegateTask.getTaskDefinitionKey();
final Map<String, Object> variables = delegateTask.getVariables();
final Object costPrice = variables.get("costPrice");
log.warn("Generate Bill...");
log.warn("costPrice:{}",costPrice);
}
}
</code></pre>
<p>下面直接看怎么解决的吧,不扯什么工作原理、底层机制,我们只是为了解决问题,直接抄了能用最好!</p>
<p>我们知道flowable引擎对象<code>ProcessEngine</code>一般是通过<code>ProcessEngineConfiguration#buildProcessEngine()</code>创建的,这是使用引擎的入口。<br/><br/>
那么需要在<code>ProcessEngineConfiguration</code>中设置一个自己扩展的<code>ProcessExpressionManager</code></p>
<pre><code>cfg.setExpressionManager(
new MicronautExpressionManager(applicationContext, cfg.getBeans()));
</code></pre>
<p>MicronautExpressionManager:</p>
<pre><code>public class MicronautExpressionManager extends ProcessExpressionManager {
protected ApplicationContext applicationContext;//micronaut的context
public MicronautExpressionManager(ApplicationContext applicationContext, Map<Object, Object> beans) {
super(beans);
this.applicationContext = applicationContext;
}
@Override
protected ELResolver createElResolver(VariableContainer variableContainer) {
CompositeELResolver compositeElResolver = new CompositeELResolver();
compositeElResolver.add(createVariableElResolver(variableContainer));
compositeElResolver.add(createMicronautElResolver());//主要是这里
compositeElResolver.add(new ArrayELResolver());
compositeElResolver.add(new ListELResolver());
compositeElResolver.add(new MapELResolver());
compositeElResolver.add(new JsonNodeELResolver());
compositeElResolver.add(new BeanELResolver());
compositeElResolver.add(new CouldNotResolvePropertyELResolver());
return compositeElResolver;
}
protected ELResolver createMicronautElResolver() {
if (beans != null) {
return new ReadOnlyMapELResolver(beans);
} else {
// 还有这里,在表达式中暴露 application-context
return new MicronautApplicationContextElResolver(applicationContext);
}
}
}
</code></pre>
<p>再看下MicronautApplicationContextElResolver:</p>
<pre><code>public class MicronautApplicationContextElResolver extends ELResolver {
protected ApplicationContext applicationContext;
public MicronautApplicationContextElResolver(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
//主要就是这里通过bean名称获取micronaut的bean
@Override
public Object getValue(ELContext context, Object base, Object property) {
if (base == null) {
String key = (String) property;
final Object o = applicationContext.getBean( Object.class, Qualifiers.byName(key));
if (o!=null) {
context.setPropertyResolved(true);
return o;
}
}
return null;
}
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
return true;
}
@Override
public void setValue(ELContext context, Object base, Object property, Object value) {
if (base == null) {
String key = (String) property;
final Object o = applicationContext.getBean( Object.class, Qualifiers.byName(key));
if (o!=null) {
throw new FlowableException("Cannot set value of '" + property + "', it resolves to a bean defined in the micronaut application-context.");
}
}
}
@Override
public Class<?> getCommonPropertyType(ELContext context, Object arg) {
return Object.class;
}
@Override
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object arg) {
return null;
}
@Override
public Class<?> getType(ELContext context, Object arg1, Object arg2) {
return Object.class;
}
}
</code></pre>
<p>好了,抄完作业就阔以了。</p>
<p>原文发表于<a href="http://jenwang.me">http://jenwang.me</a></p>
<p>更多交流请关注公众号:<br/>
<img src="media/15967788014795/15967820787386.jpg" alt="-h200"/><img src="media/15967788014795/15967823434803.jpg" alt="" style="width:300px;"/></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[[读书笔记]旅行的艺术]]></title>
<link href="http://jenwang.me/14990925690611.html"/>
<updated>2017-07-03T22:36:09+08:00</updated>
<id>http://jenwang.me/14990925690611.html</id>
<content type="html"><![CDATA[
<p><img src="media/14990925690611/14993461839206.jpg" alt="" style="width:150px;"/></p>
<h2 id="toc_0">关于期待与实际的差异</h2>
<p>实际的旅行跟我们对它的期待往往是有差异的。<br/>
因为艺术作品中的描述,以及我们的想象带有极强的简括性,而现实生活中,必须承受那些所忽略的环节。</p>
<span id="more"></span><!-- more -->
<blockquote>
<p>现实生活像是用一些重复、不着边际的强调和没有条理的情节惹我们烦恼。<br/><br/>
在艺术作品和期待中寻找有价值的因素远比从现实中来得容易。期待和艺术的想象省略、压缩,甚至切割掉生活中无聊的时段,把我们的注意力直接导向生活中的精彩时分。<br/><br/>
然而珍宝都混杂在一大堆乏味的日常影像中,只不过荷兰的画家们从不在作品中展现普通事物而已。置身于真实的荷兰,我们的体验也因此奇怪而平淡,全然不及在罗浮宫的荷兰画展厅里浏览一个下午来得兴奋。</p>
</blockquote>
<p>身体和心灵是难缠的旅伴,比如,飞行途中喉咙痛、太阳穴发胀、越来越强烈的便意等,而心智方面,焦虑、厌倦、无名的伤感,经济上的恐慌等,都会令所见的美景部分甚至全部失去意义。</p>
<p>德埃桑迪斯说,<code>想象能使平凡的现实生活远比其本身丰富多彩</code>,实际的经历往往是,_想见的场景_总是在实际见到的时候变得平庸和黯淡。,因为我们焦虑将来而不能专注现在,而且对美的欣赏还受制于复杂的物质需要和心理欲求。</p>
<h2 id="toc_1">新的角度</h2>
<p>新的观点往往产生于陌生的所在。全然陌生的环境能促使我们从一个新的高度来审查我们的生活。使我们暂时摆脱因循僵滞的日常生活中难以改变的种种自私的安逸、陋习和拘囿</p>
<h2 id="toc_2">新奇和鉴赏</h2>
<p>异域的魅力源发于新奇与变化,此外,有些东西更符合我们的个性,更能满足心愿,而在故土上并不能做到这一切。在异域发现的异国情调可能就是在本国苦求而不得的东西。</p>
<p>然而并不是所有的东西都是我们能理解和鉴赏的。</p>
<blockquote>
<p>旅行的一个危险是,我们还没积累和具备所需要的接受能力就迫不及待地去观光,而造成实际错误。正如缺乏一条链子将珠子串成项链一样,我们所接纳的新信息会变得毫无价值,并且散乱无章。</p>
</blockquote>
<h2 id="toc_3">关于壮阔</h2>
<p>一种让人感受到大过甚至威胁到人类的力量的景致。</p>
<blockquote>
<p>强大却卑劣之物让人有被羞辱之感,但强大且尊贵之物则使我们敬畏。</p>
</blockquote>
<p>作者认为参考壮阔的景物会,对比人类的脆弱,进而使人坚强。在壮阔的山河中领悟自身的局限是十分有效的,否则我们就容易在日常生活的流变中感到焦虑和愤怒。</p>
<h2 id="toc_4">艺术和画</h2>
<p>作者认为艺术作品是一系列选择的结果,哪些该表现出来,哪些该省略。凡.高之所以独树一帜,是因为他选择自己感觉最重要的东西来表达。</p>
<p>作者建议将景物画出来,从而促使我们发现和拥有美,对景物的深层原因会有更清醒的了解。</p>
<h2 id="toc_5">回归</h2>
<blockquote>
<p>人类不快乐的唯一原因是他不知道如何安静地呆在他的房间里 —— 帕斯卡尔《沉思录》</p>
</blockquote>
<h2 id="toc_6"></h2>
<blockquote>
<p>我们从旅行中获取的乐趣或许更多地取决于心境,而不是旅行目的地本身。如果可以将一种游山玩水的心境带入自己的居所,那么或许会发现,这些地方的有趣程度不亚于洪堡的南美之旅中所经过的高山和蝴蝶慢舞的丛林。</p>
</blockquote>
<p>最后这个描述让我震撼和共鸣,<code>我们遇见过穿越沙漠的人,在冰上漂泊或在丛林里穿越的人,然而在他们的灵魂里,我们无法找寻到他们所见的痕迹</code></p>
<hr/>
<p>豆瓣里的其他参考信息</p>
<p><a href="https://book.douban.com/subject/1056461/">https://book.douban.com/subject/1056461/</a></p>
<p><a href="https://book.douban.com/review/1035800/">https://book.douban.com/review/1035800/</a></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[SSH 隧道实现内网穿透]]></title>
<link href="http://jenwang.me/14982038982873.html"/>
<updated>2017-06-23T15:44:58+08:00</updated>
<id>http://jenwang.me/14982038982873.html</id>
<content type="html"><![CDATA[
<p>家里买了台 NETGEAR 路由器刷了梅林固件,可以在 USB 接口上挂个存储当 NAS 用,发现家里的移动宽带是没有公网 ip 的,于是在外网访问不到内网,ddns 也没法用了(不要给我推荐花生壳内网版😝)。那么,需求就是如何让外网访问到内网ip。</p>
<p>于是想到了从内网到公网上的某台机器建立一条 ssh 隧道,通过访问公网机器把数据穿透到内网,实现方法:<br/>
在内网 10.96.x.x 上执行 <code>ssh -N -f -R 8088:10.96.x.x:443 root@167.88.x.x -b 0.0.0.0 "vmstat 30"</code><br/>
让外网可以通过公网的 167.88.x.x:8088 访问到内网10.96.x.x:443</p>
<blockquote>
<p>备注:167.88.x.x上需要在 <code>/etc/sshd/sshd_config</code> 中修改 <code>GatewayPorts no</code> 为 <code>GatewayPorts yes</code> ,<br/>
否则 8088 端口是绑定在 127.0.0.1 上的,只有本机能访问。</p>
</blockquote>
<hr/>
<p>群晖的 quickconnect,以及内网版的 ddns 原理应该都类似,实现方式可能有所不同。这种可用性关键在于公网中转服务器的网速和通道的稳定性。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[]]></title>
<link href="http://jenwang.me/14952518458373.html"/>
<updated>2017-05-20T11:44:05+08:00</updated>
<id>http://jenwang.me/14952518458373.html</id>
<content type="html"><![CDATA[
<p>观察到许多公司常见的一种现象,出现了某些瓶颈,希望招个水平高的来突破。然而瓶颈往往是由于自身在某方面或全局的认知水平决定的,新招进来的人往往只能在原有的认知模式下工作,结果经常是这样的:要么融入其中但没啥作为,要么受不了而离开。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[初创回忆之管理]]></title>
<link href="http://jenwang.me/14867081191971.html"/>
<updated>2017-02-10T14:28:39+08:00</updated>
<id>http://jenwang.me/14867081191971.html</id>
<content type="html"><![CDATA[
<p>一路磕磕绊绊,遇到不少问题,也有一点微小的心得,这次就写记录管理方面的。</p>
<h2 id="toc_0">逻辑一致</h2>
<p>做为管理者不能逻辑混乱,前后逻辑要一致,「自洽」都做不到是很糟糕的,员工会一头雾水甚至抱怨。每次一遇到问题就想制定相应的措施,但后续的问题往往恰好相反,如果因某些小概率错误就大动干戈制定措施,只会给公司带来伤疤。</p>
<p>例如,</p>
<span id="more"></span><!-- more -->
<ul>
<li>有时说要把事情做细致完美,有时又说先抓住重要的或核心的,然后再慢慢完善。以后做事到底按照哪个标准?</li>
<li>有人未经评审讨论自己做决定出差错,然后就制定了各种流程措施,导致讨论过度,评论者众多,具体做事的人畏首畏脚不敢做任何决定,然后又提倡每个人都要有推进能力。</li>
<li>提倡每个人都要有产品意识,然后又规定所有产品需求来自于销售。</li>
</ul>
<h2 id="toc_1">尊重人性</h2>
<p>这几年的实践中凡是使用顺应人性的管理方式,往往会取得较好效果。</p>
<p>人性就是逐利的,不能否认或逃避。获得财富、自由、认同感、成就感、安全感、权力与掌控等等。<br/><br/>
人们很容易为获得的利益而做出努力,越是短期的具体的好处,效果越明显。可能并不是多大的利益也不一定要物质上的,例如,几天假期就足以。<br/><br/>
如今,期权的吸引力已大打折扣了,只靠情怀更不可取。长期的、虚拟的,往往效果不太好,例如:未来理想、发展空间、能力锻炼、财务回报等,遥遥无期的巨大利息远不如短期可得的微小好处更能立竿见影和产生驱动力。<br/><br/>
有些人的驱动力真的可能是个远大的理想,但这也是一种利益,即某种成就感。每个人最想获取的利益是跟个人环境和条件相关的,指望大家都一样不现实。<br/><br/>
例如,一个想改变世界的老板聚集一帮员工做事,但不能指望这帮员工都想改变世界。有条件想改变世界的人自己就去主导这件事了,而不是来跟你来打工,他们的理想可能就是不工作和选择自己生活方式的自由。<br/>
所以,光靠理想和愿景来提升积极性和效率这事不靠谱,必须满足员工其他的利益诉求,除非你有过把理想变为现实的成功案例才能增加那么一些可信度。 </p>
<p>所以,人性化管理不是放任,而是顺应人性,甚至利用人性。</p>
<h2 id="toc_2">助推优于强推</h2>
<p>新时代的科技企业奉行强制高压制度是落后的,不符合人性和时代进步,可以用助推的方式来达到公司和员工的双赢目的,优秀的脑力劳动者是不会喜欢被束缚的(有点代表别人的嫌疑哈,不排除有人就是喜欢被人驱使甚至虐待着,否则就不高兴),对创造性和主动性都是打击。<br/>
jenwang 在《重新定义团队》(作者: 拉兹洛·博克,google 首席人才官)这本书中首次接触到「助推」这个词,非常认同这个方式,「助推」的理念和做法在书中有详尽阐述,不在此展开。</p>
<h2 id="toc_3">初创公司的管理特点</h2>
<p>初创公司跟成熟公司显著不同的方面,需要解决最原始最初级的问题,任何一个微小的问题对于初创团队来说都是大问题,初创公司没有那么强的容错能力。</p>
<h3 id="toc_4">规则制定</h3>
<p>在成熟公司中例如流程、做事方法与规则、绩效、学习氛围、团队文化等很多方面都已经建立体系了,其实公司已经为团队负责人解决掉了很多事情,行政各方面都有了专人负责,在这种环境中做管理已经是非常省事的了,只需要关注团队主要方面。而初创公司的管理者就需要操心各种边边角角的事情,所有的制度都需要建立。有时踩坑之后总结提炼成规范,时刻关注着日常工作中的经验教训并形成制度也是一件耗时耗力的事情。凭团队负责人一己之力可能难以事无巨细的推进每一件事,倘若不推进,团队做事风格就会一塌糊涂,效率和氛围一定不会好,没有人会帮你改善。</p>
<h3 id="toc_5">贯彻执行</h3>
<p>那么,制定了规则之后需要贯彻执行下去,受限于资源因素,初创公司的团队整体水平往往不尽如意,比如所有人都是1-2年工作经验的,并不知道什么是良好的工作习惯,这都需要指导培养。也不存在部分优秀员工或团队习俗可以带动另一批人的情况。那么落实一项新制度或者倡导一种做事方式时就会很累,需要紧盯着每个人才有效果,一放松又回到老样子(强制并重罚不在此讨论,也不是这里提倡的方式)。</p>
<h3 id="toc_6">搭建梯队</h3>
<p>前面提到从无到有制度建立,以及贯彻的困难,延伸到效率、氛围、团队文化等。所有这些问题是可以通过建立团队的梯队来解决的,前提是要能找到梯队各个位置合适的人。初创公司极有可能就是找不到合适的人来站到梯队里,这时负责人自己就是梯队的各个角色(那么又回到前面的困难中了,所有事情还得你一人搞定)。<br/>
那么通过培养合适的人行不行?其实很多时候是不可行的,因为没有这个培养周期,也不是现有的人都具备培养潜力的,强行拉一个来承担不适合他的职责会形成差强人意的尴尬局面,人家觉得累你也不满意。</p>
<h3 id="toc_7">招人</h3>
<p>以前在珠海魅族工作时常常感叹招人难(至少是有人来面试的嘛),经过这两年的初创经历才感受到什么是真正的招人难。招不到最优秀的人跟招不到人是有本质区别的。初创公司招不到人有时就意味着项目处于停滞状态,何时能招到是不得而知的,这时会有一种强烈的无助感,只要有人肯来面谈已经很开心了,更别说挑选余地了。</p>
<h3 id="toc_8">效率</h3>
<p>效率就是不高怎么办?道理也讲了,截止时间也定了,任务也分配清楚了,可能还是没用。以软件开发为例,主流程写完了跟将异常情况都考虑到写得健壮,工作量可能相差好几倍,而制定任务的时候往往难以覆盖到如此细节,那么工程师只做主流程(也可能是没有考虑到异常分支)就算完成任务,当项目发布或上线时才发现问题,进度就会延后。类似的,功能需求的分配任务时也往往难以每个交互细节,在项目后期发现各个衔接的地方都没做彻底或遗漏,导致系统还是处于不可用状态。这些细节很大程度依赖于每个工程师的经验和主动性。如果一个大项目没有梯队,完全依赖于某个负责人把控质量和进度,那么十有八九都是不尽人意的。</p>
<p>前面提到管理要尊重人性,同样适用于提升效率上也是效果很好的。有何实际好处,说得好听点叫激励机制,其实对初创公司对于索要财务好处时往往是面露难色的,休假激励也难以争取,那么只能以长期利益甚至情怀来激励,效果要差很多。</p>
<p>既然初创公司在使用激励时可能有困难,对任务更进一步细化管控检验也是有一定效果的,但这么做会增加管理成本,特别是没有技术梯队的情况下。同时,过于细化的安排会不利于团队成长,消减人的主动性、积极性和责任感。但无其他良方时就暂时不顾管理成本和团队成长了,保证东西按时做出来再说,也是一种无奈的选择,如果认为这才是有效的管理就是误区了,<strong>这是下策</strong>。</p>
<p>另外,公司业务发展的状况会对团队士气得到很大的鼓舞。不知道自己干的东西价值何在是件令人沮丧的事情。</p>
<h2 id="toc_9">集权</h2>
<p>有时作出集权行为的人往往意识不到这是独断的,上级喜欢插手不熟悉的领域容易造成不良结果,上级操心,会给做事的人造成压力以及不被信任感,舞台小没有发挥余地,长期以往导致消极应对的做事态度。<br/>
正如加里.哈默所说「管理者越有权,那么他离一线实际就越远。在奥林匹斯山上做出的决定通常在山下都行不通」</p>
<h2 id="toc_10">……</h2>
<p>太多太多,以后再归纳吧</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[新时代职业个性]]></title>
<link href="http://jenwang.me/14813423363424.html"/>
<updated>2016-12-10T11:58:56+08:00</updated>
<id>http://jenwang.me/14813423363424.html</id>
<content type="html"><![CDATA[
<blockquote>
<p>这个坑是去年挖的,后来想填发现内容想不起来了,今天想起来了就简略填一下吧。</p>
</blockquote>
<p>经常听说90后的个性更加自我,崇尚自由,叛逆,不服从权威等,然后得出针对90后的管理方法应该转变,认为他们不像70、80后那样服从管理和隐忍,在做自己想做的事才能发挥最大的积极性,用僵硬的规则管理会让他们觉得缺乏自由而产生负面效果,应该以激发成就感和认同感为主,从而带动工作效率。</p>
<p>认同这种想法的人已经是善于接受新事物的了,没得出负面结论已经不错了。但是,有一点我并不认同…</p>
<span id="more"></span><!-- more -->
<p>这不是90后特有的,不应该限定在这个特殊人群中,这是新时代的个性,只不过是这个群体刚好在这个时代中成长起来的,大多数天然具备这种气质。科学的本质是培养出了能够独立思考和自由意志的人,科学是为了获得自由。70、80后同样越来越多的人崇尚自我、个性、自由、质疑、理性等价值观,他们有自己的价值观和生活态度,从骨髓里拒绝各种体制的灌输和同化,但又不得不在这样的体制下生活着。家长制统一管教的模式在管理中越来越没有市场,违背人性的管理方式将受到唾弃,人们需要有价值认同和幸福感。这是新时代社会进步带来的价值观,而非出生于某个时代人群的人可以有,其他人就不配有。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[[读书笔记]重新定义管理]]></title>
<link href="http://jenwang.me/14713174053385.html"/>
<updated>2016-08-16T11:16:45+08:00</updated>
<id>http://jenwang.me/14713174053385.html</id>
<content type="html"><![CDATA[
<p>本书主要讲了新时代科技企业的特点以及传统家长制、规划和控制等管理方式的弊端,提出了一种新的管理范式「合弄制」(这个翻译感觉怪怪的)。「合弄制」主要思想是分权自治,抛弃传统的层级管理结构采用圈子的形式作为组织结构,个人和圈子都具备自治权,同时伴随着责任,遵循章程办事,从人治到法治,既解放管理者又把权力交还给工作过程,发挥每个人的主动性和价值。 </p>
<p>有人说这是「敏捷开发」的企业版,确实有异曲同工之处。与敏捷开发类似,认为一切都是在变化之中的,因而更倾向于根据实际反馈动态调整工作,而非以初始规划为重,然后提出一套实际操作手法,例如,章程制定、会议形式、人员组织形式等。</p>
<p>倘若完全照搬未免落入死板被动的境地,具体方法都套用的话也就落入了「重量级」的俗套,许多做法听起来是这么回事,但实际工作环境和团队情况千差万别,指某种使用「银弹」般的管理方法而后就可以安枕无忧的想法不现实。至于书本中提倡整个企业采取颠覆式的变动来推行,未免过于乐观,就像作者说的<code>不愿放权的领导</code>,<code>不愿合作的中层</code>,<code>突然停止综合症</code>等阻碍,还只是冰山一角,不具体展开。确实有非常好的理念,这种做事氛围也是自己认同和向往的,但也有鼓吹的成分,有些内容信服度不高。书中的一个重要观点是<code>根据实际反馈动态调整</code>,那么扩展一下可以同样用于「合弄制」本身,通过吸收令人反思的部分来改善管理工作,获得更加适合于自己的方式。 </p>
<span id="more"></span><!-- more -->
<hr/>
<p><code>以下为摘录和归纳</code></p>
<h2 id="toc_0">新时代科技企业的挑战</h2>
<p>后工业时代企业面临新的挑战:复杂度高,透明性增强,互动紧密,节奏快,经济和环境不稳定。而现代企业通过预先计划,集中控制,预防偏差的方式不能提供企业渴望的需要的灵活度,不可能点燃员工的激情,被淘汰概率大。所以,引入新的组织管理和运营方法,合弄制</p>
<h2 id="toc_1">权力集中的弊端</h2>
<p>合弄制的基础:权力转移到了工作过程中。按章程分配权力。职权和责任的自治。</p>
<p><code>管理者越有权,那么他离一线实际就越远。在奥林匹斯山上做出的决定通常在山下都行不通</code></p>
<p>想在不熟悉的领域行使专权时,其结果通常都是很可笑的。那会造成很大的压力,却无法有效解决问题。</p>
<p>自上而下的权力系统在老板和员工之间会形成家长与孩子之间的状态。员工认为权力被剥夺了是受害者,管理者会认为他们需要承担所有责任,处理好每个人的问题,这让他们压力很大。</p>
<p>一个人应该负责什么,不同人有不同的看法。如果我们对应他应该负责的工作有不同理解,那么我们的预期就相互冲突了。</p>
<h2 id="toc_2">初衷与实际</h2>
<p><strong>组织方式</strong><br/><br/>
从一开始想要以平等的方式来运营公司,所有人都参与进来。但是,公司的组建方法,设定的工作模式,决定了仍然是在组织结构图的基础上经营公司,我们没有其他办法解决公司运营问题,没有一种我们所有人都信任的方法。</p>
<p>解法:圈子结构</p>
<p><strong>民主</strong><br/><br/>
基于共识在经营公司。过度民主不能做出多少决定,只带来无休无止又令人痛苦的会议。共识不能很好的解决问题,而且形成一个决定需要花费大量的时间和精力。<br/>
解法:个体在自己的工作范围内需要有独自处理事务的权力,而不需要其他所有人和领导的许可</p>
<h2 id="toc_3">deadline 的弊端</h2>
<p>让人重新审视承诺时间点的方式,日常工作中也经常碰到承诺了一个时间点,从而打乱了按重要程度排序的优先级,对整体影响不大的事情被优先完成,然而这些事情是永远都会临时出现的。</p>
<p>摘录:</p>
<blockquote>
<p>如果总是强调什么时候完成,很容易无意识地追着承诺去工作,而非有意识地选择当下最重要的工作来做<br/>
有时承诺时没有预料到将会出现另一件更重要的事<br/>
何时完成的做法让我们假装现实比它实际的状况更可遇见,更易控制,这是人类最具安慰性的一种自欺欺人</p>
</blockquote>
<h2 id="toc_4">战略 vs 动态操作范式</h2>
<p>战略是预测和控制思维模式的本质,选定正确的目标,然后设计实现的路径。正确的预测是前提,有些事可预测,然而更多的事无法预测。 </p>
<p>作者反对战略方法靠预测,并不是说所有的前瞻性规划和预测都是无用的。这有些自我辩解的感觉,但个人认为战略和预测都要基于事实,不可把臆想当作论据。</p>
<p>学习和适应比预测和计划更重要。<br/>
做一个能够可靠预测的简短清单比指出哪些不能预测的事情更为简单,但通常我们还是会去尝试后者。</p>
<p>骑自行车的例子,精确计算并不能使自行车不倒,而是要根据实际反馈不断感知和调整。<br/>
说出不喜欢做规划和战略往往会被认为政治,这是个很好的观点支撑。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[[读书笔记]合适]]></title>
<link href="http://jenwang.me/14861916870975.html"/>
<updated>2017-02-04T15:01:27+08:00</updated>
<id>http://jenwang.me/14861916870975.html</id>
<content type="html"><![CDATA[
<p>作者: [日] 坂井丰贵 </p>
<p><img src="media/14861916870975/14861921145666.jpg" alt="" style="width:150px;"/></p>
<p>开始看到这本书其实挺惊讶的,有种见微知著的感觉,看上去如此平常简单的事情,竟然可以通过理论体系和算法得到更合理的解决,而不是凭感觉行事。那么日常中,我们一直采用无知落后的方式来做决策而不自知的有多少?</p>
<p>深感市场设计的魅力,以及<code>为经济学家思维的层次感与创造力所折服</code>。制度和游戏规则设计绝不是件简单的事,有时甚至需要经过理论和公式的严密论证。良好的规则应该获得优化结果的同时还能防止策略性操作,使得表达真实意图和诚信的行为得到鼓励,消除碰运气和投机性而获利的可能。 </p>
<p>以下记录本书的关键点,本书分三个部分,</p>
<span id="more"></span><!-- more -->
<h2 id="toc_0">一、单边匹配问题</h2>
<p>例如“人与物”的匹配,只涉及一方的意愿表达。采用合适的算法改变组合可提高参与者的幸福度。<br/><br/>
案例:房间置换、交换肾源及 TTC 算法。随着问题规模的增大,如果没有合适的算法,凭感觉行事想要获得最优解几乎不可能。TTC 算法的目的是要导出一个「强核配置」。强核配置是<strong>一定存在且一定唯一的</strong>。</p>
<p><strong>强核配置</strong>:</p>
<ol>
<li>不存在「帕累托改进」的空间,也就是说不会使任何人处境变差的情况下还有余地让某个人的情况变好。</li>
<li>不会发生「阻止」(block),即满足个体合理性,不存在通过退出分配或私下协议而受益的可能。</li>
<li>满足防策略性的,通过虚假报告隐瞒自己的偏好并不会获利。<br/></li>
</ol>
<p><em>以上经过公理性特征证明</em></p>
<h2 id="toc_1">二、双边匹配问题</h2>
<p>参与的双方都有偏好,如何实现“两情相悦”,保证匹配的稳定性是规则设计的关键。</p>
<p>案例,选课、招生及婚姻的稳定性、医生-医院的分配。比如高校招生,最好采用结果不受运气影响的公平公正的规则,有没有这样的方法呢?那就是由盖尔和沙普利所创的「延迟接受算法」。</p>
<p><strong>延迟接受算法</strong>:</p>
<h3 id="toc_2">一对一匹配</h3>
<ol>
<li>满足「稳定匹配」。但跟单边匹配不同,双边匹配中「稳定匹配」不见得只有一个,导出的结果对于「选择的一方」是最佳的稳定匹配(对于「被选择方」不是最佳但并不意味着不稳定)。即使存在两个以上稳定匹配,它们的分配结果其实相差不远</li>
<li>满足<strong>单边</strong>防止策略性。对于「选择的一方」表明自己真实偏好是上策,对于「被选择的一方」未必。很遗憾,理论上已证明不存在双边防策略性的分配方法</li>
</ol>
<h3 id="toc_3">一对多匹配</h3>
<p>例如医院有接收多个实习医生的名额。 </p>
<ul>
<li>「单边防策略性」仅在「一」方作为「选择方」是成立,「多」方作为「选择方」则不成立。</li>
<li>择校匹配,从「波士顿」机制的弊端,中国高考志愿类似的机制,申请人气高的学校风险极高,运气成分过重引发的不满(例如,“比我排名靠后的去了我想去的学校,而我没被录取”)</li>
<li>TTC算法和延迟接受算法都可以用在学校匹配上,以帕累托改进作为标准两个算法难分伯仲,加上“人气高的学校得到排名更靠前的学生”这个标准,帕累托算法更合适。</li>
</ul>
<h2 id="toc_4">三、拍卖问题</h2>
<p>采用何种拍卖方式,才能使标的物的经济价值显现出来,那种拍卖对卖方更有利,以及背后的理论支撑。</p>
<ul>
<li>从买方角度,增价拍卖和第二价格拍卖、降价拍卖和第一价格拍卖是「策略等价」的。<br/></li>
<li>从卖方角度,第一价格和第二价格拍卖中的期望收益是一致的(惊讶),「收益等价定理」证明。意味着卖家不需要从收益角度考虑采用第一价格还是第二价格拍卖,因两者的期望收益相同。只需考虑其他条件,比如防策略性。</li>
<li>第二价格 vs 增量拍卖</li>
<li><code>「收益等价定理」只在单个商品的拍卖中有效,对于出售多单位同种商品无效,例如国债</code></li>
<li>对卖家期望收益最大化称为「最优拍卖」,比如在第二价格拍卖中通过计算设定底价。</li>
<li>私人价值,共同价值,关联价值</li>
<li>市场设计的特征在于制定规则,而不是结果。反例,计划经济</li>
</ul>
<h3 id="toc_5">公开式</h3>
<ul>
<li><p>增价拍卖</p>
<ul>
<li>从最低价格加价,买房不想出更高价格时退出,当只剩一个买家时结束。</li>
</ul></li>
<li><p>降价拍卖</p>
<ul>
<li>从高价起拍,价格不断下降,直到某个买家说“我要买”时,拍卖结束。 </li>
</ul></li>
</ul>
<h3 id="toc_6">密封式</h3>
<p>买家把出价金额写在信封里交给主办者,一旦提交不能变更。</p>
<ul>
<li>第一价格拍卖<br/>
<ul>
<li>获胜者支付自己出价的金额——最高出价金额</li>
<li>结果强烈依赖彼此之间的预测,结果容易被运气操纵,很难公正</li>
</ul></li>
<li>第二价格拍卖<br/>
<ul>
<li>获胜者支付出价第二高的金额</li>
<li>参与者按照自己的评估值出价就是「占优策略」<br/>
低于评估价,导致失败可能性增加,而支付金额并不会减少。<br/>
高于评估价,本应失败的却获胜了,需支付高于自己评估价的金额。<br/></li>
<li>所以,满足防策略性<br/></li>
<li>“第二价格”并不低,这个金额是“失败者所报出的最高评价值”,高到所有失败者都不会支付“更高的金额”</li>
</ul></li>
</ul>
<h3 id="toc_7">多单位同质商品拍卖</h3>
<ul>
<li>收益等价定理成立的情况及其有限,几乎没有意义</li>
<li>采用诸如<code>最高失败价格拍卖</code>、<code>价格歧视拍卖</code>、<code>修正最高失败价格拍卖</code>、<code>维克里拍卖</code>等,之外还要综合考虑其他因素,论证略显复杂,不在此阐述。</li>
</ul>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[[读书笔记]茶书]]></title>
<link href="http://jenwang.me/14856967077187.html"/>
<updated>2017-01-29T21:31:47+08:00</updated>
<id>http://jenwang.me/14856967077187.html</id>
<content type="html"><![CDATA[
<p>作者: [日] 冈仓天心 </p>
<p><img src="media/14856967077187/14856968085208.jpg" alt=""/></p>
<blockquote>
<p>感想与摘录</p>
</blockquote>
<p>原以为是介绍喝茶知识的书,然而并不是。一直对日本诸多侘寂之美的设计颇为赞赏,简朴亲切而又实用摒弃任何多余的干扰,然而对于这种审美的根基一无所知,看完后又刷新了对日本的一些认知,对简约朴素的审美和文化之渊源有了全新的了解,甚是惊喜。<br/>
译者的后记是本书不可忽视的篇章,更通透地解释了中日文化渊源和差异,视角非常有启发。中国艺术的背后主要是权力、审美的统治,而日本则是普通民众的力量,民艺运动通过改善民众的生活品质进而促进社会进步。日本茶道的建立是对这种审美统治的挑战,属于自己空间关系的审美而非复制中国的审美,我们总是提传统文化的保护却很少钻研传统文化的发展,传统不应该成为包袱,应该随时蜕去积攒千年的壳,关照事物纯粹的本质,没有创造无所谓传统,传统是创造的积淀。极为认同译者所言<code>中国没有茶道,中国有饮茶的方法</code>,近年来大陆的饮茶之风日盛,只是附庸风雅、软弱无力,是虚假和令人生厌的,没有文化的优雅是妖,再多的人声鼎沸也只能是繁华躁动且妖气弥漫。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[我和工具]]></title>
<link href="http://jenwang.me/14851540366764.html"/>
<updated>2017-01-23T14:47:16+08:00</updated>
<id>http://jenwang.me/14851540366764.html</id>
<content type="html"><![CDATA[
<p>好的工具对于提升效率、提高生活品质起到重要作用。梳理和分享下我正在使用的,本列表长期更新。</p>
<span id="more"></span><!-- more -->
<ul>
<li>保护眼睛
<ul>
<li>flux<br/>
过滤蓝光,晚上光线暗的时候不会那么刺眼,我在mac和安卓手机上使用,ios自带类似功能。<br/>
<a href="https://justgetflux.com/%E2%80%8B">https://justgetflux.com/</a></li>
</ul></li>
<li>白噪音<br/>
<ul>
<li>我在mac上使用「Noizio」让自己更专注和沉浸于手头的事</li>
<li>手机上可以用微信小程序「小睡眠」</li>
</ul></li>
<li><p>todolist<br/><br/>
「奇妙清单」管理任务和提醒,全平台同步,还可通过webcal订阅到日历视图中(mac 日历支持 webcal url 订阅,安卓直接支持的很少,可安装 ICSdroid 解决)。</p></li>
<li><p>知识管理和文档编写<br/><br/>
基本只用 markdown 格式编写,除非特殊需要几乎不碰 word。 </p>
<ul>
<li>Simplenote<br/>
小而美,用来随手打开记录临时想法,后整理到其他地方。一般是在手机中记录,后在电脑上整理。</li>
<li><em>mweb</em><br/>
特别推荐,到目前为止同类工具中最称心的,markdown 是必须的,支持生成静态网站。适合个人的知识沉淀和写文字,存放整理后的资料。支持一篇文档属于多个分类,利用这一特性可以选取一些文档到博客分类下,这样公开博客和私有文档就能在同一知识沉淀工具上完成。数据通过 dropbox 同步。外部模式用来打开其他 markdown 文档,用了 mweb 后很少使用 macdown、mou 等 markdown 编辑器了。</li>
<li>印象笔记<br/>
主要用于收集,未经整理的资料,杂货桶。用来写作总不舒服,不展开说为什么,个人品味。 </li>
<li><del>为知笔记</del><br/>
前几年用,还放着历史遗留文档,只在查看相关资料时打开。后因 mac 端体验不太好,以及丢了几篇文档后逐步弃用。 </li>
</ul></li>
<li><p>幻灯片<br/><br/>
讨厌 ppt,也用不惯keynote。</p>
<ul>
<li>Marp<br/>
markdown 编写幻灯片,文档中通过<code>---</code>分割成一页幻灯片。目前所知同类工具中最简单方便的。曾给 mweb 的作者提过能否直接加上 Marp 的功能,这样文档和演示就能统一起来了,被否决(认为从技术角度会导致 mweb 太庞杂)。</li>
</ul></li>
<li><p>浅阅读</p>
<ul>
<li>即刻,微博,小密圈,reeder,公众号</li>
</ul></li>
<li><p>图片处理</p>
<ul>
<li>Google Nik Collection,mac上用于照片后期处理,非常强大。</li>
<li>snapseed,手机上用。</li>
</ul></li>
<li><p>其他常用软件 </p>
<ul>
<li>Alfred 3 </li>
<li>iTerm 2 + Oh My Zsh</li>
<li>网易云音乐</li>
<li>Bartender - 让 mac 的状态栏更清爽</li>
</ul></li>
</ul>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[如何评价一个清闲的管理者]]></title>
<link href="http://jenwang.me/14850506793674.html"/>
<updated>2017-01-22T10:04:39+08:00</updated>
<id>http://jenwang.me/14850506793674.html</id>
<content type="html"><![CDATA[
<p>比如一个部门负责人看上去很「清闲」好不好?这个问题我认为不能从是否「清闲」来判断一个管理者,所谓善战者无赫赫之功也是极有可能的,重点在于他的团队氛围、工作效率如何。</p>
<p>假如管理者带来的影响是负面的,那么忙碌起来并没有意义,甚至还不如什么也不做,遗憾的是很大比例的管理者并不这么做,甚至知道也不能这么做,忙碌本身已经成为政治正确,不管事情真正的意义和价值。</p>
<span id="more"></span><!-- more -->
<p>一个负责人多数时间并没做具体事情,似乎比较「清闲」,而他的团队又是运作良好的(没出啥大问题,基本满足需求),那么就没必要去改变他,往往是他通过自身探索找到了某种节奏和平衡,组建了合适的梯队,负责人不在时团队也能运作良好是件值得高兴的事,这才是管理有方。</p>
<p>一旦外力去打破这种平衡那就会出问题,溢出成本通常不可忽视,比如来了一个接替者,制定了一系列严格措施、流程、加班等等,似乎大家都有序忙碌起来了,其实隐含着危机,员工抱怨增多,工作严重挤占个人时间,付出回报不成比例等等,基层员工的这种问题就是团队涣散的开始,往往还不容易被高层发现,为啥始终不见氛围和效率,这就是原因。另一种是貌似都已竭尽全力,工时很长,产出结果问题也很多,但是领导很满意,就要这种态度嘛,相关的管理者受到赏识,这就是劣币驱逐良币,劣币驱逐良币的事不能做大家都懂,但实际中要识别这种情况确不太容易,做了也不自知。</p>
<p>打破一个良好而清闲的管理者的动机,大概可归结为两种情况,1. 这么无所事事肯定没干好工作,2. 应该花更多的时间把工作做得更好。<br/><br/>
稍加推敲都是不成立的,第一种,无所事事只是你看起来的表面现象,实际可能是他通过一系列的努力跟团队之间达成的一种默契,从表面现象推断出工作结果就非常主观和不严谨,而应该从具体事实判断。<br/>
第二种,花更多个人时间不一定就能把工作做得更好,上面已经讲过瞎作为还不如不作为,一个人成绩达到90分想往100分努力,付出和回报是极不成比例的,既有可能永远也达不到目标,打破自身平衡去做远超能力之外的事情,往往结果不会好。</p>
<p>任何管理都不要违背人性,逼不得已可短期为之,但是一定不能也不会长久。管理要借助满足人性驱动效率,如果做不到就要反思哪里出了问题。</p>
<p>最后,在国内当前环境下,这些观点会被很多人认为是价值观有问题,以及政治不正确的。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[一个使用本地缓存引起的线程阻塞问题]]></title>
<link href="http://jenwang.me/14853486232734.html"/>
<updated>2017-01-25T20:50:23+08:00</updated>
<id>http://jenwang.me/14853486232734.html</id>
<content type="html"><![CDATA[
<h2 id="toc_0">现象</h2>
<p>有同事的java系统运行一段时间后发生请求阻塞的情况(返回504),从仅有的内存dump文件看,大部分线程都阻塞在了一个本地缓存(jodd cache)的读锁上了(ReentrantReadWriteLock$ReadLock.lock)。</p>
<span id="more"></span><!-- more -->
<h2 id="toc_1">排查过程</h2>
<h3 id="toc_2">阶段一</h3>
<p>本能的反应应该是写锁被占用了才会出现这个情况。于是开始以"WriteLock.lock"为关键字搜索写锁,怎么也搜不到。其实搜不到是正常的,因为写锁已经被占有了,当然不可能停在WriteLock.lock上了。</p>
<p>开始翻jodd LRUCache代码,发现是用LinkedHashMap实现的,在dump文件上搜索LinkedHashMap写操作的代码,果然发现有一个线程是正在执行LRUCache的put方法,代码停留在LRUCache的pruneCache方法中(就是在put的时候cache满了回收一些位置):</p>
<pre><code>protected int pruneCache() {
if (isPruneExpiredActive() == false) {
return 0;
}
int count = 0;
//cacheMap就是一个LinkedHashMap的实例
Iterator<CacheObject<K,V>> values = cacheMap.values().iterator();
while (values.hasNext()) {
CacheObject<K,V> co = values.next();
if (co.isExpired() == true) {
values.remove();
count++;
}
}
return count;
}
</code></pre>
<p>到这里就证明了最初的猜想是对的,写锁被占了才导致那么多读线程被堵住。 </p>
<blockquote>
<p>可以看出 jodd 使用 LinkedHashMap + ReentrantReadWriteLock 实现LRUCache是有性能问题的,一个写操作会锁住整个缓存,阻塞所有读操作。<u>这是第一个问题</u>。</p>
</blockquote>
<h3 id="toc_3">阶段二</h3>
<p>显然不能到此就结束了,要有更高的追求,继续分析LRUCache的具体实现,主要逻辑就是put时加上写锁,get时加上读锁,内部是一个开启了accessOrder的LinkedHashMap作为数据存储。</p>
<p>初看也貌似很正常没啥问题啊。其实开启了accessOrder的LinkedHashMap 多线程get是会有并发问题的,因为会把get到的元素移到双向链表最前面,看LinkedHashMap的get方法: </p>
<pre><code>public V get(Object key) {
Entry<K,V> e = (Entry<K,V>)getEntry(key);
if (e == null)
return null;
e.recordAccess(this);
return e.value;
}
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}
</code></pre>
<blockquote>
<p>可以看到这里改变链表结构是没有任何并发控制的,因此LinkedHashMap并发get是不OK的,jodd给get加了读锁是存在并发问题的(还不明白的请自行学习ReentrantReadWriteLock机制)。<u>这是第二个问题</u>。</p>
</blockquote>
<p>可以想象下高并发时链表被破坏成各种奇形怪状的情况(比较费脑力,我就不描述了),完全有可能让上面pruneCache()方法中的values.hasNext()永远为true。这次刚好是停在LRUCache#pruneCache中,下次就有可能停在LinkedHashMap#transfer上,一旦写锁里面的代码块hang住,所有读线程全部堵住,而且这种问题出现几率不等,很难模拟重现。</p>
<h2 id="toc_4">JUC Bug</h2>
<p>另外顺便提一下某些早期JDK版本中存在的BUG </p>
<p>ReentrantReadWriteLock可能在没有任何线程持有锁的情况下被hang住:<br/>
<a href="http://bugs.sun.com/view_bug.do?bug_id=6822370">http://bugs.sun.com/view_bug.do?bug_id=6822370</a><br/>
<a href="http://bugs.sun.com/view_bug.do?bug_id=6903249">http://bugs.sun.com/view_bug.do?bug_id=6903249</a></p>
<h2 id="toc_5">小结</h2>
<ul>
<li>不要使用Jodd的cache</li>
<li>推荐使用gauva的cache<br/>
基于<a href="https://code.google.com/p/concurrentlinkedhashmap/">concurrentlinkedhashmap</a>实现,现已整合到guava里了</li>
<li>不可轻信开源组件,使用前一定要先研究透彻</li>
</ul>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[如何一次性下载某个类库依赖的所有jar包"]]></title>
<link href="http://jenwang.me/14853486232530.html"/>
<updated>2017-01-25T20:50:23+08:00</updated>
<id>http://jenwang.me/14853486232530.html</id>
<content type="html"><![CDATA[
<p>** 经常碰到这种事情: **</p>
<p>在一些非maven工程中(由于某种原因这种工程还是手工添加依赖的),需要用到某个新的类库(假设这个类库发布在maven库中),而这个类库又间接依赖很多其他类库,如果依赖路径非常复杂的话,一个个检查手动下载是很麻烦的事. </p>
<p>** 下面给出一个便捷的办法: **</p>
<span id="more"></span><!-- more -->
<p>创建一个新目录里面建一个maven pom文件, 添加需要依赖的类库:</p>
<pre><code><?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dep.download</groupId>
<artifactId>dep-download</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.xx.xxx</groupId>
<artifactId>yy-yyy</artifactId>
<version>x.y.z</version>
<scope/>
</dependency>
</dependencies>
</project>
</code></pre>
<p>在这个目录下运行命令:<br/><br/>
<code>mvn -f download-dep-pom.xml dependency:copy-dependencies</code></p>
<p>所有跟这个类库相关的直接和间接依赖的jar包都会下载到 ./target/dependency/下</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[关于serialVersionUID与序列化"]]></title>
<link href="http://jenwang.me/14853486232320.html"/>
<updated>2017-01-25T20:50:23+08:00</updated>
<id>http://jenwang.me/14853486232320.html</id>
<content type="html"><![CDATA[
<h2 id="toc_0">java序列化trick and trap</h2>
<p>厂内经常出现序列化对象版本不匹配问题,于是发本文说明一些序列化的注意点</p>
<p><em>调用MQ、memcached、rpc等等涉及到远程通讯的都会经过序列化,虽然客户端透明的封装了细节,但底层是一定会有序列化操作的。因此了解序列化的注意事项是非常有必要的,可以避免误用导致潜在的风险</em></p>
<span id="more"></span><!-- more -->
<ul>
<li><p>通过网络传输的对象,必须实现Serializable接口,或者父类已经实现序列化接口。</p></li>
<li><p>网络传输对象继承层次不宜过深,封装在内部的对象也不宜太复杂。(太复杂很容易出现某个相关的类没实现序列化接口,而导致整个对象无法序列化)</p>
<ul>
<li>一般long/int/String/Map/List/Array等常见类组成的对象就
能解决问题 </li>
<li>最好不要在本应用对外的业务接口中传递或返回“由另一人或系统主导的业务对象"。因为你不能保证别人的对象版本会兼容,从而导致错误扩散</li>
</ul></li>
<li><p>在接口定义上用的是父类,实际远程传输过去的是子类,反序列化不了的。特别是在rpc中客户端容易出现此问题</p></li>
<li><p>远程接口上的参数、返回值类型、会抛出的异常类,都要实现序列化接口。并且server和client都要有对应的类。</p>
<ul>
<li>一个比较容易忽略的例子是:某服务接口可能会抛出某个运行时异常,但没有把这个异常类放入客户端中,一旦抛出此异常,客户端接收到此异常就会无法反序列化</li>
</ul></li>
<li><p>ArrayList.subList()返回的List实现类是内部类型,不能序列化的,通过网络传输会出错。</p></li>
<li><p>ArrayList经过网络传输后,里面的元素顺序可能不一样</p></li>
<li><p>网络传输对象要有无参构造器(如果定义了有参构造器那就要显式定义一个无参构造起),因为机器是不知道传什么内容给有参构造器进行实例化,无参构造器不是public都没关系。没定义无参构造器,有些序列化方式会在底下生成无参构造器的方式才能解决问题。</p></li>
<li><p>网络传输最好不要用enum类型,太强耦合,从网络一端传到另一端,对方可能还是旧版本而识别不了。</p>
<ul>
<li>Enum 常量的序列化不同于普通的 serializable 或 externalizable 对象。enum 常量的序列化形式只包含其名称;常量的字段值不被传送。为了序列化 enum 常量,ObjectOutputStream 需要写入由常量的名称方法返回的字符串。</li>
</ul></li>
<li><p>不需通过网络传输的field用transient定义,但有些json序列化类库是不会区别对待这种field</p></li>
<li><p>有些序列化类库,遇到反序列化不了的类,会反序列化成Map,但会在使用时遇到class cast异常。</p></li>
<li><p>同一应用不要有同package同名的类,可能隐藏在同名/不同名/不同版本的jar中。</p></li>
</ul>
<h2 id="toc_1">serialVersionUID</h2>
<ul>
<li><p><font color="red">用于网络传输的对象,第一次上线使用时,就一定要设定serialVersionUID,不要不顾编译警告</font></p>
<ul>
<li><p>NOTE: 网络对象的匹配,除了靠类名,还靠serialVersionUID,serialVersionUID在《Java语言规范》有固定算法,<u><strong>跟各field的定义相关,如果没有显式赋值,虽然看不见,但会底下会默认算出一个进行网络传输。</strong></u></p></li>
<li><p><font color="red">如果没有显式赋值,在看不见觉察不到的情况下,在你增减了field/修改了定义的情况下,serialVersionUID已被改变,这时网络两端就对接不上而悲剧了。<br/><br/>
没定义serialVersionUID,而又发生了serialVersionUID变化,网络两端只有所有机器都停掉,并且先后起有顺序时,才能不出丝毫差错。</font></p></li>
</ul></li>
<li><p>最好不要用用1L作为serialVersionUID。0L对于java enum的序列化有特殊意义。</p></li>
<li><p>没赋值serialVersionUID 只是警告,不是错误,造成没设定serialVersionUID,网络两端上线运行一段时间也感觉正常。如果再增减修改field,没赋值好serialVersionUID,网络两端就不匹配。 </p>
<ul>
<li>算出旧版本的serialVersionUID(使用serialver或eclipse),设置到新版本的代码中 </li>
</ul></li>
</ul>
<p><em>本文大部分内容取自前同事的分享资料,作了少许修改,<a href="http://lokki.iteye.com/blog/1134482">外网地址</a></em></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[一个java内存泄漏的排查案例]]></title>
<link href="http://jenwang.me/14853486232080.html"/>
<updated>2017-01-25T20:50:23+08:00</updated>
<id>http://jenwang.me/14853486232080.html</id>
<content type="html"><![CDATA[
<p>这是个比较典型的java内存使用问题,定位过程也比较直接,但对新人还是有点参考价值的,所以就纪录了一下。<br/><br/>
下面介绍一下在不了解系统代码的情况下,如何一步步分析和定位到具体代码的排查过程<br/>
<em>(以便新人参考和自己回顾)</em></p>
<span id="more"></span><!-- more -->
<h2 id="toc_0">初步的现象</h2>
<p>业务系统消费MQ中消息速度变慢,积压了200多万条消息,通过jstat观察到业务系统fullgc比较频繁,到最后干脆OOM了:<br/><br/>
<img src="media/14853486232080/14853493912031.jpg" alt=""/></p>
<h2 id="toc_1">进一步分析</h2>
<p><strong>既然知道了内存使用存在问题,那么就要知道是哪些对象占用了大量内存.</strong><br/><br/>
很多人都会想到把堆dump下来再用MAT等工具进行分析,但dump堆要花较长的时间,并且文件巨大,再从服务器上拖回本地导入工具,这个过程太折腾不到万不得已最好别这么干。 </p>
<p>可以用更轻量级的在线分析,用jmap查看存活的对象情况(jmap -histo:live [pid]),可以看出HashTable中的元素有5000多万,占用内存大约1.5G的样子:<br/><br/>
<img src="media/14853486232080/14853494189213.jpg" alt=""/></p>
<h2 id="toc_2">定位到代码</h2>
<p><strong>现在已经知道了是HashTable的问题,那么就要定位出什么代码引起的</strong> </p>
<p>接下来自然要看看是什么代码往HashTable里疯狂的put数据,于是用神器btrace跟踪Hashtable.put调用的堆栈。<br/><br/>
首先写btrace脚本TracingHashTable.java:</p>
<pre><code>import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
@BTrace
public class TracingHashTable {
/*指明要查看的方法,类*/
@OnMethod(
clazz="java.util.Hashtable",
method="put",
location=@Location(Kind.RETURN))
public static void traceExecute(@Self java.util.Hashtable object){
println("调用堆栈!!");
jstack();
}
}
</code></pre>
<p>然后运行:<br/>
bin/btrace -cp build 4947 TracingHashTable.java<br/><br/>
看到有大量类似下图的调用堆栈<br/><br/>
<img src="media/14853486232080/14853494433131.jpg" alt=""/></p>
<p>可以看出是在接收到消息后查询入库的代码造成的,业务方法调用ibatis再到mysql jdbc驱动执行statement时put了大量的属性到HashTable中。</p>
<p>通过以上排查已基本定位了由那块代码引起的,接下来就是打开代码工程进行白盒化改造了,对相应代码进行优化(不在本文范围内了。几个图中的pid不一致就别纠结了,有些是系统重启过再截图的). </p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Leiningen的profiles.clj不生效?]]></title>
<link href="http://jenwang.me/14853486231882.html"/>
<updated>2017-01-25T20:50:23+08:00</updated>
<id>http://jenwang.me/14853486231882.html</id>
<content type="html"><![CDATA[
<p>需求:<br/><br/>
比如要自定义本地maven库的路径,又不想在project.clj中定义,因为每个人的本地路径不同,写在工程中不好。那么在profiles.clj中定义比较好:</p>
<pre><code>{:user {:local-repo "D:\\m2\\repository"}}
</code></pre>
<p>当庆幸找到解决方法时,一运行发现根本没生效是件很扫兴的事。<br/>
网上能找到的资料都告诉你profiles.clj这个文件是放在~/.lein/这个目录下的。</p>
<p><strong>实际情况是:</strong> <br/>
<code>如果自定义了LEIN_HOME的路径,那么profiles.clj就应该放在LEIN_HOME目录下,而不是~/.lein/下</code><br/><br/>
否则不会生效,切记。 </p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[避免jar依赖冲突的一种办法]]></title>
<link href="http://jenwang.me/14853486231695.html"/>
<updated>2017-01-25T20:50:23+08:00</updated>
<id>http://jenwang.me/14853486231695.html</id>
<content type="html"><![CDATA[
<p>java中的依赖冲突问题一直比较头疼,特别是做公用包给其他系统用的时候,现在都不敢引入太多的依赖,基本上每次都要帮别人解决依赖冲突的问题,非常麻烦。 </p>
<p>特别是碰到一些老系统还不是用maven管理的,人家用你的一个功能还要拷一堆jar包过去,然后排出哪些包在系统中已经有了,版本是否兼容等问题,非常蛋疼。为了方便人家使用就想把所有依赖打成一个jar包提供出去,但这样潜在的依赖冲突问题就会更严重,以后出现冲突时都不知道哪个jar包含了冲突的类。 </p>
<p>更不想引入OSGi这种重量级的东西来隔离依赖。 </p>
<span id="more"></span><!-- more -->