06.CAT-集群&机制

CAT集群,不同以往的ZK集群,但是类似于Redis的Cluster集群。
CAT服务定位:
    1.无中心节点(目的是避开集群的选举);
    2.永久性的提供服务,前提是集群只有有活的节点

6.01 集群&机制

CAT的集群本身既是服务端,又是客户端。
Client 定时向 Server 发送请求,汇报自己的状态。
Server 收集 Client 进行汇总数据,监控 Client 的健康状态 ,如果出现异常,进行 告警通知项目负责人

6.02 获取集群信息

部署服务端代码,cat-2.0.0.war,本身的war包是含有客户端的cat-client-2.0.0.jar
cat-client模块通过读取 /data/appdatas/cat/client.xml

ClientConfigManager 组件初始化

public class DefaultClientConfigManager implements LogEnabled, ClientConfigManager, Initializable {
    private static final String CAT_CLIENT_XML = "/META-INF/cat/client.xml";

    private static final String PROPERTIES_CLIENT_XML = "/META-INF/app.properties";
    
    private static final String XML = "/data/appdatas/cat/client.xml";
    
    ....
    
    @Override
    public void initialize() throws InitializationException {
        File configFile = new File(XML);
        
        initialize(configFile);
    }
    
        @Override
   public void initialize(File configFile) throws InitializationException {
        try {
            ClientConfig globalConfig = null;
            ClientConfig clientConfig = null;

            if (configFile != null) {
                if (configFile.exists()) {
                    String xml = Files.forIO().readFrom(configFile.getCanonicalFile(), "utf-8");

                    globalConfig = DefaultSaxParser.parse(xml);
                    m_logger.info(String.format("Global config file(%s) found.", configFile));
                } else {
                    m_logger.warn(String.format("Global config file(%s) not found, IGNORED.", configFile));
                }
            }
.....


  • ClientConfigManager 服务在被lookup时,进行 initialize 方法调用
  • initialize 初始化,首先加载配置文件 /data/appdatas/cat/client.xml

/data/appdatas/cat/client.xml 配置

<?xml version="1.0" encoding="utf-8"?>

<config mode="client" xmlns:xsi="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="config.xsd">
    <servers>
        <!-- Local mode for development -->
        <server ip="127.0.0.1" port="2280" http-port="8080" />
        <!-- If under production environment, put actual server address as list. -->
        <!--
            <server ip="192.168.7.71" port="2280" />
            <server ip="192.168.7.72" port="2280" />
        -->
    </servers>
</config>
  • xml 可以配置 servers,即集群中所有节点的配置(ip、tcp和http-port)

  • 通过 cat-client 模块的加载配置获取所有的集群节点信息

6.03 持有自身客户端信息

客户代码信息读取

public class DefaultClientConfigManager implements LogEnabled, ClientConfigManager, Initializable {
    private static final String CAT_CLIENT_XML = "/META-INF/cat/client.xml";

    private static final String PROPERTIES_CLIENT_XML = "/META-INF/app.properties";
    
    
@Override
    public void initialize() throws InitializationException {
        .....       
        initialize(configFile);
    }

    @Override
   public void initialize(File configFile) throws InitializationException {
        try {
            ClientConfig globalConfig = null;
            ClientConfig clientConfig = null;

            ......

            // load the client configure from Java class-path
            clientConfig = loadConfigFromEnviroment();

            if (clientConfig == null) {
                clientConfig = loadConfigFromXml();
            }
            // merge the two configures together to make it effected
            if (globalConfig != null && clientConfig != null) {
                globalConfig.accept(new ClientConfigMerger(clientConfig));
            }

            if (clientConfig != null) {
                clientConfig.accept(new ClientConfigValidator());
            }

            m_config = clientConfig;
        } catch (Exception e) {
            throw new InitializationException(e.getMessage(), e);
        }
   }
}

    private ClientConfig loadConfigFromEnviroment() {
        String appName = loadProjectName();

        if (appName != null) {
            ClientConfig config = new ClientConfig();

            config.addDomain(new Domain(appName));
            return config;
        }
        return null;
    }
    private String loadProjectName() {
        String appName = null;
        InputStream in = null;
        try {
            in = Thread.currentThread().getContextClassLoader().getResourceAsStream(PROPERTIES_CLIENT_XML);

            if (in == null) {
                in = Cat.class.getResourceAsStream(PROPERTIES_CLIENT_XML);
            }
            ....
    }
  • 读取配置 /META-INF/app.properties

app.properties

app.name=cat

6.04 client->Server 发送消息

client的message需要发送

public class DefaultMessageManager extends ContainerHolder implements MessageManager, Initializable, LogEnabled {

    @Inject
    private TransportManager m_transportManager;
    
    public void flush(MessageTree tree) {
        if (tree.getMessageId() == null) {
            tree.setMessageId(nextMessageId());
        }

        MessageSender sender = m_transportManager.getSender();

        if (sender != null && isMessageEnabled()) {
            sender.send(tree);

            reset();
        } else {
            m_throttleTimes++;

            if (m_throttleTimes % 10000 == 0 || m_throttleTimes == 1) {
                m_logger.info("Cat Message is throttled! Times:" + m_throttleTimes);
            }
        }
    }
    ......
通过 MessageSender 进行消息的 send 发送
public class TcpSocketSender implements Task, MessageSender, LogEnabled {

    private transient boolean m_active;

    @Override
    public void initialize() {
        int len = getQueueSize();

        m_queue = new DefaultMessageQueue(len);
        m_atomicTrees = new DefaultMessageQueue(len);

        m_manager = new ChannelManager(m_logger, m_serverAddresses, m_queue, m_configManager, m_factory);

        Threads.forGroup("cat").start(this);
        Threads.forGroup("cat").start(m_manager);
        Threads.forGroup("cat").start(new MergeAtomicTask());
    }
  
    @Override
    public void run() {
        m_active = true;

        try {
            while (m_active) {
                ChannelFuture channel = m_manager.channel();

                if (channel != null && checkWritable(channel)) {
                    try {
                        MessageTree tree = m_queue.poll();

                        if (tree != null) {
                            sendInternal(tree);
                            tree.setMessage(null);
                        }

                    } catch (Throwable t) {
                        m_logger.error("Error when sending message over TCP socket!", t);
                    }
                } else {
                    long current = System.currentTimeMillis();
                    long oldTimestamp = current - HOUR;

                    while (true) {
                        try {
                            MessageTree tree = m_queue.peek();

                            if (tree != null && tree.getMessage().getTimestamp() < oldTimestamp) {
                                MessageTree discradTree = m_queue.poll();

                                if (discradTree != null) {
                                    m_statistics.onOverflowed(discradTree);
                                }
                            } else {
                                break;
                            }
                        } catch (Exception e) {
                            m_logger.error(e.getMessage(), e);
                            break;
                        }
                    }

                    TimeUnit.MILLISECONDS.sleep(5);
                }
            }
        } catch (InterruptedException e) {
            // ignore it
            m_active = false;
        }
    }

    @Override
    public void send(MessageTree tree) {
        if (isAtomicMessage(tree)) {
            boolean result = m_atomicTrees.offer(tree, m_manager.getSample());

            if (!result) {
                logQueueFullInfo(tree);
            }
        } else {
            boolean result = m_queue.offer(tree, m_manager.getSample());

            if (!result) {
                logQueueFullInfo(tree);
            }
        }
    }

client与server通讯建立

public class ChannelManager implements Task {

    public ChannelManager(Logger logger, List<InetSocketAddress> serverAddresses, MessageQueue queue,
            ClientConfigManager configManager, MessageIdFactory idFactory) {
        m_logger = logger;
        m_queue = queue;
        m_configManager = configManager;
        m_idfactory = idFactory;

        EventLoopGroup group = new NioEventLoopGroup(1, new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r);
                t.setDaemon(true);
                return t;
            }
        });

        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group).channel(NioSocketChannel.class);
        bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
        bootstrap.handler(new ChannelInitializer<Channel>() {
            @Override
            protected void initChannel(Channel ch) throws Exception {
            }
        });
        m_bootstrap = bootstrap;

        String serverConfig = loadServerConfig();

        if (StringUtils.isNotEmpty(serverConfig)) {
            List<InetSocketAddress> configedAddresses = parseSocketAddress(serverConfig);
            ChannelHolder holder = initChannel(configedAddresses, serverConfig);

            if (holder != null) {
                m_activeChannelHolder = holder;
            } else {
                m_activeChannelHolder = new ChannelHolder();
                m_activeChannelHolder.setServerAddresses(configedAddresses);
            }
        } else {
            ChannelHolder holder = initChannel(serverAddresses, null);

            if (holder != null) {
                m_activeChannelHolder = holder;
            } else {
                m_activeChannelHolder = new ChannelHolder();
                m_activeChannelHolder.setServerAddresses(serverAddresses);
                m_logger.error("error when init cat module due to error config xml in /data/appdatas/cat/client.xml");
            }
        }
    }
    @Override
    public void run() {
        while (m_active) {
            // make save message id index asyc
            m_idfactory.saveMark();
            checkServerChanged();

            ChannelFuture activeFuture = m_activeChannelHolder.getActiveFuture();
            List<InetSocketAddress> serverAddresses = m_activeChannelHolder.getServerAddresses();

            doubleCheckActiveServer(activeFuture);
            reconnectDefaultServer(activeFuture, serverAddresses);

            try {
                Thread.sleep(10 * 1000L); // check every 10 seconds
            } catch (InterruptedException e) {
                // ignore
            }
        }
    }
  • ChannelManagerTcpSocketSenderinitialize 实例化
  • ChannelManager 的 线程 run 方法

6.05 server 处理消息

server 只是对埋点消息统一处理

6.02 小结

  • 原理很简单,client 找到需要通讯的 server ,建立 Netty 连接,发送请求
  • 服务端,解析请求,监控频率下的请求次数
posted @ 2021-02-10 10:40  可可逗豆  阅读(142)  评论(0)    收藏  举报