谈谈程序员成长

本篇文章主要侧重的是程序员的硬技能方面的,不涉及软技能。

关于程序员的成长,网上有很多人都在谈,也有很多的文章,我也看过许多,个人感觉写的最好的是下面2个文章,建议大家阅读。

我此处整理一下:

误区

拜大牛为师

  • 大牛很忙,不太可能单独给你开小灶
  • 大牛不多,不太可能每个团队都有技术大牛,只能说团队里面会有比你水平高的人,即使他每天给你开小灶,最终你也只能提升到他的水平

综合上述的几个原因,我认为对于大部分人来说,要想成为技术大牛,首先还是要明白“主要靠自己”这个道理,适当的时候可以通过请教大牛或者和大牛探讨来提升自己,但大部分时间还是自己系统性、有针对性的提升。

业务代码一样很牛逼

有人认为写业务代码一样可以很牛逼,理由是业务代码一样可以有各种技巧,例如可以使用封装和抽象使得业务代码更具可扩展性,可以通过和产品多交流以便更好的理解和实现业务,日志记录好了问题定位效率可以提升10倍等等。

业务代码一样有技术含量,这点是肯定的,业务代码中的技术是每个程序员的基础,但只是掌握了这些技巧,并不能成为技术大牛,就像游戏中升级打怪一样,开始打小怪,经验值很高,越到后面经验值越少,打小怪已经不能提升经验值了,这个时候就需要打一些更高级的怪,刷一些有挑战的副本了,成为技术大牛的路也是类似的,你要 不断的提升自己的水平,然后面临更大的挑战,通过应对这些挑战从而使自己水平更上一级,然后如此往复。所以我认为:业务代码都写不好的程序员肯定无法成为技术大牛,但只把业务代码写好的程序员也还不能成为技术大牛。

上班太忙没时间自己学习

很多人认为自己没有成为技术大牛并不是自己不聪明,也不是自己不努力,而是中国的这个环境下,技术人员加班都太多了,导致自己没有额外的时间进行学习。

这个理由有一定的客观性,毕竟和欧美相比,我们的加班确实要多一些,但这个因素只是一个需要克服的问题,并不是不可逾越的鸿沟,毕竟我们身边还是有那么多的大牛也是在中国这个环境成长起来的。

我认为有几个误区导致了这种看法的形成:

1)上班做的都是重复工作,要想提升必须自己额外去学习
2)学习需要大段的连续时间

正确的做法

做的更多,做的比你主管安排给你的任务更多。

要想有机会,首先你得从人群中冒出来,要想冒出来,你就必须做到与众不同,要做到与众不同,你就要做得更多!不分哪些是我该做的、哪些不是我该做的

怎么做得更多呢?可以从以下几个方面着手:

  • 熟悉更多业务,不管是不是你负责的;熟悉更多代码,不管是不是你写的
  • 熟悉端到端 比如说你负责web后台开发,但实际上用户发起一个http请求,要经过很多中间步骤才到你的服务器(例如浏览器缓存、DNS、nginx等),服务器一般又会经过很多处理才到你写的那部分代码(路由、权限等)这整个流程中的很多系统或者步骤,绝大部分人是不可能去参与写代码的,但掌握了这些知识对你的综合水平有很大作用,例如方案设计、线上故障处理这些更加有含金量的技术工作都需要综合技术水平。 “系统性”、“全局性”、“综合性”这些字眼看起来比较虚,但其实都是技术大牛的必备的素质,要达到这样的境界,必须去熟悉更多系统、业务、代码。
  • 自学 以垃圾回收为例,我自己平时就抽时间学习了这些知识,学了1年都没用上,但后来用上了几次,每次都解决了卡死的大问题,而有的同学,写了几年的java代码,对于stop-the-world是什么概念都不知道,更不用说去优化了。

这一点和张一鸣说的是完全吻合的:

  • 我工作时,不分哪些是我该做的、哪些不是我该做的
  • 我做事从不设边界

做的更好

要知道这个世界上没有完美的东西,你负责的系统和业务,总有不合理和可以改进的地方,这些“不合理”和“可改进”的地方,都是更高级别的怪物,打完后能够增加更多的经验值。识别出这些地方,并且给出解决方案,然后向主管提出,一次不行两次,多提几次,只要有一次落地了,这就是你的机会。

多练

在做职业等级沟通的时候,发现有很多同学确实也在尝试Do more、Do better,但在执行的过程中,几乎每个人都遇到同一个问题:光看不用效果很差,怎么办?

例如:

  • 学习了jvm的垃圾回收,但是线上比较少出现FGC导致的卡顿问题,就算出现了,恢复业务也是第一位的,不太可能线上出现问题然后让每个同学都去练一下手,那怎么去实践这些jvm的知识和技能呢?
  • Netty我也看了,也了解了Reactor的原理,但是我不可能参与Netty开发,怎么去让自己真正掌握Reactor异步模式呢?
  • 看了《高性能MySQL》,但是线上的数据库都是DBA管理的,测试环境的数据库感觉又是随便配置的,我怎么去验证这些技术呢?
  • 框架封装了DAL层,数据库的访问我们都不需要操心,我们怎么去了解分库分表实现?

诸如此类问题还有很多,我这里分享一下个人的经验,其实就是3个词:learning、trying、teaching!

Learning

这个是第一阶段,看书、google、看视频、看别人的博客都可以,但要注意一点是 “系统化”,特别是一些基础性的东西,例如JVM原理、Java编程、网络编程,HTTP协议等等,这些基础技术不能只通过google或者博客学习,我的做法一般是先完整的看完一本书全面的了解,然后再通过google、视频、博客去有针对性的查找一些有疑问的地方,或者一些技巧。

Trying

这个步骤就是解答前面提到的很多同学的疑惑的关键点,形象来说就是“自己动手丰衣足食”,也就是自己去尝试搭建一些模拟环境,自己写一些测试程序。例如:

  • Jvm垃圾回收:可以自己写一个简单的测试程序,分配内存不释放,然后调整各种jvm启动参数,再运行的过程中使用jstack、jstat等命令查看jvm的堆内存分布和垃圾回收情况。这样的程序写起来很简单,简单一点的就几行,复杂一点的也就几十行。
  • Reactor原理:自己真正去尝试写一个Reactor模式的Demo,不要以为这个很难,最简单的Reactor模式代码量(包括注释)不超过200行(可以参考Doug Lee的PPT)。自己写完后,再去看看netty怎么做,一对比理解就更加深刻了。
  • MySQL:既然有线上的配置可以参考,那可以直接让DBA将线上配置发给我们(注意去掉敏感信息),直接学习;然后自己搭建一个MySQL环境,用线上的配置启动;要知道很多同学用了很多年MySQL,但是连个简单的MySQL环境都搭不起来。
  • 框架封装了DAL层:可以自己用JDBC尝试去写一个分库分表的简单实现,然后与框架的实现进行对比,看看差异在哪里。
  • 用浏览器的工具查看HTTP缓存实现,看看不同种类的网站,不同类型的资源,具体是如何控制缓存的;也可以自己用Python写一个简单的HTTP服务器,模拟返回各种HTTP Headers来观察浏览器的反应。

当然,如果能够在实际工作中使用,效果会更好,毕竟实际的线上环境和业务复杂度不是我们写个模拟程序就能够模拟的,但这样的机会可遇不可求,大部分情况我们还真的只能靠自己模拟,然后等到真正业务要用的时候,能够信手拈来。

Teaching

一般来说,经过Learning和Trying,能掌握70%左右,但要真正掌握,我觉得一定要做到能够跟别人讲清楚。因为在讲的时候,我们既需要将一个知识点系统化,也需要考虑各种细节,这会促使我们进一步思考和学习。同时,讲出来后看或者听的人可以有不同的理解,或者有新的补充,这相当于继续完善了整个知识技能体系。

这样的例子很多,包括我自己写博客的时候经常遇到,本来我觉得自己已经掌握很全面了,但一写就发现很多点没考虑到;组内培训的时候也经常看到,有的同学写了PPT,但是讲的时候,大家一问,或者一讨论,就会发现很多点还没有讲清楚,或者有的点其实是理解错了。写PPT、讲PPT、讨论PPT,这个流程全部走一遍,基本上对一个知识点掌握就比较全面了。

谈谈面试

今晚看到了这篇文章最近面试Java后端开发的感受, 深有感触,感觉写的特别好,有感而发,所以打断写这么一篇文章,来谈谈我的经验吧。

面试是一个综合评分的过程

首先一定要认识到面试是个综合评分的过程,不一定仅仅是考察面试者的技术能力,很多时候都会有一定(甚至很大)的运气成分,这一点其实很像公司内部的
晋级答辩一样,这一点一定要深刻理解。比如碰上一个性格不错,有礼貌的面试官,通过的可能性大多数情况下都会比全程绷着脸,接N个电话的面试官好一些。
所以即便最后最差情况,面试没过,不一定代表你能力不行,可能是技能和工作要求不匹配啊,可能是运气不行啊,说不定换一个面试官就过了等等的,都是可能
的,所以首先要正确看待面试这个环节。

面试前

不可否认的是,跳槽可能(基本)是互联网行业涨薪最快的手段了。而跳槽以后的薪资,虽然会在一定程度上参考上一家公司的薪资水平,但是只要你在面试阶段
的表现能够表现出:【上一家公司在薪资方面亏了我,我其实更值钱】这种情况的话,一般情况下,新公司在初步评级,以及你在和HR的薪资谈判过程,会占有很大
的主动性,所以面试期间的表现很重要。

为了面试期间表现的好,那么一定是需要提前准备的。虽然我不太喜欢突击准备,更喜欢平时多留心留意。但是不可否认面试其实是一个异常情况出现概率很大的事情。指不定面试官会问到哪里,所以面试者首先一定要尽可能的准备充分,并且有意的在简历以及在面试过程中,留下一些【坑】来引导面试官跟随自己提前准备好
的计划来面试,这样可以减少面试过程中异常情况发生。

简历不要瞎编,项目经验不要瞎编,一定要能自圆其说,最起码写在简历上面的要不怕被人问,如果被人三两句问住那就不好了,所以尽可能的多考虑一下面试官
会问什么,同时也百度一下最近的面试题,自己也做做相关的面试题。

面试期间

面试期间要注意的东西其实很多时候是表达能力。不能茶壶里面煮饺子,倒不出来。

尽量提前演练几下,提前想好如果面试官问xxx的时候,怎么回答,这样就不至于太仓促了。另外贴一下这篇文章最近面试Java后端开发的感受中提到的一些注意点,我觉的写的挺好的:

  • 别让人感觉你只会山寨别人的代码
  • 单机版够用?适当了解些分布式
  • 别就知道增删改查,得了解性能优化
  • 围绕数据结构和性能优化准备面试题
  • 至少了解如何看日志排查问题
  • 通读一段底层代码,作为加分项
  • 把上述技能嵌入到你做过的项目里

我总结了一下主要突出下面2个方面:

大家都能做的事情,你做的比别人好

首先普通的事情能做,这是最基本的要求,活都不会干,怎么可能面试通过呢。其次一定要让别人觉的大家都能做的事情,你已经做的比别人好了。就如同上面提到的那篇文章说的:别让人感觉你只会山寨别人的代码。要能发现当前的不足和并给出解决方案。如果和大多数人的水平差不多了,在这点就没法体现出你的优势了。

举几个例子:

  • 都在用Spring + Mybatis + mysql架构,现有的系统的问题在哪,你做了哪些改进和优化
  • 线上出现问题了,你如何更快速的排查出来了呢?有没有自己的一套方法论和问题QA集
  • 现有项目代码存在安全漏洞,你是否主动发现并fix上线了呢?
  • 项目有delay了,是否主动推动了呢?

别人不知道的你知道,别人干不了的你可以

别人不知道的你知道,别人干不了的你可以,这才是你真正和HR谈价钱的资本。所以一定要注重这个表达。当然了很少出现别人完全不知道的和别人完全干不了的情况,所以很多的时候是需要扩展知识的深度和广度

比如:

  • 熟悉Nginx dubbo redis等的原理和底层
  • 比如熟悉业界的cloud native等

最后希望大家都能找到好工作,拿到好工资

Mac privoxy+Shadowsocks+iTerm2走代理

本篇文章讲述如何通过Shadowsocksprivoxy让mac的终端terminal可以翻墙

首先下载安装privoxy

1
brew install privoxy

默认就会启动privoxy,可通过以下终端命令查看privoxy进程是否启动成功:

1
ps aux | grep privoxy

然后配置privoxy,配置文件在/usr/local/etc/privoxy/config, 在文件末尾的listen-address 127.0.0.1:8118的下一行增加:
forward-socks5 / 127.0.0.1:1086 .,其中1086是shadowsocks的本地Sock5监听端口

终端执行以下两条命令即可访问privoxy:

1
2
export http_proxy='http://localhost:8118'
export https_proxy='http://localhost:8118'

一般建议将这2个命令增加到zsh.zshrc文件中去。

至于terminal中ping google失败的问题,请参考请问如何在ss代理下ping通google.com?, 其实此时已经termianl可以访问外网了,不信的话试试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
~ » curl -I -XGET https://www.google.com
HTTP/1.1 200 Connection established

HTTP/2 200
date: Thu, 14 Feb 2019 16:18:17 GMT
expires: -1
cache-control: private, max-age=0
content-type: text/html; charset=ISO-8859-1
p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
server: gws
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
set-cookie: 1P_JAR=2019-02-14-16; expires=Sat, 16-Mar-2019 16:18:17 GMT; path=/; domain=.google.com
set-cookie: NID=160=Mggo_ejHqi_HSj4vULKNmc47pjxGoKK0qcewujmXpdUm9avyK-vw09NrkF_mGZJdRVznZpvww2dlwh8C8LvhyX9KIEQFDQXtN7v0Gt9QrBaBWB1_9HN0XhXaDI0MFP2p_Y519oso-yZggi6HpZ_HynnMyih3EcdQW4nyYQQKXSo; expires=Fri, 16-Aug-2019 16:18:17 GMT; path=/; domain=.google.com; HttpOnly
alt-svc: quic=":443"; ma=2592000; v="44,43,39"
accept-ranges: none
vary: Accept-Encoding

Linux查看端口占用情况

排查问题的时候,可能需要知道这个端口目前被哪个服务占用着,在linux中,一般会用到lsofnetstat这2个命令。比如检查80端口的占用情况

lsof

1
2
3
4
5
6
[root@VM_43_49_centos ~]# sudo lsof -i:80
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 5358 root 6u IPv4 236554022 0t0 TCP *:http (LISTEN)
nginx 5358 root 7u IPv6 236554023 0t0 TCP *:http (LISTEN)
nginx 28325 nginx 6u IPv4 236554022 0t0 TCP *:http (LISTEN)
nginx 28325 nginx 7u IPv6 236554023 0t0 TCP *:http (LISTEN)

netstat

1
2
3
[root@VM_43_49_centos ~]# sudo netstat -tunlp | grep 80
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 5358/nginx: master
tcp6 0 0 :::80 :::* LISTEN 5358/nginx: master

注意在Mac上面,netstat的命令可能会出现下面的异常:

1
2
3
4
5
6
7
8
9
10
~ » netstat -tunlp | grep 80
netstat: option requires an argument -- p
Usage: netstat [-AaLlnW] [-f address_family | -p protocol]
netstat [-gilns] [-f address_family]
netstat -i | -I interface [-w wait] [-abdgRtS]
netstat -s [-s] [-f address_family | -p protocol] [-w wait]
netstat -i | -I interface -s [-f address_family | -p protocol]
netstat -m [-m]
netstat -r [-Aaln] [-f address_family]
netstat -rs [-s]

查询了一下stackoverflow,发现How to query ports are using by one process with knowing its name or pid on mac?

If you are only interested in inet ports then you can use:

1
netstat -anvf inet

Or TCP sockets:

1
netstat -anvp tcp

Or UDP sockets:

1
netstat -anvp udp

dubbo-SPI扩展(二)

本篇文章主要描述一下dubbo的扩展点中的一些基本概念和常见的一些注解

基本概念

扩展点(Extension Point)

扩展点其实就是一个Java的接口。比如dubbo中的LoadBalance接口其实就是一个扩展点

1
2
3
4
5
6
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {

@Adaptive("loadbalance")
<T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}

扩展(Extension)

扩展其实扩展点的实现类。比如以扩展点LoadBalance来说,RandomLoadBalance其实就是他的一个实现类,也是一个扩展。

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
package org.apache.dubbo.rpc.cluster.loadbalance;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
* random load balance.
*
*/
public class RandomLoadBalance extends AbstractLoadBalance {

public static final String NAME = "random";

@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
int length = invokers.size(); // Number of invokers
boolean sameWeight = true; // Every invoker has the same weight?
int firstWeight = getWeight(invokers.get(0), invocation);
int totalWeight = firstWeight; // The sum of weights
for (int i = 1; i < length; i++) {
int weight = getWeight(invokers.get(i), invocation);
totalWeight += weight; // Sum
if (sameWeight && weight != firstWeight) {
sameWeight = false;
}
}
if (totalWeight > 0 && !sameWeight) {
// If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
int offset = ThreadLocalRandom.current().nextInt(totalWeight);
// Return a invoker based on the random value.
for (int i = 0; i < length; i++) {
offset -= getWeight(invokers.get(i), invocation);
if (offset < 0) {
return invokers.get(i);
}
}
}
// If all invokers have the same weight value or totalWeight=0, return evenly.
return invokers.get(ThreadLocalRandom.current().nextInt(length));
}

}

扩展实例(Extension Instance)

扩展实例其实就是扩展点实现类的实例。比如new RandomLoadBalance()其实就可以得到LoadBalance扩展点的一个扩展实例

扩展自适应实例(Extension Adaptive Instance)

这个自适应实例需要好好理解一下,这个很重要。

常见注解

TODO

基本使用

TODO

dubbo-SPI扩展(一)

本篇文章描述一下dubbo的扩展性实现,主要有下面几个部分:

  • 什么叫可扩展性
  • 常见的扩展性的解决方案
  • java spi简介
  • 为什么dubbo不采用java spi,而是自己实现一个SPI机制呢
  • dubbo spi基本使用
  • dubbo扩展点的基本概念
  • dubbo SPI源码阅读

本篇文章也参考了很多业界资料,详见文件结尾

什么叫可扩展性

如同罗马不是一天建成的,任何系统都一定是从小系统不断发展成为大系统的,想要从一开始就把系统设计的足够完善是不可能的,相反的,我们应该关注当下的需求,然后再不断地对系统进行迭代。在代码层面,要求我们适当的对关注点进行抽象和隔离,在软件不断添加功能和特性时,依然能保持良好的结构和可维护性,同时允许第三方开发者对其功能进行扩展。在某些时候,软件设计者对扩展性的追求甚至超过了性能。

在谈到软件设计时,可扩展性一直被谈起,那到底什么才是可扩展性,什么样的框架才算有良好的可扩展性呢?它必须要做到以下两点:

  • 作为框架的维护者,在添加一个新功能时,只需要添加一些新代码,而不用大量的修改现有的代码,即符合开闭原则。
  • 作为框架的使用者,在添加一个新功能时,不需要去修改框架的源码,在自己的工程中添加代码即可。

Dubbo很好的做到了上面两点。这要得益于Dubbo的微内核+插件的机制。接下来的章节中我们会慢慢揭开Dubbo扩展机制的神秘面纱。

常见的扩展性的解决方案

  • Factory模式
  • IoC容器
  • OSGI容器

Dubbo作为一个框架,不希望强依赖其他的IoC容器,比如Spring,Guice。OSGI也是一个很重的实现,不适合Dubbo。最终Dubbo的实现参考了Java原生的SPI机制,但对其进行了一些扩展,以满足Dubbo的需求。

java spi简介

SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。

本节通过一个示例演示 Java SPI 的使用方法。首先,我们定义一个接口,名称为 Robot。

1
2
3
public interface Robot {
void sayHello();
}

接下来定义两个实现类,分别为 OptimusPrime 和 Bumblebee。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class OptimusPrime implements Robot {

@Override
public void sayHello() {
System.out.println("Hello, I am Optimus Prime.");
}
}

public class Bumblebee implements Robot {

@Override
public void sayHello() {
System.out.println("Hello, I am Bumblebee.");
}
}

接下来 META-INF/services 文件夹下创建一个文件,名称为 Robot 的全限定名 org.apache.spi.Robot。文件内容为实现类的全限定的类名,如下:

1
2
org.apache.spi.OptimusPrime
org.apache.spi.Bumblebee

做好所需的准备工作,接下来编写代码进行测试。

1
2
3
4
5
6
7
8
9
public class JavaSPITest {

@Test
public void sayHello() throws Exception {
ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);
System.out.println("Java SPI");
serviceLoader.forEach(Robot::sayHello);
}
}

程序将输出:

1
2
3
JAVA SPI
Hello, I am Optimus Prime.
Hello, I am Bumblebee.

从测试结果可以看出,我们的两个实现类被成功的加载,并输出了相应的内容。关于 Java SPI 的演示先到这里

为什么dubbo不采用java spi,而是自己实现一个SPI机制呢

Java SPI的使用很简单。也做到了基本的加载扩展点的功能。但Java SPI有以下的不足:

  • 需要遍历所有的实现,并实例化,然后我们在循环中才能找到我们需要的实现。
  • 配置文件中只是简单的列出了所有的扩展实现,而没有给他们命名。导致在程序中很难去准确的引用它们。
  • 扩展如果依赖其他的扩展,做不到自动注入和装配
  • 不提供类似于Spring的IOC和AOP功能
  • 扩展很难和其他的框架集成,比如扩展里面依赖了一个Spring bean,原生的Java SPI不支持

所以Java SPI应付一些简单的场景是可以的,但对于Dubbo,它的功能还是比较弱的。Dubbo对原生SPI机制进行了一些扩展。接下来,我们就更深入地了解下Dubbo的SPI机制。

Dubbo 改进了 JDK 标准的 SPI 的以下问题:

  • JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
  • 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。
  • 增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。

其实最核心的改进是第一个和第三个问题。

dubbo spi基本使用

我们继续使用上面的例子。由于Dubbo 并未使用 Java SPI,而是重新实现了一套功能更强的 SPI 机制。Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路径下,配置内容如下。

1
2
optimusPrime = org.apache.spi.OptimusPrime
bumblebee = org.apache.spi.Bumblebee

需要在 Robot 接口上标注 @SPI 注解。下面来演示 Dubbo SPI 的用法:

1
2
3
4
5
6
7
8
9
10
11
12
public class DubboSPITest {

@Test
public void sayHello() throws Exception {
ExtensionLoader<Robot> extensionLoader =
ExtensionLoader.getExtensionLoader(Robot.class);
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
optimusPrime.sayHello();
Robot bumblebee = extensionLoader.getExtension("bumblebee");
bumblebee.sayHello();
}
}

程序将输出

1
2
Hello, I am Optimus Prime.
Hello, I am Bumblebee.

参考资料

dubbo自定义filter

dubbo的扩展性是特别的好,本篇文章通过例子来说明如何自定义dubbo的filter。为了文章完整性,贴一下官网对于filter的一些说明。

扩展说明

服务提供方和服务消费方调用过程拦截,Dubbo 本身的大多功能均基于此扩展点实现,每次远程方法执行,该拦截都会被执行,请注意对性能的影响。

约定:

  • 用户自定义 filter 默认在内置 filter 之后。
  • 特殊值 default,表示缺省扩展点插入的位置。比如:filter="xxx,default,yyy",表示 xxx` 在缺省 filter 之前,yyy 在缺省 filter 之后。
  • 特殊符号 -,表示剔除。比如:filter="-foo1",剔除添加缺省扩展点 foo1。比如:filter="-default",剔除添加所有缺省扩展点。
  • provider 和 service 同时配置的 filter 时,累加所有 filter,而不是覆盖。比如:<dubbo:provider filter="xxx,yyy"/><dubbo:service filter="aaa,bbb" />,则 xxx,yyy,aaa,bbb 均会生效。如果要覆盖,需配置:<dubbo:service filter="-xxx,-yyy,aaa,bbb" />

扩展接口

org.apache.dubbo.rpc.Filter

扩展配置

1
2
3
4
5
6
7
8
<!-- 消费方调用过程拦截 -->
<dubbo:reference filter="xxx,yyy" />
<!-- 消费方调用过程缺省拦截器,将拦截所有reference -->
<dubbo:consumer filter="xxx,yyy"/>
<!-- 提供方调用过程拦截 -->
<dubbo:service filter="xxx,yyy" />
<!-- 提供方调用过程缺省拦截器,将拦截所有service -->
<dubbo:provider filter="xxx,yyy"/>

已知扩展

  • org.apache.dubbo.rpc.filter.EchoFilter
  • org.apache.dubbo.rpc.filter.GenericFilter
  • org.apache.dubbo.rpc.filter.GenericImplFilter
  • org.apache.dubbo.rpc.filter.TokenFilter
  • org.apache.dubbo.rpc.filter.AccessLogFilter
  • org.apache.dubbo.rpc.filter.CountFilter
  • org.apache.dubbo.rpc.filter.ActiveLimitFilter
  • org.apache.dubbo.rpc.filter.ClassLoaderFilter
  • org.apache.dubbo.rpc.filter.ContextFilter
  • org.apache.dubbo.rpc.filter.ConsumerContextFilter
  • org.apache.dubbo.rpc.filter.ExceptionFilter
  • org.apache.dubbo.rpc.filter.ExecuteLimitFilter
  • org.apache.dubbo.rpc.filter.DeprecatedFilter

官网扩展示例

Maven 项目结构:

1
2
3
4
5
6
7
8
9
10
src
|-main
|-java
|-com
|-xxx
|-XxxFilter.java (实现Filter接口)
|-resources
|-META-INF
|-dubbo
|-org.apache.dubbo.rpc.Filter (纯文本文件,内容为:xxx=com.xxx.XxxFilter)

XxxFilter.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.xxx;

import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;

public class XxxFilter implements Filter {
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
// before filter ...
Result result = invoker.invoke(invocation);
// after filter ...
return result;
}
}

然后配置dubbo spi:
META-INF/dubbo/org.apache.dubbo.rpc.Filter:
xxx=com.xxx.XxxFilter

实际例子

比如希望dubbo consumer在请求dubbo provider的时候,自动携带自己的jar的版本信息,那么就可以写一个JarVersionAttachFilter

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
package com.xxxx.xx.xx.rpc.dubbo.filter;

import java.util.Map;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.Filter;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcException;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.xxx.common.util.JarUtils;
import com.xxx.common.util.Safes;
import com.xxx.xxx.xxx.rpc.dubbo.DubboConstants;

import org.springframework.util.StringUtils;

@Activate(group = {Constants.CONSUMER}, order = -1)
public class JarVersionAttachFilter implements Filter {

private LoadingCache<Class<?>, String> versionMapping = CacheBuilder.newBuilder().maximumSize(1024).build(new CacheLoader<Class<?>, String>() {
@Override
public String load(Class<?> key) throws Exception {
return Safes.of(JarUtils.getVersion(key, null));
}
});

@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
Map<String, String> attachments = invocation.getAttachments();
String version = versionMapping.getUnchecked(invoker.getInterface());
if (StringUtils.hasText(version)) {
attachments.put(DubboConstants.JAR_VERSION_NAME, version);
}
return invoker.invoke(invocation);
}
}

然后在dubbo的spi文件中写入:jarattach=com.xxxx.xx.xx.rpc.dubbo.filter.JarVersionAttachFilter就好了。这样当consumer发起请求的时候,会自动携带自己的jar的版本信息

参考资料

terminal快捷键

终端跳转解说图

bash-shortcut.jpg

常用快捷键

编辑

  • Ctrl + a – 跳到行首
  • Ctrl + e – 跳到行尾
  • Ctrl + k – 删除当前光标至行尾内容
  • Ctrl + u – 删除当前光标至行首内容
  • Ctrl + w – 删除当前光标至词首内容
  • Ctrl + y – 将剪切的内容粘贴在光标后
  • Ctrl + xx – 在行首和当前光标处(来回)移动
  • Alt + b – 跳到词首
  • Alt + f – 跳到词尾
  • Alt + d – 删除自光标处起的单词内容
  • Alt + c – 大写光标处的字符(注:该条内容与原文不同)
  • Alt + u – 大写自光标处起的单词内容
  • Alt + l – 小写自光标处起的单词内容
  • Alt + t – 将光标处单词与上一个词交换
  • Ctrl + f – 向前移动一个字符(相当于按向左箭头)
  • Ctrl + b – 向后移动一个字符(相当于按向右箭头)
  • Ctrl + d – 删除光标后一个字符(相当于按Delete)
  • Ctrl + h – 删除光标前一个字符(相当于按后退键)
  • Ctrl + t – 交换光标处的两个字符

搜索

  • Ctrl + r – 反向搜索历史命令
  • Ctrl + g – 退出历史搜索模式(相当于按Esc)
  • Ctrl + p – 上一个历史命令(相当于按向上箭头)
  • Ctrl + n – 下一个历史命令(相当于按向下箭头)
  • Alt + . – 使用上一个命令的最后一个单词

控制

  • Ctrl + l – 清屏
  • Ctrl + s – 终止输出到屏幕(对长时间运行并打印详细信息的命令)
  • Ctrl + q – 允许输出到屏幕(如果之前用过终止输出命令)
  • Ctrl + c – 终止命令
  • Ctrl + z – 中断命令

Bang(即感叹号)

  • !! – 执行上一条命令
  • !blah –执行最近运行过的以blah开头的命令
  • !blah:p – 打印!blah要执行的命令(并将其作为最后一条命令加入到命令历史中)
  • !$ – 上一条命令的最后一个单词 (等同于Alt + .)
  • !$:p – 打印!$指代的单词
  • !* – 上一条命令除最后一个词的部分
  • !:p – 打印!指代部分

参考资料

MAT对象之间的差异浅堆和保留堆

原文地址:SHALLOW HEAP, RETAINED HEAP

更贴切的标题应该是Difference between Eclipse MAT objects Shallow Heap and Retained Heap

Eclipse MAT (Memory Analyzer Tool) is a powerful tool to analyze heap dumps. It comes quite handy when you are trying to debug memory related problems. In Eclipse MAT two types of object sizes are reported:

  • Shallow Heap
  • Retained Heap

In this article lets study the difference between them. Let’s study how are they calculated?

Shallow-heap-1.png

It’s easier to learn new concepts through example. Let’s say your application’s has object model as shown in Fig #1:

  • Object A is holding reference to objects B and C.
  • Object B is holding reference to objects D and E.
  • Object C is holding reference to objects F and G.

Let’s say each object occupies 10 bytes of memory. Now with this context let’s begin our study.

Shallow Heap size

Shallow heap of an object is its size in the memory. Since in our example each object occupies 10 bytes, shallow heap size of each object is 10 bytes. Very simple.

Retained Heap size of B

From the Fig #1 you can notice that object B is holding reference to objects D and E. So, if object B is garbage collected from memory, there will be no more active references to object D and E. It means D & E can also be garbage collected. Retained heap is the amount of memory that will be freed when the particular object is garbage collected. Thus, retained heap size of B is:

= B’s shallow heap size + D’s shallow heap size + E’s shallow heap size

= 10 bytes + 10 bytes + 10 bytes

= 30 bytes

Thus, retained heap size of B is 30 bytes.

Retained Heap size of C

Object C is holding reference to objects F and G. So, if object C is garbage collected from memory, there will be no more references to object F & G. It means F & G can also be garbage collected. Thus, retained heap size of C is:

= C’s shallow heap size + F’s shallow heap size + G’s shallow heap size

= 10 bytes + 10 bytes + 10 bytes

= 30 bytes

Thus, retained heap size of C is 30 bytes as well

shallow-heap-2-1.png

Retained Heap size of A

Object A is holding reference to objects B and C, which in turn are holding references to objects D, E, F, G. Thus, if object A is garbage collected from memory, there will be no more reference to object B, C, D, E, F and G. With this understanding let’s do retained heap size calculation of A.

Thus, retained heap size of A is:

= A’s shallow heap size + B’s shallow heap size + C’s shallow heap size + D’s shallow heap size + E’s shallow heap size + F’s shallow heap size + G’s shallow heap size

= 10 bytes + 10 bytes + 10 bytes + 10 bytes + 10 bytes + 10 bytes + 10 bytes

= 70 bytes

Thus, retained heap size of A is 70 bytes.

Retained heap size of D, E, F and G

Retained heap size of D is 10 bytes only i.e. their shallow size only. Because D don’t hold any active reference to any other objects. Thus, if D gets garbage collected no other objects will be removed from memory. As per the same explanation objects E, F and G’s retained heap size are also 10 bytes only.

Let’s make our study more interesting

Now let’s make our study little bit more interesting, so that you will gain thorough understanding of shallow heap and retained heap size. Let’s have object H starts to hold reference to B in the example. Note object B is already referenced by object A. Now two guys A and H are holding references to object B. In this circumstance lets study what will happen to our retained heap calculation.

Shallow-heap-3-1.png

In this circumstance retained heap size of object A will go down to 40 bytes. Surprising? Puzzling? 🙂 continue reading on. If object A gets garbage collected, then there will be no more reference to objects C, F and G only. Thus, only objects C, F and G will be garbage collected. On the other hand, objects B, D and E will continue to live in memory, because H is holding active reference to B. Thus B, D and E will not be removed from memory even when A gets garbage collected.

Thus, retained heap size of A is:

= A’s shallow heap size + C’s shallow heap size + F’s shallow heap size + G’s shallow heap size

= 10 bytes + 10 bytes + 10 bytes + 10 bytes

= 40 bytes.

Thus, retained heap size of A will become 40 bytes. All other objects retained heap size will remain undisturbed, because there is no change in their references.

Hope this article helped to clarify Shallow heap size and Retained heap size calculation in Eclipse MAT. You might also consider exploring HeapHero – another powerful heap dump analysis tool, which shows the amount of memory wasted due to inefficient programming practices such as duplication of objects, overallocation and underutilization of data structures, suboptimal data type definitions,….

ShadowsocksX-NG 1.8.2设置kcptun

在自己的mac上升级了一下ShadowsocksX-NG1.8.2版本。发现无法使用了,检查发现新版本中没有设置kcptun的地方了,

新版本在【服务器设置】的地方

  • 插件输入框中写kcptun
  • 插件选项写:key=【此处为自己的key】;crypt=aes;mode=fast2;mtu=1350;sndwnd=2048;rcvwnd=2048;datashard=10;parityshard=3;dscp=0

检查发现kcptun会自动打开

1
2
~ » ps -ef | grep kcp
501 31273 31272 0 5:52PM ?? 0:00.37 plugins/kcptun

参考资料

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×