Java实现心跳机制

一、心跳机制简介

     在分布式系统中,分布在不同主机上的节点需要检测其他节点的状态,如服务器节点需要检测从节点是否失效。为了检测对方节点的有效性,每隔固定时间就发送一个固定信息给对方,对方回复一个固定信息,如果长时间没有收到对方的回复,则断开与对方的连接。

     发包方既可以是服务端,也可以是客户端,这要看具体实现。因为是每隔固定时间发送一次,类似心跳,所以发送的固定信息称为心跳包。心跳包一般为比较小的包,可根据具体实现。心跳包主要应用于长连接的保持与短线链接。

      一般而言,应该客户端主动向服务器发送心跳包,因为服务器向客户端发送心跳包会影响服务器的性能。

二、心跳机制实现方式

    心跳机制有两种实现方式,一种基于TCP自带的心跳包,TCP的SO_KEEPALIVE选项可以,系统默认的默认跳帧频率为2小时,超过2小时后,本地的TCP 实现会发送一个数据包给远程的 Socket. 如果远程Socket 没有发回响应, TCP实现就会持续尝试 11 分钟, 直到接收到响应为止。 否则就会自动断开Socket连接。但TCP自带的心跳包无法检测比较敏感地知道对方的状态,默认2小时的空闲时间,对于大多数的应用而言太长了。可以手工开启KeepAlive功能并设置合理的KeepAlive参数。

    另一种在应用层自己进行实现,基本步骤如下:

  1. Client使用定时器,不断发送心跳;
  2. Server收到心跳后,回复一个包;
  3. Server为每个Client启动超时定时器,如果在指定时间内没有收到Client的心跳包,则Client失效。

三、Java实现心跳机制

    这里基于Java实现的简单RPC框架实现心跳机制。Java实现代码如下所示:

    心跳客户端类:

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

30

31

32

33

34

35

36

37

38

39

40

41

42

43

public class HeartbeatClient implements Runnable {

 

    private String serverIP = "127.0.0.1";

    private int serverPort = 8089;

    private String nodeID = UUID.randomUUID().toString();

    private boolean isRunning = true;

    //  最近的心跳时间

    private long lastHeartbeat;

    // 心跳间隔时间

    private long heartBeatInterval = 10 * 1000;

 

    public void run() {

        try {

            while (isRunning) {

                HeartbeatHandler handler = RPClient.getRemoteProxyObj(HeartbeatHandler.class, new InetSocketAddress(serverIP, serverPort));

                long startTime = System.currentTimeMillis();

                // 是否达到发送心跳的周期时间

                if (startTime - lastHeartbeat > heartBeatInterval) {

                    System.out.println("send a heart beat");

                    lastHeartbeat = startTime;

 

                    HeartbeatEntity entity = new HeartbeatEntity();

                    entity.setTime(startTime);

                    entity.setNodeID(nodeID);

 

                    // 向服务器发送心跳,并返回需要执行的命令

                    Cmder cmds = handler.sendHeartBeat(entity);

 

                    if (!processCommand(cmds))

                        continue;

                }

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

 

    private boolean processCommand(Cmder cmds) {

        // ...

        return true;

    }

 

}

      心跳包实体类:

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

30

31

32

33

34

35

36

37

38

39

public class HeartbeatEntity implements Serializable {

 

    private long time;

    private String nodeID;

    private String error;

    private Map<String, Object> info = new HashMap<String, Object>();

 

    public String getNodeID() {

        return nodeID;

    }

 

    public void setNodeID(String nodeID) {

        this.nodeID = nodeID;

    }

 

    public String getError() {

        return error;

    }

 

    public void setError(String error) {

        this.error = error;

    }

 

    public Map<String, Object> getInfo() {

        return info;

    }

 

    public void setInfo(Map<String, Object> info) {

        this.info = info;

    }

 

    public long getTime() {

        return time;

    }

 

    public void setTime(long time) {

        this.time = time;

    }

}

  服务器接受心跳包返回的命令对象类:

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

30

public class Cmder implements Serializable {

 

    private String nodeID;

    private String error;

    private Map<String, Object> info = new HashMap<String, Object>();

 

    public String getNodeID() {

        return nodeID;

    }

 

    public void setNodeID(String nodeID) {

        this.nodeID = nodeID;

    }

 

    public String getError() {

        return error;

    }

 

    public void setError(String error) {

        this.error = error;

    }

 

    public Map<String, Object> getInfo() {

        return info;

    }

 

    public void setInfo(Map<String, Object> info) {

        this.info = info;

    }

}

  RPC服务注册中心:

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

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

public class ServiceCenter {

 

    private ExecutorService executor = Executors.newFixedThreadPool(20);

 

    private final ConcurrentHashMap<String, Class> serviceRegistry = new ConcurrentHashMap<String, Class>();

 

    private AtomicBoolean isRunning = new AtomicBoolean(true);

 

    // 服务器监听端口

    private int port = 8089;

 

    // 心跳监听器

    HeartbeatLinstener linstener;

 

    // 单例模式

    private static class SingleHolder {

        private static final ServiceCenter INSTANCE = new ServiceCenter();

    }

 

    private ServiceCenter() {

    }

 

    public static ServiceCenter getInstance() {

        return SingleHolder.INSTANCE;

    }

 

    public void register(Class serviceInterface, Class impl) {

        System.out.println("regeist service " + serviceInterface.getName());

        serviceRegistry.put(serviceInterface.getName(), impl);

    }

 

    public void start() throws IOException {

        ServerSocket server = new ServerSocket();

        server.bind(new InetSocketAddress(port));

        System.out.println("start server");

        linstener = HeartbeatLinstener.getInstance();

        System.out.println("start listen heart beat");

        try {

            while (true) {

                // 1.监听客户端的TCP连接,接到TCP连接后将其封装成task,由线程池执行

                executor.execute(new ServiceTask(server.accept()));

            }

        } finally {

            server.close();

        }

    }

 

    public void stop() {

        isRunning.set(false);

        executor.shutdown();

    }

 

 

    public boolean isRunning() {

        return isRunning.get();

    }

 

    public int getPort() {

        return port;

    }

 

    public void settPort(int port) {

        this.port = port;

    }

 

    public ConcurrentHashMap<String, Class> getServiceRegistry() {

        return serviceRegistry;

    }

 

    private class ServiceTask implements Runnable {

        Socket clent = null;

 

        public ServiceTask(Socket client) {

            this.clent = client;

        }

 

        public void run() {

            ObjectInputStream input = null;

            ObjectOutputStream output = null;

            try {

                // 2.将客户端发送的码流反序列化成对象,反射调用服务实现者,获取执行结果

                input = new ObjectInputStream(clent.getInputStream());

                String serviceName = input.readUTF();

                String methodName = input.readUTF();

                Class<?>[] parameterTypes = (Class<?>[]) input.readObject();

                Object[] arguments = (Object[]) input.readObject();

                Class serviceClass = serviceRegistry.get(serviceName);

                if (serviceClass == null) {

                    throw new ClassNotFoundException(serviceName + " not found");

                }

                Method method = serviceClass.getMethod(methodName, parameterTypes);

                Object result = method.invoke(serviceClass.newInstance(), arguments);

 

                // 3.将执行结果反序列化,通过socket发送给客户端

                output = new ObjectOutputStream(clent.getOutputStream());

                output.writeObject(result);

            } catch (Exception e) {

                e.printStackTrace();

            } finally {

                if (output != null) {

                    try {

                        output.close();

                    } catch (IOException e) {

                        e.printStackTrace();

                    }

                }

                if (input != null) {

                    try {

                        input.close();

                    } catch (IOException e) {

                        e.printStackTrace();

                    }

                }

                if (clent != null) {

                    try {

                        clent.close();

                    } catch (IOException e) {

                        e.printStackTrace();

                    }

                }

            }

 

        }

    }

}

  心跳监听类:

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

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

package com.cang.heartbeat;

 

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.lang.reflect.Method;

import java.net.InetSocketAddress;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.Iterator;

import java.util.Map;

import java.util.concurrent.ConcurrentHashMap;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.atomic.AtomicBoolean;

 

/**

 * 心跳监听保存信息

 *

 * @author cang

 * @create_time 2016-09-28 11:40

 */

public class HeartbeatLinstener {

 

    private ExecutorService executor = Executors.newFixedThreadPool(20);

 

    private final ConcurrentHashMap<String, Object> nodes = new ConcurrentHashMap<String, Object>();

    private final ConcurrentHashMap<String, Long> nodeStatus = new ConcurrentHashMap<String, Long>();

 

    private long timeout = 10 * 1000;

 

    // 服务器监听端口

    private int port = 8089;

 

    // 单例模式

    private static class SingleHolder {

        private static final HeartbeatLinstener INSTANCE = new HeartbeatLinstener();

    }

 

    private HeartbeatLinstener() {

    }

 

    public static HeartbeatLinstener getInstance() {

        return SingleHolder.INSTANCE;

    }

 

    public ConcurrentHashMap<String, Object> getNodes() {

        return nodes;

    }

 

    public void registerNode(String nodeId, Object nodeInfo) {

        nodes.put(nodeId, nodeInfo);

        nodeStatus.put(nodeId, System.currentTimeMillis());

    }

 

    public void removeNode(String nodeID) {

        if (nodes.containsKey(nodeID)) {

            nodes.remove(nodeID);

        }

    }

 

    // 检测节点是否有效

    public boolean checkNodeValid(String key) {

        if (!nodes.containsKey(key) || !nodeStatus.containsKey(key)) return false;

        if ((System.currentTimeMillis() - nodeStatus.get(key)) > timeout) return false;

        return true;

    }

 

    // 删除所有失效节点

    public void removeInValidNode() {

        Iterator<Map.Entry<String, Long>> it = nodeStatus.entrySet().iterator();

        while (it.hasNext()) {

            Map.Entry<String, Long> e = it.next();

            if ((System.currentTimeMillis() - nodeStatus.get(e.getKey())) > timeout) {

                nodes.remove(e.getKey());

            }

        }

    }

 

}

  心跳处理类接口:

1

2

3

public interface HeartbeatHandler {

    public Cmder sendHeartBeat(HeartbeatEntity info);

}

   心跳处理实现类:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

public class HeartbeatHandlerImpl implements HeartbeatHandler {

    public Cmder sendHeartBeat(HeartbeatEntity info) {

        HeartbeatLinstener linstener = HeartbeatLinstener.getInstance();

 

        // 添加节点

        if (!linstener.checkNodeValid(info.getNodeID())) {

            linstener.registerNode(info.getNodeID(), info);

        }

 

        // 其他操作

        Cmder cmder = new Cmder();

       cmder.setNodeID(info.getNodeID());

        // ...

 

        System.out.println("current all the nodes: ");

        Map<String, Object> nodes = linstener.getNodes();

        for (Map.Entry e : nodes.entrySet()) {

            System.out.println(e.getKey() + " : " + e.getValue());

        }

        System.out.println("hadle a heartbeat");

        return cmder;

    }

}

  测试类:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

public class HeartbeatTest {

 

    public static void main(String[] args) {

        new Thread(new Runnable() {

            public void run() {

                try {

                    ServiceCenter serviceServer = ServiceCenter.getInstance();

                    serviceServer.register(HeartbeatHandler.class, HeartbeatHandlerImpl.class);

                    serviceServer.start();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

        }).start();

        Thread client1 = new Thread(new HeartbeatClient());

        client1.start();

        Thread client2 = new Thread(new HeartbeatClient());

        client2.start();

    }

}

四、总结

    上面的代码还有很多不足的地方,希望有空能进行改善:

  1.  配置为硬编码;
  2.  命令类Cmder没有实际实现,返回的Cmder对象没有实际进行处理;

   其他小问题就暂时不管了,希望以后能重写上面的代码。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值