apache ignite系列(六): 服务网格
简介
服务网格本质上还是远程方法调用(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系列(六): 服务网格的更多相关文章
- apache ignite系列(八):问题汇总
1,java.lang.ClassNotFoundException Unknown pair 1.Please try to turn on isStoreKeepBinary in cache s ...
- apache ignite系列(三):数据处理(数据加载,数据并置,数据查询)
使用ignite的一个常见思路就是将现有的关系型数据库中的数据导入到ignite中,然后直接使用ignite中的数据,相当于将ignite作为一个缓存服务,当然ignite的功能远不止于此,下面以 ...
- apache ignite系列(一): 简介
apache-ignite简介(一) 1,简介 ignite是分布式内存网格的一种实现,其基于java平台,具有可持久化,分布式事务,分布式计算等特点,此外还支持丰富的键值存储以及SQL语法(基于 ...
- apache ignite系列(二):配置
ignite有两种配置方式,一种是基于XML文件的配置,一种是基于JAVA代码的配置: 这里将ignite常用的配置集中罗列出来了,一般建议使用xml配置. 1,基于XML的配置 <beans ...
- apache ignite系列(九):ignite调优
1,配置文件调优 1.1 设置页面大小(pagesize) 先查看系统pagesiz,使用PAGE_SIZE或者PAGESIZE # getconf PAGE_SIZE 4096 # getconf ...
- apache ignite系列(九):使用ddl和dml脚本初始化ignite并使用mybatis查询缓存
博客又断了一段时间,本篇将记录一下基于ignite对jdbc支持的特性在实际使用过程中的使用. 使用ddl和dml脚本初始化ignite 由于spring-boot中支持通过spring.dataso ...
- apache ignite系列(四):持久化
ignite持久化与固化内存 1.持久化的机制 ignite持久化的关键点如下: ignite持久化可防止内存溢出导致数据丢失的情况: 持久化可以定制化配置,按需持久化; 持久化能解决在大量缓存数据情 ...
- apache ignite系列(五):分布式计算
ignite分布式计算 在ignite中,有传统的MapReduce模型的分布式计算,也有基于分布式存储的并置计算,当数据分散到不同的节点上时,根据提供的并置键,计算会传播到数据所在的节点进行计算,再 ...
- Apache Kafka系列(六)客制化Serializer和Deserializer
已经迁移,请移步:http://www.itrensheng.com/archives/apache-kafka-repartition
随机推荐
- ZooKeeper实现同步屏障(Barrier)
按照维基百科的解释:同步屏障(Barrier)是并行计算中的一种同步方法.对于一群进程或线程,程序中的一个同步屏障意味着任何线程/进程执行到此后必须等待,直到所有线程/进程都到达此点才可继续执行下文. ...
- 定时清理docker私服镜像
定时清理docker私服镜像 使用CI构建docker镜像进行发布极大促进了大家的版本发布效率,于是镜像仓库也就急速膨胀.为了缓解磁盘压力,我们需要设置一些清理策略. 对于不同docker镜像的清理策 ...
- python学习之路(2)---字符编码
二进制编码 bin(300) python计算二进制编码,十进制转2进制 一个二进制位就是1bit 1bit代表了8个字节,00001111 1bit = 1bytes 缩写1b 1kb ...
- SpringBoot打包部署简单说明
SpringBoot项目打包部署 一.jar包方式 这种使用SpringBoot内嵌的Tomcat进行部署 打包方式默认jar,所以下面加也行,不加也行 <packaging>war< ...
- HBase 系列(十一)—— Spring/Spring Boot + Mybatis + Phoenix 整合
一.前言 使用 Spring+Mybatis 操作 Phoenix 和操作其他的关系型数据库(如 Mysql,Oracle)在配置上是基本相同的,下面会分别给出 Spring/Spring Boot ...
- 人脸识别Demo
★.本实例使用百度智能云-人工智能-人脸识别API实现. ★.楼下安装了刷脸进门.闲暇时无聊写了个Demo 主界面显示如下图: 本实例,包括了所有人脸识别API的调用. 1. 创建楼号,对应API中创 ...
- 随笔编号-02 阿里云CentOS7系列三 -- 配置防火墙
前面讲到了安装JDK以及Tomcat.但是大家会发现,当我们访问 http:// XXX.XXX.XXX.XXX:8080/80 时候,tomcat 猫并没有出现.原因就是没有设置防火墙. 再次介绍下 ...
- 11_for语句的使用
for是一种循环结构 go语言中,for语句结构: for 初始语句; 条件语句; 迭代后语句 { 代码体 } 例子: package main import "fmt" impo ...
- Nginx总结(四)基于域名的虚拟主机配置
前面讲了如何安装配置Nginx,大家可以去这里看看nginx系列文章:https://www.cnblogs.com/zhangweizhong/category/1529997.html 今天要说的 ...
- OpenCvSharp 通过特征点匹配图片
现在的手游基本都是重复操作,一个动作要等好久,结束之后继续另一个动作.很麻烦,所以动起了自己写一个游戏辅助的心思. 这个辅助本身没什么难度,就是通过不断的截图,然后从这个截图中找出预先截好的能代表相应 ...