简介

​ 服务网格本质上还是远程方法调用(RPC),而在ignite中注册的服务本质体现还是以cache的形式存在,集群中的节点可以相互调用部署在其它节点上的服务,而且ignite集群会负责部署服务的容错和负载均衡,并且服务可以在集群节点间传播(前提是节点类路径中包含服务类),并且给服务的部署方式提供了多种选择。

ignite服务部署的最常见的两种方式: 集群单例和节点单例



节点单例(deployNodeSingleton) : 在节点范围内的单例,表示针对同一个服务集群中每个节点上只有一个实例。当在集群组中启动了新的节点时,Ignite会自动地在每个新节点上部署一个新的服务实例。

集群单例(deployClusterSingleton) :在集群范围内的单例,表示一个服务在整个集群中只有一个实例。当部署该服务的节点故障或者停止时,Ignite会自动在另一个节点上重新部署该服务。然而,如果部署该服务的节点仍然在网络中,那么服务会一直部署在该节点上,除非拓扑发生了变化。

服务节点

  • 定义服务
public interface MyCounterService {
/**
* Increment counter value and return the new value.
*/
int increment() throws CacheException;
/**
* Get current counter value.
*/
int get() throws CacheException;
}
@Component
public class MyCounterServiceImpl implements Service, MyCounterService {
/** Auto-injected instance of Ignite. */
@IgniteInstanceResource
private Ignite ignite;
/** Distributed cache used to store counters. */
private IgniteCache<String, Integer> cache;
/** Service name. */
private String svcName;
/**
* Service initialization.
*/
@Override public void init(ServiceContext ctx) {
// Pre-configured cache to store counters.
cache = ignite.cache("myCounterCache");
svcName = ctx.name();
System.out.println("Service was initialized: " + svcName);
}
/**
* Cancel this service.
*/
@Override public void cancel(ServiceContext ctx) {
// Remove counter from cache.
cache.remove(svcName);
System.out.println("Service was cancelled: " + svcName);
}
/**
* Start service execution.
*/
@Override public void execute(ServiceContext ctx) {
// Since our service is simply represented by a counter
// value stored in cache, there is nothing we need
// to do in order to start it up.
System.out.println("Executing distributed service: " + svcName);
}
@Override public int get() throws CacheException {
Integer i = cache.get(svcName);
return i == null ? 0 : i;
}
@Override public int increment() throws CacheException {
return cache.invoke(svcName, new CounterEntryProcessor());
}
/**
* Entry processor which atomically increments value currently stored in cache.
*/
private static class CounterEntryProcessor implements EntryProcessor<String, Integer, Integer> {
@Override public Integer process(MutableEntry<String, Integer> e, Object... args) {
int newVal = e.exists() ? e.getValue() + 1 : 1;
// Update cache.
e.setValue(newVal);
return newVal;
}
}
}
  • 配置服务节点过滤器并注册服务
public class ServiceNodeFilter implements IgnitePredicate<ClusterNode>{

    public boolean apply(ClusterNode node) {
Boolean dataNode = node.attribute("service.node");
return dataNode != null && dataNode;
}
}
<property name="userAttributes">
<map key-type="java.lang.String" value-type="java.lang.Boolean">
<entry key="service.node" value="true"/> <!--服务节点属性-->
</map>
</property>
<property name="serviceConfiguration">
<list>
<!--Setting up MaintenanceService. -->
<bean class="org.apache.ignite.services.ServiceConfiguration">
<!-- Unique service name -->
<property name="name" value="myCounterService"/> <!-- Service implementation's class -->
<property name="service">
<!--<bean class="org.cord.ignite.servicegrid.MyCounterServiceImpl"/>-->
<ref bean="myCounterServiceImpl" />
</property> <!-- Only one instance of the service will be deployed cluster wide. -->
<property name="totalCount" value="1"/> <!-- Only one instance of the service can be deployed on a single node. -->
<property name="maxPerNodeCount" value="1"/> <!-- Enabling a special nodes filter for this service.-->
<property name="nodeFilter">
<bean class="org.cord.ignite.initial.ServiceNodeFilter"/>
</property>
</bean>
</list>
</property>
  • 调用服务
    @GetMapping("/test1")
public String test1() {
//分布式计算如果不指定集群组的话则会传播到所有节点
IgniteCompute compute = ignite.compute(ignite.cluster().forAttribute("service.node", true));
// IgniteCompute compute = ignite.compute(); //未部署服务的节点会抛出空指针
compute.run(new IgniteRunnable() {
@ServiceResource(serviceName = "myCounterService", proxySticky = false) //非粘性代理
private MyCounterService counterService; @Override
public void run() {
int newValue = counterService.increment();
System.out.println("Incremented value : " + newValue);
}
});
return "all executed.";
}

分布式计算默认会传播到集群中的所有节点,如果某个节点没有部署相关服务,则调此服务负载均衡到该节点的时候会报空指针异常,因为该节点找不到此服务。针对此情景有两种解决办法:

1.获取分布式计算对象的时候过滤节点,则计算只会传播到过滤后的节点上

ignite.compute(ignite.cluster().forAttribute("service.node", true))

2.设置粘性代理

如果代理是粘性的,Ignite会总是访问同一个集群节点的服务,如果代理是非粘性的,那么Ignite会在服务部署的所有集群节点内对远程服务代理的调用进行负载平衡。

服务网格故障转移

只有在分布式计算中使用服务网格调用才能实现服务调用故障转移

所谓故障转移,也就是服务调用过程中节点宕机,这时候会在其它节点继续执行。另外,如果服务是集群单例的话,那么如果节点宕机,首先发生的是服务的故障转移,这个时候分布式计算的故障转移会出错,因为其它节点的服务不一定会已经初始化成功。所以如果要保证服务调用能故障转移,最好在服务部署的时候保证服务是集群内有多个实例,并且在不同的节点,这样在节点故障的时候进行服务调用可以进行故障转移。

分布式计算中使用服务网格(可以故障转移):

IgniteCompute compute = ignite.compute(ignite.cluster().forServers());
compute.run(new IgniteRunnable() {
@ServiceResource(serviceName = "simpleMapService", proxyInterface = SimpleMapService.class, proxySticky = true)
private SimpleMapService simpleMapService; @Override
public void run() {
if (simpleMapService != null) {
Object ret = simpleMapService.get("sleep");
} else {
System.out.println("simpleMapService is null");
}
}
});

正常调用服务网格(无法故障转移):

        Ignite ignite = Ignition.ignite();
IgniteServices svcs = ignite.services(ignite.cluster().forRemotes());
//非粘性,负载均衡
SimpleMapService sms = svcs.serviceProxy("simpleMapService", SimpleMapService.class, false);
Object ret = sms.get("sleep");

数据节点

ignite节点可以配置成server模式或者client模式,主要区别在于client模式无法存储数据,而server能存储数据,但是无法更细粒度控制数据,比如控制数据在某部分节点上存储。通过节点过滤器可以实现集群中划分部分节点作为某缓存的节点。

注意:如果一个节点本身是client模式,那么即使这个节点配置成了数据节点,它也是无法存储数据的。

1,实现数据节点过滤器

public class DataNodeFilter implements IgnitePredicate<ClusterNode>{

    public boolean apply(ClusterNode node) {
Boolean dataNode = node.attribute("data.node");
return dataNode != null && dataNode;
}
}

该过滤器通过检查节点属性中是否含有data.node==true的过滤标志来确定该节点是否会被当做数据节点。

过滤器的实现类需要放到每个节点的classpath路径下,不管该节点是否会成为数据节点。

2,缓存配置中添加过滤器

<bean class="org.apache.ignite.configuration.CacheConfiguration">
<property name="name" value="student"/>
<property name="cacheMode" value="REPLICATED"/>
<property name="backups" value="1"/>
<property name="atomicityMode" value="ATOMIC"/>
<!--Force cache to return the instance that is stored in cache instead of creating a copy. -->
<property name="copyOnRead" value="false"/>
<property name="dataRegionName" value="Default_Region"/>
<property name="sqlSchema" value="PUBLIC"/>
<property name="indexedTypes">
<list>
<value>java.lang.Long</value>
<value>org.cord.ignite.data.domain.Student</value>
</list>
</property>
<property name="nodeFilter"> <!--配置节点过滤器-->
<bean class="org.cord.ignite.initial.DataNodeFilter"/>
</property>
</bean>
这个过滤器会在缓存启动时调用,它会定义一个要存储缓存数据的集群节点的子集--数据节点。同样的过滤器在网络拓扑发生变化时也会被调用,比如新节点加入集群或者旧节点离开集群。

3,为需要作为数据节点的节点添加过滤标志属性

<property name="userAttributes">
<map key-type="java.lang.String" value-type="java.lang.Boolean">
<entry key="data.node" value="true"/> <!--数据节点属性-->
</map>
</property>

示例:启动两个节点,其中一个cache配置了过滤器,并且只有一个节点添加了过滤标志,最后数据的存储状态如下:

visor> cache
Time of the snapshot: 11/14/18, 21:57:06
+=========================================================================
| Name(@) | Mode | Nodes | Entries (Heap / Off-heap) |
+=========================================================================
| myCounterCache(@c0) | REPLICATED | 2 | min: 0 (0 / 0) |
| | | | avg: 0.50 (0.00 / 0.50) |
| | | | max: 1 (0 / 1) |
+---------------------+------------+-------+-----------------------------+
| student(@c1) | REPLICATED | 1 | min: 500 (0 / 500) |
| | | | avg: 500.00 (0.00 / 500.00) |
| | | | max: 500 (0 / 500) |
+-------------------------------------------------------------------------

可见student缓存只分布在一个节点上了,并没有像普通情况一样发生数据的再平衡,说明节点的过滤器起作用了。

​ 通过节点过滤器,可以将数据节点和服务节点进行分离,因为部署新服务需要部署新的服务相关的包,涉及到重启之类的,这样便于维护。另外,通过部署一系列的服务,这就形成了一套微服务的解决方案,而且不用考虑负载均衡和容错处理,这些ignite都会处理,我们需要做的就是实现服务并将服务部署到ignite集群,就能实现服务的拆分,和服务的治理,达到微服务的效果。

附:

完整代码参考 https://github.com/cording/ignite-example

apache ignite系列(六): 服务网格的更多相关文章

  1. apache ignite系列(八):问题汇总

    1,java.lang.ClassNotFoundException Unknown pair 1.Please try to turn on isStoreKeepBinary in cache s ...

  2. apache ignite系列(三):数据处理(数据加载,数据并置,数据查询)

    ​ 使用ignite的一个常见思路就是将现有的关系型数据库中的数据导入到ignite中,然后直接使用ignite中的数据,相当于将ignite作为一个缓存服务,当然ignite的功能远不止于此,下面以 ...

  3. apache ignite系列(一): 简介

    apache-ignite简介(一) 1,简介 ​ ignite是分布式内存网格的一种实现,其基于java平台,具有可持久化,分布式事务,分布式计算等特点,此外还支持丰富的键值存储以及SQL语法(基于 ...

  4. apache ignite系列(二):配置

    ignite有两种配置方式,一种是基于XML文件的配置,一种是基于JAVA代码的配置: 这里将ignite常用的配置集中罗列出来了,一般建议使用xml配置. 1,基于XML的配置 <beans ...

  5. apache ignite系列(九):ignite调优

    1,配置文件调优 1.1 设置页面大小(pagesize) 先查看系统pagesiz,使用PAGE_SIZE或者PAGESIZE # getconf PAGE_SIZE 4096 # getconf ...

  6. apache ignite系列(九):使用ddl和dml脚本初始化ignite并使用mybatis查询缓存

    博客又断了一段时间,本篇将记录一下基于ignite对jdbc支持的特性在实际使用过程中的使用. 使用ddl和dml脚本初始化ignite 由于spring-boot中支持通过spring.dataso ...

  7. apache ignite系列(四):持久化

    ignite持久化与固化内存 1.持久化的机制 ignite持久化的关键点如下: ignite持久化可防止内存溢出导致数据丢失的情况: 持久化可以定制化配置,按需持久化; 持久化能解决在大量缓存数据情 ...

  8. apache ignite系列(五):分布式计算

    ignite分布式计算 在ignite中,有传统的MapReduce模型的分布式计算,也有基于分布式存储的并置计算,当数据分散到不同的节点上时,根据提供的并置键,计算会传播到数据所在的节点进行计算,再 ...

  9. Apache Kafka系列(六)客制化Serializer和Deserializer

    已经迁移,请移步:http://www.itrensheng.com/archives/apache-kafka-repartition

随机推荐

  1. 工作中常见的五种技术leader

    力不从心型 在工作中有种技术leader,总认为自己是最好的.在方案设计的时候,自己有一种方案,下属有一种方案.leader非要别人听他的.如果两种方案没有优劣之分,比较建议的做法是让真正实施的人按照 ...

  2. arukas 樱花免费docker容器获取IP和端口

    arukas 樱花免费docker容器,可以安装linux系统,但是每隔一段时间会重启,重启以后IP地址和映射到公网的端口都会变,获取IP和端口,我研究了很久终于找到了C#获取IP和端口的办法,用来搭 ...

  3. Java 调用http接口(基于OkHttp的Http工具类方法示例)

    目录 Java 调用http接口(基于OkHttp的Http工具类方法示例) OkHttp3 MAVEN依赖 Http get操作示例 Http Post操作示例 Http 超时控制 工具类示例 Ja ...

  4. Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Job Manager 启动

    Job Manager 启动 https://t.zsxq.com/AurR3rN 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac ...

  5. copy and mutableCopy

    结论: 1, 深复制与浅复制 2,immutable和mutable 3,代码分析: #pragma mark - String - (void)stringCopyAndMutableCopy { ...

  6. 十分钟搞懂Lombok使用与原理

    1 简介 Lombok是一款好用顺手的工具,就像Google Guava一样,在此予以强烈推荐,每一个Java工程师都应该使用它.Lombok是一种Java™实用工具,可用来帮助开发人员消除Java的 ...

  7. springboot整合webservice采用CXF技术

    转载自:https://blog.csdn.net/qq_31451081/article/details/80783220 强推:https://blog.csdn.net/chjskarl/art ...

  8. Interger等包装类的比较

    Integer a = 1; integer b = 1; integer c = 500; integer d=500; System.out.print(a==b); System.out.pri ...

  9. codeforce303C-Minimum Modular-剪枝,暴力

    Minimum Modular 题意:就是在一堆数字中,每一个数字对m取模不能等于这堆数字中的其他数字,同时给了K个机会可以删除一些数字.求最小的m: 思路:我一开始完全没思路,队长说的并查集什么的不 ...

  10. POJ 1077 Eight (BFS+康托展开)详解

    本题知识点和基本代码来自<算法竞赛 入门到进阶>(作者:罗勇军 郭卫斌) 如有问题欢迎巨巨们提出 题意:八数码问题是在一个3*3的棋盘上放置编号为1~8的方块,其中有一块为控制,与空格相邻 ...