Code Monkey home page Code Monkey logo

memo's Introduction

memo

memo's People

Contributors

tianzhen000 avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar

memo's Issues

线程通信与进程通信的区别

每个进程有自己的地址空间。两个进程中的地址即使值相同,实际指向的位置也不同。进程间通信一般通过操作系统的公共区进行。

同一进程中的线程因属同一地址空间,可直接通信。

不仅是系统内部独立运行的实体,而且是独立竞争资源的实体。

线程也被称为轻权进程,同一进程的线程共享全局变量和内存,使得线程之间共享数据很容易也很方便,但会带来某些共享数据的互斥问题。

许对程序为了提高效率也都是用了线程来编写。

父子进程的派生是非常昂贵的,而且父子进程的通讯需要ipc或者其他方法来实现,比较麻烦。而线程的创建就花费少得多,并且同一进程内的线程共享全局存储区,所以通讯方便。

线程的缺点也是由它的优点造成的,主要是同步,异步和互斥的问题,值得在使用的时候小心设计。

只有进程间需要通信,同一进程的线程share地址空间,没有通信的必要,但要做好同步/互斥mutex,保护共享的全局变量。线程拥有自己的栈。同步/互斥是原语primitives。

而进程间通信无论是信号,管道pipe还是共享内存都是由操作系统保证的,是系统调用.

线程间通信:由于多线程共享地址空间和数据空间,所以多个线程间的通信是一个线程的数据可以直接提供给其他线程使用,而不必通过操作系统(也就是内核的调度)。

进程间的通信则不同,它的数据空间的独立性决定了它的通信相对比较复杂,需要通过操作系统。以前进程间的通信只能是单机版的,现在操作系统都继承了基于套接字(socket)的进程间的通信机制。这样进程间的通信就不局限于单台计算机了,实现了网络通信。

一、进程间的通信方式
1、管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
2、有名管道 (namedpipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
3、信号量(semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
4、消息队列( messagequeue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
5、信号 (sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
6、共享内存(shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
7、套接字(socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。

二、线程间的通信方式
1、锁机制:包括互斥锁、条件变量、读写锁
*互斥锁提供了以排他方式防止数据结构被并发修改的方法。
*读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
*条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
2、信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
3、信号机制(Signal):类似进程间的信号处理
线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。

分布式事务方案

原文: “分布式事务”,这次彻底懂了!

Java 中的事务

Java 中我们平时用的最多的就是在 Service 层的增删改方法上添加 @transactional 注解,让 Spring 去帮我们管理事务。

它底层会给我们的 Service 组件生成一个对应的 Proxy 动态代理,这样所有对 Service 组件的方法都由它对应的 Proxy 来接管。

当 Proxy 在调用对应业务方法比如 add() 时,Proxy 就会基于 AOP 的**在调用真正的业务方法前执行 setAutoCommit(false)打开事务。

然后在业务方法执行完后执行 Commit 提交事务,当在执行业务方法的过程中发生异常时就会执行 Rollback 来回滚事务。

当然 @transactional 注解具体的实现细节这里不再展开,这个不是本篇文章的重点,本文的 Topic 是“分布式事务”,关于 @transactional 注解大家有兴趣的话,可以自己打断点 Debug 源码研究下,源码出真知。

啥又是分布式事务

首先大家想过没:既然有了事务,并且使用 Spring 的 @transactional 注解来控制事务是如此的方便,那为啥还要搞一个分布式事务的概念出来啊?

更进一步,分布式事务和普通事务到底是啥关系?有什么区别?分布式事务又是为了解决什么问题出现的?

各种疑问接踵而至,别着急,带着这些思考,咱们接下来就详细聊聊分布式事务。

既然叫分布式事务,那么必然和分布式有点关系啦!简单来说,分布式事务指的就是分布式系统中的事务。
好,那咱们继续,首先来看看下面的图:

image

如上图所示,一个单块系统有 3 个模块:员工模块、财务模块和请假模块。我们现在有一个操作需要按顺序去调用完成这 3 个模块中的接口。

这个操作是一个整体,包含在一个事务中,要么同时成功要么同时失败回滚。不成功便成仁,这个都没有问题。

但是当我们把单块系统拆分成分布式系统或者微服务架构,事务就不是上面那么玩儿了。

首先我们来看看拆分成分布式系统之后的架构图,如下所示:

image

上图是同一个操作在分布式系统中的执行情况。员工模块、财务模块和请假模块分别给拆分成员工系统、财务系统和请假系统。

比如一个用户进行一个操作,这个操作需要先调用员工系统预先处理一下,然后通过HTTP或者RPC的方式分别调用财务系统和请假系统的接口做进一步的处理,它们的操作都需要分别落地到数据库中。

这 3 个系统的一系列操作其实是需要全部被包裹在同一个分布式事务中的,此时这 3 个系统的操作,要么同时成功要么同时失败。

分布式系统中完成一个操作通常需要多个系统间协同调用和通信,比如上面的例子。

三个子系统:员工系统、财务系统、请假系统之间就通过HTTP或者RPC进行通信,而不再是一个单块系统中不同模块之间的调用,这就是分布式系统和单块系统最大的区别。

一些平时不太关注分布式架构的同学,看到这里可能会说:我直接用 Spring 的 @transactional 注解就 OK 了啊,管那么多干嘛!

但是这里极其重要的一点:单块系统是运行在同一个 JVM 进程中的,但是分布式系统中的各个系统运行在各自的 JVM 进程中。

因此你直接加 @transactional 注解是不行的,因为它只能控制同一个 JVM 进程中的事务,但是对于这种跨多个 JVM 进程的事务无能无力。

分布式事务的几种实现思路

搞清楚了啥是分布式事务,那么分布式事务到底是怎么玩儿的呢?下边就来给大家介绍几种分布式事务的实现方案。

  • 可靠消息最终一致性方案

整个流程图如下所示:

image

我们来解释一下这个方案的大概流程:

  1. A 系统先发送一个 Prepared 消息到 MQ,如果这个 Prepared 消息发送失败那么就直接取消操作别执行了,后续操作都不再执行。

  2. 如果这个消息发送成功了,那么接着执行 A 系统的本地事务,如果执行失败就告诉 MQ 回滚消息,后续操作都不再执行。

  3. 如果 A 系统本地事务执行成功,就告诉 MQ 发送确认消息。

  4. 那如果 A 系统迟迟不发送确认消息呢?此时 MQ 会自动定时轮询所有 Prepared 消息,然后调用 A 系统事先提供的接口,通过这个接口反查 A 系统的上次本地事务是否执行成功。

如果成功,就发送确认消息给 MQ;失败则告诉 MQ 回滚消息。(后续操作都不再执行)

  1. 此时 B 系统会接收到确认消息,然后执行本地的事务,如果本地事务执行成功则事务正常完成。

  2. 如果系统 B 的本地事务执行失败了咋办?基于 MQ 重试咯,MQ 会自动不断重试直到成功,如果实在是不行,可以发送报警由人工来手工回滚和补偿。

这种方案的要点就是可以基于 MQ 来进行不断重试,最终一定会执行成功的。

因为一般执行失败的原因是网络抖动或者数据库瞬间负载太高,都是暂时性问题。

通过这种方案,99.9% 的情况都是可以保证数据最终一致性的,剩下的 0.1% 出问题的时候,就人工修复数据呗。

适用场景:这个方案的使用还是比较广,目前国内互联网公司大都是基于这种思路玩儿的。

最大努力通知方案

整个流程图如下所示:

image

这个方案的大致流程:

  1. 系统 A 本地事务执行完之后,发送个消息到 MQ。

  2. 这里会有个专门消费 MQ 的最大努力通知服务,这个服务会消费 MQ,然后写入数据库中记录下来,或者是放入个内存队列。接着调用系统 B 的接口。

  3. 假如系统 B 执行成功就万事 OK 了,但是如果系统 B 执行失败了呢?

  4. 那么此时最大努力通知服务就定时尝试重新调用系统 B,反复 N 次,最后还是不行就放弃。

这套方案和上面的可靠消息最终一致性方案的区别:可靠消息最终一致性方案可以保证的是只要系统 A 的事务完成,通过不停(无限次)重试来保证系统 B 的事务总会完成。

但是最大努力方案就不同,如果系统 B 本地事务执行失败了,那么它会重试 N 次后就不再重试,系统 B 的本地事务可能就不会完成了。至于你想控制它究竟有“多努力”,这个需要结合自己的业务来配置。

比如对于电商系统,在下完订单后发短信通知用户下单成功的业务场景中,下单正常完成,但是到了发短信的这个环节由于短信服务暂时有点问题,导致重试了 3 次还是失败。

那么此时就不再尝试发送短信,因为在这个场景中我们认为 3 次就已经算是尽了“最大努力”了。

**简单总结:**就是在指定的重试次数内,如果能执行成功那么皆大欢喜,如果超过了最大重试次数就放弃,不再进行重试。

**适用场景:**一般用在不太重要的业务操作中,就是那种完成的话是锦上添花,但失败的话对我也没有什么坏影响的场景。

比如上边提到的电商中的部分通知短信,就比较适合使用这种最大努力通知方案来做分布式事务的保证。

TCC 强一致性方案

TCC的 全称是:

Try(尝试)

Confirm(确认/提交)

Cancel(回滚)

这个其实是用到了补偿的概念,分为了三个阶段:

Try 阶段:这个阶段说的是对各个服务的资源做检测以及对资源进行锁定或者预留。

Confirm 阶段:这个阶段说的是在各个服务中执行实际的操作。

Cancel 阶段:如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功的业务逻辑的回滚操作。

还是给大家举个例子:

image

比如跨银行转账的时候,要涉及到两个银行的分布式事务,如果用TCC方案来实现,思路是这样的:

1.Try阶段:先把两个银行账户中的资金给它冻结住就不让操作了。

2.Confirm阶段:执行实际的转账操作,A银行账户的资金扣减,B银行账户的资金增加。

3.Cancel阶段:如果任何一个银行的操作执行失败,那么就需要回滚进行补偿,就是比如A银行账户如果已经扣减了,但是B银行账户资金增加失败了,那么就得把A银行账户资金给加回去。

适用场景:这种方案说实话几乎很少有人使用,我们用的也比较少,但是也有使用的场景。

因为这个事务回滚实际上是严重依赖于你自己写代码来回滚和补偿了,会造成补偿代码巨大,非常之恶心。

比如说我们,一般来说跟钱相关的,跟钱打交道的,支付、交易相关的场景,我们会用TCC,严格保证分布式事务要么全部成功,要么全部自动回滚,严格保证资金的正确性,在资金上不允许出现问题。

比较适合的场景:除非你是真的一致性要求太高,是你系统中核心之核心的场景,比如常见的就是资金类的场景,那你可以用TCC方案了。

你需要自己编写大量的业务逻辑,自己判断一个事务中的各个环节是否OK,不OK就执行补偿/回滚代码。

而且最好是你的各个业务执行的时间都比较短。但是说实话,一般尽量别这么搞,自己手写回滚逻辑,或者是补偿逻辑,实在太恶心了,那个业务代码很难维护。

简单查询过滤并按距离排序

//查询过滤器
BoolQueryBuilder filter = QueryBuilders.boolQuery();
//过滤集团ID
filter.must(termQuery("groupID", req.getGroupID()));
//过滤城市
if (StringUtils.isNotBlank(req.getCityID())) {
	filter.must(termQuery("cityID", req.getCityID()));
}
//过滤区域
if (StringUtils.isNotBlank(req.getAreaID())) {
	filter.must(termQuery("areaID", req.getAreaID()));
}
//过滤菜系
if (StringUtils.isNotBlank(req.getCuisineID())) {
	filter.must(termQuery("cuisineID1", req.getCuisineID()));
}
//过滤品牌
if (StringUtils.isNotBlank(req.getBrandID())) {
	filter.must(termQuery("brandID", req.getBrandID()));
}
//过滤店铺运营模式
if (StringUtils.isNotBlank(req.getOperationMode())) {
	filter.must(termQuery("operationMode", req.getOperationMode()));
}

//模糊过滤业务开关
if (StringUtils.isNotBlank(req.getFeatures())) {

	Set<String> featureSet = new HashSet<>(Arrays.asList(req.getFeatures().split(",")));
	featureSet.remove("");

	if (featureSet.size() > 0) {
		//正则demo  ([a-zA-Z,_])*(xxx)([a-zA-Z,_])*
		String regexpPrefix = "([a-zA-Z,_])*(";
		String regexpSuffix = ")([a-zA-Z,_])*";
		for (String feature : featureSet) {
			filter.must(regexpQuery("serviceFeatures", regexpPrefix + feature + regexpSuffix));

		}

	}

}

//距离坐标排序器
GeoDistanceSortBuilder sorter = SortBuilders.geoDistanceSort("location", new GeoPoint(lat, lon))
		.point(lat, lon)
		.unit(SHOP_LOCATION_DISTANCE_UNIT)
		.geoDistance(GeoDistance.PLANE)
		.order(SortOrder.ASC);

//搜索器
SearchRequestBuilder searcher = client.prepareSearch(SHOP_LOCATION_INDEX_NAME).setTypes(SHOP_LOCATION_INDEX_TYPE);
searcher.setPostFilter(filter)
		.addSort(sorter)
		.setFrom(from)
		.setSize(size);

SearchResponse searchResponse = searcher.execute().actionGet();

SearchHits hits = searchResponse.getHits();
//店铺数据列表
SearchHit[] searchHists = hits.getHits();

if (searchHists.length > 0) {

	List<ShopLocationBean> shopList = new ArrayList<>();

	for (SearchHit hit : searchHists) {

		ShopLocationBean shop = DataUtils.mapToBean(hit.getSource(), ShopLocationBean.class);

		//距离和单位设置
		String distance = String.valueOf(hit.getSortValues()[0]);
		shop.setDistance(distance);
		shop.setDistanceUnit(SHOP_LOCATION_DISTANCE_UNIT.toString());

		shopList.add(shop);
	}


	res.setShopList(shopList);
	//总数
	Long total = hits.getTotalHits();

	res.setPageNo(req.getPageNo());
	res.setPageSize(req.getPageSize());
	res.setTotalSize(total.intValue());
	res.setPageCount(total.intValue() % req.getPageSize() == 0 ? total.intValue() / req.getPageSize() : total.intValue() / req.getPageSize() + 1);

}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.