码农戏码

新生代农民工的自我修养


  • 首页

  • 归档

  • 标签

  • 关于

  • 在线工具

  • 搜索

motan扩展机制

发表于 2017-01-08 | 分类于 源码解读
字数统计: 1.3k 字数 | 阅读时长 ≈ 5 分钟

motan第二篇,本来想写motan的rpc调用过程的,但项目中的需求需要对motan进行扩展,所以就先记录下

引导

在写一个框架或者在项目中提供一些底层服务时,都会有这种情况,会有一些默认的实现,但你知道这些默认实现只是很满足很基本的自身需求,开发人员可能会扩展,想自定义一个实现

处理方式

  1. 提供一个设置实现类的setter,开发者在初始化时调用一下
  2. 提供配置入口,给个key,配置上自定义类名
  3. 类似slf4j一样,提供桥接类

SPI

motan使用了spi的方式

SPI(Service Provider Interface),服务提供接口;也是一种服务发现机制

1
2
3
4
5
6
7
8
9
系统里抽象的各个模块,往往有很多不同的实现方案,
比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,
我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码
。一旦代码里涉及具体的实现类,就违反了可拔插的原则,
如果需要替换一种实现,就需要修改代码。

为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。
java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。
有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。

在JDK6之前,你可能会自己定义一种服务提供的约定,在JDK6之后,java也提供了标准约定

1
2
扩展者在jar包的META-INF/services/目录下放置与接口同名的文本文件 
内容为接口实现类名,多个实现类名用换行符分隔

java.util.ServiceLoader类来实现从配置文件中加载子类或者接口的实现类

SPI与API的区别

1
2
3
4
5
6
7
8
9
10
11
12
What is the difference between Service Provider Interface (SPI) and Application Programming Interface (API)?
More specifically, for Java libraries, what makes them an API and/or SPI?
the API is the description of classes/interfaces/methods/...
that you call and use to achieve a goal
the SPI is the description of classes/interfaces/methods/...
that you extend and implement to achieve a goal
Put differently, the API tells you what a specific class/method does for you and the SPI tells you what you must do to conform.
Sometimes SPI and API overlap.
For example in JDBC the Driver class is part of the SPI:
If you simply want to use JDBC, you don't need to use it directly, but everyone who implements a JDBC driver must implement that class.
The Connection interface on the other hand is both SPI and API:
You use it routinely when you use a JDBC driver and it needs to be implemented by the developer of the JDBC driver。

JDK spi

使用jdk自带的ServiceLoader写一个示例:

一个接口

1
2
3
4
public interface Spi {

public void provide();
}

一个实现类

1
2
3
4
5
6
7
public class DefaultSpi implements Spi{

@Override
public void provide() {
System.out.println("默认spi实现");
}
}

入口类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 一个spi的demo
*
*/
public class SpiDemoMain
{
public static void main( String[] args )
{
ServiceLoader<Spi> spiServiceLoader = ServiceLoader.load(Spi.class);

Iterator<Spi> spiIterator = spiServiceLoader.iterator();

while ( spiIterator.hasNext()) {
spiIterator.next().provide();
}
}
}

在META-INF文件夹里面新建个services文件夹,在services文件夹里面新建一个
com.jjk.spi.Spi文件

完整的代码可从https://github.com/zhuxingsheng/spidemo下载

ServiceLoader源码解析

原理很简单,一个类实现这个SPI机制

它的本质,也就是从某个地方加载服务实现类,文件名是服务接口名

定义存放文件的地方

1
private static final String PREFIX = "META-INF/services/";

类内部使用了延迟加载LazyIterator,在使用到了实现类时,才去实例化。

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
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {

c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service,
"Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
fail(service,
"Provider " + cn + " not a subtype");
}
try {
// 调用next方法时,才实例化
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}

motan spi

motan的spi,跟java spi差不多,但做了一些加强

先看官方文档

  1. 实现SPI扩展点接口
  1. 实现类增加注解
    @Spi(scope = Scope.SINGLETON) //扩展加载形式,单例或多例
    @SpiMeta(name = “motan”) //name表示扩展点的名称,根据name加载对应扩展
    @Activation(sequence = 100) //同类型扩展生效顺序,部分扩展点支持。非必填
    增加SPI实现声明 ${classpath}/MATA-INF/services/${SPI interface fullname}文件中添加对应SPI接口实现类全名。 可参照motan-core模块/MATA-INF/services/下的配置

主要类就是com.weibo.api.motan.core.extension.ExtensionLoader
代码在https://github.com/zhuxingsheng/motan/blob/master/motan-core/src/main/java/com/weibo/api/motan/core/extension/ExtensionLoader.java#L100-99
其实代码很简单,不需要额外的解读,都能看明白

还有几个注解类

@Spi 指定类生命周期

@SpiMeta 给类一个别名,方便配置时使用

motan入门

发表于 2017-01-04 | 分类于 源码解读
字数统计: 324 字数 | 阅读时长 ≈ 1 分钟

motan weibo的RPC框架,Motan是一套高性能、易于使用的分布式远程服务调用(RPC)框架

这次在项目中引入了此框架。

在使用中学习。研读下源码。记录下使用学习过程。

RPC原理

image

什么是Stub?

Stub是一段代码,用来转换RPC过程中传递的参数。处理内容包括不同OS之间的大小端问题。另外,Client端一般叫Stub,Server端一般叫Skeleton。

生产方式:1)手动生成,比较麻烦;2)自动生成,使用IDL(InterfaceDescriptionLanguate),定义C/S的接口。

交互机制标准:一般采用IDL,生成IDL的工具 RPCGEN()。

为什么引入motan

引入它,主要是因为它能满足项目需求;

  1. 它比较牛,支撑了整个weibo
  2. 集成了spring,基本无侵入
  3. 具有集群功能,支持zookeeper

还有别的优点了,官方文档写得很漂亮,但这几点已经足以吸引我

motan demo

直接官方示例吧,也可以

fork me

我会在阅读源码的过程中,加上注释

The clean coder 读书笔记

发表于 2016-12-11 | 分类于 读书
字数统计: 1.9k 字数 | 阅读时长 ≈ 6 分钟

这本书不厚,但都是干货。

在前言中,就写了本书的目的:

  1. 什么是软件专业人士
  2. 软件专业人士如何行事
  3. 软件专业人士如何处理冲突,应对很紧的工期,如何和不讲道理的管理人员打交道
  4. 软件专业人士何时应该说”不”?怎么说?
  5. 软件专业人士如何应对压力?

这些问题,也正是一名软件开发人员需要去了解的问题。

所谓专业人士:就是能对自己犯下错误负责的人,哪怕那些错误实际上在所难免

专业人士有哪些特性呢?

  1. 不担当,比如代码测试的全覆盖
  2. 不随便承诺
  3. 不会阻止别人修改自己的代码
  4. 满足雇主的需求
  5. 需要专门训练提升自己的技能

摘录一些干货,再对比一下当下的开发环境,那是相当贴切

为什么大多数开发人员不敢不断修改他的代码呢?因为他们害怕会改坏代码!为什么会有这样的担心呢?因为他们没有做过测试

这个对于现在的开发环境太真实了,在一个项目上线后,其实在开发中后期,就已经出现这样的情况

  1. 一个方法,代码很乱,逻辑看着有点不通,出于对代码整洁的追求,重构起来,结果发布之后,出现了bug,原先功能已经不能正常运作了,而这个bug,得等很多才会被发现,因为这个功不在本期开发版本计划中,过了测试期了
  2. 一个方法里面有行代码,看似无用,删除了吧,结果又出了一个隐形的bug

正是以上问题,所以项目中的人越来越不敢动代码,不能删,不能改,只能加,有新需求,就只能加点代码,只要能正常工作,就不要管别的代码了,管了就得挂。

这就是完全没有测试的问题。

要用这些自动化单元测试去测多少代码呢?还要说吗?全部!全部都要测

这一种方法,就是TDD,来规避这个问题。

作者是TDD的践行者

如果连所有代码是否都可以正常运行都不知道,还算什么专业人士?

如果说多年前,很多人对TDD有疑问,当然现在还是有人有疑问,有疑问的原因,就是你还没有践行TDD。

对于TDD:

  1. 此事已有定论
  2. 争论已经结束
  3. GOTO是有害的
  4. TDD确实可行

有此人事先写代码,事后再写测试,这种方法相对于TDD来讲呢?

如果很仔细地来看,也许后写测试还可以达到较高的覆盖率真。但是事后写的测试只是一种防守。而先行编写的测试则是进攻,事后编写测试的作者已经受制于已有代码,他已经知道问题是如何解决的。与采用测试先行的方式编写的测试代码比起来,后写的测试在深度和捕获错误的灵敏度方面要逊色很多

一个开发方法,业界已经得到普遍认可,但国内有多少公司实施了?至少我所经历的公司都没有。
聪明的开发者们宁可在出了问题花大量的精力去一步步调试,也不愿花一点点的时间去写单元测试,执行TDD。

曾经在读云风博客时,发现一段有共鸣的话

反感围绕着调试的开发方式,也是不断的在测试,试错,纠正的循环中奔波,好的程序员应该努力的在编写的过程中,在头脑中排错,在预感到坏味道时,就赶紧重写,而坏味道就是代码陷入了复杂度太高的境地,无法一眼看潜在的问题。对付复杂度最好的武器是简化代码

在遇到bug时,应该仔细浏览代码,设想各种出错的可能。而不是将错误代码运行起来,查看运行中的状态变化

这段话中不赞成的解决问题方式,其实是很多开发人员普遍具备的。

作者在对编码也有相似的体会

我发现,要精熟掌握每项技艺,关键都是要具备“信心”和“出错感知”能力。

我想作者的“出错感知” 与 云风的“预感到坏味道” 是一样的

作者给了一套了编码规则与原则

  • 做好准备

    1. 快速响应,做出第一版本
    2. 深入需求分析
    3. 插件式,低耦合
    4. 可读性,可维护性

    当感到疲劳,焦虑的时候,千万不要写代码

  • 流态区

    避免进入流态区

    这种意识状态并非真的极为高效,也绝非毫无错误。这其实只是一种“浅层冥想”状态,在这种状态下,为了追求所谓的速度,理性思考的能力会下降

其实这不是我第一次听到的建议,不要进入流态区,只是一种精神酸爽,对于实质工作没有意义,因为进入了这种状态会出现只见树木不见森林,当时流线性的写出代码,事后经常会发现偏离了方向,不得不重写。

  • 中断

    作者给出了两种方法

    1. 结对编程,让搭档维护住中断的上下文
    2. TDD,失败的测试能帮你维护住编码进度的上下文
  • 阻塞

    当情绪低落,焦虑,恐惧时,最好的方法:找一个搭档结对编程

作者是一个TDD以及结对编程的提倡与践行人,在书可你可以看到很多次作者对这两种方法的推荐,可惜国内很少!

书中有两个章节与近期订阅的李笑来老师文章有重合

  1. 练习

任何事情,只要想做得快,都离不开练习。
两个武者搏斗,每个人都必须能够迅速识别出对方的意图,并且百分之一秒内正确应对。在搏斗中,你不可能有充足的时间来研究架势,思考如何应对,这时候你只能依靠身体的反应。实际上,真正做出反应的是你的身体,大脑是在更高级的层面上思考

也就是李笑来老师提的刻意练习,如果程序员不去刻意练习,写各种demo,那么我想水平永远是hello world的水平了

  1. 注意力

    注意力是稀缺的资源,它类似魔力点数,如果你用光了自己的注意力点数,必须花一个小时或更多的时间都不需要注意的事情,来补充它。

    这是时间管理时提到的,李笑来也讲,注意力是你最宝贵的财富

    时间在本质上不属于你,你只能试着与它做朋友,让它为你所用。你的注意力才是你所拥有最重要、最宝贵的资源。你可以自己作主,要把它放在“成长”上。

1…1213
朱兴生

朱兴生

123 日志
3 分类
48 标签
© 2016 — 2022 朱兴生 | Site words total count: 296.3k
由 Hexo 强力驱动
|
主题 — NexT.Mist v5.1.4
沪ICP备18040647号-1