在前两篇文章中,我们把Ignite集群当做一个黑盒子,用二进制包自带的脚本启动Ignite节点后,我们用不同的客户端连接上Ignite进行操作,展示了Ignite作为一个分布式内存缓存,内存数据库的基本功能。从这篇文章开始,让我们打开这个黑盒子,逐步的深入到Ignite内部了解更多的特性。

Ignite集群没有采用master/slave架构设计。在集群里,每个节点都是平等的,并且可以互相通讯,这样的架构保证Ignite集群可以添加,移除节点,对集群的内存容量进行不间断的扩容/减容。也使得Ignite集群有很强的容错能力,可以快速的检测到一个或多个失效节点并恢复。 但在前面的文章中,我们提到过在Ignite集群里有client和server节点,还有thin client。刚开始我也不理解为什么要引入这么多不同角色的节点,但Ignite作为一个数据网格,计算网格以及服务网格,不同角色的节点在不同的场景下都有其作用。 这篇文章里,我们先比较一下client和server节点有什么不同;然后介绍下不同场景下,应该采用什么样的节点来搭建集群;最后我们看看如何在你自己的Java程序里启动一个server或者client节点。

Client和Server节点比较


  • Serve节点:存储数据,参与缓存,执行计算和流处理,部署服务网格。换句话说,server节点是功能最全的节点。默认情况下,启动一个Ignite节点都是以server节点的角色启动。
  • Client节点:不存储数据,但是可以通过Ignite APIs连接到server节点上,进行缓存的读写,参与计算任务,流处理,事务和服务网格。和server节点不同,如果需要把一个节点作为client节点,需要修改默认的配置。Client节点和server节点同时组成了Ignite集群,所以它们可以相互发现,相互连接。
  • Thin client:上一篇文章我们做过介绍,thin client不会加入Ignite的集群拓扑,它也不存储数据,也不参与执行计算和流处理。和Ignite原生的client节点不同,它并不感知集群的拓扑结构变化(增加/删除节点后,thin client并不知道),而且一次只能和Ignite集群中的一个节点连接通讯,当该节点失效后,它会从事先配置好节点列表中挑选一个新的节点,重新连接。

下图就展示了不同节点能提供的能力,以及它们互相之间的连接关系:

Thin client是跑在Ignite集群外的,它只能连接集群中某些节点,所有的操作和数据传输都需要通过这些节点。Client和server节点组成了Ignite集群的拓扑,它们之间是可以互相发现以及互相通讯的,可以充分利用不同节点间的带宽进行数据传输。除了不能存储数据,client和server节点基本一样。那为什么Ignite还需要引入client和server不同的节点呢?Ignite同时提供了数据网格,计算网格和服务网格服务,通过client和server节点,可以很方便的实现存储和计算分离的架构。设想一下,如果所有服务都部署在同一组节点来提供,如果计算任务需要消耗大量系统资源,或者需要升级计算/服务网格,势必要影响要影响数据服务。反之,对数据网格的减容,扩容也会影响计算和服务网格。因此,我们可以将计算网格和服务网格部署在client节点上,而数据网格部署在server节点上,这样保证了计算和数据服务不会互相竞争资源,而且可以独立的对计算和数据网格进行减容/扩容。当然,这么做的一个缺点client节点都需要通过server节点获取数据,对一些追求高性能的计算任务来说,网络延时和带宽就有可能成为瓶颈。对于这种场景,我们可以将client节点和server节点部署在同一台主机上,减少一部分的网络传输。

在应用程序中启动Ignite server/client节点


前两篇文章,我们通过二级制安装包中的脚本启动几个server节点,组成Ignite集群,现在让我们来看看怎么在自己的代码里启动一个server节点或者是client节点。

启动server节点代码

我们先来看看启动server节点的代码:

public class IgniteServerNodeExample {
    public static void main(String[] args) {
        Ignite ignite;

        if(args.length == 1 && !args[0].isEmpty())
        {
            //如果启动时指定了xml配置文件,则用指定的配置文件
            System.out.println("Use " + args[0] + " to start.");
            ignite = Ignition.start(args[0]);
        }
        else
        {
            //如果启动时没指定配置文件,则生成一个配置文件
            System.out.println("Create an IgniteConfiguration to start.");
            TcpDiscoverySpi spi = new TcpDiscoverySpi();
            TcpDiscoveryMulticastIpFinder ipFinder = new TcpDiscoveryMulticastIpFinder();
            ipFinder.setMulticastGroup("224.0.0.251");
            spi.setIpFinder(ipFinder);
            IgniteConfiguration cfg = new IgniteConfiguration();
            cfg.setDiscoverySpi(spi);
            ignite = Ignition.start(cfg);
        }

        // 创建一个TEST缓存并写入一些数据, key是城市的名字,value是省的名字
        IgniteCache<String, String> cityProvinceCache = ignite.getOrCreateCache("TEST");
        cityProvinceCache.put("Edmonton", "Alberta");
        cityProvinceCache.put("Markham", "Ontario");
        cityProvinceCache.put("Montreal", "Quebec");
    }
}

在启动Ignite节点时,我们需要传入节点的配置信息。代码里我们用了两种方式:1)如果启动时传入一个xml配置文件路径,我们就用该配置文件启动节点;2)如果没指定配置文件,我们就在代码里生成一个IgniteConfiguration对象,并配置Ignite节点用multicast的方式发现局域网内的其他节点并组成集群(除了multicast,Ignite还支持指定静态ip地址或者用zookeeper进行节点探测发现,具体的配置方式会有一篇文章专门来介绍)。xml配置文件中的每个配置项都可以通过IgniteConfiguration对象用代码进行配置,所以二者是等效的。和代码里IgniteConfiguration对象等效的xml配置如下:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
        <property name="discoverySpi">
            <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
                <property name="ipFinder">
                    <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder">
                        <property name="multicastGroup" value="224.0.0.251"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
</beans>

在用Ignite.start()启动节点后,我们在server节点上创建了一个名字叫“TEST”的缓存,缓存的key是城市的名字(String),value是城市所在省份的名字(String)。 然后往缓存里插入三条数据。

启动Client节点的代码

下面是启动client节点的代码:

public class IgniteClientNodeExample {
    public static void main(String[] args) {
        Ignite ignite;
        if(args.length == 1 && !args[0].isEmpty())
        {
            //如果启动时指定了配置文件,则用指定的配置文件
            System.out.println("Use " + args[0] + " to start.");
            ignite = Ignition.start(args[0]);
        }
        else
        {
            //如果启动时没指定配置文件,则生成一个配置文件
            System.out.println("Create an IgniteConfiguration to start.");
            TcpDiscoverySpi spi = new TcpDiscoverySpi();
            TcpDiscoveryMulticastIpFinder ipFinder = new TcpDiscoveryMulticastIpFinder();
            ipFinder.setMulticastGroup("224.0.0.251");
            spi.setIpFinder(ipFinder);
            IgniteConfiguration cfg = new IgniteConfiguration();
            cfg.setDiscoverySpi(spi);
            //显式配置client模式启动该节点.
            cfg.setClientMode(true);
            ignite = Ignition.start(cfg);
        }
        //从ignite中读取缓存,并读取数据
        IgniteCache<String, String> cityProvinceCache = ignite.getOrCreateCache("TEST");
        System.out.println("Montreal is in " + cityProvinceCache.get("Montreal"));
        System.out.println("Edmonton is in " + cityProvinceCache.get("Edmonton"));
        System.out.println("Markham is in " + cityProvinceCache.get("Markham"));
        System.out.println("Toronto is in " + cityProvinceCache.get("Toronto"));
    }
}

和server节点的代码类似,启动client节点时我们同样可以传入一个xml配置文件,或者在代码中生成一个IgniteConfiguration对象,然后进行配置。和server节点不同的是,如果需要启动一个client节点,需要显式的配置client modetrue(对应代码为cfg.setClientMode(true))。和代码里等效的xml配置文件如下:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
        <property name="discoverySpi">
            <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
                <property name="ipFinder">
                    <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder">
                        <property name="multicastGroup" value="224.0.0.251"/>
                    </bean>
                </property>
            </bean>
        </property>
        <property name="clientMode" value="true"/>
    </bean>
</beans>

在用Ignite.start()启动节点后,client节点从Ignite集群中获取名为“TEST”的缓存,然后试着从缓存里读取不同城市对应的省份名字。

Server和client节点的启动

启动server和client时有两点需要注意的:

  • Client节点启动时必须有可用的server节点在Ignite集群中,所以启动的顺序是先server节点后client节点。当然,在某些情况下,如果需要client节点不管是否有可用的server节点都必须要成功启动,则需要在client节点上配置强制服务端发现模式
  • 如果通过传入xml配置文件的方式启动节点,则需要在CLASS_PATH中包含ignite-spring模块的jar文件(如果你通过二级制包安装了Ignite,ignite-spring模块就在IGNITE_HOME/lib/ignite-spring目录下)。在maven成功编译代码后,我用下面的命令启动server节点:
$cd ignite-client-server-node-example/target
$java -cp ./ignite-client-server-node-example-1.0-SNAPSHOT.jar:$IGNITE_HOME/libs/*:$IGNITE_HOME/libs/ignite-spring/* IgniteServerNodeExample ../src/main/resources/ignite-server-config.xml

同理,用下面的命令启动client节点:

$cd ignite-client-server-node-example/target
$java -cp ./ignite-client-server-node-example-1.0-SNAPSHOT.jar:$IGNITE_HOME/libs/*:$IGNITE_HOME/libs/ignite-spring/* IgniteClientNodeExample ../src/main/resources/ignite-client-config.xml

在启动client节点前后,我们可以稍微留意下server节点关于Ignite集群拓扑结构的相关日志,在启动client节点前:

[00:55:52] Ignite node started OK (id=42996817)
[00:55:52] Topology snapshot [ver=1, servers=1, clients=0, CPUs=2, offheap=1.6GB, heap=1.7GB]
[00:55:52]   ^-- Node [id=42996817-925A-4FF5-8B5D-4B80D4774905, clusterState=ACTIVE]
[00:55:52] Data Regions Configured:
[00:55:52]   ^-- default [initSize=256.0 MiB, maxSize=1.6 GiB, persistenceEnabled=false]

集群的拓扑版本为1,集群内有1个server节点,0个client节点。 在启动client节点后:

[00:59:06] Topology snapshot [ver=2, servers=1, clients=1, CPUs=2, offheap=1.6GB, heap=3.5GB]
[00:59:06]   ^-- Node [id=42996817-925A-4FF5-8B5D-4B80D4774905, clusterState=ACTIVE]
[00:59:06] Data Regions Configured:
[00:59:06]   ^-- default [initSize=256.0 MiB, maxSize=1.6 GiB, persistenceEnabled=false]

集群的拓扑版本为2,集群内有1个server节点,1个client节点,这代表我们成功的启动了一个server节点和client节点。

在client节点启动后,它会试着读取server节点写入“TEST”缓存的数据,所以我们应该可以在client节点日志中看到以下的输出:

Montreal is in Quebec
Edmonton is in Alberta
Markham is in Ontario
Toronto is in null

除了Toronto以外,其他的城市都能读到对应的省份信息。这也验证了client节点读取的就是server节点写入的那份缓存。

总结


我们介绍了Ignite集群节点中两个不同的角色--server和client,并比较了它们之间的不同。我们还展示了如何在java程序中启动server和client节点。 完整的代码和maven工程可以在这里找到。 Server和client对应的xml配置文件在src/main/resources目录下。

Ignite集群的配置,节点发现以及集群管理,感兴趣的同学可以参考下官方文档。在后面会有专门的文章介绍细节和例子。从下一篇文章开始,让我们先聚焦在Ignite的数据网格服务上,看看和其他key/value缓存系统相比,Ignite提供了哪些不一样的能力。

12-10 18:58