处理机说明:
读取配置文件,根据配置文件信息保持一个连接配置变量集合。
根据连接变量集合初始化相应的连接。
启动一个线程,处理检查连接的有效性,处理方法是发送心跳包,如发现不能使用的连接,则设置该连接为占用,并启动一个线程去不断的初始化该连接。
用户使用接口根据名称去获得连接,如果是保持的连接则直接从连接集合获取,并检查有效性。如果是不用保持的,则去创建一个连接返回。
备注:如果返回了空的信息,则说明没有正常的连接可以返回,你可以尝试不断的获取。
下面来看看代码:
首先创建一个实体对象,对应配置信息
1. package com.socket;
2. /**
3. * @说明 连接的公共属性
4. * @author cuisuqiang
5. * @version 1.0
6. * @since
7. */
8. public class SocketEntity {
9. /**
10. * 连接的名字,以名字作为Key
11. */
12. private String name;
13. /**
14. * 连接的IP
15. */
16. private String ip;
17. /**
18. * 连接的端口
19. */
20. private int port;
21. /**
22. * 是否保持连接
23. */
24. private boolean keepConn;
25.
26. public String getName() {
27. return name;
28. }
29. public void setName(String name) {
30. this.name = name;
31. }
32. public String getIp() {
33. return ip;
34. }
35. public void setIp(String ip) {
36. this.ip = ip;
37. }
38. public int getPort() {
39. return port;
40. }
41. public void setPort(int port) {
42. this.port = port;
43. }
44. public boolean isKeepConn() {
45. return keepConn;
46. }
47. public void setKeepConn(boolean keepConn) {
48. this.keepConn = keepConn;
49. }
50. }
创建配置文件socket.properties
socket1_isKeep属性相当于Spring中的单例还是每次创建
1. # @author cuisuqiang
2.
3. socket1=socket1
4. socket1_ip=127.0.0.1
5. socket1_port=8001
6. socket1_isKeep=1
7.
8. socket2=socket2
9. socket2_ip=127.0.0.1
10. socket2_port=8001
11. socket2_isKeep=0
12.
13. # 连接的数量,一定要和实际配置的数量匹配
14. socketConnCount=2
15. # 公共的检测间隔 秒
16. commonCheckTime=5
然后初始化配置信息和连接,同时该类中有静态变量来保持连接对象
1. package com.socket;
2. import java.util.ArrayList;
3. import java.util.LinkedHashMap;
4. import java.util.List;
5. import java.util.Map;
6. import java.util.Properties;
7. import java.util.concurrent.ExecutorService;
8. import java.util.concurrent.Executors;
9. import org.apache.commons.logging.Log;
10. import org.apache.commons.logging.LogFactory;
11. /**
12. * @说明 变量保持
13. * @author cuisuqiang
14. * @version 1.0
15. * @since
16. */
17. public class SocketKeep {
18. private static Log logger = LogFactory.getLog(SocketKeep.class);
19. /**
20. * 配置信息加载
21. */
22. public static List<SocketEntity> socketEntityList = new ArrayList<SocketEntity>();
23. /**
24. * 连接对象保持,只保持需要系统保持的连接
25. */
26. public static Map<String, SocketCui> socketMap = new LinkedHashMap<String, SocketCui>();
27. /**
28. * 连接对象是否锁定 1:锁定,其他未锁定
29. */
30. public static Map<String, String> socketIsLock = new LinkedHashMap<String, String>();
31. /**
32. * 共用连接检测间隔
33. */
34. public static int commonCheckTime = 2;
35. /**
36. * 连接的数量,一定要和实际配置的数量匹配
37. */
38. public static int socketConnCount = 0;
39. public static ExecutorService executorService = null;// 线程池
40. /**
41. * 初始化所有连接信息
42. */
43. public static void initSocketKeep() {
44. Properties properties = null;
45. try {
46. properties = new Properties();
47. properties.load(SocketKeep.class.getClassLoader().getResourceAsStream("socket.properties"));
48. logger.warn("加载socket.properties文件成功!");
49. } catch (Exception e) {
50. logger.error("加载socket.properties文件失败!", e);
51. properties = null;
52. }
53. if (null != properties) {
54. try {
55. commonCheckTime = Integer.parseInt(properties.getProperty("commonCheckTime"));
56. socketConnCount = Integer.parseInt(properties.getProperty("socketConnCount"));
57. executorService = Executors.newFixedThreadPool(socketConnCount + 1);
58. } catch (Exception e) {
59. executorService = Executors.newFixedThreadPool(1);
60. logger.error("解析共用信息时错误!", e);
61. // 系统忽略这两个属性的加载异常
62. }
63. SocketEntity socketEntity = null;
64. for (int i = 1; i <= socketConnCount; i++) {
65. String name = properties.getProperty("socket" + i);
66. if(null != name){
67. socketEntity = new SocketEntity();
68. String ip = properties.getProperty("socket" + i + "_ip");
69. String port = properties.getProperty("socket" + i + "_port");
70. String isKeep = properties.getProperty("socket" + i + "_isKeep");
71.
72. socketEntity.setName(name);
73. socketEntity.setIp(ip);
74. socketEntity.setPort(Integer.parseInt(port));
75. boolean keepConn = false;
76. if(null != isKeep && "1".equals(isKeep)){
77. keepConn = true;
78. }
79. socketEntity.setKeepConn(keepConn);
80. socketEntityList.add(socketEntity);
81. }
82. }
83. }
84. logger.warn("加载Socket连接配置信息结束!");
85. logger.warn("开始初始化Socket连接!");
86. SocketCui socket = null;
87. for(SocketEntity socketEntity : socketEntityList){
88. if(null != socketEntity && socketEntity.isKeepConn()){
89. try {
90. socket = new SocketCui(socketEntity.getIp(),socketEntity.getPort());
91. socket.setSoTimeout(0);
92. socket.setKeepAlive(true);
93. socket.setName(socketEntity.getName());
94. } catch (Exception e) {
95. logger.error("初始化某个连接时错误!错误的连接将放弃!资源名称:" + socketEntity.getName(), e);
96. socket = null;
97. }
98. if(null != socket){
99. socketMap.put(socketEntity.getName(), socket);
100. }else{
101. socketMap.put(socketEntity.getName(), new SocketCui());
102. }
103. socketIsLock.put(socketEntity.getName(), "0");
104. }
105. }
106. // 开始执行检查
107. executorService.execute(new CheckThread());
108. logger.warn("初始化Socket连接结束!");
109. }
110. }
启动的线程是用于检查连接的
1. package com.socket;
2. import org.apache.commons.logging.Log;
3. import org.apache.commons.logging.LogFactory;
4. /**
5. * @说明 轮询检测某个连接当前是否可用
6. * @author cuisuqiang
7. * @version 1.0
8. * @since 当遇到一个错误的连接,将会启动重连,同时挂起该连接的使用
9. */
10. public class CheckThread implements Runnable {
11. private static Log logger = LogFactory.getLog(CheckThread.class);
12. public void run() {
13. while(true){
14. SocketCui socket = null;
15. for(SocketEntity socketEntity : SocketKeep.socketEntityList){
16. if(null != socketEntity && socketEntity.isKeepConn()){
17. String isLock = SocketKeep.socketIsLock.get(socketEntity.getName());
18. // 如果当前未被使用
19. if(!"1".equals(isLock)){
20. // 锁定引用
21. SocketKeep.socketIsLock.put(socketEntity.getName(), "1");
22. socket = SocketKeep.socketMap.get(socketEntity.getName());
23. try {
24. // 发送一个心跳包
25. socket.sendUrgentData(0xFF);
26. // 释放资源
27. SocketKeep.socketIsLock.put(socketEntity.getName(), "0");
28. } catch (Exception e) {
29. logger.error("检查连接时异常!启动重连!资源名称:" + socketEntity.getName(), e);
30. // 如果异常,应该建立一个线程去初始化该连接
31. InitSocket initS = new InitSocket(socketEntity.getName());
32. new Thread(initS).start();
33. }
34. }
35. }
36. }
37. // 执行间隔
38. try {
39. logger.error("本次检测结束!");
40. Thread.sleep(SocketKeep.commonCheckTime * 1000);
41. } catch (Exception e) {
42. }
43. }
44. }
45. }
当检查线程发现无效的连接时会启动新的线程初始化该连接
1. package com.socket;
2. import org.apache.commons.logging.Log;
3. import org.apache.commons.logging.LogFactory;
4. /**
5. * @说明 负责初始化失效的连接
6. * @author cuisuqiang
7. * @version 1.0
8. * @since
9. */
10. public class InitSocket implements Runnable{
11. private static Log logger = LogFactory.getLog(InitSocket.class);
12. /**
13. * 是否有某个连接的配置信息,只有有配置信息才能建立连接
14. */
15. private static boolean isHave = false;
16. private SocketEntity socketEntity = null;
17. private String name;
18. public InitSocket(String name){
19. this.name = name;
20. // 检测是否有某个连接的配置信息
21. for(SocketEntity socketEntity : SocketKeep.socketEntityList){
22. if(null != socketEntity && socketEntity.isKeepConn()){
23. if(socketEntity.getName().equals(name)){
24. this.setSocketEntity(socketEntity);
25. isHave = true;
26. }
27. }
28. }
29. }
30. public void run() {
31. boolean isError = true;
32. SocketCui socket = null;
33. if(isHave){
34. while(isError){
35. try {
36. socket = new SocketCui(this.getSocketEntity().getIp(),this.getSocketEntity().getPort());
37. socket.setSoTimeout(0);
38. socket.setKeepAlive(true);
39. socket.setName(this.name);
40. // 发送一个心跳包
41. socket.sendUrgentData(0xFF);
42. } catch (Exception e) {
43. logger.error("建立资源连接时错误!资源:" + this.name, e);
44. socket = null;
45. }
46. if(null != socket){
47. SocketKeep.socketMap.put(this.getSocketEntity().getName(), socket);
48. // 设置连接当前可用
49. SocketKeep.socketIsLock.put(this.getSocketEntity().getName(), "0");
50. logger.warn("建立资源连接成功!资源名称:" + this.name);
51. isError = false;
52. }
53. try {
54. Thread.sleep(2 * 1000);
55. } catch (Exception e) {
56. }
57.
58. }
59. }else{
60. logger.error("没有发现指定资源的配置信息!资源名称:" + this.name);
61. }
62. logger.warn("初始化资源执行结束!资源名称:" + this.name);
63. }
64. public SocketEntity getSocketEntity() {
65. return socketEntity;
66. }
67. public void setSocketEntity(SocketEntity socketEntity) {
68. this.socketEntity = socketEntity;
69. }
70. }
同时注意,用户在使用连接后会调用关闭方法。我们是不能让连接关闭的,要保持常连接。所以如果用户指定的是保持这个连接,那么返回的连接对象就不是原来的Socket对象了,我们要重写这个对象
1. package com.socket;
2. import java.io.IOException;
3. import java.net.Socket;
4. import java.net.UnknownHostException;
5. /**
6. * @说明 被重新定义的连接对象,增加了名字这个属性,重写了关闭的方法
7. * @author cuisuqiang
8. * @version 1.0
9. * @since
10. */
11. public class SocketCui extends Socket{
12. /**
13. * 为对象增加名称属性
14. */
15. private String name;
16. public SocketCui() {
17. }
18. public SocketCui(String ip,int port) throws UnknownHostException, IOException{
19. super(ip, port);
20. }
21. /**
22. * 覆盖关闭的方法
23. */
24. @Override
25. public synchronized void close() throws IOException {
26. SocketKeep.socketIsLock.put(this.name, "0");
27. }
28. public String getName() {
29. return name;
30. }
31. public void setName(String name) {
32. this.name = name;
33. }
34. }
这样,关闭时只会解除其占用,而不会真正关闭该连接。
我们来写一个服务端,这个服务端一直接受连接,并检查连接的有效性,当失效时不处理
同时打印接收到的连接信息
1. package com.test;
2. import java.net.*;
3. import java.text.SimpleDateFormat;
4. import java.util.Date;
5. /**
6. * @说明 服务端,始终接受连接
7. * @author cuisuqiang
8. * @version 1.0
9. * @since
10. */
11. public class ServiceTest {
12. public static void main(String[] args) {
13. try {
14. ServerSocket ss1 = new ServerSocket(8001);
15. Runnable accumelatora1 = new Accumulatort(ss1);
16. Thread threada = new Thread(accumelatora1, "ThreadA");
17. threada.start();
18. System.out.println("服务启动完毕!");
19. } catch (Exception e) {
20. e.printStackTrace();
21. }
22. }
23. }
24. class Accumulatort implements Runnable {
25. ServerSocket ss = null;
26. public Accumulatort(ServerSocket s) {
27. this.ss = s;
28. }
29. @SuppressWarnings("unchecked")
30. public void run() {
31. try {
32. SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
33. while (true) {
34. Socket s = ss.accept();
35. System.out.println(format.format(new Date()) + " " + "---------收到请求!");
36. new Thread(new ServiceImpl(s)).start();
37. }
38. } catch (Exception e) {
39. e.printStackTrace();
40. }
41. }
42. }
服务端处理连接的实现类
1. package com.test;
2. import java.net.Socket;
3. /**
4. * @说明 循环发送心跳包保持连接属性
5. * @author cuisuqiang
6. * @version 1.0
7. * @since
8. */
9. public class ServiceImpl implements Runnable {
10. Socket socket = null;
11. public ServiceImpl(Socket s) {
12. this.socket = s;
13. }
14. public void run() {
15. boolean isKeep = true;
16. try {
17. while (isKeep) {
18. socket.sendUrgentData(0xFF);
19. Thread.sleep(1 * 1000);
20. }
21. } catch (Exception e) {
22. isKeep = false;
23. }
24. }
25. }
先启动服务端,然后我们再写一个测试端,这个测试类会不断去管理器中获取相应的连接,同时打印连接信息
通过打印的连接信息,我们可以知道获取的是不是同一个对象
同时如果你一直获取单例的对象,那么可能出现该连接被检查线程占用的情况
1. package com.test;
2. import java.net.Socket;
3. import java.text.SimpleDateFormat;
4. import java.util.Date;
5. import com.socket.CommonSocket;
6. import com.socket.SocketKeep;
7. /**
8. * @说明 循环去请求获得相应的连接然后打印连接地址
9. * @author cuisuqiang
10. * @version 1.0
11. * @since
12. */
13. public class GetSocketTest {
14. public static void main(String[] args) {
15. SocketKeep.initSocketKeep();
16. while(true){
17. SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
18. try {
19. Socket socket1 = CommonSocket.getSocketByName("socket1");
20. if(null != socket1){
21. System.out.println(format.format(new Date()) + " " + socket1.toString());
22. socket1.close();
23. }
24. } catch (Exception e) {
25. e.printStackTrace();
26. }
27. try {
28. Socket socket2 = CommonSocket.getSocketByName("socket2");
29. if(null != socket2){
30. System.out.println(format.format(new Date()) + " " + socket2.toString());
31. socket2.close();
32. }
33. } catch (Exception e) {
34. e.printStackTrace();
35. }
36. try {
37. Thread.sleep(1000);
38. } catch (Exception e) {
39.
40. }
41. }
42. }
43. }
当然别忘了加日志的包!
我们来看一下服务端的打印信息
1. 服务启动完毕!
2. 2012-04-18 16:59:48 ---------收到请求!
3. 2012-04-18 16:59:48 ---------收到请求!
4. 2012-04-18 16:59:49 ---------收到请求!
5. 2012-04-18 16:59:50 ---------收到请求!
6. 2012-04-18 16:59:51 ---------收到请求!
7. 2012-04-18 16:59:52 ---------收到请求!
8. 2012-04-18 16:59:53 ---------收到请求!
9. 2012-04-18 16:59:54 ---------收到请求!
10. 2012-04-18 16:59:55 ---------收到请求!
11. 2012-04-18 16:59:56 ---------收到请求!
12. 2012-04-18 16:59:57 ---------收到请求!
13. 2012-04-18 16:59:58 ---------收到请求!
14. 2012-04-18 16:59:59 ---------收到请求!
我们可以看到,除刚开始外,会一直收到连接,因为我们一直在获取一个非单例的连接对象
我们再看一下客户端打印信息
1. 2012-04-18 16:59:48 Socket[addr=/127.0.0.1,port=8001,localport=4389]
2. 2012-04-18 16:59:48 Socket[addr=/127.0.0.1,port=8001,localport=4390]
3. 2012-04-18 16:59:49 Socket[addr=/127.0.0.1,port=8001,localport=4389]
4. 2012-04-18 16:59:49 Socket[addr=/127.0.0.1,port=8001,localport=4391]
5. 2012-04-18 16:59:50 Socket[addr=/127.0.0.1,port=8001,localport=4389]
6. 2012-04-18 16:59:50 Socket[addr=/127.0.0.1,port=8001,localport=4392]
7. 2012-04-18 16:59:51 Socket[addr=/127.0.0.1,port=8001,localport=4389]
8. 2012-04-18 16:59:51 Socket[addr=/127.0.0.1,port=8001,localport=4393]
9. 2012-04-18 16:59:52 Socket[addr=/127.0.0.1,port=8001,localport=4389]
10. 2012-04-18 16:59:52 Socket[addr=/127.0.0.1,port=8001,localport=4394]
11. 2012-04-18 16:59:53 Socket[addr=/127.0.0.1,port=8001,localport=4389]
12. 2012-04-18 16:59:53 Socket[addr=/127.0.0.1,port=8001,localport=4395]
13. 2012-04-18 16:59:54 Socket[addr=/127.0.0.1,port=8001,localport=4389]
14. 2012-04-18 16:59:54 Socket[addr=/127.0.0.1,port=8001,localport=4396]
15. 2012-04-18 16:59:55 Socket[addr=/127.0.0.1,port=8001,localport=4389]
16. 2012-04-18 16:59:55 Socket[addr=/127.0.0.1,port=8001,localport=4397]
17. 2012-04-18 16:59:56 Socket[addr=/127.0.0.1,port=8001,localport=4389]
18. 2012-04-18 16:59:56 Socket[addr=/127.0.0.1,port=8001,localport=4398]
19. 2012-04-18 16:59:57 Socket[addr=/127.0.0.1,port=8001,localport=4389]
20. 2012-04-18 16:59:57 Socket[addr=/127.0.0.1,port=8001,localport=4399]
21. 2012-04-18 16:59:58 Socket[addr=/127.0.0.1,port=8001,localport=4400]
22. 2012-04-18 16:59:59 Socket[addr=/127.0.0.1,port=8001,localport=4389]
23. 2012-04-18 16:59:59 Socket[addr=/127.0.0.1,port=8001,localport=4401]
可以看到,socket1是一直被保持的,而socket2每次创建一个
再看一下日志
1. 2012-04-18 16:59:48 [com.socket.SocketKeep]-[WARN] 加载socket.properties文件成功!
2. 2012-04-18 16:59:48 [com.socket.SocketKeep]-[WARN] 加载Socket连接配置信息结束!
3. 2012-04-18 16:59:48 [com.socket.SocketKeep]-[WARN] 开始初始化Socket连接!
4. 2012-04-18 16:59:48 [com.socket.SocketKeep]-[WARN] 初始化Socket连接结束!
5. 2012-04-18 16:59:48 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
6. 2012-04-18 16:59:48 [com.socket.CheckThread]-[ERROR] 本次检测结束!
7. 2012-04-18 16:59:49 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
8. 2012-04-18 16:59:50 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
9. 2012-04-18 16:59:51 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
10. 2012-04-18 16:59:52 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
11. 2012-04-18 16:59:53 [com.socket.CheckThread]-[ERROR] 本次检测结束!
12. 2012-04-18 16:59:53 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
13. 2012-04-18 16:59:54 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
14. 2012-04-18 16:59:55 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
15. 2012-04-18 16:59:56 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
16. 2012-04-18 16:59:57 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
17. 2012-04-18 16:59:58 [com.socket.CommonSocket]-[WARN] 当前连接正被占用,请稍候尝试!资源名称:socket1
18. 2012-04-18 16:59:58 [com.socket.CheckThread]-[ERROR] 本次检测结束!
19. 2012-04-18 16:59:58 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
20. 2012-04-18 16:59:59 [com.socket.CommonSocket]-[WARN] 为用户建立请求连接!资源名称:socket2
可以看到,socket2会一直被重新创建,而且socket1会发现被占用的情况。
你也可以中断服务端然后再重启服务端试试,看看效果。
一.心跳机制
1. hadoop集群是master/slave模式,master包括Namenode和Jobtracker,slave包括Datanode和Tasktracker。
2. master启动的时候,会开一个ipc server在那里,等待slave心跳。
3. slave启动时,会连接master,并每隔3秒钟主动向master发送一个“心跳”,这个时间可以通过”heartbeat.recheck.interval”属性来设置。将自己的状态信息告诉master,然后master也是通过这个心跳的返回值,向slave节点传达指令。
4. 需要指出的是:namenode与datanode之间的通信,jobtracker与tasktracker之间的通信,都是通过“心跳”完成的。
二.Datanode、Namenode心跳源码分析
既然“心跳”是Datanode主动给Namenode发送的。那Datanode是怎么样发送的呢?下面贴出Datanode.class中的关键代码:
代码一:
/** * 循环调用“发送心跳”方法,直到shutdown * 调用远程Namenode的方法 */ publicvoid offerService() throws Exception { ••• while (shouldRun) { try { long startTime = now(); // heartBeatInterval是在启动Datanode时根据配置文件设置的,是心跳间隔时间 if (startTime - lastHeartbeat > heartBeatInterval) { lastHeartbeat = startTime; //Datanode发送心跳 DatanodeCommand[] cmds =namenode.sendHeartbeat(dnRegistration, data.getCapacity(), data.getDfsUsed(), data.getRemaining(), xmitsInProgress.get(), getXceiverCount()); myMetrics.addHeartBeat(now() - startTime); if (!processCommand(cmds)) continue; } ••• } } // while(shouldRun) } // offerService
需要注意的是:发送心跳的对象并不是datanode,而是一个名为namenode的对象,难道在datanode端就直接有个namenode的引用吗?其实不然,我们来看看这个namenode吧:
代码二:
public DatanodeProtocol namenode = null;
namenode其实是一个DatanodeProtocol的引用,在对hadoop RPC机制分析的文章中我提到过,这是一个Datanode和Namenode通信的协议,其中有许多未实现的接口方法,sendHeartbeat()就是其中的一个。下面看看这个namenode对象是怎么被实例化的吧:
代码三:
this.namenode = (DatanodeProtocol) RPC.waitForProxy(DatanodeProtocol.class, DatanodeProtocol.versionID, nameNodeAddr, conf);
其实这个namenode并不是Namenode的一个对象,而只是一个Datanode端对Namenode的代理对象,正是这个代理完成了“心跳”。代理的底层实现就是RPC机制了。
三.Tasktracker、Jobtracker心跳源码分析
同样我们从Tasktracker入手,下面贴出Tasktracker.class的关键代码:
代码四:
代码一: State offerService() throws Exception { long lastHeartbeat = System.currentTimeMillis(); while (running && !shuttingDown) { ••• // 发送心跳,调用代码二 HeartbeatResponse heartbeatResponse = transmitHeartBeat(now); ••• return State.NORMAL; } 代码二: HeartbeatResponse transmitHeartBeat(long now) throws IOException { ••• HeartbeatResponse heartbeatResponse = jobClient.heartbeat(status, justStarted, justInited, askForNewTask, heartbeatResponseId); ••• return heartbeatResponse; }
其实我觉得分析到这里大家就可以自己分析了,jobClient也是一个协议:
代码五:
InterTrackerProtocoljobClient;
该协议用于定义Tasktracker和Jobtracker的通信。同样,它也是一个代理对象:
代码六:
this.jobClient = (InterTrackerProtocol) UserGroupInformation.getLoginUser().doAs( new PrivilegedExceptionAction<Object>() { public Object run() throws IOException { return RPC.waitForProxy(InterTrackerProtocol.class, InterTrackerProtocol.versionID, jobTrackAddr, fConf); } });
终于,hadoop底层通信整个系列的源码分析全部完成了。