《ignite系列一》初识

616 阅读9分钟

ignit就是一款分布式内存库。日常使用的时候其实就是和其他的内存库功能类似,本质上都是利用内存缩短读取时间。无论数据源是何处来。

一、结构

整体结构

ignite整体分为服务端和客户端。而客户端又分为胖客户端(官方文档中只是叫客户端)和瘦客户端

服务端存储、计算、处理数据;胖客户端本质和服务端没有区别,但是其不具备服务端的存储功能,最多缓存部分数据(近缓存),其是和服务端一起构成一个集群,集群中一个个服务端/客户端视为一个个节点。

而客户端则是处于集群之外,只能存取集群内部节点数据,仅作为用户跳板来操作集群内部数据,瘦客户端与当前节点连接失败时,客户端可自动切换到可用节点(需要在客户端配置中提供用于故障转移节点的地址列表)。

胖客户端部署.png 瘦客户端访问.png

数据结构

ignite的数据结构可以分为两个维度:逻辑结构和物理存储结构。

在ignite的逻辑中,数据结构分为SQL表和键值对结构,虽然两者是等同的。

这也就给了我们两个方向去获取ignite数据:通过键获取值和sql语句。 在后续也会说明:通过键获取的存储数据对应通过sql语句获取,其中键与表名对等。通过键获取的存储数据也是一个个键值对,与表字段和字段值对等。 cache_table.png 但是,当数据作为表存储时,需要指定一个字段作为主键。

从物理存储逻辑上看,这两者是一致的。ignite的物理存储单元是数据条目(或表数据行),以二进制对象存储,数据条目的集合被分为:分区。分区均匀地分布在所有节点上。数据和分区之间以及分区和节点之间的映射方式都由关联函数控制。关联函数控制数据条目和分区以及分区和节点之间的映射。

ignite也提供了不同的分区模式:PARTITIONED和REPLICATED模式。

  • PARTITIONED模式将节点均匀的分布在每一个节点上,每个节点只存在部分分区,所有分区共同组成一个至多个完整缓存。

PARTITION.png

  • REPLICATED模式则是在一个节点中只有一个主分区,其余为分区副本。但是分区数和节点数并不一定要一致。在该分区模式下,一个节点即可提供完整缓存。

REPLICATED.png

二、基本使用

以ignite的2.12.0版本为例。服务端往往按需定制ignite的bean配置文件即可,而客户端则需要根据代码手动配置。

2.0.服务端

以使用服务端最基本的配置为例。ignite的bean实现格式是固定的,所有的配置都是放在唯一的一个bean配置中:

<bean id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
</bean>

开启的方式就是启动脚本ignite.cmd/bat 加上指定的配置类就可以,成功创建服务端节点会打节点信息。

服务端启动.jpg 下面说明一些服务端基本的配置:

  1. 日志配置 把日志提到最开始说是因为ignite的配置采用插件的方式。在ignite-core包中并不包含其他的功能,例如log4j的配置。其默认的是java.util.logging框架。以配置log4j为例,首先需要将ignite官方包libs目录下的optional目录中的ignite-log4j文件夹整体放入lib下即可。然后再配置:

    <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    
        <bean id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
        <!-- 日志ignite-log4j2 模块,(部分低版本需要ignite-cust)-->
            <property name="gridLogger">
                <bean class="org.apache.ignite.logger.log4j2.Log4J2Logger">
                    <constructor-arg type="java.lang.String" value="apache-ignite-2.12.0-bin/node-1-log4j2.xml"/>
                </bean>
            </property>
        </bean>
    </beans>
    
  2. 集群间节点发现方式配置 节点发现方式常用的有两种:zookeeper和tcp,其中以zookeeper使用最多。

    • zk发现配置
       <property name="discoverySpi">
          <bean class="org.apache.ignite.spi.discovery.zk.ZookeeperDiscoverySpi">
              <property name="zkConnectionString" value="127.0.0.1:2181,127.0.0.1:2191,127.0.0.1:2201"/>
              <property name="sessionTimeout" value="60000"/>
              <property name="zkRootPath" value="/ignite_cache"/>
              <property name="joinTimeout" value="30000"/>
          </bean>
      </property>
      
      • 其中zkConnectionString配置为zk的地址:端口。这个是必须配置的。
      • sessionTimeout表示与zk会话超时时间,注意该值配置最好要大于zk的tickTime * syncLimit时间,因为当zk超过该时间时会发起zk集群间节点会发起同步,如果ignite节的超时时间恰好到该时刻,很有可能导致某一zk节点(向其他zk节点发起同步状态)下的ignite节点误认为该zk以掉线,从而触发ignite的断尾保全机制:直接丢弃该部分igite节点。
      • zkRootPath则指定了ignite节点信息在zk的根路径名字。
    • tcp发现配置,基本是采用组播的方式
      <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="addresses">
                           <list>
                               <value>127.0.0.1:47500..47599</value>
                           </list>
                       </property>
                   </bean>
               </property>
       </bean>
      
      其中,TcpDiscoveryMulticastIpFinder类是tcp发现方式的核心类,其最低配置要求是addresses内的地址:端口范围,同一集群的节点配置必须一致。如果不一致,则会造成无意的集群隔离现象,使得原本的一个集群被分为多个互不干扰的集群。
  3. 针对瘦客户端连接的配置

    <property name="clientConnectorConfiguration">
     <bean class="org.apache.ignite.configuration.ClientConnectorConfiguration">
             <property name="port" value="23333"/>
         <property name="portRange" value="50"/>
         <property name="socketSendBufferSize" value="32768"/>
         <property name="socketReceiveBufferSize" value="32768"/>
     </bean>
     </property>
    
    

    客户端是处于集群外的,和服务端的通信需要另外配置。ClientConnectorConfiguration 就是管理瘦客户端的配置类。

    配置的最低要求是port,其指定该服务端与胖客户端的端口。此外,还可以配置portRange设置端口开启范围,当配置的端口被占用时,ignite会依次向后开启端口,直到有一个找到一个能用的端口或者全部不行后报错。

    此外,ocketSendBufferSizesocketReceiveBufferSize可以配置每次客户端查询时服务端收发的缓存大小。如果不配置,ignite的默认值都是0。

  4. 对等类配置,默认是不开启的 对于对等类的配置我也不是很懂,只知道官方是建议配置开启的。欢迎大佬来补充。

    个人认为开启后可以使得Java客户端可以互相通信。

    <property name="peerClassLoadingEnabled" value="true"/>
    

2.1.胖客户端

  • 开启客户端

    胖客户端可以认为是无法存储数据的服务端,但是其可以开启近缓存,缓存少量(相较于服务端的存储而言)热点数据,其配置也只需要设置ignite的节点模式为client即可:

    IgniteConfiguration cfg = new IgniteConfiguration();
    //客戶端方式启动
    cfg.setClientMode(true);
    

    同样,其也可以配置节点发现方式,以下是zk和tcp的发现方式配置:

    //设置节点发现
    //需要引入ignite-zookeeper包
    ZookeeperDiscoverySpi zkSpi = new ZookeeperDiscoverySpi();
    zkSpi.setZkConnectionString("127.0.0.1:2181");
    zkSpi.setSessionTimeout(30000);
    zkSpi.setZkRootPath("/ignite");
    zkSpi.setJoinTimeout(15000);
    cfg.setDiscoverySpi(zkSpi);
    
    
    /*------------------------*/
    //ip finder 找到本机上的节点
    TcpDiscoveryMulticastIpFinder ipFinder = new TcpDiscoveryMulticastIpFinder();
    ipFinder.setAddresses(Collections.singletonList("127.0.0.1:47500..47509"));
    cfg.setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(ipFinder));
    
    

    需要额外注意的是,客户端的zk配置的zk地址和节点路径需要和服务端一致,而tcp的地址也是如此。

    开启客户端也很简单,只需调用 Ignition 类的 start(IgniteConfiguration cfg) 方法即可。

    下面附上完整代码:

    package org.client
    
    import org.apache.ignite.Ignite;
    import org.apache.ignite.Ignition;
    import org.apache.ignite.configuration.IgniteConfiguration;
    import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
    import org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder;
    import org.apache.ignite.spi.discovery.zk.ZookeeperDiscoverySpi;
    
    import java.util.Collections;
    
    /**
     * 胖客户端演示
     */
    public class RichClient
    {
        private Ignite ignite;
    
        public Ignite getIgniteByTcpIp() {
            IgniteConfiguration cfg = new IgniteConfiguration();
            //客戶端方式启动
            cfg.setClientMode(true);
    
            //可以通过该类进行网络传输
            cfg.setPeerClassLoadingEnabled(true);
    
            //ip finder 找到本机上的节点
            TcpDiscoveryMulticastIpFinder ipFinder = new TcpDiscoveryMulticastIpFinder();
            ipFinder.setAddresses(Collections.singletonList("127.0.0.1:47500..47509"));
    
            cfg.setDiscoverySpi(new TcpDiscoverySpi().setIpFinder(ipFinder));
    
            // Starting the node
            ignite = Ignition.start(cfg);
            return ignite;
        }
    
        public Ignite getIgniteByZk() {
            IgniteConfiguration cfg = new IgniteConfiguration();
            cfg.setClientMode(true);
            cfg.setPeerClassLoadingEnabled(true);
    
            //设置节点发现
            //需要引入ignite-zookeeper包
            ZookeeperDiscoverySpi zkSpi = new ZookeeperDiscoverySpi();
            zkSpi.setZkConnectionString("127.0.0.1:2181");
            zkSpi.setSessionTimeout(30000);
            zkSpi.setZkRootPath("/ignite");
            zkSpi.setJoinTimeout(15000);
    
            cfg.setDiscoverySpi(zkSpi);
            return Ignition.start(cfg);
        }
    }
    
  • 常用的方法

    胖客户端常见创建和获取缓存的方法很简单:

    public void getIgniteByTcpIpTest() {
      final Ignite ignite = new RichClient().getIgniteByTcpIp();
    
      //如果缓存存在就销毁
      if (ignite.cacheNames().contains("cache1")) {
          ignite.destroyCache("cache1");
      }
      //创建缓存
      IgniteCache<String, String> firstCache = ignite.createCache("cache1");
      firstCache.put("1","演示1");
    
      //获取缓存,不存在就报错
      IgniteCache<String, String> cache = ignite.cache("cache1");
      String example = cache.get("1");
      System.out.println("第一次演示" + example);
    
      /*上述创建的方法在缓存存在时会报错,可以用下面的方法*/
      IgniteCache<String, String> sedCache = ignite.getOrCreateCache("cache2");
      sedCache.put("2","演示2");
      System.out.println("第二次演示" + sedCache.get("2"));
    }
    

    ignite缓存每次创建都是独立的,每个缓存都可以配置缓存策略等等。这就要用到CacheConfiguration,事实上如果将数据库加载至ignite中时,常常会用该类来制定每张表的缓存策略(每张表都归为独立的缓存)。

      public void igniteCreateCacheByConfig() {
          final Ignite ignite = new RichClient().getIgniteByTcpIp();
    
          /*通过缓存配置来设置改缓存策略,同时,该方式适用于sql存储
          * Person类也就三个String字段:name,age,number。这里就不详细列出了。
          */
          CacheConfiguration<String, Person> peopleCacheConfig = new CacheConfiguration<>("people");
          /*设置分区策略和该缓存备份*/
          peopleCacheConfig.setCacheMode(CacheMode.PARTITIONED).setBackups(1);
    
          /*创建缓存*/
          IgniteCache<String, Person> orCreateCache = ignite.getOrCreateCache(peopleCacheConfig);
          Person p1 = new Person("紬壮壮", "17", "17372121082");
          orCreateCache.put(p1.getNumber(),p1);
    
          /*-------------------------*/
    
          IgniteCache<String, Person> peopleCache = ignite.cache("people");
          /*通过索引来找到对应的记录*/
          System.out.println(peopleCache.get("17372121082").toString());
         
          peopleCache.remove("17372121082");
    
      }
    

2.2.瘦客户端

瘦客户端的使用和胖客户端类似,但是类完全不同。

  • 开启瘦客户端

    虽然官方建议用try-with-resources的方式来开启瘦客户端,但是通常客户端创建后再使用ignite期间是不关闭的,用单例或者连接池的方式管理都可以。

    public class ThinClient {
    
       /*这里用的是IgniteClient*/
       private IgniteClient thinClient;
    
       public IgniteClient initClient() {
    
           if (thinClient != null) {
               return thinClient;
           }
    
           synchronized (this) {
               if (thinClient != null) {
                   return thinClient;
               }
               //瘦客户端为轻量客户端,不支持配置zk搜寻节点
               ClientConfiguration clientConfig = new ClientConfiguration();
               //这里的配置是集群中指定的节点和通信端口
               clientConfig.setAddresses("127.0.0.1:23333");
    
               thinClient = Ignition.startClient(clientConfig);
           }
    
           return thinClient;
       }
    }
    

    注意到,瘦客户端用的是IgniteClient类,而对应的对应的客户端配置用的是ClientConfiguration,而非胖客户端的IgniteConfiguration。因为瘦客户端是独立于ignite集群,所以瘦客户端不能像胖客户端那样配置集群发现。但是,可以使用curator去读约定的ignite集群在zk里的配置,然后传入该配置类中。

    此外,需注意的是,瘦客户端配置的地址只是集群中的节点和端口,该端口要在服务端中额外配置,这一点在开始的服务端配置第三点已经示例的了。

    而开启客户端的方法也变为了startClient方法。

  • 常见方法

    瘦客户端的缓存类是ClientCache。其存储缓存则和胖客户端一致。

    public void AddressTest(){
        IgniteClient thinClient = new ThinClient().initClient();
    
        ClientCache<Object, Object> cache1 = thinClient.getOrCreateCache("thinClientCache1");
        //瘦客户端能够操作胖客户端大多数的操作,比如存入/读取缓存
        cache1.put("1","瘦客户端通过地址首次配置");
        //没有就报错
        cache1 = thinClient.cache("thinClientCache1");
        System.out.println(cache1.get("1"));
    
        /*-------------------------------*/
        /*采用缓存配置创建缓存,但是无法再配置阶段就指定键和缓存类型*/
        ClientCacheConfiguration peopleClientConfig = new ClientCacheConfiguration();
        peopleClientConfig.setName("Person")
                .setCacheMode(CacheMode.PARTITIONED)
                .setBackups(1);
    
        ClientCache<String, Person> cache = thinClient.getOrCreateCache(peopleClientConfig);
        Person daiwei = new Person("呆唯", "17", "16526771092");
        cache.put(daiwei.getNumber(),daiwei);
    
        //另一种获取缓存方式
        ClientCache<String, Person> peopleCache = thinClient.cache("Person");
        System.out.println(peopleCache.get("16526771092").toString());
        peopleCache.remove("16526771092");
    }
    

    瘦客户端的缓存配置类是ClientCacheConfiguration,其无法像胖客户端那样创建时就制定键和缓存类型,需要在创建缓存时单独指定。其余缓存策略和胖客户端一致。