Hadoop阅读笔记(七)——代理模式

  关于Hadoop已经小记了六篇,《Hadoop实战》也已经翻完7章。仔细想想,这么好的一个框架,不能只是流于应用层面,跑跑数据排序、单表链接等,想得其精髓,还需深入内部。

  按照《Hadoop阅读笔记(五)——重返Hadoop目录结构》中介绍的hadoop目录结构,前面已经介绍了MapReduce的内部运行机制,今天准备入手Hadoop RPC,它是hadoop一种通信机制。

RPCRemote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

  不同于其他RPC产品,Hadoop有属于自己RPC组件,依赖于《Hadoop阅读笔记(六)——洞悉Hadoop序列化机制Writable》中介绍的Hadoop Writable类型的支持。Hadoop Writable接口要求每个实现类都得确保将本类的对象正确序列化(writeObject)与反序列化(readObject)。因此,Hadoop RPC使用Java动态代理与反射实现对象调用方式,客户端到服务器数据的序列化与反序列化由Hadoop框架或用户自己来实现,也就是数据组装是定制的。 

显然我们没法上来就直接剖析RPC,因为这里有一些绕不开的东西要先介绍:Writable序列化反序列化(已在第六篇介绍)、动态代理。所以,这里先介绍什么是动态代理。

  动态代理是代理模式的一种,还有一种就是静态代理。

代理模式为其他对象提供一种代理,并以控制对这个对象的访问。而对一个对象进行访问控制的一个原因是为了只有在我们确实需要这个对象时才对它进行创建和初始化。它是给某一个对象提供一个替代者(占位者),使之在client对象和subject对象之间编码更有效率。代理可以提供延迟实例化(lazy instantiation),控制访问, 等等,包括只在调用中传递。

静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。  动态代理:在程序运行时,运用反射机制动态创建而成。 

1.静态代理

  Person.java(接口)

1 package hadoop.jackie.dao;
2 
3 public interface Person {
4     
5     public void eat();//人具有吃饭的行为
6     
7     public void sleep();//人具有睡觉的行为
8 
9 }

  PersonImpl.java(实现类,也就是这里的委托类)

 1 package hadoop.jackie.dao.impl;
 2 
 3 import hadoop.jackie.dao.Person;
 4 
 5 public class PersonImpl implements Person {
 6 
 7     @Override
 8     public void eat() {
 9         System.out.println("我是吃货");
10 
11     }
12 
13     @Override
14     public void sleep() {
15         System.out.println("我是猪,别管我");
16 
17     }
18 
19 }

  PersonProxy.java(代理类)

 1 package hadoop.jackie.dao.impl;
 2 
 3 import hadoop.jackie.dao.Person;
 4 
 5 public class PersonProxy implements Person {
 6 
 7     private PersonImpl personImpl;
 8     
 9     public PersonProxy(PersonImpl personImpl){
10         this.personImpl = personImpl;
11     }
12     
13     @Override
14     public void eat() {
15         System.out.println("吃饭前");
16         personImpl.eat();//调用委托类PersonImpl的吃饭方法
17         System.out.println("吃饭后");
18 
19     }
20 
21     @Override
22     public void sleep() {
23         System.out.println("睡觉前");
24         personImpl.sleep();//调用委托类PersonImpl的睡觉方法
25         System.out.println("睡觉后");
26 
27     }
28 
29 }

  PersonTest.java

 1 package hadoop.jackie.dao.test;
 2 
 3 import hadoop.jackie.dao.impl.PersonImpl;
 4 import hadoop.jackie.dao.impl.PersonProxy;
 5 
 6 public class PersonTest {
 7 
 8     /**
 9      * @param args
10      */
11     public static void main(String[] args) {
12         
13         PersonImpl personImpl = new PersonImpl();
14         PersonProxy personProxy = new PersonProxy(personImpl);
15         personProxy.eat();
16         personProxy.sleep();
17         
18     }
19 
20 }

 PersonTest.java运行结果为:

  从运行结果可以发现,类PersonProxy具备了PersonImpl的eat()和sleep()的方法,实现了对于类PersonImpl的委托。

  但是为什么称该模式为静态代理模式,从代码中可以发现,在代理类PersonProxy中需要手动配置委托类PersonImpl

  该模式效率较低,适用性较弱,代理类和委托类的耦合性较高。如果需要为多个委托类做代理,则会出现很多的代理类,从而有可能产生冗余代码。

  能够克服以上缺点的模式就是动态代理。

  2.动态代理

  介绍动态代理前,需要先了解相关的一个接口InvocationHandler和一个类Proxy

(1)interface InvocationHandler

  该接口只有一个方法:Object invoke(Object proxy, Method method, Object[] args) 

  该方法用来处理方法的调用,实现类也有同样的意义;与代理类对象相关联则表示,    负责处理代理类应该有的动作,把所有的方法请求分发到invoke这个方法上。 (具体可以结合后面的代码理解)

(2)class Proxy

Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)    该方法用于产生代理对象,它与代理对象关联,当请求分发到代理对象后,会自动执行h.invoke(...)方法, 

  Person.java(接口)

1 package hadoop.jackie.dynamic.dao;
2 
3 public interface Person {
4     
5     public void addGirlFriend();//小伙子具备找女朋友的方法
6 
7 }

  PersonImpl.java(实现类,即委托类)

 1 package hadoop.jackie.dynamic.dao.impl;
 2 
 3 import hadoop.jackie.dynamic.dao.Person;
 4 
 5 public class PersonImpl implements Person {
 6 
 7     @Override
 8     public void addGirlFriend() {
 9         
10         System.out.println("吃饱睡好,该找个女盆友啦");
11 
12     }
13 
14 }

  PersonProxy.java(代理类)

 1 package hadoop.jackie.dynamic.dao.impl;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6 
 7 public class PersonProxy implements InvocationHandler {
 8     
 9     private Object targetGirl;
10     
11     public Object bind(Object targetGirl){
12         this.targetGirl = targetGirl;
13         return Proxy.newProxyInstance(targetGirl.getClass().getClassLoader(), targetGirl.getClass().getInterfaces(), this);
14     }
15 
16     @Override
17     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
18         Object result = null;
19         System.out.println("找女盆友前");
20         result = method.invoke(targetGirl, args);
21         System.out.println("找女盆友后");
22         return result;
23     }
24 
25 }

  PersonTest.java

 1 package hadoop.jackie.dynamic.dao;
 2 
 3 import hadoop.jackie.dynamic.dao.impl.PersonImpl;
 4 import hadoop.jackie.dynamic.dao.impl.PersonProxy;
 5 
 6 public class PersonTest {
 7 
 8     /**
 9      * @param args
10      */
11     public static void main(String[] args) {
12         PersonProxy proxy = new PersonProxy();
13         Person personProxy = (Person)proxy.bind(new PersonImpl());
14         personProxy.addGirlFriend();
15 
16     }
17 
18 }

  运行结果为:

  通过以上两种模式的介绍以及展示的代码,需要注意一下几点:

1.如何从生活场景理解代理模式?

  就这里的动态代理的代码逻辑来看:某小伙(Person.java)已经快奔三,急缺女友一枚(PersonImpl.java),但是由于种种原因如长得相貌出众、海拔太高、人品太好等,已经到了自己完全搞不定的时候了,而且这时家里催的又紧,怎么办?找代理啊,谁?媒婆啊(PersonProxy.java)。媒婆手头资源丰富,轻轻松松帮你搞定,还能按照你的需求进行定制比如瓜子壳脸还是猪腰子脸。这就是代理!

2.动态代理中的invoke是什么鬼?

  在请求代理类的方法时,比如PersonTest.java:

personProxy.addGirlFriend();

  这个请求会被转到执行与代理类关联InvocationHandler 的invoke方法,然后通过

result = method.invoke(targetGirl, args);

  利用java的反射机制调用到方法addGirlFriend()。

  3.代理模式是无懈可击的?

  JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理。

  在攻克hadoop源码中rpc模块的道路上,又前进了一个关卡,多走了几步^_^

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏nnngu

百度搜索 “Java面试题” 前200页(面试必看)

本文中的题目来源于网上的一篇文章《百度搜索 “Java面试题” 前200页》,但该文章里面只有题目,没有答案。因此,我整理了一些答案发布于本文。本文整理答案的原...

80111
来自专栏酷玩时刻

微信扫码支付(模式一)微信扫码支付(模式一)

官方文档地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4

1963
来自专栏aoho求索

Java SPI机制详解

3242
来自专栏猿人谷

堆和栈的区别

堆和栈的区别 一般认为在c中分为这几个存储区 1栈 - 有编译器自动分配释放 2堆 - 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收...

1975
来自专栏猿人谷

volatile

   当一个对象的值可能会在编译器的控制或监测之外被改变时,例如一个被系统时钟更新的变量,那么该对象应该声明成volatile。因此编译器执行的某些例行优化行为...

2117
来自专栏程序员互动联盟

【C语言基础】编码规范

1. 基本要求 1.1 程序结构清析,简单易懂,单个函数的程序行数不得超过100行。 1.2 打算干什么,要简单,直接了当,代码精简,避免垃圾程序。 1....

3325
来自专栏图形学与OpenGL

实验1 C++函数

int main() { int total, n; cout << "Welcome to NIM. Pick a starting tot...

1102
来自专栏黑泽君的专栏

(正)斜杠 与 反斜杠

在 Windows 系统中,正斜杠/ 表示除法,用来进行整除运算;反斜杠\ 用来表示目录,例如:E:\学习资料\java\传智播客_2015年Java基础视频-...

2722
来自专栏草根专栏

用ASP.NET Core 2.1 建立规范的 REST API -- 翻页/排序/过滤等

本文所需的一些预备知识可以看这里: http://www.cnblogs.com/cgzl/p/9010978.html 和 http://www.cnblog...

1511
来自专栏Java帮帮-微信公众号-技术文章全总结

Java中的24种设计模式与7大原则

一、创建型模式 1、抽象工厂模式(Abstract factory pattern): 提供一个接口, 用于创建相关或依赖对象的家族, 而不需要指定具体类. 2...

3627

扫码关注云+社区

领取腾讯云代金券