Socket连接管理器(心跳机制)(转)

java 专栏收录该内容
30 篇文章 0 订阅

http://cuisuqiang.iteye.com/

处理机说明:

读取配置文件,根据配置文件信息保持一个连接配置变量集合。

根据连接变量集合初始化相应的连接。

启动一个线程,处理检查连接的有效性,处理方法是发送心跳包,如发现不能使用的连接,则设置该连接为占用,并启动一个线程去不断的初始化该连接。

用户使用接口根据名称去获得连接,如果是保持的连接则直接从连接集合获取,并检查有效性。如果是不用保持的,则去创建一个连接返回。

备注:如果返回了空的信息,则说明没有正常的连接可以返回,你可以尝试不断的获取。

下面来看看代码:

首先创建一个实体对象,对应配置信息

Java代码  

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中的单例还是每次创建

 

 

Java代码  

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  

 

然后初始化配置信息和连接,同时该类中有静态变量来保持连接对象

Java代码  

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. }  

 

启动的线程是用于检查连接的

Java代码  

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. }  

 

当检查线程发现无效的连接时会启动新的线程初始化该连接

Java代码  

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对象了,我们要重写这个对象

Java代码  

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. }  

 这样,关闭时只会解除其占用,而不会真正关闭该连接。

 

我们来写一个服务端,这个服务端一直接受连接,并检查连接的有效性,当失效时不处理

同时打印接收到的连接信息

Java代码  

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. }  

 服务端处理连接的实现类

Java代码  

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. }  

 

先启动服务端,然后我们再写一个测试端,这个测试类会不断去管理器中获取相应的连接,同时打印连接信息

通过打印的连接信息,我们可以知道获取的是不是同一个对象

同时如果你一直获取单例的对象,那么可能出现该连接被检查线程占用的情况

Java代码  

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. }  

 

当然别忘了加日志的包!

 

我们来看一下服务端的打印信息

Java代码  

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 ---------收到请求!  

 

我们可以看到,除刚开始外,会一直收到连接,因为我们一直在获取一个非单例的连接对象

我们再看一下客户端打印信息

Java代码  

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每次创建一个

再看一下日志

Java代码  

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包括NamenodeJobtrackerslave包括DatanodeTasktracker

2. master
启动的时候,会开一个ipc server在那里,等待slave心跳。

3. slave
启动时,会连接master,并每隔3秒钟主动向master发送一个心跳,这个时间可以通过”heartbeat.recheck.interval”属性来设置。将自己的状态信息告诉master,然后master也是通过这个心跳的返回值,向slave节点传达指令。

4.
需要指出的是:namenodedatanode之间的通信,jobtrackertasktracker之间的通信,都是通过心跳完成的。

二.DatanodeNamenode心跳源码分析


既然心跳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机制分析的文章中我提到过,这是一个DatanodeNamenode通信的协议,其中有许多未实现的接口方法,sendHeartbeat()就是其中的一个。下面看看这个namenode对象是怎么被实例化的吧: 

代码三:

  this.namenode = (DatanodeProtocol)       RPC.waitForProxy(DatanodeProtocol.class,                        DatanodeProtocol.versionID,                        nameNodeAddr,                         conf);

 

其实这个namenode并不是Namenode的一个对象,而只是一个Datanode端对Namenode的代理对象,正是这个代理完成了心跳。代理的底层实现就是RPC机制了。

三.TasktrackerJobtracker心跳源码分析


同样我们从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;

 

该协议用于定义TasktrackerJobtracker的通信。同样,它也是一个代理对象:

 

代码六:

   this.jobClient = (InterTrackerProtocol)     UserGroupInformation.getLoginUser().doAs(         new PrivilegedExceptionAction<Object>() {       public Object run() throws IOException {         return RPC.waitForProxy(InterTrackerProtocol.class,            InterTrackerProtocol.versionID,             jobTrackAddr, fConf);       }    });

 


终于,hadoop底层通信整个系列的源码分析全部完成了。

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值