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
随机推荐
- 使用bibtex为latex论文添加参考文献
此文以引用Shannon的Prediction and Entropy of Printed English为例 1. bib文件 1.1 准备工作 进入Google Scholar 点击设置 ...
- 源码解读 Spring Boot Profiles
前言 上文<一文掌握 Spring Boot Profiles> 是对 Spring Boot Profiles 的介绍和使用,因此本文将从源码角度探究 Spring Boot Profi ...
- C#串口类封装 SuperSerialPort
C#串口类封装 SuperSerialPort 基于SerialPort类做了简单的封装方便调用 代码 /// <summary> /// SuperSerialPort /// < ...
- 8.14 day32 TCP服务端并发 GIL解释器锁 python多线程是否有用 死锁与递归锁 信号量event事件线程q
TCP服务端支持并发 解决方式:开多线程 服务端 基础版 import socket """ 服务端 1.要有固定的IP和PORT 2.24小时不间断提供服务 3.能够支 ...
- 《大牛到底是如何阅读JDK源码的?》一起来学习一下
前言: 如何阅读源码,是每个程序员需要面临的一项挑战,为什么需要阅读源码?从实用性的角度来看,主要有三个目的: 第一,解决手头的新问题或者新需求; 第二,真正理解一部分理论的落地实现; 第三,应对面试 ...
- Hive 系列(七)—— Hive 常用 DML 操作
一.加载文件数据到表 1.1 语法 LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename [PARTITION (p ...
- sql server 怎么查看blocked的线程
select spid ,blocked from master..sysprocesses
- 0807 创建vue实例以及vue的基础指令
lession1 1.Vue的了解 渐进式框架 作者:尤雨溪 mvvm 2.创建vue实例 引入<script src="vue.js"><scr ...
- Chrome 开发工具之 Application
Chrome 开发者工具有 Application 这么一个面板,主要作用是检查 web 应用加载的所有资源,包括 Manifest.Service Workers.Local Storage.Ses ...
- Java 安全之:csrf防护实战分析
上文总结了csrf攻击以及一些常用的防护方式,csrf全称Cross-site request forgery(跨站请求伪造),是一类利用信任用户已经获取的注册凭证,绕过后台用户验证,向被攻击网站发送 ...