Hadoop-0.20.0源代码分析(12)

1559人阅读 评论(2) 收藏 举报

在org.apache.hadoop.ipc包中,Server类是一个抽象类,抽象了IPC模型中Server端的基本行为。下面对RPC类进行阅读分析。

RPC类给出了一个简单的RPC机制,它的协议是基于一个Java接口,协议界定,所有的参数和返回类型必须是下面之一:

1、一个基本类型:boolean、byte、char、short、int、long、float、double,或void;

2、String;

3、org.apache.hadoop.io.Writable;

4、上述类型的数组。

在RPC类中,RPC.Server类继承自org.apache.hadoop.ipc.Server抽象类,实现了一个RPC服务器。在分析RPC.Server类之前,还是先看一下与它相关的几个内部类的实现。

  • RPC.Invocation内部类

该内部类定义了方法的调用,包括方法的名称和参数,它实现了Writable,Configurable接口。该内部类定义的属性如下所示:

  1. private String methodName; // 方法名   
  2. private Class[] parameterClasses; // 参数类型集合   
  3. private Object[] parameters; // 参数值   
  4. private Configuration conf; // 配置类实例  

因为该内部类实现了Writable接口,所以必须实现该接口定义的两个方法,如下所示:

  1. public void readFields(DataInput in) throws IOException {  
  2.   methodName = UTF8.readString(in); // 读取方法名   
  3.   parameters = new Object[in.readInt()]; // 读取调用方法的参数值   
  4.   parameterClasses = new Class[parameters.length]; // 参数类型   
  5.   ObjectWritable objectWritable = new ObjectWritable();   
  6.   for (int i = 0; i < parameters.length; i++) {  
  7.     parameters[i] = ObjectWritable.readObject(in, objectWritable, this.conf); // 读取每个调用参数值   
  8.     parameterClasses[i] = objectWritable.getDeclaredClass(); // 读取每个参数类型   
  9.   }  
  10. }  
  11.   
  12. public void write(DataOutput out) throws IOException {  
  13.   UTF8.writeString(out, methodName); // 向输出流out中写入方法名   
  14.   out.writeInt(parameterClasses.length);  // 写入方法参数类型个数   
  15.   for (int i = 0; i < parameterClasses.length; i++) {  
  16.     ObjectWritable.writeObject(out, parameters[i], parameterClasses[i], conf);  
  17.   }  
  18. }  

因此,RPC.Invocation类对象是可序列化的。

  • RPC.ClientCache内部类

该内部类定义了一个缓存Map:

  1. private Map<SocketFactory, Client> clients = new HashMap<SocketFactory, Client>();  

通过客户端org.apache.hadoop.ipc.Client的SocketFactory可以快速取出对应的Client实例。

该内部类中实现了获取一个Client的方法:

  1. /**  
  2.  * 从缓存Map中取出一个IPC Client实例,如果缓存够中不存在,就创建一个兵加入到缓存Map中 
  3.  */  
  4. private synchronized Client getClient(Configuration conf, SocketFactory factory) {  
  5.   Client client = clients.get(factory);  
  6.   if (client == null) {  
  7.     client = new Client(ObjectWritable.class, conf, factory); // 通过反射实例化一个ObjectWritable对象,构造Client实例   
  8.     clients.put(factory, client); // 加入缓存Map   
  9.   } else {  
  10.     client.incCount(); // 增加客户端client实例的引用计数   
  11.   }  
  12.   return client;  
  13. }  

终止一个RPC客户端连接,实现方法为:

  1. private void stopClient(Client client) {  
  2.   synchronized (this) {  
  3.     client.decCount(); // 该client实例的引用计数减1   
  4.     if (client.isZeroReference()) { // 如果client实例的引用计数此时为0   
  5.       clients.remove(client.getSocketFactory()); // 从缓存中删除   
  6.     }  
  7.   }  
  8.   if (client.isZeroReference()) { // 如果client实例引用计数为0,需要关闭   
  9.     client.stop(); // 停止所有与该client实例相关的线程   
  10.   }  
  11. }  

  • RPC.VersionMismatch内部类

该内部类主要是用来:当检测到版本与RPC协议不匹配的时候,作为异常来处理信息的类。

  • RPC.Invoker内部类

该内部类实现了java.lang.reflect.InvocationHandler接口,是一个代理实例的调用处理程序实现类。

该内部类实现如下所示:

  1. private static class Invoker implements InvocationHandler {  
  2.   
  3.   private InetSocketAddress address; // 远程服务器地址   
  4.   private UserGroupInformation ticket; // 客户端用户身份信息   
  5.   private Client client; // 客户端实例   
  6.   private boolean isClosed = false// 客户端是否关闭   
  7.   
  8.   public Invoker(InetSocketAddress address, UserGroupInformation ticket, Configuration conf, SocketFactory factory) {  
  9.     this.address = address;  
  10.     this.ticket = ticket;  
  11.     this.client = CLIENTS.getClient(conf, factory);  
  12.   }  
  13.   
  14.   /** 
  15.    * Stop a RPC client connection  
  16.    * @param proxy 代理实例 
  17.    * @param method 某个类的方法实例  
  18.    * @param args  方法参数 
  19.    */  
  20.   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  21.     final boolean logDebug = LOG.isDebugEnabled();  
  22.     long startTime = 0;  
  23.     if (logDebug) {  
  24.       startTime = System.currentTimeMillis();  
  25.     }  
  26.     // 构造一个RPC.Invocation实例作为参数传递给调用程序,执行调用,返回值为value   
  27.     ObjectWritable value = (ObjectWritable)client.call(new Invocation(method, args), address, method.getDeclaringClass(), ticket);  
  28.     if (logDebug) {  
  29.       long callTime = System.currentTimeMillis() - startTime;  
  30.       LOG.debug("Call: " + method.getName() + " " + callTime);  
  31.     }  
  32.     return value.get(); // 返回调用处理结果(value的实例)   
  33.   }  
  34.     
  35.   /* 关闭client */   
  36.   synchronized private void close() {  
  37.     if (!isClosed) {  
  38.       isClosed = true;  
  39.       CLIENTS.stopClient(client);  
  40.     }  
  41.   }  
  42. }  

该内部类出列了客户端调用,并返回调用处理结果。

RPC.Server内部类

该内部类是RPC最核心的,它继承自org.apache.hadoop.ipc.Server抽象类,实现的最核心的是调用处理方法:

  1. public Writable call(Class<?> protocol, Writable param, long receivedTime) throws IOException {  
  2.   try {  
  3.     Invocation call = (Invocation)param;  
  4.     if (verbose) log("Call: " + call);  
  5.   
  6.     Method method = protocol.getMethod(call.getMethodName(), call.getParameterClasses()); // 通过反射,根据调用方法名和方法参数类型得到Method实例   
  7.     method.setAccessible(true); // 设置反射的对象在使用时取消Java语言访问检查,提高效率   
  8.   
  9.     long startTime = System.currentTimeMillis();  
  10.     Object value = method.invoke(instance, call.getParameters()); // 执行调用(instance是调用底层方法的对象,第二个参数是方法调用的参数)   
  11.     int processingTime = (int) (System.currentTimeMillis() - startTime);  
  12.     int qTime = (int) (startTime-receivedTime);  
  13.     if (LOG.isDebugEnabled()) {  
  14.       LOG.debug("Served: " + call.getMethodName() +  " queueTime= " + qTime + " procesingTime= " + processingTime);  
  15.     }  
  16.     rpcMetrics.rpcQueueTime.inc(qTime);  
  17.     rpcMetrics.rpcProcessingTime.inc(processingTime);  
  18.   
  19.     MetricsTimeVaryingRate m = (MetricsTimeVaryingRate) rpcMetrics.registry.get(call.getMethodName());  
  20.     if (m == null) {  
  21.       try {  
  22.         m = new MetricsTimeVaryingRate(call.getMethodName(),  
  23.                                             rpcMetrics.registry);  
  24.       } catch (IllegalArgumentException iae) {  
  25.         // the metrics has been registered; re-fetch the handle   
  26.         LOG.info("Error register " + call.getMethodName(), iae);  
  27.         m = (MetricsTimeVaryingRate) rpcMetrics.registry.get(call.getMethodName());  
  28.       }  
  29.     }  
  30.     m.inc(processingTime);  
  31.   
  32.     if (verbose) log("Return: "+value);  
  33.   
  34.     return new ObjectWritable(method.getReturnType(), value); // 返回:调用的返回值对象   
  35.   
  36.   } catch (InvocationTargetException e) {  
  37.     Throwable target = e.getTargetException();  
  38.     if (target instanceof IOException) {  
  39.       throw (IOException)target;  
  40.     } else {  
  41.       IOException ioe = new IOException(target.toString());  
  42.       ioe.setStackTrace(target.getStackTrace());  
  43.       throw ioe;  
  44.     }  
  45.   } catch (Throwable e) {  
  46.     IOException ioe = new IOException(e.toString());  
  47.     ioe.setStackTrace(e.getStackTrace());  
  48.     throw ioe;  
  49.   }  
  50. }  

还有一个方法实现了对用户的授权,这在org.apache.hadoop.ipc.Server抽象类中并没实现,如下所示:

  1. @Override  
  2. public void authorize(Subject user, ConnectionHeader connection)   
  3. throws AuthorizationException {  
  4.   if (authorize) { // authorize默认为false,除非在Configuration配置类实例中获取到的为true。可见,该简单的RPC默认不需要对用户进行授权操作   
  5.     Class<?> protocol = null;  
  6.     try {  
  7.       protocol = getProtocolClass(connection.getProtocol(), getConf());  
  8.     } catch (ClassNotFoundException cfne) {  
  9.       throw new AuthorizationException("Unknown protocol: " + connection.getProtocol());  
  10.     }  
  11.     ServiceAuthorizationManager.authorize(user, protocol); // 执行授权操作,使得用户可以访问被使用的Protocol   
  12.   }  
  13. }  

上面,已经对RPC类的内部类的实现进行了阅读分析,现在看RPC类提供的操作。

获取到一个到远程服务器的代理:

  1. /** 
  2.  * 获取到一个到远程服务器的代理连接 
  3.  * @param protocol 协议类 
  4.  * @param clientVersion 客户端版本 
  5.  * @param addr 远程地址 
  6.  * @param conf 使用配置类实例 
  7.  * @param timeout 超时时间 
  8.  * @return 返回代理 
  9.  */  
  10. static VersionedProtocol waitForProxy(Class protocol,  
  11.                                              long clientVersion,  
  12.                                              InetSocketAddress addr,  
  13.                                              Configuration conf,  
  14.                                              long timeout  
  15.                                              ) throws IOException;  

另外,还有几个获取代理的getProxy方法:

  1. public static VersionedProtocol getProxy(Class<?> protocol, long clientVersion, InetSocketAddress addr, Configuration conf) throws IOException;  
  2. public static VersionedProtocol getProxy(Class<?> protocol, long clientVersion, InetSocketAddress addr, Configuration conf, SocketFactory factory) throws IOException;  
  3. public static VersionedProtocol getProxy(Class<?> protocol, long clientVersion, InetSocketAddress addr, UserGroupInformation ticket, Configuration conf, SocketFactory factory) throws IOException;  

下面是RPC服务器处理调用的过程实现:

  1. public static Object[] call(Method method, Object[][] params,  
  2.                             InetSocketAddress[] addrs,   
  3.                             UserGroupInformation ticket, Configuration conf)  
  4.   throws IOException {  
  5.   
  6.   Invocation[] invocations = new Invocation[params.length]; // 一组方法调用实例   
  7.   for (int i = 0; i < params.length; i++)  
  8.     invocations[i] = new Invocation(method, params[i]);  
  9.   Client client = CLIENTS.getClient(conf); // 创建并缓存一个org.apache.hadoop.ipc.Client实例   
  10.   try {  
  11.   Writable[] wrappedValues = client.call(invocations, addrs, method.getDeclaringClass(), ticket); // 根据参数,客户端发送调用方法及其参数   
  12.     
  13.   if (method.getReturnType() == Void.TYPE) {   
  14.     return null;  
  15.   }  
  16.   
  17.   Object[] values = (Object[])Array.newInstance(method.getReturnType(), wrappedValues.length); // 客户端执行RPC调用,获取到返回值   
  18.   for (int i = 0; i < values.length; i++)  
  19.     if (wrappedValues[i] != null)  
  20.       values[i] = ((ObjectWritable)wrappedValues[i]).get(); // 获取返回值的实例   
  21.     
  22.   return values; // 返回   
  23.   } finally {  
  24.     CLIENTS.stopClient(client); // 如果该client实例的引用计数为0,该client就被关闭   
  25.   }  
  26. }  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值