Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15893 articles
Browse latest View live

到底是否应该使用“微服务架构”? - LinkinStar - 博客园

$
0
0

前言

经过当前服务端的洗礼之后,市场出现了一波微服务的热潮。然后就出现了很大的一个问题,无论什么项目,很多人想都不想,都直接开始说我们使用微服务架构来完成吧,用这个、这个组件很简单就能实现。。。而且,现在市场上很多学习教程都直接教授微服务的架构使用。很多学习的人看到这样的趋势就会随大流,就导致了当前的问题,炒作这样概念的人很多,很少人知其所以然。

经过一段时间的整理,梳理出了下面几个点,可供参考。

希望经过这些简短的参考能帮助你认识,技术的所以然。

 

什么是“微服务架构”

官方:一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制。这些服务围绕业务能力构建并且可以独立部署。依赖一个最小型的集中式管理。

总结为几点:

1、独立运行

2、独立部署

3、独立开发

4、轻量级通信

5、集中管理

这样说还是有点抽象,举个实际的例子来说,比如购物:我们可以拆分为,用户微服务,订单微服务,商品微服务…..

每个服务都有以上特点,之间独立,又可以通信,依赖一些管理的东西去管理他们。

中心思想:把大系统拆分成小型系统,把大事化小,以降低系统的复杂性

 

“微服务架构”的优点和缺点

如果只是说一个东西的优点是没用的,只有对比来看,所以我们对比单体应用来说明其优点。

1、部署:

单体应用部署肯定简单,一个包扔进去,容器启动就可以了。

微服务应用部署会负责,服务越多部署越麻烦,而且有些依赖与一些中间件,所以运维和部署的压力变大是肯定的。(这里并不是说一定的,已经有一些运维部署的软件方便了微服务的部署)

2、开发和维护:

单体应用如果要进行开发,代码即使分离的再好,那么还是在一起,所以会显得臃肿,维护起来不方便,如果需要改动一个点,整个服务必须全部重新启动。

微服务开发因为本身分离,所以显得清晰,维护起来方便,一个地方的服务出现问题,只需要改动对应服务并重启对应服务即可。

3、扩展:

单体应用扩展可想而知,受限并且压力很大,到最后很多人会发现,加入或者扩展功能时宁可新开发一个也不愿意去依赖原来的代码就怕改了原来的代码之前的代码出现问题。

微服务扩展能力较好,新加入一个功能不会对原来的系统造成影响。而且如果一个大的功能被禁用,直接停止对应服务即可。

4、通信:

对于单体应用来说,自己本身都是内部服务调用不存在通信问题,对于外部库来说,通信方式取决于外部库的依赖。

微服务之间的通信就需要依赖比较靠谱的通信系统了,因为难免服务与服务之间会有依赖,那么通信方式的选择就尤为重要了。

 

到底是否应该使用“微服务架构”?

最后我们再来看看我们一开始的问题,是不是就能总结出以下几个点了。然后我结合一些书本和经验做下面一个总结,希望对你有帮助。

1、系统大小

这是我们首要的考虑目标,如果一个系统很小,比如一个官网,那你说做微服务就是扯淡了。那么如何确定一个系统的大小呢?可以参考一下下面这个标准。

如果你的项目能分成三个或三个以上的耦合度很低的项目,那么就算大。

如果你的项目数据库表超过30张,且单表数据轻松百万,那么就算大。

如果你的项目之后会进行扩展,并且扩展之后会达到上面的如果,那么也算大。

虽然只是经验上的估计参考,但是也从侧面体现出,如果项目不大,那么真的就没必要。

 

2、技术能力

微服务依赖的能力有以下几点

拆分服务的能力

处理分布式问题(网络请求,分布式事务等)的能力

强大的运维能力

如果一个系统决定使用微服务架构,那么前期的拆分就显得非常重要,有经验的拆分可以让服务之间的耦合对降到最低,并且相应的业务没有问题。相应的,如果没有处理分布式问题的能力也是不行的,最后才是项目部署运维的能力。

 

3、团队规模和时间

如果你的团队规模不超过10人,那么除非你们能力都非常牛,而且都能独当一面,那么当我没说,理论上不建议。

在开发周期时间不允许的情况下不要执意去切换,从单体切换到微服务,因为两者区别不仅仅是在服务上,包括通信等等方面耗时都不短,测试上面就需要更加多的时间去测试。而且微服务的开发效率上面是一开始慢,到项目大了之后开发效率才慢慢的体现出来。

微服务毕竟存在通信,而且服务器想对多,项目稳定性上肯定要打折扣。你的团队需要提前了解到这样的问题,并做好遇到问题的准备和处理,这也是需要时间的。

团队之间的沟通,有通信必然有交流,不然别人怎么知道你的服务是怎么样的。那么接口文档编写的时间和对接接口的时间,调试的时间,剩下我就不多说了,你应该懂了。

 

总结

一个技术或者一个架构不是万能的,每个技术都有适用的场景,我们所要做的不是一味的追求最新,而是明白它的使用场景或者优点缺点,从而来考虑是否使用。

这里上面也只是抛砖引玉,所有的细节肯定不是一篇文章或者一本书能说完的,只要你去考虑了,借鉴一些别人的经验去发现可能存在的问题,那么即使最后出现问题也可以被解决。

 

参考文档:

《SpringCloud与Docker微服务架构实战》

http://www.infoq.com/cn/minibooks/microservice--from-zero

 


Kafka幂等性原理及实现剖析 - 哥不是小萝莉 - 博客园

$
0
0

1.概述

最近和一些同学交流的时候反馈说,在面试Kafka时,被问到Kafka组件组成部分、API使用、Consumer和Producer原理及作用等问题都能详细作答。但是,问到一个平时不注意的问题,就是Kafka的幂等性,被卡主了。那么,今天笔者就为大家来剖析一下Kafka的幂等性原理及实现。

2.内容

2.1 Kafka为啥需要幂等性?

Producer在生产发送消息时,难免会重复发送消息。Producer进行retry时会产生重试机制,发生消息重复发送。而引入幂等性后,重复发送只会生成一条有效的消息。Kafka作为分布式消息系统,它的使用场景常见与分布式系统中,比如消息推送系统、业务平台系统(如物流平台、银行结算平台等)。以银行结算平台来说,业务方作为上游把数据上报到银行结算平台,如果一份数据被计算、处理多次,那么产生的影响会很严重。

2.2 影响Kafka幂等性的因素有哪些?

在使用Kafka时,需要确保Exactly-Once语义。分布式系统中,一些不可控因素有很多,比如网络、OOM、FullGC等。在Kafka Broker确认Ack时,出现网络异常、FullGC、OOM等问题时导致Ack超时,Producer会进行重复发送。可能出现的情况如下:

 

 

2.3 Kafka的幂等性是如何实现的?

Kafka为了实现幂等性,它在底层设计架构中引入了ProducerID和SequenceNumber。那这两个概念的用途是什么呢?

  • ProducerID:在每个新的Producer初始化时,会被分配一个唯一的ProducerID,这个ProducerID对客户端使用者是不可见的。
  • SequenceNumber:对于每个ProducerID,Producer发送数据的每个Topic和Partition都对应一个从0开始单调递增的SequenceNumber值。

2.3.1 幂等性引入之前的问题?

Kafka在引入幂等性之前,Producer向Broker发送消息,然后Broker将消息追加到消息流中后给Producer返回Ack信号值。实现流程如下:

 

上图的实现流程是一种理想状态下的消息发送情况,但是实际情况中,会出现各种不确定的因素,比如在Producer在发送给Broker的时候出现网络异常。比如以下这种异常情况的出现:

 

上图这种情况,当Producer第一次发送消息给Broker时,Broker将消息(x2,y2)追加到了消息流中,但是在返回Ack信号给Producer时失败了(比如网络异常) 。此时,Producer端触发重试机制,将消息(x2,y2)重新发送给Broker,Broker接收到消息后,再次将该消息追加到消息流中,然后成功返回Ack信号给Producer。这样下来,消息流中就被重复追加了两条相同的(x2,y2)的消息。

2.3.2 幂等性引入之后解决了什么问题?

面对这样的问题,Kafka引入了幂等性。那么幂等性是如何解决这类重复发送消息的问题的呢?下面我们可以先来看看流程图:

 

 同样,这是一种理想状态下的发送流程。实际情况下,会有很多不确定的因素,比如Broker在发送Ack信号给Producer时出现网络异常,导致发送失败。异常情况如下图所示:

 

 当Producer发送消息(x2,y2)给Broker时,Broker接收到消息并将其追加到消息流中。此时,Broker返回Ack信号给Producer时,发生异常导致Producer接收Ack信号失败。对于Producer来说,会触发重试机制,将消息(x2,y2)再次发送,但是,由于引入了幂等性,在每条消息中附带了PID(ProducerID)和SequenceNumber。相同的PID和SequenceNumber发送给Broker,而之前Broker缓存过之前发送的相同的消息,那么在消息流中的消息就只有一条(x2,y2),不会出现重复发送的情况。

2.3.3 ProducerID是如何生成的?

客户端在生成Producer时,会实例化如下代码:

//实例化一个Producer对象Producer<String, String> producer =newKafkaProducer<>(props);

在org.apache.kafka.clients.producer.internals.Sender类中,在run()中有一个maybeWaitForPid()方法,用来生成一个ProducerID,实现代码如下:

privatevoidmaybeWaitForPid() {if(transactionState ==null)return;while(!transactionState.hasPid()) {try{
                Node node=awaitLeastLoadedNodeReady(requestTimeout);if(node !=null) {
                    ClientResponse response=sendAndAwaitInitPidRequest(node);if(response.hasResponse() && (response.responseBody()instanceofInitPidResponse)) {
                        InitPidResponse initPidResponse=(InitPidResponse) response.responseBody();
                        transactionState.setPidAndEpoch(initPidResponse.producerId(), initPidResponse.epoch());
                    }else{
                        log.error("Received an unexpected response type for an InitPidRequest from {}. " +"We will back off and try again.", node);
                    }
                }else{
                    log.debug("Could not find an available broker to send InitPidRequest to. " +"We will back off and try again.");
                }
            }catch(Exception e) {
                log.warn("Received an exception while trying to get a pid. Will back off and retry.", e);
            }
            log.trace("Retry InitPidRequest in {}ms.", retryBackoffMs);
            time.sleep(retryBackoffMs);
            metadata.requestUpdate();
        }
    }

3.事务

与幂等性有关的另外一个特性就是事务。Kafka中的事务与数据库的事务类似,Kafka中的事务属性是指一系列的Producer生产消息和消费消息提交Offsets的操作在一个事务中,即原子性操作。对应的结果是同时成功或者同时失败。

这里需要与数据库中事务进行区别,操作数据库中的事务指一系列的增删查改,对Kafka来说,操作事务是指一系列的生产和消费等原子性操作。

3.1 Kafka引入事务的用途?

在事务属性引入之前,先引入Producer的幂等性,它的作用为:

  • Producer多次发送消息可以封装成一个原子性操作,即同时成功,或者同时失败;
  • 消费者&生产者模式下,因为Consumer在Commit Offsets出现问题时,导致重复消费消息时,Producer重复生产消息。需要将这个模式下Consumer的Commit Offsets操作和Producer一系列生产消息的操作封装成一个原子性操作。

产生的场景有:

比如,在Consumer中Commit Offsets时,当Consumer在消费完成时Commit的Offsets为100(假设最近一次Commit的Offsets为50),那么执行触发Balance时,其他Consumer就会重复消费消息(消费的Offsets介于50~100之间的消息)。

3.2 事务提供了哪些可使用的API?

Producer提供了五种事务方法,它们分别是:initTransactions()、beginTransaction()、sendOffsetsToTransaction()、commitTransaction()、abortTransaction(),代码定义在org.apache.kafka.clients.producer.Producer<K,V>接口中,具体定义接口如下:

//初始化事务,需要注意确保transation.id属性被分配voidinitTransactions();//开启事务voidbeginTransaction()throwsProducerFencedException;//为Consumer提供的在事务内Commit Offsets的操作voidsendOffsetsToTransaction(Map<TopicPartition, OffsetAndMetadata>offsets,
                              String consumerGroupId)throwsProducerFencedException;//提交事务voidcommitTransaction()throwsProducerFencedException;//放弃事务,类似于回滚事务的操作voidabortTransaction()throwsProducerFencedException;

3.3 事务的实际应用场景有哪些?

在Kafka事务中,一个原子性操作,根据操作类型可以分为3种情况。情况如下:

  • 只有Producer生产消息,这种场景需要事务的介入;
  • 消费消息和生产消息并存,比如Consumer&Producer模式,这种场景是一般Kafka项目中比较常见的模式,需要事务介入;
  • 只有Consumer消费消息,这种操作在实际项目中意义不大,和手动Commit Offsets的结果一样,而且这种场景不是事务的引入目的。

4.总结

Kafka的幂等性和事务是比较重要的特性,特别是在数据丢失和数据重复的问题上非常重要。Kafka引入幂等性,设计的原理也比较好理解。而事务与数据库的事务特性类似,有数据库使用的经验对理解Kafka的事务也比较容易接受。

5.结束语

这篇博客就和大家分享到这里,如果大家在研究学习的过程当中有什么问题,可以加群进行讨论或发送邮件给我,我会尽我所能为您解答,与君共勉!

另外,博主出书了《 Kafka并不难学》和《 Hadoop大数据挖掘从入门到进阶实战》,喜欢的朋友或同学, 可以在公告栏那里点击购买链接购买博主的书进行学习,在此感谢大家的支持。关注下面公众号,根据提示,可免费获取书籍的教学视频。 

你可能不知道的mysql | LinkinStar's Blog

$
0
0

以下是针对mysql的知识点整理,用于复习,主要以罗列为主,详细具体讲解可以参考书《高性能mysql》,你可以过一遍看看有无知识点遗漏。

执行sql过程

客户端 -> 连接器 -> 分析器 -> 优化器 -> 执行器 -> 存储引擎
连接器:连接上数据库,长连接
分析器:分析语法(包含解析器和预处理器,解析器生成解析树,预处理器判断字段存在歧义)
优化器:选择正确的索引进行优化执行
执行器:执行具体sql返回结果

mysql的两个重要日志

redo-log(重做日志):固定大小的循环缓存,InnoDB使用,即使重启,只要记录到了redo-log就不会丢失。防止mysql意外。
bin-log:归档日志,所有sql都会记录,并且采用追加,满了之后新开,有两种方式,一种是记录sql语句(statement),一种是row,记录出现的事件。
如果只记录sql语句会导致主从同步上面存在问题,从库执行相同的sql得到效果不同,所以还有一种混合的方式,mysql会自动判断当前语句是否会造成主从不同步的情况,如果会,那么就使用row记录如果不会就是用sql记录,因为row记录会增加存储空间。

undo-log(回滚日志):记录修改的状态和回滚信息,利用这个实现mvcc(多版本并发控制),系统会自动判断回滚日志什么时候会被删除。用于回滚操作。

两个日志记录的顺序:
更新的行如果不在内存,从磁盘取出 -> 修改内存中的值 -> 写入redo-log状态为prepare -> 写binlog -> 提交事务redo-log进行commit

数据库的隔离级别

读未提交:能读到别人未提交事务修改的数据
读已提交:能读到别人提交事务之后修改的数据
可重复读:在读已提交的基础上,当前事务读取第一次和第二次的结果相同
串行化:读会加读锁,写会加写锁,读写冲突串行化执行

隔离级别通过视图实现,读未提交没有视图,读已提交每次sql执行创建一个视图,可重复读在开始之前创建一个视图,串行化直接加锁没有视图。

事务与隔离级别:更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”;所以即使是可以重复读的隔离级别,更新数据时还是会进行当前读来保证别人已经提交的事务不被覆盖。

幻读:幻读是出现在范围查询,第二次查询之前,由于其他事务新增记录导致查询两次不同,区别于可重复读。InnoDB引入间隙锁来解决,锁住范围内的各个间隙。但是要注意间隙锁也容易导致死锁,跟间隙锁存在冲突关系的,是“往这个间隙中插入一个记录”这个操作,间隙锁之间都不存在冲突关系。

索引基础

常见的索引类型有:哈希、数组、搜索树
哈希用于等值查询,不适合范围查询;数组查询很快,但是更新效率低
数据库使用N叉树降低树的层级,innodb使用的是B+树

在InnoDb中,主键索引又叫聚簇索引,非主键索引又叫二级索引
主键索引可以拿到全部数据,而非主键索引只能拿到主键id通过回表查询来拿到数据
如果一个数据页满了需要新增一个数据页也叫做页分裂性能下降并且空间利用率下降,所以使用自增主键更加合理

覆盖索引:当我们查询的时候只需要查询出id字段的时候就可以直接使用单个索引来完成,不需要进行回表操作,减少搜索次数。

最左前缀原则:当我们进行一个字段查询的时候,如果这个字段没有单独做索引,但是有别的联合索引包含这个字段,且刚好以这个字段开头,那么也可以进行匹配。所以在建立联合索引的时候需要考虑字段排序,这样就可以减少维护的索引个数。

索引下堆优化:mysql5.6之后,当查询的条件中包含索引中的字段,会优先对索引中的字段做判断,而非直接回表查询。

重建索引:当删除很多数据之后,由于索引没有被删除,所以会导致数据页有空洞,而且占用资源,这个时候可以考虑再低谷期重建索引 alter table T engine=InnoDB

唯一索引和普通索引:插入上面性能几乎没有区别,更新上面普通索引可以使用change buffer所以更加快一些,而唯一索引需要判断所以慢一些。选择还是需要根据业务出发去考虑。

合理设置前缀索引:索引可以设置只用前面几位,可以减少索引占用空间,同时设置时应保证合适的区分度。

锁相关

全局锁:用于备份的时候,锁住整个库,防止备份过程中数据修改导致问题。
表锁:有两种,一种是表锁,在引擎不支持行锁的时候使用,锁住之后不能进行增删改查;另一种是元数据锁,访问表的时候自动加上,读写锁。默认就是。

行锁:在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。 如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。

间隙锁:专门用来解决幻读的问题,在可重复读的情况下才会生效。
(间隙锁和行锁合称next-key lock)
next-key lock锁的规则:
加锁范围是前开后闭区间;查找过程中访问到的对象才会加锁;当遇到索引等值查询,如果是唯一索引,那么因为只可能有一行记录那么就退化为行锁;如果索引等值查询,发现没有满足情况,就只能退化为间隙锁去锁间隙;如果是范围查询那么就会查询到第一个不满足条件的情况为止。

死锁:当对于同一个表的多行数据进行修改的时候,容易出现死锁,相互等待。死锁可以通过死锁检测或者是超时回滚来解决,但是对于性能损失巨大,最好通过业务或者客户端优化处理。

count(*):针对这个有特殊优化,但innodb没有直接记录行数,还是需要遍历计数,实在不行可以业务实现计数。

MyISAM不支持事务
MyISAM不支持行锁
在InnoDB中,每个数据页的大小默认是16KB。

order by的实现:在不用索引的时候,如果内存够用,那么会将查询全部查出来然后放到内存中快排,如果内部不够,使用磁盘进行排序后归并。更好的情况是去使用索引,因为存储的时候默认就是有顺序的,这样能减少排序从而加速。

无法使用索引的情况

  1. 如果对字段做了函数计算,就用不上索引了
  2. 如果触发隐式转换也用不上索引了
  3. 字符集不同触发转换也无法使用索引

查看相关命令

show processlist命令查看Waiting for table metadata lock
查看各个线程锁的情况

select from information_schema.innodb_trx\G
select
from t sys.innodb_lock_waits where locked_table= 'test'.'t'\G
可以查看具体是被那个线程锁住了

一些小的

for update和lock in share mode
lock in share mode是意向共享锁,其他session可以读取相关记录,也可以继续加IS,但是无法修改
for update是意向排他锁,其他session无法进行select…for update操作,也就是排除别的想要加排它锁的情况。
两者都不会阻塞别的session进行的快照读。
用法,lock in share mode用于两个表之间要保证一致性,a表的操作时要保证b表中的某条数据不能被修改;
for update用于同一个表中的数据,a事务操作时不允许b事务进行修改。

在删除数据的时候尽量加limit。这样不仅可以控制删除数据的条数,让操作更安全,还可以减小加锁的范围。

不要一次性地用delete语句删除太多数据。其实,这就是一个典型的大事务场景。

sql慢的原因

索引设计不合理
sql设计不合理
mysql索引自动选择错误

运维上的一些

双主的时候,通过binlog上面的serverid记录来判断是否与自己相同,如果不同才会更新,避免循环复制

主备延迟的来源:
备库机器性能差,备库查询压力大,大事务一直正在处理。
mysql5.7采用并行复制的策略减少主备延迟

因为主备同步会存在延迟,所以在开发的时候一定要注意读取从库的时候不一定是最新的值。
1、读取的时候读主库,最常用
2、读取之前进行睡眠一段时间保证同步
保证seconds_behind_master一定为0的时候才执行查询
或者可以使用semi-sync replication,当从库收到binlog之后会返回主库一个ack,主库只有收到这个ack之后才认为事务完成

如何进行主备切换????过程是怎么样的?

对比位点
Master_Log_File和Read_Master_Log_Pos,表示的是读到的主库的最新位点;
Relay_Master_Log_File和Exec_Master_Log_Pos,表示的是备库执行的最新位点。
如果位点相同可以认为已经同步

对比GTID集合确保主备无延迟:

如何判断一个数据库正常
1、使用select进行查询,查询一个创建在mysql库中的表;容易实现,但是因为只是查询所以会漏掉一些错误条件,比如当磁盘满了,binlog写不进去了,但是可以读不能写。那么可以使用update来进行优化一下下。尝试去修改一个值来实现。

当出现误删除(delete)的时候,这个时候要指望binlog存放了数据,然后逆执行去恢复(Flashback),但是需要确保binlog_format=row 和 binlog_row_image=FULL。

预防才是关键:
把sql_safe_updates参数设置为on。这样一来,如果我们忘记在delete或者update语句中写where条件,或者where条件里面没有包含索引字段的话,这条语句的执行就会报错。

如果是直接执行的drop的话,binlog也无能为力,因为log中没有存放删除的数据,这个时候只能依赖备份了,利用最近一次备份的数据进行恢复,然后进行binlog重放。

故意延迟复制的从库,弄一个故意延迟一个小时复制的从库,这样无论什么时候都能快速拿到一个小时前的数据。

账号权限很关键,没有权限去执行对应操作的sql就可以了

kill query +线程id,可以终止一个线程正在执行的sql语句

mysql采用的是边查边给的,查到就会发给客户端,而不是全部查到全部结果之后再发

使用join的时候一定要注意,使用是有条件的:
当使用join的时候被驱动表能使用索引,那么是可以的,同时也需要注意,使用小表作为驱动表,这样能让扫描行数更加少一些,大表去走索引去。
当使用join的时候如果不能走索引的情况,那么mysql会使用BNL算法,将驱动表的数据和被驱动表的数据加载到内存中,并且使用join_buffer来进行合并操作,但是这样扫描行会变的非常的巨大,所以这个时候如果表的数据太多就不适合使用。

mysql面试问题

主从复制的原理与流程?

  1. 主库将修改写入本地binlog中
  2. 从库将拉取主库binlog写入本地relay log中
  3. 从库读取relay log并执行(这里是单线程执行,不能并发,所以慢)

innodb和myisam与区别

innodb支持事务,myisam不支持
innodb支持行锁,myisam支持表锁
innodb支持mvcc,myisam不支持
innodb支持外键,myisam不支持
myisam不支持崩溃后安全恢复

innodb引擎的4大特性

  1. 插入缓冲insert buffer,change buffer;将一系列的操作缓存,然后一次性写到磁盘,目的还是为了减少随机IO带来性能损耗。
  2. 二次写:从innodb buffer pool中flush写文件之前存doublewrite buffer写到物理磁盘上共享表空间。
  3. 自适应哈希索引:当二级索引访问频繁的时候,会自动建立哈希索引来加速
  4. 预读

mysql索引方法有哪些

B-Tree索引:利用二叉树的特性,同时优化磁盘io,然后查询更快,同时优化索引查询和排序
Hash索引:基于hash实现,在hash冲突不高的情况下,速度快,但是对于范围查询和排序都不支持

mysql索引类型有哪些

主键索引,普通索引(组合索引),唯一索引,全文索引,空间索引

HomePwn:一款专用于物联网设备渗透测试的“瑞士军刀”

$
0
0

HomePwn

HomePwn是一款功能强大的物联网渗透测试框架,它可谓是该领域的一把“瑞士军刀”。HomePwn可以提供设备安全审计和渗透测试功能,企业员工可以使用HomePwn来测试同一工作环境中家庭或办公设备的安全性,并利用其中存在的安全漏洞来读取这些设备中的敏感信息,或向这些设备发送控制命令。该工具集成了非常强大的模块库,研究人员可以使用这些模块库来加载更多新的功能,并在各种不同类型的设备中来使用它们。

HomePwn采用模块化架构开发,任何用户都可以使用不同的技术来扩展HomePwn的知识库。HomePwn由以下两个部分组成:

扫描模块

这些模块用于提供设备和漏洞的扫描和发现相关的功能,比如说它们可以用于在监控模式下通过适配器来进行WiFi扫描、BLE设备发现、蓝牙低功耗设备检测,并查看目标设备的连接状态等等。除此之外,它还可以使用SSDP、MDNS或多播DNS来扫描和发现家庭或办公室物联网服务。

特殊模块

该模块主要针对的是需要进行审计的技术。HomePwn可以针对类似WiFi、NFC或BLE这样的技术来进行安全审计和渗透测试。换句话说,这些技术都会涉及到各种功能模块,并且这些模块的使用方式也非常多样化,而这些技术在应用到物联网设备中之后,又会产生各种安全问题以及漏洞。因此,这些模块可以评估和设计这些技术在物联网通信设备中的安全等级。

依赖组件

1、 Python环境

2、 Python命令行工具

工具使用

广大研究人员需要将本项目拷贝到本地设备中,然后通过部署之后才可以使用HomePwn生态系统。首先,我们需要完成工具依赖环境的配置。

HomePwn目前仅支持在Linux平台上使用,并且需要安装和配置python 3.6+:

1、 UbuntuDebian或类Unix平台;

2、 Python 3.6+

以Ubuntu 18.04为例,首先运行下列命令更新操作系统:

sudo apt-get update

运行下列命令将HomePwn项目克隆至本地:

git clone https://github.com/ElevenPaths/HomePWN.git

接下来,切换到项目目录,然后运行安装脚本install.sh:

cd [path to the HomePWN project]

sudo ./install.sh

基本运行后,会询问用户是否需要创建一个virtualenv虚拟环境,如果输入“y”,那么脚本则会自动在virtualenv环境中安装Python依赖库。

工具使用

如果你在虚拟环境中使用HomePwn的话,还需要激活环境:

source homePwn/bin/activate

接下来,运行HomePwn脚本:

sudo python3 homePwn.py

工具使用样例

低功耗蓝牙渗透PoC:【 视频地址

蓝牙嗅探:【 视频地址

NFC克隆:【 视频地址

BLE嗅探(捕捉PCAP文件):【 视频地址

QR选项渗透:【 视频地址

苹果BLE发现:【 视频地址

小米物联网设备渗透:【 视频地址

许可证协议

本项目遵循GNU General Public开源许可证协议。

项目地址

HomePwn:【 GitHub主页

参考文档

1、 https://github.com/ElevenPaths/HomePWN/blob/master/Papers/%5BPAPER%5Dhomepwn_ENG.pdf

*参考来源: ElevenPaths,FB小编Alpha_h4ck编译,转载请注明来自FreeBuf.COM

详解 Flink Metrics 原理与监控实战

$
0
0

本文主要讲解 Metrics、如何使用 Metrics 分析问题并解决问题,并对 Metrics 监控实战进行解释说明。

本文作者:Apache Flink Contributor 刘彪

什么是 Metrics?

Flink 提供的 Metrics 可以在 Flink 内部收集一些指标,通过这些指标让开发人员更好地理解作业或集群的状态。由于集群运行后很难发现内部的实际状况,跑得慢或快,是否异常等,开发人员无法实时查看所有的 Task 日志,比如作业很大或者有很多作业的情况下,该如何处理?此时 Metrics 可以很好的帮助开发人员了解作业的当前状况。

Metric Types

Metrics 的类型如下:

  • 首先,常用的如 Counter,写过 mapreduce 作业的开发人员就应该很熟悉 Counter,其实含义都是一样的,就是对一个计数器进行累加,即对于多条数据和多兆数据一直往上加的过程。
  • 第二,Gauge,Gauge 是最简单的 Metrics,它反映一个值。比如要看现在 Java heap 内存用了多少,就可以每次实时的暴露一个 Gauge,Gauge 当前的值就是heap使用的量。
  • 第三,Meter,Meter 是指统计吞吐量和单位时间内发生“事件”的次数。它相当于求一种速率,即事件次数除以使用的时间。
  • 第四,Histogram,Histogram 比较复杂,也并不常用,Histogram 用于统计一些数据的分布,比如说 Quantile、Mean、StdDev、Max、Min 等。

Metric Group

Metric 在 Flink 内部有多层结构,以 Group 的方式组织,它并不是一个扁平化的结构,Metric Group + Metric Name 是 Metrics 的唯一标识。

Metric Group 的层级有 TaskManagerMetricGroup 和TaskManagerJobMetricGroup,每个 Job 具体到某一个 task 的 group,task 又分为 TaskIOMetricGroup 和 OperatorMetricGroup。Operator 下面也有 IO 统计和一些 Metrics,整个层级大概如下图所示。Metrics 不会影响系统,它处在不同的组中,并且 Flink支持自己去加 Group,可以有自己的层级。

1     
2
3
4
5
6
7
8
9
•TaskManagerMetricGroup     
•TaskManagerJobMetricGroup
•TaskMetricGroup
•TaskIOMetricGroup
•OperatorMetricGroup
•${User-defined Group} / ${User-defined Metrics}
•OperatorIOMetricGroup
•JobManagerMetricGroup
•JobManagerJobMetricGroup

JobManagerMetricGroup 相对简单,相当于 Master,它的层级也相对较少。

Metrics 定义还是比较简单的,即指标的信息可以自己收集,自己统计,在外部系统能够看到 Metrics 的信息,并能够对其进行聚合计算。

如何使用 Metrics?

System Metrics

System Metrics,将整个集群的状态已经涵盖得非常详细。具体包括以下方面:

  • Master 级别和 Work 级别的 JVM 参数,如 load 和 time;其 Memory 划分也很详细,包括 heap 的使用情况、non-heap 的使用情况、direct 的使用情况,以及 mapped 的使用情况;Threads 可以看到具体有多少线程;还有非常实用的 Garbage Collection。
  • Network 使用比较广泛,当需要解决一些性能问题的时候,Network 非常实用。Flink 不只是网络传输,还是一个有向无环图的结构,可以看到它的每个上下游都是一种简单的生产者消费者模型。Flink 通过网络相当于标准的生产者和消费者中间通过有限长度的队列模型。如果想要评估定位性能,中间队列会迅速缩小问题的范围,能够很快的找到问题瓶颈。
1     
2
3
4
5
6
7
8
9
10
11
12
•CPU     
•Memory
•Threads
•Garbage Collection
•Network
•Classloader
•Cluster
•Availability
•Checkpointing
•StateBackend
•IO
•详见: [https://ci.apache.org/projects/flink/flink-docs-release-1.8/monitoring/metrics.html#system-metrics](https://ci.apache.org/projects/flink/flink-docs-release-1.8/monitoring/metrics.html)
  • 运维集群的人会比较关心 Cluster 的相关信息,如果作业太大,则需要非常关注 Checkpointing,它有可能会在一些常规的指标上无法体现出潜在问题。比如 Checkpointing 长时间没有工作,数据流看起来没有延迟,此时可能会出现作业一切正常的假象。另外,如果进行了一轮 failover 重启之后,因为 Checkpointing 长时间没有工作,有可能会回滚到很长一段时间之前的状态,整个作业可能就直接废掉了。
  • RocksDB 是生产环境当中比较常用的 state backend 实现,如果数据量足够大,就需要多关注 RocksDB 的 Metrics,因为它随着数据量的增大,性能可能会下降。

User-defined Metrics

除了系统的 Metrics 之外,Flink 支持自定义 Metrics ,即 User-defined Metrics。上文说的都是系统框架方面,对于自己的业务逻辑也可以用 Metrics 来暴露一些指标,以便进行监控。

User-defined Metrics 现在提及的都是 datastream 的 API,table、sql 可能需要 context 协助,但如果写 UDF,它们其实是大同小异的。

Datastream 的 API 是继承 RichFunction ,继承 RichFunction 才可以有 Metrics 的接口。然后通过 RichFunction 会带来一个 getRuntimeContext().getMetricGroup().addGroup(…) 的方法,这里就是 User-defined Metrics 的入口。通过这种方式,可以自定义 user-defined Metric Group。如果想定义具体的 Metrics,同样需要用getRuntimeContext().getMetricGroup().counter/gauge/meter/histogram(…) 方法,它会有相应的构造函数,可以定义到自己的 Metrics 类型中。

1     
2
3
继承 RichFunction     
•Register user-defined Metric Group: getRuntimeContext().getMetricGroup().addGroup(…)
•Register user-defined Metric: getRuntimeContext().getMetricGroup().counter/gauge/meter/histogram(…)

User-defined Metrics Example

下面通过一段简单的例子说明如何使用 Metrics。比如,定义了一个 Counter 传一个 name,Counter 默认的类型是 single counter(Flink 内置的一个实现),可以对 Counter 进行 inc()操作,并在代码里面直接获取。

Meter 也是这样,Flink 有一个内置的实现是 Meterview,因为 Meter 是多长时间内发生事件的记录,所以它是要有一个多长时间的窗口。平常用 Meter 时直接 markEvent(),相当于加一个事件不停地打点,最后用 getrate() 的方法直接把这一段时间发生的事件除一下给算出来。

Gauge 就比较简单了,把当前的时间打出来,用 Lambda 表达式直接把 System::currentTimeMillis 打进去就可以,相当于每次调用的时候都会去真正调一下系统当天时间进行计算。

Histogram 稍微复杂一点,Flink 中代码提供了两种实现,在此取一其中个实现,仍然需要一个窗口大小,更新的时候可以给它一个值。

这些 Metrics 一般都不是线程安全的。如果想要用多线程,就需要加同步,更多详情请参考下面链接。

1     
2
3
4
5
6
7
8
•Counter processedCount = getRuntimeContext().getMetricGroup().counter("processed_count");     
processedCount.inc();
•Meter processRate = getRuntimeContext().getMetricGroup().meter("rate", new MeterView(60));
processRate.markEvent();
•getRuntimeContext().getMetricGroup().gauge("current_timestamp", System::currentTimeMillis);
•Histogram histogram = getRuntimeContext().getMetricGroup().histogram("histogram", new DescriptiveStatisticsHistogram(1000));
histogram.update(1024);
•[https://ci.apache.org/projects/flink/flink-docs-release-1.8/monitoring/metrics.html#metric-types]

获取 Metrics

获取 Metrics 有三种方法,首先可以在 WebUI 上看到;其次可以通过 RESTful API 获取,RESTful API 对程序比较友好,比如写自动化脚本或程序,自动化运维和测试,通过 RESTful API 解析返回的 Json 格式对程序比较友好;最后,还可以通过 Metric Reporter 获取,监控主要使用 Metric Reporter 功能。

获取 Metrics 的方式在物理架构上是怎样实现的?

了解背景和原理会对使用有更深刻的理解。WebUI 和 RESTful API 是通过中心化节点定期查询把各个组件中的 Metrics 拉上来的实现方式。其中,fetch 不一定是实时更新的,默认为 10 秒,所以有可能在 WebUI 和 RESTful API 中刷新的数据不是实时想要得到的数据;此外,fetch 有可能不同步,比如两个组件,一边在加另一边没有动,可能是由于某种原因超时没有拉过来,这样是无法更新相关值的,它是 try best 的操作,所以有时我们看到的指标有可能会延迟,或许等待后相关值就更新了。

红色的路径通过 MetricFetcher,会有一个中心化的节点把它们聚合在一起展示。而 MetricReporter 不一样,每一个单独的点直接汇报,它没有中心化节点帮助做聚合。如果想要聚合,需要在第三方系统中进行,比如常见的 TSDB 系统。当然,不是中心化结构也是它的好处,它可以免去中心化节点带来的问题,比如内存放不下等,MetricReporter 把原始数据直接 Reporter 出来,用原始数据做处理会有更强大的功能。

Metric Reporter

Flink 内置了很多 Reporter,对外部系统的技术选型可以参考,比如 JMX 是 java 自带的技术,不严格属于第三方。还有 InfluxDB、Prometheus、Slf4j(直接打 log 里)等,调试时候很好用,可以直接看 logger,Flink 本身自带日志系统,会打到 Flink 框架包里面去。详见:详见: https://ci.apache.org/projects/flink/flink-docs-release-1.8/monitoring/metrics.html#reporter

1     
2
3
4
5
6
7
8
//配置     
metrics.reporters: your_monitor,jmx
metrics.reporter.jmx.class: org.apache.flink.metrics.jmx.JMXReporter
metrics.reporter.jmx.port: 1025-10000
metrics.reporter.your_monitor.class: com.your_company.YourMonitorClass
metrics.reporter.your_monitor.interval: 10 SECONDS
metrics.reporter.your_monitor.config.a: your_a_value
metrics.reporter.your_monitor.config.b: your_b_value

Metric Reporter 是如何配置的?如上所示,首先 Metrics Reporters 的名字用逗号分隔,然后通过 metrics.reporter.jmx.class 的 classname 反射找 reporter,还需要拿到 metrics.reporter.jmx.port 的配置,比如像第三方系统通过网络发送的比较多。但要知道往哪里发,ip 地址、port 信息是比较常见的。此外还有 metrics.reporter.your_monitor.class 是必须要有的,可以自己定义间隔时间,Flink 可以解析,不需要自行去读,并且还可以写自己的 config。

实战:利用 Metrics 监控

常用 Metrics 做自动化运维和性能分析。

自动化运维

自动化运维怎么做?

首先,收集一些关键的 Metrics 作为决策依据,利用 Metric Reporter 收集 Metrics 到存储/分析系统 (例如 TSDB),或者直接通过 RESTful API 获取。
有了数据之后,可以定制监控规则,关注关键指标,Failover、Checkpoint,、业务 Delay 信息。定制规则用途最广的是可以用来报警,省去很多人工的工作,并且可以定制 failover 多少次时需要人为介入。
当出现问题时,有钉钉报警、邮件报警、短信报警、电话报警等通知工具。
自动化运维的优势是可以通过大盘、报表的形式清晰的查看数据,通过大盘时刻了解作业总体信息,通过报表分析优化。

性能分析

性能分析一般遵循如下的流程:

首先从发现问题开始,如果有 Metrics 系统,再配上监控报警,就可以很快定位问题。然后对问题进行剖析,大盘看问题会比较方便,通过具体的 System Metrics 分析,缩小范围,验证假设,找到瓶颈,进而分析原因,从业务逻辑、JVM、 操作系统、State、数据分布等多维度进行分析;如果还不能找到问题原因,就只能借助 profiling 工具了。

实战:“我的任务慢,怎么办”

“任务慢,怎么办?”可以称之为无法解答的终极问题之一。

其原因在于这种问题是系统框架问题,比如看医生时告诉医生身体不舒服,然后就让医生下结论。而通常医生需要通过一系列的检查来缩小范围,确定问题。同理,任务慢的问题也需要经过多轮剖析才能得到明确的答案。

除了不熟悉 Flink 机制以外,大多数人的问题是对于整个系统跑起来是黑盒,根本不知道系统在如何运行,缺少信息,无法了解系统状态。此时,一个有效的策略是求助 Metrics 来了解系统内部的状况,下面通过一些具体的例子来说明。

  • 发现问题

比如下图 failover 指标,线上有一个不是 0,其它都是 0,此时就发现问题了。

再比如下图 Input 指标正常都在四、五百万,突然跌成 0,这里也存在问题。

业务延时问题如下图,比如处理到的数据跟当前时间比对,发现处理的数据是一小时前的数据,平时都是处理一秒之前的数据,这也是有问题的。

  • 缩小范围,定位瓶颈

当出现一个地方比较慢,但是不知道哪里慢时,如下图红色部分,OUTQ 并发值已经达到 100% 了,其它都还比较正常,甚至优秀。到这里生产者消费者模型出现了问题,生产者 INQ 是满的,消费者 OUT_Q 也是满的,从图中看出节点 4 已经很慢了,节点 1 产生的数据节点 4 处理不过来,而节点 5 的性能都很正常,说明节点 1 和节点 4 之间的队列已经堵了,这样我们就可以重点查看节点 1 和节点 4,缩小了问题范围。

500 个 InBps 都具有 256 个 PARALLEL ,这么多个点不可能一一去看,因此需要在聚合时把 index 是第几个并发做一个标签。聚合按着标签进行划分,看哪一个并发是 100%。在图中可以划分出最高的两个线,即线 324 和线 115,这样就又进一步的缩小了范围。

利用 Metrics 缩小范围的方式如下图所示,就是用 Checkpoint Alignment 进行对齐,进而缩小范围,但这种方法用的较少。

  • 多维度分析

分析任务有时候为什么特别慢呢?

当定位到某一个 Task 处理特别慢时,需要对慢的因素做出分析。分析任务慢的因素是有优先级的,可以从上向下查,由业务方面向底层系统。因为大部分问题都出现在业务维度上,比如查看业务维度的影响可以有以下几个方面,并发度是否合理、数据波峰波谷、数据倾斜;其次依次从 Garbage Collection、Checkpoint Alignment、State Backend 性能角度进行分析;最后从系统性能角度进行分析,比如 CPU、内存、Swap、Disk IO、吞吐量、容量、Network IO、带宽等。

Q & A

Q:Metrics 是系统内部的监控,那是否可以作为 Flink 日志分析的输出?

可以,但是没有必要,都用 Flink 去处理其他系统的日志了,输出或报警直接当做 sink 输出就好了。因为 Metrics 是统计内部状态,你这是处理正常输入数据,直接输出就可以了。

Q:Reporter 是有专门的线程吗?

每个 Reporter 都有自己单独的线程。在 Flink 的内部,线程其实还是挺多的,如果跑一个作业,直接到 TaskManager 上,jstack 就能看到线程的详情。

关注我

微信公众号: zhisheng

另外我自己整理了些 Flink 的学习资料,目前已经全部放到微信公众号(zhisheng)了,你可以回复关键字: Flink即可无条件获取到。另外也可以加我微信 你可以加我的微信: yuanblog_tzs,探讨技术!

更多私密资料请加入知识星球!

专栏介绍

扫码下面专栏二维码可以订阅该专栏

首发地址: http://www.54tianzhisheng.cn/2019/11/15/flink-in-action/

专栏地址: https://gitbook.cn/gitchat/column/5dad4a20669f843a1a37cb4f

Github 代码仓库

https://github.com/zhisheng17/flink-learning/

以后这个项目的所有代码都将放在这个仓库里,包含了自己学习 flink 的一些 demo 和博客

博客

1、 Flink 从0到1学习 —— Apache Flink 介绍

2、 Flink 从0到1学习 —— Mac 上搭建 Flink 1.6.0 环境并构建运行简单程序入门

3、 Flink 从0到1学习 —— Flink 配置文件详解

4、 Flink 从0到1学习 —— Data Source 介绍

5、 Flink 从0到1学习 —— 如何自定义 Data Source ?

6、 Flink 从0到1学习 —— Data Sink 介绍

7、 Flink 从0到1学习 —— 如何自定义 Data Sink ?

8、 Flink 从0到1学习 —— Flink Data transformation(转换)

9、 Flink 从0到1学习 —— 介绍 Flink 中的 Stream Windows

10、 Flink 从0到1学习 —— Flink 中的几种 Time 详解

11、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 ElasticSearch

12、 Flink 从0到1学习 —— Flink 项目如何运行?

13、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 Kafka

14、 Flink 从0到1学习 —— Flink JobManager 高可用性配置

15、 Flink 从0到1学习 —— Flink parallelism 和 Slot 介绍

16、 Flink 从0到1学习 —— Flink 读取 Kafka 数据批量写入到 MySQL

17、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 RabbitMQ

18、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 HBase

19、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 HDFS

20、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 Redis

21、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 Cassandra

22、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 Flume

23、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 InfluxDB

24、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 RocketMQ

25、 Flink 从0到1学习 —— 你上传的 jar 包藏到哪里去了

26、 Flink 从0到1学习 —— 你的 Flink job 日志跑到哪里去了

27、 阿里巴巴开源的 Blink 实时计算框架真香

28、 Flink 从0到1学习 —— Flink 中如何管理配置?

29、 Flink 从0到1学习—— Flink 不可以连续 Split(分流)?

30、 Flink 从0到1学习—— 分享四本 Flink 国外的书和二十多篇 Paper 论文

31、 Flink 架构、原理与部署测试

32、 为什么说流处理即未来?

33、 OPPO 数据中台之基石:基于 Flink SQL 构建实时数据仓库

34、 流计算框架 Flink 与 Storm 的性能对比

35、 Flink状态管理和容错机制介绍

36、 Apache Flink 结合 Kafka 构建端到端的 Exactly-Once 处理

37、 360深度实践:Flink与Storm协议级对比

38、 如何基于Flink+TensorFlow打造实时智能异常检测平台?只看这一篇就够了

39、 Apache Flink 1.9 重大特性提前解读

40、 Flink 全网最全资源(视频、博客、PPT、入门、原理、实战、性能调优、源码解析、问答等持续更新)

41、 Flink 灵魂两百问,这谁顶得住?

42、 Flink 从0到1学习 —— 如何使用 Side Output 来分流?

43、 你公司到底需不需要引入实时计算引擎?

44、 一文让你彻底了解大数据实时计算引擎 Flink

源码解析

1、 Flink 源码解析 —— 源码编译运行

2、 Flink 源码解析 —— 项目结构一览

3、 Flink 源码解析—— local 模式启动流程

4、 Flink 源码解析 —— standalone session 模式启动流程

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

6、 Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Task Manager 启动

7、 Flink 源码解析 —— 分析 Batch WordCount 程序的执行过程

8、 Flink 源码解析 —— 分析 Streaming WordCount 程序的执行过程

9、 Flink 源码解析 —— 如何获取 JobGraph?

10、 Flink 源码解析 —— 如何获取 StreamGraph?

11、 Flink 源码解析 —— Flink JobManager 有什么作用?

12、 Flink 源码解析 —— Flink TaskManager 有什么作用?

13、 Flink 源码解析 —— JobManager 处理 SubmitJob 的过程

14、 Flink 源码解析 —— TaskManager 处理 SubmitJob 的过程

15、 Flink 源码解析 —— 深度解析 Flink Checkpoint 机制

16、 Flink 源码解析 —— 深度解析 Flink 序列化机制

17、 Flink 源码解析 —— 深度解析 Flink 是如何管理好内存的?

18、 Flink Metrics 源码解析 —— Flink-metrics-core

19、 Flink Metrics 源码解析 —— Flink-metrics-datadog

20、 Flink Metrics 源码解析 —— Flink-metrics-dropwizard

21、 Flink Metrics 源码解析 —— Flink-metrics-graphite

22、 Flink Metrics 源码解析 —— Flink-metrics-influxdb

23、 Flink Metrics 源码解析 —— Flink-metrics-jmx

24、 Flink Metrics 源码解析 —— Flink-metrics-slf4j

25、 Flink Metrics 源码解析 —— Flink-metrics-statsd

26、 Flink Metrics 源码解析 —— Flink-metrics-prometheus

26、 Flink Annotations 源码解析

27、 Flink 源码解析 —— 如何获取 ExecutionGraph ?

28、 大数据重磅炸弹——实时计算框架 Flink

29、 Flink Checkpoint-轻量级分布式快照

30、 Flink Clients 源码解析

数据仓库简介、发展、架构演进、实时数仓建设、与离线数仓对比

$
0
0

数据仓库也是公司数据发展到一定规模后必然会提供的一种基础服务,数据仓库的建设也是“数据智能”中必不可少的一环。本文将从数据仓库的简介、经历了怎样的发展、如何建设、架构演变、应用案例以及实时数仓与离线数仓的对比六个方面全面分享关于数仓的详细内容。

本文作者:郭华(付空)

原地地址: https://ververica.cn/developers/how-to-do-real-time-counting/

1. 数据仓库简介

数据仓库是一个面向主题的(Subject Oriented)、集成的(Integrate)、相对稳定的(Non-Volatile)、反映历史变化(Time Variant)的数据集合,用于支持管理决策。

数据仓库是伴随着企业信息化发展起来的,在企业信息化的过程中,随着信息化工具的升级和新工具的应用,数据量变的越来越大,数据格式越来越多,决策要求越来越苛刻,数据仓库技术也在不停的发展。

数据仓库的趋势

  • 实时数据仓库以满足实时化&自动化决策需求;

  • 大数据&数据湖以支持大量&复杂数据类型(文本、图像、视频、音频);

2. 数据仓库的发展

数据仓库有两个环节:数据仓库的构建与数据仓库的应用。

早期数据仓库构建主要指的是把企业的业务数据库如 ERP、CRM、SCM 等数据按照决策分析的要求建模并汇总到数据仓库引擎中,其应用以报表为主,目的是支持管理层和业务人员决策(中长期策略型决策)。

随着业务和环境的发展,这两方面都在发生着剧烈变化。

  • 随着IT技术走向互联网、移动化,数据源变得越来越丰富,在原来业务数据库的基础上出现了非结构化数据,比如网站 log,IoT 设备数据,APP 埋点数据等,这些数据量比以往结构化的数据大了几个量级,对 ETL 过程、存储都提出了更高的要求;

  • 互联网的在线特性也将业务需求推向了实时化,随时根据当前客户行为而调整策略变得越来越常见,比如大促过程中库存管理,运营管理等(即既有中远期策略型,也有短期操作型);同时公司业务互联网化之后导致同时服务的客户剧增,有些情况人工难以完全处理,这就需要机器自动决策。比如欺诈检测和用户审核。

总结来看,对数据仓库的需求可以抽象成两方面:实时产生结果、处理和保存大量异构数据。

注:这里不讨论数据湖技术。

3. 数据仓库建设方法论

3.1 面向主题

从公司业务出发,是分析的宏观领域,比如供应商主题、商品主题、客户主题和仓库主题

3.2 为多维数据分析服务

数据报表;数据立方体,上卷、下钻、切片、旋转等分析功能。

3.3 反范式数据模型

以事实表和维度表组成的星型数据模型

4. 数据仓库架构的演变

数据仓库概念是 Inmon 于 1990 年提出并给出了完整的建设方法。随着互联网时代来临,数据量暴增,开始使用大数据工具来替代经典数仓中的传统工具。此时仅仅是工具的取代,架构上并没有根本的区别,可以把这个架构叫做 离线大数据架构

后来随着业务实时性要求的不断提高,人们开始在离线大数据架构基础上加了一个加速层,使用流处理技术直接完成那些实时性要求较高的指标计算,这便是 Lambda 架构

再后来,实时的业务越来越多,事件化的数据源也越来越多,实时处理从次要部分变成了主要部分,架构也做了相应调整,出现了以实时事件处理为核心的 Kappa 架构

4.1 离线大数据架构

数据源通过离线的方式导入到离线数仓中。下游应用根据业务需求选择直接读取 DM 或加一层数据服务,比如 MySQL 或 Redis。数据仓库从模型层面分为三层:

ODS,操作数据层,保存原始数据;

  • DWD,数据仓库明细层,根据主题定义好事实与维度表,保存最细粒度的事实数据;

  • DM,数据集市/轻度汇总层,在 DWD 层的基础之上根据不同的业务需求做轻度汇总;

  • 典型的数仓存储是 HDFS/Hive,ETL 可以是 MapReduce 脚本或 HiveSQL。

4.2 Lambda 架构

随着大数据应用的发展,人们逐渐对系统的实时性提出了要求,为了计算一些实时指标,就在原来离线数仓的基础上增加了一个实时计算的链路,并对数据源做流式改造(即把数据发送到消息队列),实时计算去订阅消息队列,直接完成指标增量的计算,推送到下游的数据服务中去,由数据服务层完成离线&实时结果的合并。

注:流处理计算的指标批处理依然计算,最终以批处理为准,即每次批处理计算后会覆盖流处理的结果。(这仅仅是流处理引擎不完善做的折中)

Lambda 架构问题:

  • 同样的需求需要开发两套一样的代码:这是 Lambda 架构最大的问题,两套代码不仅仅意味着开发困难(同样的需求,一个在批处理引擎上实现,一个在流处理引擎上实现,还要分别构造数据测试保证两者结果一致),后期维护更加困难,比如需求变更后需要分别更改两套代码,独立测试结果,且两个作业需要同步上线。

  • 资源占用增多:同样的逻辑计算两次,整体资源占用会增多(多出实时计算这部分

4.3 Kappa 架构

Lambda 架构虽然满足了实时的需求,但带来了更多的开发与运维工作,其架构背景是流处理引擎还不完善,流处理的结果只作为临时的、近似的值提供参考。后来随着 Flink 等流处理引擎的出现,流处理技术很成熟了,这时为了解决两套代码的问题,LickedIn 的 Jay Kreps 提出了 Kappa 架构。

  • Kappa 架构可以认为是 Lambda 架构的简化版(只要移除 lambda 架构中的批处理部分即可)。

  • 在 Kappa 架构中,需求修改或历史数据重新处理都通过上游重放完成。

  • Kappa 架构最大的问题是流式重新处理历史的吞吐能力会低于批处理,但这个可以通过增加计算资源来弥补。

Kappa 架构的重新处理过程:

重新处理是人们对 Kappa 架构最担心的点,但实际上并不复杂:

  • 选择一个具有重放功能的、能够保存历史数据并支持多消费者的消息队列,根据需求设置历史数据保存的时长,比如 Kafka,可以保存全部历史数据。

  • 当某个或某些指标有重新处理的需求时,按照新逻辑写一个新作业,然后从上游消息队列的最开始重新消费,把结果写到一个新的下游表中。

  • 当新作业赶上进度后,应用切换数据源,读取 2 中产生的新结果表。

  • 停止老的作业,删除老的结果表。

4.4 Lambda 架构与 Kappa 架构的对比

  • 在真实的场景中,很多时候并不是完全规范的 Lambda 架构或 Kappa 架构,可以是两者的混合,比如大部分实时指标使用 Kappa 架构完成计算,少量关键指标(比如金额相关)使用 Lambda 架构用批处理重新计算,增加一次校对过程。

  • Kappa 架构并不是中间结果完全不落地,现在很多大数据系统都需要支持机器学习(离线训练),所以实时中间结果需要落地对应的存储引擎供机器学习使用,另外有时候还需要对明细数据查询,这种场景也需要把实时明细层写出到对应的引擎中。参考后面的案例。

  • 另外,随着数据多样性的发展,数据仓库这种提前规定 schema 的模式显得越来难以支持灵活的探索&分析需求,这时候便出现了一种数据湖技术,即把原始数据全部缓存到某个大数据存储上,后续分析时再根据需求去解析原始数据。简单的说,数据仓库模式是 schema on write,数据湖模式是 schema on read。

5. 实时数仓案例

菜鸟仓配实时数据仓库本案例参考自菜鸟仓配团队的分享,涉及全局设计、数据模型、数据保障等几个方面。

注:特别感谢缘桥同学的无私分享。

5.1 整体设计

整体设计如下图,基于业务系统的数据,数据模型采用中间层的设计理念,建设仓配实时数仓;计算引擎,选择更易用、性能表现更佳的实时计算作为主要的计算引擎;数据服务,选择天工数据服务中间件,避免直连数据库,且基于天工可以做到主备链路灵活配置秒级切换;数据应用,围绕大促全链路,从活动计划、活动备货、活动直播、活动售后、活动复盘五个维度,建设仓配大促数据体系。

5.2 数据模型

不管是从计算成本,还是从易用性,还是从复用性,还是从一致性等等,我们都必须避免烟囱式的开发模式,而是以中间层的方式建设仓配实时数仓。与离线中间层基本一致,我们将实时中间层分为两层。

第一层 DWD 公共实时明细层

实时计算订阅业务数据消息队列,然后通过数据清洗、多数据源 join、流式数据与离线维度信息等的组合,将一些相同粒度的业务系统、维表中的维度属性全部关联到一起,增加数据易用性和复用性,得到最终的实时明细数据。这部分数据有两个分支,一部分直接落地到 ADS,供实时明细查询使用,一部分再发送到消息队列中,供下层计算使用;

第二层 DWS 公共实时汇总层

以数据域+业务域的理念建设公共汇总层,与离线数仓不同的是,这里汇总层分为轻度汇总层和高度汇总层,并同时产出,轻度汇总层写入 ADS,用于前端产品复杂的 olap 查询场景,满足自助分析和产出报表的需求;高度汇总层写入 Hbase,用于前端比较简单的 kv 查询场景,提升查询性能,比如实时大屏等;

注:

  • ADS 是一款提供 OLAP 分析服务的引擎。开源提供类似功能的有,Elastic Search、Kylin、Druid 等;
  • 案例中选择把数据写入到 Hbase 供 KV 查询,也可根据情况选择其他引擎,比如数据量不多,查询压力也不大的话,可以用 MySQL;
  • 因主题建模与业务关系较大,这里不做描述;

5.3 数据保障

阿里巴巴每年都有双十一等大促,大促期间流量与数据量都会暴增。实时系统要保证实时性,相对离线系统对数据量要更敏感,对稳定性要求更高。所以为了应对这种场景,还需要在这种场景下做两种准备:

大促前的系统压测;

大促中的主备链路保障;

菜鸟双11「仓储配送数据实时化」详情了解: https://yq.aliyun.com/articles/658787

6. 实时数仓与离线数仓的对比

在看过前面的叙述与菜鸟案例之后,我们看一下实时数仓与离线数仓在几方面的对比:

  • 首先,从架构上,实时数仓与离线数仓有比较明显的区别,实时数仓以 Kappa 架构为主,而离线数仓以传统大数据架构为主。Lambda 架构可以认为是两者的中间态。

  • 其次,从建设方法上,实时数仓和离线数仓基本还是沿用传统的数仓主题建模理论,产出事实宽表。另外实时数仓中实时流数据的 join 有隐藏时间语义,在建设中需注意。

  • 最后,从数据保障看,实时数仓因为要保证实时性,所以对数据量的变化较为敏感。在大促等场景下需要提前做好压测和主备保障工作,这是与离线数据的一个较为明显的区别。

关注我

微信公众号: zhisheng

另外我自己整理了些 Flink 的学习资料,目前已经全部放到微信公众号(zhisheng)了,你可以回复关键字: Flink即可无条件获取到。另外也可以加我微信 你可以加我的微信: yuanblog_tzs,探讨技术!

更多私密资料请加入知识星球!

专栏介绍

扫码下面专栏二维码可以订阅该专栏

首发地址: http://www.54tianzhisheng.cn/2019/11/15/flink-in-action/

专栏地址: https://gitbook.cn/gitchat/column/5dad4a20669f843a1a37cb4f

Github 代码仓库

https://github.com/zhisheng17/flink-learning/

以后这个项目的所有代码都将放在这个仓库里,包含了自己学习 flink 的一些 demo 和博客

博客

1、 Flink 从0到1学习 —— Apache Flink 介绍

2、 Flink 从0到1学习 —— Mac 上搭建 Flink 1.6.0 环境并构建运行简单程序入门

3、 Flink 从0到1学习 —— Flink 配置文件详解

4、 Flink 从0到1学习 —— Data Source 介绍

5、 Flink 从0到1学习 —— 如何自定义 Data Source ?

6、 Flink 从0到1学习 —— Data Sink 介绍

7、 Flink 从0到1学习 —— 如何自定义 Data Sink ?

8、 Flink 从0到1学习 —— Flink Data transformation(转换)

9、 Flink 从0到1学习 —— 介绍 Flink 中的 Stream Windows

10、 Flink 从0到1学习 —— Flink 中的几种 Time 详解

11、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 ElasticSearch

12、 Flink 从0到1学习 —— Flink 项目如何运行?

13、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 Kafka

14、 Flink 从0到1学习 —— Flink JobManager 高可用性配置

15、 Flink 从0到1学习 —— Flink parallelism 和 Slot 介绍

16、 Flink 从0到1学习 —— Flink 读取 Kafka 数据批量写入到 MySQL

17、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 RabbitMQ

18、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 HBase

19、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 HDFS

20、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 Redis

21、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 Cassandra

22、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 Flume

23、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 InfluxDB

24、 Flink 从0到1学习 —— Flink 读取 Kafka 数据写入到 RocketMQ

25、 Flink 从0到1学习 —— 你上传的 jar 包藏到哪里去了

26、 Flink 从0到1学习 —— 你的 Flink job 日志跑到哪里去了

27、 阿里巴巴开源的 Blink 实时计算框架真香

28、 Flink 从0到1学习 —— Flink 中如何管理配置?

29、 Flink 从0到1学习—— Flink 不可以连续 Split(分流)?

30、 Flink 从0到1学习—— 分享四本 Flink 国外的书和二十多篇 Paper 论文

31、 Flink 架构、原理与部署测试

32、 为什么说流处理即未来?

33、 OPPO 数据中台之基石:基于 Flink SQL 构建实时数据仓库

34、 流计算框架 Flink 与 Storm 的性能对比

35、 Flink状态管理和容错机制介绍

36、 Apache Flink 结合 Kafka 构建端到端的 Exactly-Once 处理

37、 360深度实践:Flink与Storm协议级对比

38、 如何基于Flink+TensorFlow打造实时智能异常检测平台?只看这一篇就够了

39、 Apache Flink 1.9 重大特性提前解读

40、 Flink 全网最全资源(视频、博客、PPT、入门、原理、实战、性能调优、源码解析、问答等持续更新)

41、 Flink 灵魂两百问,这谁顶得住?

42、 Flink 从0到1学习 —— 如何使用 Side Output 来分流?

43、 你公司到底需不需要引入实时计算引擎?

44、 一文让你彻底了解大数据实时计算引擎 Flink

源码解析

1、 Flink 源码解析 —— 源码编译运行

2、 Flink 源码解析 —— 项目结构一览

3、 Flink 源码解析—— local 模式启动流程

4、 Flink 源码解析 —— standalone session 模式启动流程

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

6、 Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Task Manager 启动

7、 Flink 源码解析 —— 分析 Batch WordCount 程序的执行过程

8、 Flink 源码解析 —— 分析 Streaming WordCount 程序的执行过程

9、 Flink 源码解析 —— 如何获取 JobGraph?

10、 Flink 源码解析 —— 如何获取 StreamGraph?

11、 Flink 源码解析 —— Flink JobManager 有什么作用?

12、 Flink 源码解析 —— Flink TaskManager 有什么作用?

13、 Flink 源码解析 —— JobManager 处理 SubmitJob 的过程

14、 Flink 源码解析 —— TaskManager 处理 SubmitJob 的过程

15、 Flink 源码解析 —— 深度解析 Flink Checkpoint 机制

16、 Flink 源码解析 —— 深度解析 Flink 序列化机制

17、 Flink 源码解析 —— 深度解析 Flink 是如何管理好内存的?

18、 Flink Metrics 源码解析 —— Flink-metrics-core

19、 Flink Metrics 源码解析 —— Flink-metrics-datadog

20、 Flink Metrics 源码解析 —— Flink-metrics-dropwizard

21、 Flink Metrics 源码解析 —— Flink-metrics-graphite

22、 Flink Metrics 源码解析 —— Flink-metrics-influxdb

23、 Flink Metrics 源码解析 —— Flink-metrics-jmx

24、 Flink Metrics 源码解析 —— Flink-metrics-slf4j

25、 Flink Metrics 源码解析 —— Flink-metrics-statsd

26、 Flink Metrics 源码解析 —— Flink-metrics-prometheus

26、 Flink Annotations 源码解析

27、 Flink 源码解析 —— 如何获取 ExecutionGraph ?

28、 大数据重磅炸弹——实时计算框架 Flink

29、 Flink Checkpoint-轻量级分布式快照

30、 Flink Clients 源码解析

如何利用快照( snapshot )功能快速定位性能问题

$
0
0

我们常常会遇到这样的困惑,收到用户或者客服的反馈,平台使用有问题,但是测试人员搭建环境后又没办法复现故障,最后导致问题没法解决,眼睁睁地看着用户流失。

这是因为线上生产环境非常复杂、很多时候是偶发性 bug ,但却很难捕捉。特别是随着微服务盛行,系统复杂度增加,线上故障的快速定位和及时分析解决面临着巨大挑战,以前只能靠人来解决。但是人的问题解决能力与速度依赖于经验,有时候甚至需要跨部门的配合,这样的成本非常高,一旦关键人员流失、部门配合不融洽,整个故障的解决速度就会极速下降。

这时候我要给大家带来一款好用的性能分析工具- Application Insight (应用性能管理平台,以下简称 Ai ),至于它能干什么呢?往下看你就知道了~


 trace & snapshot 功能介绍


01 业务运行中有哪些错误呢?

在代码运行中,常常会遇到这几类型问题:

JVM

常见的比如 oom 内存溢出、内存泄漏、gc pause 、磁盘运行不足等

● 数据库

常见的比如数据库负载繁忙、数据库服务器超负载、单一 sql 语句执行缓慢、数据库连接池获取时间长、对数据库连接池的访问次数过多等

● 外部服务

常见的比如外部服务网络问题、不同应用之间的调用阻塞、索引设置不合适等

● 其它问题

上面都是我们常见的场景,但是还有一些问题是深深的潜藏在程序中,需要我们深入地分析代码才能找到原因。

线程死锁、循环操作、事务异常、jvm crash 等


02 什么是 trace ?

trace 收集程序运行时的信息来查询程序运行情况,定位代码中需要修改和优化的部分,提高代码运行速度,提升用户体验。

业内常用的方法有如下三种:


● 事件方法

在 Java 语言中,使用 jvmti( jvm tools interface )api 方法来捕捉诸如方法调用、类载入、类卸载、进入离开线程等事件,然后再基于这些事件进行代码行为的分析。


● 统计抽样

每隔一段事件中断调用系统,收集当前的调用栈信息,记录调用栈中出现的函数及这些函数的调用结构,基于这些信息得到函数的调用关系图及每个函数的 cpu 使用信息。


● 插码

在目标程序中插入指令代码,这些指令代码会记录程序运行的开始时间、结束时间等,再经过统计得出函数调用情况、函数 cpu 使用情况。


03  AI 的 trace

Ai 采用插码与快照(统计抽样)的方式来实现 trace ,trace 包括慢 trace 、错误 trace 、sql trace 、snapshot 四类:


● 慢 trace

在用户的事务响应时间超过阈值的时候去进行采集。系统默认是2秒,但用户可以根据自己实际情况在 Ai 的“设置-慢事务”处进行设置。 

图1.png

图2.png

● 错误 trace

当程序运行异常或直接报错时,我们会直接采集错误 trace,还原现场。

图3.png

● Sql trace

在平台开启慢 sql 追踪,并设置慢 sql 追踪阈值,当 sql 的性能大于该阈值的时候,agent 记录慢 sql 的堆栈信息。默认为0.5s,用户可以根据自己实际情况在 Ai 的“设置-数据库”进行设置。

同时用户可以开启 sql 执行计划,对于执行缓慢的 sql 进行抓取。

● snapshot

snapshot 实现原理为在客户业务运行缓慢时对调用进行多次代码快照,并且进行方法、耗时分析,得出快照 trace 。

采集规则为 web 事务连续两分钟的平均响应时间超过4*apdex_T(默认两秒),且每分钟的快照数不超过1个。

用户无需做任何配置,即可使用此功能,还可以根据业务动态调整阈值。

图6.png


用户案例分析

 

01 问题类型:连接服务被拒绝

1)问题现象:用户反馈无法退出登录


2)问题调查和分析

a:登录平台查看登出的接口情况

登录接口为 rest / api / login ,选择该 web 事务后进入查看详情,使用“筛选”进行条件过滤,获取失败的 trace 。

图7.png

b:查看错误 trace

在总览页面,通过异常分析,看到 http 响应码和异常类、异常信息,这时候我们已经清楚问题原因了。

图8.png

点击“详情”查看程序的执行情况,可以看到程序循环了三次,均出现错误

图9.png

在错误详情,查看详情的调用栈,我们排查到“caused by java.net.ConnectException:拒绝连接(connection refused)



3)解决方案

与运维人员配合重启负责注册模块的服务器后,业务恢复正常。

4)建议方案

在报警处针对重要业务服务进行配置,当响应时间超过阈值或者出现频率超过阈值时,提前报警,挽回损失。

4.1 新功能体验

登录平台后可以查看报警状态

图13.png

进入事务详情,鼠标悬停查看该时间段内发生的最近最严重的报警详情

图14.png

点击红点跳转报警记录查看该事务在该时间段内的所有详情

图15.png

4.2 如何配置报警

a:从 Ai 页面点击报警

进入报警页面,再选择报警规则,创建报警规则名称、选择类型、自定义规则的可用与不可用时间(比如节假日不可用等)

图16.png

b:选择报警对象

目前系统支持按照具体 web 事务、按照不同集群、按照高频 web 事务入口( cpm >10)来进行选择,我们选择按照具体的 web 事务为报警对象。

图17.png

c:选择严重条件

根据我们的业务述求,只要过去10分钟内该事务的平均响应时间大于2秒,而且至少有5分钟的平均响应时间大于2s, 则触发严重报警。

图18.png

d:选择警告条件

根据我们的业务述求,只要过去10分钟内该事务的平均响应时间大于1秒,而且至少有5分钟的平均响应时间大于1s, 则触发严重报警。

图19.png

此时我们报警规则已配置好,当事务触发报警时,便可在 Ai 平台直接查看是否有报警、是否严重。如果您想及时感知报警,可进行进一步的配置。

4.3 如何接收报警

a:创建接收人

图20.png

b:选择接收方式

目前 Ai 默认邮件与 webhook 报警方式,但通过与 Cloud Alert (智能告警平台,简称 CA 平台)的打通,支持短信、微信、钉钉等通知方式,更详细的报警分发与报警压缩可查看(通过免费版,中小运维团队够够的)https://www.aiops.com/CAintroduce.html

c:将接收人和报警规则关联

选择报警规则和触发行为即可

图21.png

常常使用我们 Ai 平台看报警的小伙伴一定有点奇怪,为什么我没有在 Ai 平台中看到报警状态的相关信息呢?


没错,Ai 与报警深度关联功能是我们新开发的功能,我也提前体验了一把,感觉棒棒哒!该功能于12月中旬上线,小伙伴们敬请期待吧~


若您还不是 OneAPM 用户,请点击  https://user.oneapm.com/pages/v2/signup 立即注册免费试用,即刻感受性能优化吧!


在使用的过程中,如您有任何疑问和建议,欢迎随时联系我们,我们将竭诚为您服务:


qq:321095806

社区:http://club.oneapm.com


聊聊 Nacos 配置隔离和分类的使用

$
0
0




今天的主题还是Nacos

正所谓一入江湖身不由己

至今还在探索中

如今到底如何

↓↓



最近在使用Nacos来作为配置中心和注册中心,在使用的过程难免会有些问题。有的是框架问题,有的是使用方式的问题,不久前也分享了一篇 《最近使用Nacos的一些问题》,感兴趣的可以看看。

 

今天要聊的话题也是在使用过程中发现的,主要是前期赶进度太忙了,停下来之后才有时间去整理,去思考更优的方式。


-

1

-

环境隔离



环境隔离是最基本的一个需求,在日常开发过程中,常需要不同的环境,比如开发,测试,预发,线上环境。

 

在Nacos中有命名空间的概念,通过空间来支持多环境隔离,也就是一个环境对应一个命名空间

 

             

 

我们可以创建如下图所示的多个空间来进行隔离:

 

             

 

如果需要物理隔离,就要部署多套Nacos环境,我们目前就是部署的多套,部署多套的主要原因就是目前还没有完善的权限控制,生产环境的配置直接暴露给所有人是很危险的事情,据说在下个版本中会增加权限相关的功能。

 

还有一种使用场景就是租户隔离,从多个租户(用户)的角度来看,每个租户(用户)可能会有自己的 namespace,每个租户(用户)的配置数据以及注册的服务数据都会归属到自己的 namespace 下,以此来实现多租户间的数据隔离。例如超级管理员分配了三个租户,分别为张三、李四和王五。分配好了之后,各租户用自己的账户名和密码登录后,创建自己的命名空间。如下图所示:

 

             

 

但此功能还在规划中,后面才会支持。目前我们在使用上也在往这个方面靠拢,不同环境目前是通过多套部署来进行隔离,那么namespace我们就得用在其他地方才能体现它的价值和意义。

 

可以根据内部产品线来划分namespace,每个namespace下再细分配置文件,这样存在的一个问题是如果我多个产品线之前有共用的配置信息,也就是共享配置,目前看下来,只能每边都存放一份。

 

namespace已经隔离了,如果要跨namespace进行配置的共享,不知道后面有没有计划支持这样的功能。

 

Nacos的namespace设计也就是为了区分多环境或者多租户,这样来看跨namespace就属于特殊需求了,所以我们在做配置规划的需要需要考虑进去,有共同配置需要共享的,得放入相同的namespace中。


-

2

-

配置分类


一般在最开始使用的时候,也不会考虑太多,直接为每个项目建一个对应的application配置,所有的配置都放在里面。配置量少还可以,配置量大的时候不建议这么做,我们需要有具体的分类才能让配置更加的一目了然。

 

除了这个问题,还有就是像一些需要共用的配置,没有独立出来,每个项目的application中都存在一份相同的,万一哪天需要修改了,你会发现改了一个地方还不行,很多地方都得改,苦啊。。。

 

             

 

下面说下我是怎么分类的,每个人都有自己的想法,并没有什么标准,仅供参考:


Group


Group是用来分组的,默认是DEFAULT_GROUP,我这边分了三个组,如下:

 

  • MIDDLEWARE_GROUP

中间件配置,比如Redis, Mq等。


  • APPLICATION_GROUP

应用配置,比如jackson,SpringBoot Actuator等。


  • BIZ_GROUP

业务配置,跟业务相关,比如订单超时未支付的时间,全局的邮费等。


DataId


DataId是配置的ID,也就是唯一标识。

 

通常以服务名称来命名,示列:

  • xxx-order-biz (BIZ_GROUP)

  • xxx-order-application (APPLICATION_GROUP)


中间件的配置就以中间件名称来命名,示列:

  • xxx-redis (MIDDLEWARE_GROUP)

  • xxx-rocketmq (MIDDLEWARE_GROUP)

  • xxx-elasticsearch (MIDDLEWARE_GROUP)

 

有了细致的分类后,我们需要哪个配置就引入哪个DataId即可,不用全部引入,修改也不用每个配置文件都去修改,使用如下:

 

@NacosPropertySource(dataId = NacosConstant.REDIS, groupId = NacosConstant.MIDDLEWARE_GROUP, autoRefreshed = true)@NacosPropertySource(dataId = NacosConstant.ORDER_BIZ, groupId = NacosConstant.BIZ_GROUP, autoRefreshed = true)@NacosPropertySource(dataId = NacosConstant.ORDER_APPLICATION, groupId = NacosConstant.APPLICATION_GROUP, autoRefreshed = true)


-

3

-

配置使用


最常用的我们是通过@NacosValue注解来读取对应的配置内容,比较尴尬的是经常忘记将@NacosValue中的autoRefreshed设置为true,然后就会发现配置修改了没实时生效,得重启才行。

 

我建议还是不要到处使用@NacosValue注解来读取配置,可以将配置统一管理起来,比如使用@NacosConfigurationProperties就很方便。

 

@Data@Configuration@NacosConfigurationProperties(dataId = NacosConstant.ORDER_BIZ, groupId = NacosConstant.BIZ_GROUP, autoRefreshed =true)public class OrderBizConfig {privateBigDecimal postage;}

像业务配置也有多种类型,每种类型就可以使用一个@NacosConfigurationProperties来管理,计算不用@NacosConfigurationProperties也可以创建一个单独的配置类,在这个类中使用@NacosValue,使用方就直接注入这个配置类,万一哪天配置的key要修改或者要去掉这个配置也非常方便。

 

@Data@Configurationpublic class OrderBizConfig {@NacosValue(value ="${postage}", autoRefreshed = true)   private BigDecimal postage;}





都来分享一下自己的效率工具吧,互相借鉴,我先来

$
0
0
上面说的基本都用过, 说几个 Windows 上小众的吧, 以前回过太多月经贴

mouse manager, 帮我把鼠标侧键改成其它热键, 内存比鼠标驱动自带的小很多

Strokeit, 用了十几年, 依然兼容的全局鼠标手势, 不是 WGestures 和 StrokePlus 不好, 是它太优秀, linux 上的差远了

Wox, 大名鼎鼎不用介绍, 用了才三四年吧, 内存还是挺大的, 不是说 uTools 不够好, 而是 Wox 目前够我用了, 毕竟当年 everything + altrun 的七八年也用的好好的

Claunch, 用了 2 年左右的快捷方式 launcher panel, 用了十几年的酷鱼快速启动, 结果去年 Windows10 一次小更新居然突然给我 C++ 丢指针了, 团队跑路了没法 fix, 换了市面上几乎所有 roland 什么的替代品, 比 Claunch 好的真没有

cmder, 终端, 简而言之, 不是它多好使, 实在没什么对手... tmux redraw 的问题在底层没法修, 凑合用它或者临时 git-bash

autohotkey, 用了十几年的老东西, 因为有以图找位置的功能, 所以可以把我 90% 的手动操作都自动完成了, 比如某些 app 签到刷分配合雷电模拟器(逆向是违法的, 所以怎么拟人怎么来)

chrome 的 tampermonkey, 不用多说, 会的自然懂, 不懂的也不用会

geek uninstaller, 这玩意卸载软件比某些软件自带的卸载工具都干净... 注册表相关都给找出来

wiztree, 早年用的 spacesniffer 的替代品, 可视化查看磁盘使用情况, 清理某些大片时候用的, 不清理都不知道有的电影要十几 GB........

anydesk, 下载安装使用都只单文件的远程协助工具, 不如 teamviewer 快, 不如 QQ 的稳定, 但是, 没招啊, 谁让它简单

dism++, Windows 官方优化工具 dism 的 GUI 版

===========================

手打有点累, 错字请包涵

2019 年 11 月 26 日 10:32:30

如何识别文件的真假

$
0
0

每个人都下载文件,大家有没有想过,文件可能是假的,尤其来自网盘或专门的下载站。

本文就来谈谈如何识别文件的真假。

一、XcodeGhost 事件

我们从一件真实的事件说起。

2015年9月,苹果手机的一些 App 被 发现向可疑网站发送数据。进一步调查确认,可疑代码是 Xcode 打包时植入的。也就是说,开发者的编程工具 Xcode 被动过手脚了。

腾讯的安全团队公布 调查报告,应用商店的前 5000 名应用有76个被感染。360 应用商店检查后 发现,共有1076个 App 被感染,包括微信、网易云音乐、滴滴打车、高德地图、12306、同花顺等热门应用。苹果公司将所有被感染的版本,都从官方软件商店下架了。这个事件就称为 XcodeGhost 事件

国家互联网应急中心专门发出了预警通知。

追查下去,那些动过手脚的 Xcode 都不是从官方渠道下载的,而是来自网盘或下载站。一个网名"coderfun"的人,在各种 iOS 开发者论坛或者微博留言,引诱其他开发者下载修改过的 Xcode,版本从 Xcode 6.1 到 6.4。

事后,这位 coderfun 发出致歉公告,表示这只是自己的一次实验,没有恶意。但是,这个事件足以引起警惕,任何下载的文件都不一定安全,很可能被修改过或植入恶意代码。

二、软件的防伪措施

为了防止来源不明的软件,很多平台都有签名机制。软件发布必须由认证过的开发商,使用平台的密钥签名。如果用户安装未签名的软件,平台会弹出警告,阻止安装。下面就是 MacOS 的警告。

但是,不可能所有开发者都去认证,尤其是认证要收费。而且,用户对这种警告不在乎,一般都会忽略或手动关闭。所以,这种做法的效果不明显。

目前的常用做法是,软件发布时,同时给出哈希码和签名文件。前者保证没有被第三方修改,后者保证确实出自原始作者。

举例来说,Linux 的发行版 Manjaro 除了提供原始的 iso 文件,还提供另外三个文件:sha1 哈希文件、sha 256 哈希文件和 sig 签名文件。 它们保证了软件的真实性。

三、哈希码验证

哈希码指的是,文件内容经过哈希函数的计算,会返回一个独一无二的字符串。哪怕原始内容只改动一个字节,哈希码也会完全不同。用户下载软件后,只要计算一下哈希码,再跟作者给出的哈希码比较一下,就会知道软件有没有被改动。

目前,常用的三种哈希函数是 MD5、SHA1 和 SHA256。其中,SHA256 最安全,SHA1 次之,MD5 垫底。一般来说,软件至少会提供其中一种哈希码。

下面是哈希码的验证方法。

(1)Linux 系统

Linux 系统直接用 md5sumsha1sumsha256sum这三个命令,计算哈希码。


$ md5sum foo.zip
$ sha1sum foo.zip
$ sha256sum foo.zip

上面命令返回文件 foo.zip的三种哈希码。用户再跟作者给出的哈希码比对。如果不一致,文件就是被改动了,或者没有完整下载。

有时,就像前面 Manjaro 的例子,哈希码不是写在网页上,而是作为一个单独的文本文件下载。这时可以使用 -c参数。


$ md5sum -c foo.zip.md5file
$ sha1sum -c foo.zip.sha1file
$ sha256sum -c foo.zip.sha256file

上面命令会返回哈希码的比对结果,直接告诉用户是否一致。

(2)Mac 系统

MacOS 的验证命令需要自己安装。


$ brew install md5sha1sum

执行上面命令以后, md5sumsha1sum就可以使用了。至于 sha256sum要用 shasum -a256命令代替。

(3)Windows 系统

Windows 可以下载安装免费软件 Quick hash或者 Raymond's MD5 & SHA Checksum Utility。其中,Quick hash 是跨平台的,还支持 Linux 和 MacOS。

四、签名验证

哈希码只能保证文件内容没有修改,但是哈希码本身也有可能仿冒,完全可能连带原始文件一起造假。

文件签名能解决这个问题。软件发布时,作者用自己的私钥,对发布的软件生成一个签名文件(Manjaro 例子的 sig 文件),用户使用作者的公钥验证签名文件。

第一步,下载公钥。

软件的官网一般都会给出作者公钥的下载方法。比如,Manjaro 就可以从 GitHub 仓库下载公钥。


$ wget github.com/manjaro/packages-core/raw/master/manjaro-keyring/manjaro.gpg

公钥也有可能放在专门的公钥服务器,这时可以使用 gpg命令在从公钥服务器下载。


$ gpg --keyserver hkp://eu.pool.sks-keyservers.net --search-keys [公钥 ID]

上面命令会列出搜索结果,让你选择是否下载某一个公钥。 --keyserver参数指定公钥服务器, search-keys参数给出搜索参数,可以是作者的名称,也可以是公钥的指纹。

gpg命令在 Linux 下可以直接使用,MacOS 和 Windows 需要安装 GnuPG

第二步,导入公钥。

下载得到公钥后,将其导入操作系统。


$ gpg --import [公钥文件]

如果有完整的公钥指纹, gpg命令的 --recv-key参数可以直接从服务器导入公钥。


$ gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-key "27DE B156 44C6 B3CF 3BD7 D291 300F 846B A25B AE09"

第三步,验证签名。

导入公钥以后,就可以验证签名文件(后缀名为 sig的 文件)了。


# 用法一
$ gpg --verify [签名文件]

# 用法二
$ gpg --verify [签名文件] [原始文件]

上面命令的两种用法,效果是一样的。但是,用法一要求原始文件与签名文件同名,且在一个目录下。比如,签名文件是 foo.iso.sig,原始文件必须是同目录下的 foo.iso

签名文件一般包括完整的公钥指纹,所以也可以跳过上面的第一步和第二步,直接从公钥服务器获取公钥,验证签名。


$ gpg --keyserver-options auto-key-retrieve --verify [签名文件]

(完)

文档信息

  • 版权声明:自由转载-非商用-非衍生-保持署名( 创意共享3.0许可证
  • 发表日期: 2019年11月26日

GitHub - jgraph/drawio: Source to www.draw.io

$
0
0

Build Status

About

draw.iois an online diagramming web site that delivers the source in this project.

draw.io uses the mxGraph libraryas the base of the stack, with the GraphEditor examplefrom mxGraph as the base of the application part. The mxGraph library build used is stored under /etc/mxgraph/mxClient.js.

License

draw.io is licensed under the Apache v2.

Development

A development guide is being started on the GitHub project wiki. There is a draw.iotag on Stack Overflow currently, please make sure any questions adhere to their guidelines for question.

The mxGraph documentationprovides a lot of the docs for the bottom part of the stack. There is an mxgraph tag on SO.

Running

One way to run draw.io is to fork this project, publish the master branch to GitHub pagesand the pages siteswill have the full editor functionality (sans the integrations).

Another way is to use the recommended Docker projector to download draw.io Desktop.

The full packaged .war of the client and servlets is built when the project is tagged and available on the releases page.

Supported Browsers

draw.io supports IE 11, Chrome 32+, Firefox 38+, Safari 9.1.x, 10.1.x and 11.0.x, Opera 20+, Native Android browser 5.1.x+, the default browser in the current and previous major iOS versions (e.g. 11.2.x and 10.3.x) and Edge 23+.

微服务的4个设计原则和19个解决方案 - 晓晨Master - 博客园

$
0
0

本文转自: http://developer.51cto.com/art/201709/552085.htm

微服务架构现在是谈到企业应用架构时必聊的话题,微服务之所以火热也是因为相对之前的应用开发方式有很多优点,如更灵活、更能适应现在需求快速变更的大环境。
本文将介绍微服务架构的演进、优缺点和微服务应用的设计原则,然后着重介绍作为一个“微服务应用平台”需要提供哪些能力、解决哪些问题才能更好的支撑企业应用架构。
微服务平台也是我目前正在参与的,还在研发过程中的平台产品,平台是以SpringCloud为基础,结合了普元多年来对企业应用的理解和产品的设计经验,逐步孵化的一个微服务应用平台。

一、微服务架构演进过程

近年来我们大家都体会到了互联网、移动互联带来的好处,作为IT从业者,在生活中时刻感受互联网好处的同时,在工作中可能感受的却是来自自互联网的一些压力,那就是我们传统企业的IT建设也是迫切需要转型,需要面向外部客户,我们也需要应对外部环境的快速变化、需要快速创新,那么我们的IT架构也需要向互联网企业学习作出相应的改进,来支撑企业的数字化转型。
我们再看一下应用架构的演进过程,回忆一下微服务架构是如何一步一步进化产生的,最早是应用是单块架构,后来为了具备一定的扩展和可靠性,就有了垂直架构,也就是加了个负载均衡,接下来是前几年比较火的SOA,主要讲了应用系统之间如何集成和互通,而到现在的微服务架构则是进一步在探讨一个应用系统该如何设计才能够更好的开发、管理更加灵活高效。
微服务架构的基本思想就是“围绕业务领域组件来创建应用,让应用可以独立的开发、管理和加速”。

二、微服务架构的好处

我们总结了四个方面的优点,分别如下:
是每个微服务组件都是简单灵活的,能够独立部署。不再像以前一样,应用需要一个庞大的应用服务器来支撑。
可以由一个小团队负责更专注专业,相应的也就更高效可靠。
微服务之间是松耦合的,微服务内部是高内聚的,每个微服务很容易按需扩展。
微服务架构与语言工具无关,自由选择合适的语言和工具,高效的完成业务目标即可。
看到这里,大家会觉得微服务架构挺不错,然而还会有一些疑问,什么样的应用算是一个微服务架构的应用?该怎样设计一个微服务架构的应用?那我们来一起看看我们推荐的微服务应用的设计原则。

三、微服务应用4个设计原则

我们总结了四个原则推荐给大家:

  • AKF拆分原则
  • 前后端分离
  • 无状态服务
  • Restful通信风格

1.AKF拆分原则

AKF扩展立方体(参考《The Art of Scalability》),是一个叫AKF的公司的技术专家抽象总结的应用扩展的三个维度。理论上按照这三个扩展模式,可以将一个单体系统,进行无限扩展。
X 轴 :指的是水平复制,很好理解,就是讲单体系统多运行几个实例,做个集群加负载均衡的模式。
Z 轴 :是基于类似的数据分区,比如一个互联网打车应用突然或了,用户量激增,集群模式撑不住了,那就按照用户请求的地区进行数据分区,北京、上海、四川等多建几个集群。
Y 轴 :就是我们所说的微服务的拆分模式,就是基于不同的业务拆分。
场景说明:比如打车应用,一个集群撑不住时,分了多个集群,后来用户激增还是不够用,经过分析发现是乘客和车主访问量很大,就将打车应用拆成了三个乘客服务、车主服务、支付服务。三个服务的业务特点各不相同,独立维护,各自都可以再次按需扩展。

2.前后端分离

前后端分离原则,简单来讲就是前端和后端的代码分离也就是技术上做分离,我们推荐的模式是最好直接采用物理分离的方式部署,进一步促使进行更彻底的分离。不要继续以前的服务端模板技术,比如JSP ,把Java JS HTML CSS 都堆到一个页面里,稍复杂的页面就无法维护。这种分离模式的方式有几个好处:
前后端技术分离,可以由各自的专家来对各自的领域进行优化,这样前端的用户体验优化效果会更好。
分离模式下,前后端交互界面更加清晰,就剩下了接口和模型,后端的接口简洁明了,更容易维护。
前端多渠道集成场景更容易实现,后端服务无需变更,采用统一的数据和模型,可以支撑前端的web UI 移动App等访问。

3.无状态服务

对于无状态服务,首先说一下什么是状态:如果一个数据需要被多个服务共享,才能完成一笔交易,那么这个数据被称为状态。进而依赖这个“状态”数据的服务被称为有状态服务,反之称为无状态服务。
那么这个无状态服务原则并不是说在微服务架构里就不允许存在状态,表达的真实意思是要把有状态的业务服务改变为无状态的计算类服务,那么状态数据也就相应的迁移到对应的“有状态数据服务”中。
场景说明:例如我们以前在本地内存中建立的数据缓存、Session缓存,到现在的微服务架构中就应该把这些数据迁移到分布式缓存中存储,让业务服务变成一个无状态的计算节点。迁移后,就可以做到按需动态伸缩,微服务应用在运行时动态增删节点,就不再需要考虑缓存数据如何同步的问题。

4.Restful通信风格

作为一个原则来讲本来应该是个“无状态通信原则”,在这里我们直接推荐一个实践优选的Restful 通信风格 ,因为他有很多好处:
无状态协议HTTP,具备先天优势,扩展能力很强。例如需要安全加密是,有现成的成熟方案HTTPS可用。
JSON 报文序列化,轻量简单,人与机器均可读,学习成本低,搜索引擎友好。
语言无关,各大热门语言都提供成熟的Restful API框架,相对其他的一些RPC框架生态更完善。
当然在有些特殊业务场景下,也需要采用其他的RPC框架,如thrift、avro-rpc、grpc。但绝大多数情况下Restful就足够用了。

四、微服务架构带来的问题

做到了前面讲的四个原则,那么就可以说是构建了一个微服务应用,感觉上也不复杂。但实际上微服务也不是个万金油,也是有利有弊的,接下来我们来看看引入微服务架构后带来的问题有哪些。

依赖服务变更很难跟踪,其他团队的服务接口文档过期怎么办?依赖的服务没有准备好,如何验证我开发的功能。
部分模块重复构建,跨团队、跨系统、跨语言会有很多的重复建设。
微服务放大了分布式架构的系列问题,如分布式事务怎么处理?依赖服务不稳定怎么办?
运维复杂度陡增,如:部署物数量多、监控进程多导致整体运维复杂度提升。
上面这些问题我们应该都遇到过,并且也会有一些解决方案,比如提供文档管理、服务治理、服务模拟的工具和框架; 实现统一认证、统一配置、统一日志框架、分布式汇总分析; 采用全局事务方案、采用异步模拟同步;搭建持续集成平台、统一监控平台等等。
这些解决方案折腾到最后终于搞明白了,原来我们是需要一个微服务应用平台才能整体性的解决这些问题。

五、微服务平台的19个落地实践

1.企业IT建设的三大基础环境

我们先来宏观的看一下,一个企业的IT建设非常重要的三大基础环境:团队协作环境、个人基础环境、IT基础设施。

团队协作环境:主要是DevOps领域的范畴,负责从需求到计划任务,团队协作,再到质量管理、持续集成和发布。
个人基础环境:就是本文介绍的微服务应用平台,他的目标主要就是要支撑微服务应用的设计开发测试,运行期的业务数据处理和应用的管理监控。
IT基础设施:就是我们通常说的各种运行环境支撑如IaaS (VM虚拟化)和CaaS (容器虚拟化)等实现方式。

2.微服务应用平台总体架构

微服务应用平台的总体架构,主要是从开发集成、微服务运行容器与平台、运行时监控治理和外部渠道接入等维度来划分的。

  • 开发集成:主要是搭建一个微服务平台需要具备的一些工具和仓库
  • 运行时:要有微服务平台来提供一些基础能力和分布式的支撑能力,我们的微服务运行容器则会运行在这个平台之上。
  • 监控治理:则是致力于在运行时能够对受管的微服务进行统一的监控、配置等能力。
  • 服务网关: 则是负责与前端的WEB应用 移动APP 等渠道集成,对前端请求进行认真鉴权,然后路由转发。

3.微服务应用平台的运行视图

参考上图,在运行期,作为一个微服务架构的平台与业务系统,除了业务应用本身外,还需要有接入服务、统一门户、基础服务等平台级服务来保障业务系统的可靠运行。图中的公共服务就是业务处理过程中需要用到的一些可选服务。

4.微服务平台的设计目标

微服务平台的主要目标主要就是要支撑微服务应用的全生命周期管理,从需求到设计开发测试,运行期的业务数据处理和应用的管理监控等,后续将从应用生命周期的几个重要阶段切入,结合前面提到的设计原则和问题,介绍平台提供的能力支撑情况。

5.微服务开发:前端、后端、混合

我们一起看一下我们正在开发中的微服务应用平台EOS8.0的一些开发工具截图,了解一下开发期提供了哪些关键的能力支撑。
前面的设计原则中提到了一个前后端分离的原则,那么我们的开发环境中,目前支持创建前端项目、后端项目和混合项目。其中前端项目、后端项目就对应前后端分离的原则,利用平台中集成的开发工具和框架可以做到前后端开发分离,利用持续集成工具可以方便的将前端、后端项目编译打包成可独立运行的程序。混合项目则是为了兼容传统模式而保留的,为企业应用向微服务架构演进提供过渡方案。

6.服务契约与API管理

对于前面提到的微服务带来的依赖管理问题,我们可以通过平台提供的API管理能力来解决。说到API管理,那首先就用提到服务契约。平台开发工具中提供了方便的服务发布能力,能够快速的将业务功能对外发布,生成服务的规格契约,当然也可以先设计服务契约,在根据契约来生成服务的默认实现代码。
这里强调一下,我们提到的服务契约是一个很重要的东西,他有点类似web service的wsdl描述,主要描述服务接口的输入输出规格标准和其他一些服务调用集成相关的规格内容。

7.服务契约与服务模拟

有了服务契约,我们就可以根据契约自动生成服务的文档和服务模拟测试环境,这样,开发者就可以方便的获取到依赖服务变更的情况,能够及时的根据依赖服务的变化调整自己的程序,并且能够方便的进行模拟测试验证。

8.服务契约与服务编排

有了服务契约,那就有了服务接口的输入输出规格,那么restful的服务编排也就变得可行。在我们设计的契约标准中,还定义了调用集成相关的内容,比如服务支持的事务模式等等。通过这些约定,我们就可以采用简单图形化的方式来对业务服务流程进行编排。编排能够很大程度上简化分布式服务调用的复杂度,如同步、异步、异步模拟同步、超时重试、事务补偿等,均有服务编排引擎完成,不再完全依赖老师傅的编码能力。
服务编排的作用和意义很大,可以快速的将已经提供的微服务能力进行组合发布,非常适合业务的快速创新。
但是大家要注意,逻辑流编排的是业务流程,尽量能够简单明了,一眼看上去就明白业务含义。而业务规则推荐采用服务内部进行编码实现。千万不要将我们的 “逻辑流” 图形化服务编排完全取代程序编码,这样就会可能会走入另外一个极端,比如设计出像蜘蛛网一样的逻辑流图,简直就是灾难。

9.微服务容器

我们再来看一下微服务运行容器的一个逻辑图,大家可以看到,我们要做微服务架构的应用,可靠高效的微服务应用,实际上我们需要做的事情还是非常多的。如果没有一个统一的微服务容器,这些能力在每个微服务组件中都需要建设一遍,而且会五花八门,也很难集成到一起。有了统一的微服务运行容器和一些公共的基础服务,前面所提到的微服务架构下部分组件重复建设的问题也迎刃而解。

10.三方能力集成说明

我们的API管理契约文档API模拟我们是集成了Swagger的工具链。微服务应用平台的基础就是SpringCloud,从容器框架到注册发现再到安全认证这些基础方案均采用了他的能力来支撑。下面简单看下我们集成的一些开源框架和工具。

SpringCloud在微服务平台中的定位是基础框架,本文重点是要介绍一个企业级的微服务平台在落地过程中的一些设计原则和解决方案。具体Spring Cloud相关的技术就不在文中多做介绍了,大家可以在我们的公众号里面查看相关文章。

11.服务注册发现路由

接下来我们聊一下注册发现,以前的单块应用之间互相调用时配置个IP就行了,但在微服务架构下,服务提供者会有很多,手工配置IP地址又变成了一个不可行的事情。那么服务自动注册发现的方案就解决了这个问题。
我们的服务注册发现能力是依赖SpringCloud Eureka组件实现的。服务在启动的时候,会将自己要发布的服务注册到服务注册中心,运行时,如果需要调用其他微服务的接口,那么就要先到注册中心获取服务提供者的地址,拿到地址后,通过微服务容器内部的简单负载均衡期进行路由用。
一般情况,系统内微服务的调用都通过这种客户端负载的模式进行,否则就需要有很多的负载均衡进程。跨业务系统的服务调用,也可以采用这种去中心化的路由方式。当然采用SOA的模式,由中心化的服务网管来管理系统间的调用也是另一种选择,要结合企业的IT现状和需求来决定。

12.统一认证鉴权

安全认证方面,我们基于Spring Security结合Auth2再加上JWT(Json web token)做安全令牌,实现统一的安全认证与鉴权,使得微服务之间能够按需隔离和安全互通。后续在统一认证和权限方面我们产品会陆续推出较完善并且扩展性良好的微服务组件,可以作为微服务平台的公共的认证和鉴权服务。再啰嗦一句,认证鉴权一定是个公共的服务,而不是多个系统各自建设。

13.日志与流水设计

作为一个微服务应用平台除了提供支撑开发和运行的技术组件和框架之外,我们还提供一些运维友好的经验总结,我们一起来看一下我们推荐的日志与流水实现,先来看日志,平台默认回会提供的日志主要有三种,系统日志,引擎日志还有跟踪日志。有了这些日志,在出问题的时候能够帮助我们获取一些关键信息进行问题定位。
要想做到出了问题能够追根溯源,那么右边的这些流水号的设计也是非常重要的,日志与各种流水号配合,能够让我们快速定位问题发生的具体时间地点以及相关信息,能够快速还原业务交易全链路。对这些日志与流水的细节处理,对于系统运维问题定位有非常大的帮助,没有这些有用的日志内容,ELK日志收集套件搭建的再漂亮,收一对垃圾日志也是没用的。通常开源框架只是提供个框架有开发人员自由发挥,而设计一个平台则一定要考虑直接提供统一规范的基础能力。

14.集中配置管理

微服务分布式环境下,一个系统拆分为很多个微服务,一定要告别投产或运维手工修改配置配置的方式。需要采用集中配置管理的方式来提升运维的效率。
配置文件主要有运行前的静态配置和运行期的动态配置两种。静态配置通常是在编译部署包之前设置好。动态配置则是系统运行过程中需要调整的系统变量或者业务参数。要想做到集中的配置管理,那么需要注意以下几点。
是配置与介质分离,这个就需要通过制定规范的方式来控制。千万别把配置放在Jar包里。
是配置的方式要统一,格式、读写方式、变更热更新的模式尽量统一,要采用统一的配置框架
就是需要运行时需要有个配置中心来统一管理业务系统中的配置信息,这个就需要平台来提供配置中心服务和配置管理门户。

15.统一管理门户

微服务架构下,一个大的EAR、WAR应用被拆为了多个小的可独立运行的微服务程序,通常这些微服务程序都不再依赖应用服务器,不依赖传统应用服务器的话,应用服务器提供管理控制台也就没得用了,所以微服务的运行时管理需要有统一的管理门户来支撑。我们规划了的统一集中的微服务门户,可以支撑 应用开发、业务处理、应用管理、系统监控等。上图是应用管理页面,就是对我们传统意义上的业务系统进行管理,点击一个业务系统,我们就能够看到系统下有哪些微服务,每个微服务有几个节点实例再运行,可以监控微服务的子节点状态,对微服务进行配置管理和监控。

16.分布式事务问题

微服务架构的系统下,进程成倍增多,那么也分布式事务一致性的问题也就更加明显。我们这里说的事务一致性,不是传统说的基于数据库实现的技术事务。微服务之间是独立的、调用协议也是无状态的,因此数据库事务方案在一开始就已经不再我们考虑的范围内。我们要解决的是一定时间后的数据达到最终一致状态,准确的说就是采用传统的业务补偿与冲正方式。
推荐的事务一致性方案有三种:

  • 可靠事件模式:即事件的发送和接收保障高可靠性,来实现事务的一致性。
  • 补偿模式:Confirm Cancel ,如果确认失败,则全部逆序取消。
  • TCC模式:Try Confirm Cancel ,补偿模式的一种特殊实现 通常转账类交易会采用这种模式。

晓晨的补充:还有 最大努力型、异步确保、2PC、3PC

17.分布式同步调用问题

微服务架构下,相对于传统部署方式,存在更多的分布式调用,那么“如何在不确定的环境中交付确定的服务”,这句话可以简单理解为,我所依赖的服务的可靠性是无法保证的情况下,我如何保证自己能够正常的提供服务,不被我依赖的其他服务拖垮?
我们推荐SEDA架构来解决这个问题。

SEDA : staged event-driven architecture本质上就是采用分布式事件驱动的模式,用异步模拟来同步,无阻塞等待,再加上资源分配隔离结起来的一个解决方案。

18.持续集成与持续交付设计

在运维方面,首先我们要解决的就是持续集成和持续交付,而微服务应用平台的职责范围目前规划是只做持续集成,能够方便的用持续集成环境把程序编译成介质包和部署包。(目前规划持续部署由DevOps平台提供相应能力,微服务平台可与DevOps平台集成)
这里要厘清一个概念:介质,是源码编译后的产物,与环境无关,多环境下应该是可以共用的,如:jar、dockerfile;配置:则是环境相关的信息。配置+介质=部署包。
获取到部署包之后,微服务应用平台的职责就完成了,接下来就是运维人员各显神通来进行上线部署操作。

19.微服务平台与容器云、DevOps的关系

就微服务应用平台本身来说,并不依赖DevOps和容器云,开发好的部署包可以运行在物理机、虚拟机或者是容器中。
然而当微服务应用平台结合了DevOps和容器云之后,我们就会发现,持续集成和交付变成了一个非常简单便捷并且又可靠的过程。
简单几步操作,整套开发、测试、预发或者生产环境就能够搭建完成。整个过程的复杂度都由平台给屏蔽掉了,通过三大基础环境的整合,我们能够使分散的微服务组件更简单方便的进行统一管理和运维交付。

真的,Kafka 入门一篇文章就够了

$
0
0

image.png

初识 Kafka

什么是 Kafka

Kafka 是由 Linkedin公司开发的,它是一个分布式的,支持多分区、多副本,基于 Zookeeper 的分布式消息流平台,它同时也是一款开源的 基于发布订阅模式的消息引擎系统

Kafka 的基本术语

消息:Kafka 中的数据单元被称为 消息,也被称为记录,可以把它看作数据库表中某一行的记录。

批次:为了提高效率, 消息会 分批次写入 Kafka,批次就代指的是一组消息。

主题:消息的种类称为 主题(Topic),可以说一个主题代表了一类消息。相当于是对消息进行分类。主题就像是数据库中的表。

分区:主题可以被分为若干个分区(partition),同一个主题中的分区可以不在一个机器上,有可能会部署在多个机器上,由此来实现 kafka 的 伸缩性,单一主题中的分区有序,但是无法保证主题中所有的分区有序

image.png

生产者: 向主题发布消息的客户端应用程序称为 生产者(Producer),生产者用于持续不断的向某个主题发送消息。

消费者:订阅主题消息的客户端程序称为 消费者(Consumer),消费者用于处理生产者产生的消息。

消费者群组:生产者与消费者的关系就如同餐厅中的厨师和顾客之间的关系一样,一个厨师对应多个顾客,也就是一个生产者对应多个消费者, 消费者群组(Consumer Group)指的就是由一个或多个消费者组成的群体。

image.png

偏移量: 偏移量(Consumer Offset)是一种元数据,它是一个不断递增的整数值,用来记录消费者发生重平衡时的位置,以便用来恢复数据。

broker: 一个独立的 Kafka 服务器就被称为 broker,broker 接收来自生产者的消息,为消息设置偏移量,并提交消息到磁盘保存。

broker 集群:broker 是 集群的组成部分,broker 集群由一个或多个 broker 组成,每个集群都有一个 broker 同时充当了 集群控制器的角色(自动从集群的活跃成员中选举出来)。

副本:Kafka 中消息的备份又叫做 副本(Replica),副本的数量是可以配置的,Kafka 定义了两类副本:领导者副本(Leader Replica) 和 追随者副本(Follower Replica),前者对外提供服务,后者只是被动跟随。

重平衡:Rebalance。消费者组内某个消费者实例挂掉后,其他消费者实例自动重新分配订阅主题分区的过程。Rebalance 是 Kafka 消费者端实现高可用的重要手段。

Kafka 的特性(设计原则)

  • 高吞吐、低延迟:kakfa 最大的特点就是收发消息非常快,kafka 每秒可以处理几十万条消息,它的最低延迟只有几毫秒。
  • 高伸缩性: 每个主题(topic) 包含多个分区(partition),主题中的分区可以分布在不同的主机(broker)中。
  • 持久性、可靠性: Kafka 能够允许数据的持久化存储,消息被持久化到磁盘,并支持数据备份防止数据丢失,Kafka 底层的数据存储是基于 Zookeeper 存储的,Zookeeper 我们知道它的数据能够持久存储。
  • 容错性: 允许集群中的节点失败,某个节点宕机,Kafka 集群能够正常工作
  • 高并发: 支持数千个客户端同时读写

Kafka 的使用场景

  • 活动跟踪:Kafka 可以用来跟踪用户行为,比如我们经常回去淘宝购物,你打开淘宝的那一刻,你的登陆信息,登陆次数都会作为消息传输到 Kafka ,当你浏览购物的时候,你的浏览信息,你的搜索指数,你的购物爱好都会作为一个个消息传递给 Kafka ,这样就可以生成报告,可以做智能推荐,购买喜好等。
  • 传递消息:Kafka 另外一个基本用途是传递消息,应用程序向用户发送通知就是通过传递消息来实现的,这些应用组件可以生成消息,而不需要关心消息的格式,也不需要关心消息是如何发送的。
  • 度量指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。
  • 日志记录:Kafka 的基本概念来源于提交日志,比如我们可以把数据库的更新发送到 Kafka 上,用来记录数据库的更新时间,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、Hbase、Solr等。
  • 流式处理:流式处理是有一个能够提供多种应用程序的领域。
  • 限流削峰:Kafka 多用于互联网领域某一时刻请求特别多的情况下,可以把请求写入Kafka 中,避免直接请求后端程序导致服务崩溃。

Kafka 的消息队列

Kafka 的消息队列一般分为两种模式:点对点模式和发布订阅模式

Kafka 是支持消费者群组的,也就是说 Kafka 中会有一个或者多个消费者,如果一个生产者生产的消息由一个消费者进行消费的话,那么这种模式就是点对点模式

image.png

如果一个生产者或者多个生产者产生的消息能够被多个消费者同时消费的情况,这样的消息队列成为发布订阅模式的消息队列

image.png

Kafka 系统架构

image.png

如上图所示,一个典型的 Kafka 集群中包含若干Producer(可以是web前端产生的Page View,或者是服务器日志,系统CPU、Memory等),若干broker(Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高),若干Consumer Group,以及一个Zookeeper集群。Kafka通过Zookeeper管理集群配置,选举leader,以及在Consumer Group发生变化时进行rebalance。Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息。

核心 API

Kafka 有四个核心API,它们分别是

  • Producer API,它允许应用程序向一个或多个 topics 上发送消息记录
  • Consumer API,允许应用程序订阅一个或多个 topics 并处理为其生成的记录流
  • Streams API,它允许应用程序作为流处理器,从一个或多个主题中消费输入流并为其生成输出流,有效的将输入流转换为输出流。
  • Connector API,它允许构建和运行将 Kafka 主题连接到现有应用程序或数据系统的可用生产者和消费者。例如,关系数据库的连接器可能会捕获对表的所有更改

image.png

Kafka 为何如此之快

Kafka 实现了 零拷贝原理来快速移动数据,避免了内核之间的切换。Kafka 可以将数据记录分批发送,从生产者到文件系统(Kafka 主题日志)到消费者,可以端到端的查看这些批次的数据。

批处理能够进行更有效的数据压缩并减少 I/O 延迟,Kafka 采取顺序写入磁盘的方式,避免了随机磁盘寻址的浪费,更多关于磁盘寻址的了解,请参阅 程序员需要了解的硬核知识之磁盘

总结一下其实就是四个要点

  • 顺序读写
  • 零拷贝
  • 消息压缩
  • 分批发送

Kafka 安装和重要配置

Kafka 安装我在 Kafka 系列第一篇应该比较详细了,详情见 带你涨姿势的认识一下kafka这篇文章。

那我们还是主要来说一下 Kafka 中的重要参数配置吧,这些参数对 Kafka 来说是非常重要的。

broker 端配置

  • broker.id

每个 kafka broker 都有一个唯一的标识来表示,这个唯一的标识符即是 broker.id,它的默认值是 0。这个值在 kafka 集群中必须是唯一的,这个值可以任意设定,

  • port

如果使用配置样本来启动 kafka,它会监听 9092 端口。修改 port 配置参数可以把它设置成任意的端口。要注意,如果使用 1024 以下的端口,需要使用 root 权限启动 kakfa。

  • zookeeper.connect

用于保存 broker 元数据的 Zookeeper 地址是通过 zookeeper.connect 来指定的。比如我可以这么指定 localhost:2181表示这个 Zookeeper 是运行在本地 2181 端口上的。我们也可以通过 比如我们可以通过 zk1:2181,zk2:2181,zk3:2181来指定 zookeeper.connect 的多个参数值。该配置参数是用冒号分割的一组 hostname:port/path列表,其含义如下

hostname 是 Zookeeper 服务器的机器名或者 ip 地址。

port 是 Zookeeper 客户端的端口号

/path 是可选择的 Zookeeper 路径,Kafka 路径是使用了 chroot环境,如果不指定默认使用跟路径。

如果你有两套 Kafka 集群,假设分别叫它们 kafka1 和 kafka2,那么两套集群的 zookeeper.connect参数可以这样指定: zk1:2181,zk2:2181,zk3:2181/kafka1zk1:2181,zk2:2181,zk3:2181/kafka2
  • log.dirs

Kafka 把所有的消息都保存到磁盘上,存放这些日志片段的目录是通过 log.dirs来制定的,它是用一组逗号来分割的本地系统路径,log.dirs 是没有默认值的, 你必须手动指定他的默认值。其实还有一个参数是 log.dir,如你所知,这个配置是没有 s的,默认情况下只用配置 log.dirs 就好了,比如你可以通过 /home/kafka1,/home/kafka2,/home/kafka3这样来配置这个参数的值。

  • num.recovery.threads.per.data.dir

对于如下3种情况,Kafka 会使用 可配置的线程池来处理日志片段。

服务器正常启动,用于打开每个分区的日志片段;

服务器崩溃后重启,用于检查和截断每个分区的日志片段;

服务器正常关闭,用于关闭日志片段。

默认情况下,每个日志目录只使用一个线程。因为这些线程只是在服务器启动和关闭时会用到,所以完全可以设置大量的线程来达到井行操作的目的。特别是对于包含大量分区的服务器来说,一旦发生崩愤,在进行恢复时使用井行操作可能会省下数小时的时间。设置此参数时需要注意,所配置的数字对应的是 log.dirs 指定的单个日志目录。也就是说,如果 num.recovery.threads.per.data.dir 被设为 8,并且 log.dir 指定了 3 个路径,那么总共需要 24 个线程。

  • auto.create.topics.enable

默认情况下,kafka 会使用三种方式来自动创建主题,下面是三种情况:

当一个生产者开始往主题写入消息时

当一个消费者开始从主题读取消息时

当任意一个客户端向主题发送元数据请求时

auto.create.topics.enable参数我建议最好设置成 false,即不允许自动创建 Topic。在我们的线上环境里面有很多名字稀奇古怪的 Topic,我想大概都是因为该参数被设置成了 true 的缘故。

主题默认配置

Kafka 为新创建的主题提供了很多默认配置参数,下面就来一起认识一下这些参数

  • num.partitions

num.partitions 参数指定了新创建的主题需要包含多少个分区。如果启用了主题自动创建功能(该功能是默认启用的),主题分区的个数就是该参数指定的值。该参数的默认值是 1。要注意,我们可以增加主题分区的个数,但不能减少分区的个数。

  • default.replication.factor

这个参数比较简单,它表示 kafka保存消息的副本数,如果一个副本失效了,另一个还可以继续提供服务default.replication.factor 的默认值为1,这个参数在你启用了主题自动创建功能后有效。

  • log.retention.ms

Kafka 通常根据时间来决定数据可以保留多久。默认使用 log.retention.hours 参数来配置时间,默认是 168 个小时,也就是一周。除此之外,还有两个参数 log.retention.minutes 和 log.retentiion.ms 。这三个参数作用是一样的,都是决定消息多久以后被删除,推荐使用 log.retention.ms。

  • log.retention.bytes

另一种保留消息的方式是判断消息是否过期。它的值通过参数 log.retention.bytes来指定,作用在每一个分区上。也就是说,如果有一个包含 8 个分区的主题,并且 log.retention.bytes 被设置为 1GB,那么这个主题最多可以保留 8GB 数据。所以,当主题的分区个数增加时,整个主题可以保留的数据也随之增加。

  • log.segment.bytes

上述的日志都是作用在日志片段上,而不是作用在单个消息上。当消息到达 broker 时,它们被追加到分区的当前日志片段上,当日志片段大小到达 log.segment.bytes 指定上限(默认为 1GB)时,当前日志片段就会被关闭,一个新的日志片段被打开。如果一个日志片段被关闭,就开始等待过期。这个参数的值越小,就越会频繁的关闭和分配新文件,从而降低磁盘写入的整体效率。

  • log.segment.ms

上面提到日志片段经关闭后需等待过期,那么 log.segment.ms这个参数就是指定日志多长时间被关闭的参数和,log.segment.ms 和 log.retention.bytes 也不存在互斥问题。日志片段会在大小或时间到达上限时被关闭,就看哪个条件先得到满足。

  • message.max.bytes

broker 通过设置 message.max.bytes参数来限制单个消息的大小,默认是 1000 000, 也就是 1MB,如果生产者尝试发送的消息超过这个大小,不仅消息不会被接收,还会收到 broker 返回的错误消息。跟其他与字节相关的配置参数一样,该参数指的是压缩后的消息大小,也就是说,只要压缩后的消息小于 mesage.max.bytes,那么消息的实际大小可以大于这个值

这个值对性能有显著的影响。值越大,那么负责处理网络连接和请求的线程就需要花越多的时间来处理这些请求。它还会增加磁盘写入块的大小,从而影响 IO 吞吐量。

  • retention.ms

规定了该主题消息被保存的时常,默认是7天,即该主题只能保存7天的消息,一旦设置了这个值,它会覆盖掉 Broker 端的全局参数值。

  • retention.bytes

retention.bytes:规定了要为该 Topic 预留多大的磁盘空间。和全局参数作用相似,这个值通常在多租户的 Kafka 集群中会有用武之地。当前默认值是 -1,表示可以无限使用磁盘空间。

JVM 参数配置

JDK 版本一般推荐直接使用 JDK1.8,这个版本也是现在中国大部分程序员的首选版本。

说到 JVM 端设置,就绕不开 这个话题,业界最推崇的一种设置方式就是直接将 JVM 堆大小设置为 6GB,这样会避免很多 Bug 出现。

JVM 端配置的另一个重要参数就是垃圾回收器的设置,也就是平时常说的 GC设置。如果你依然在使用 Java 7,那么可以根据以下法则选择合适的垃圾回收器:

  • 如果 Broker 所在机器的 CPU 资源非常充裕,建议使用 CMS 收集器。启用方法是指定 -XX:+UseCurrentMarkSweepGC
  • 否则,使用吞吐量收集器。开启方法是指定 -XX:+UseParallelGC

当然了,如果你已经在使用 Java 8 了,那么就用默认的 G1 收集器就好了。在没有任何调优的情况下,G1 表现得要比 CMS 出色,主要体现在更少的 Full GC,需要调整的参数更少等,所以使用 G1 就好了。

一般 G1 的调整只需要这两个参数即可

  • MaxGCPauseMillis

该参数指定每次垃圾回收默认的停顿时间。该值不是固定的,G1可以根据需要使用更长的时间。它的默认值是 200ms,也就是说,每一轮垃圾回收大概需要200 ms 的时间。

  • InitiatingHeapOccupancyPercent

该参数指定了 G1 启动新一轮垃圾回收之前可以使用的堆内存百分比,默认值是45,这就表明G1在堆使用率到达45之前不会启用垃圾回收。这个百分比包括新生代和老年代。

Kafka Producer

在 Kafka 中,我们把产生消息的那一方称为 生产者,比如我们经常回去淘宝购物,你打开淘宝的那一刻,你的登陆信息,登陆次数都会作为消息传输到 Kafka 后台,当你浏览购物的时候,你的浏览信息,你的搜索指数,你的购物爱好都会作为一个个消息传递给 Kafka 后台,然后淘宝会根据你的爱好做智能推荐,致使你的钱包从来都禁不住诱惑,那么这些生产者产生的 消息是怎么传到 Kafka 应用程序的呢?发送过程是怎么样的呢?

尽管消息的产生非常简单,但是消息的发送过程还是比较复杂的,如图

我们从创建一个 ProducerRecord对象开始,ProducerRecord 是 Kafka 中的一个核心类,它代表了一组 Kafka 需要发送的 key/value键值对,它由记录要发送到的主题名称(Topic Name),可选的分区号(Partition Number)以及可选的键值对构成。

在发送 ProducerRecord 时,我们需要将键值对对象由序列化器转换为字节数组,这样它们才能够在网络上传输。然后消息到达了分区器。

如果发送过程中指定了有效的分区号,那么在发送记录时将使用该分区。如果发送过程中未指定分区,则将使用key 的 hash 函数映射指定一个分区。如果发送的过程中既没有分区号也没有,则将以循环的方式分配一个分区。选好分区后,生产者就知道向哪个主题和分区发送数据了。

ProducerRecord 还有关联的时间戳,如果用户没有提供时间戳,那么生产者将会在记录中使用当前的时间作为时间戳。Kafka 最终使用的时间戳取决于 topic 主题配置的时间戳类型。

  • 如果将主题配置为使用 CreateTime,则生产者记录中的时间戳将由 broker 使用。
  • 如果将主题配置为使用 LogAppendTime,则生产者记录中的时间戳在将消息添加到其日志中时,将由 broker 重写。

然后,这条消息被存放在一个记录批次里,这个批次里的所有消息会被发送到相同的主题和分区上。由一个独立的线程负责把它们发到 Kafka Broker 上。

Kafka Broker 在收到消息时会返回一个响应,如果写入成功,会返回一个 RecordMetaData 对象, 它包含了主题和分区信息,以及记录在分区里的偏移量,上面两种的时间戳类型也会返回给用户。如果写入失败,会返回一个错误。生产者在收到错误之后会尝试重新发送消息,几次之后如果还是失败的话,就返回错误消息。

创建 Kafka 生产者

要向 Kafka 写入消息,首先需要创建一个生产者对象,并设置一些属性。Kafka 生产者有3个必选的属性

  • bootstrap.servers

该属性指定 broker 的地址清单,地址的格式为 host:port。清单里不需要包含所有的 broker 地址,生产者会从给定的 broker 里查找到其他的 broker 信息。不过建议至少要提供 两个 broker 信息,一旦其中一个宕机,生产者仍然能够连接到集群上。

  • key.serializer

broker 需要接收到序列化之后的 key/value 值,所以生产者发送的消息需要经过序列化之后才传递给 Kafka Broker。生产者需要知道采用何种方式把 Java 对象转换为字节数组。key.serializer 必须被设置为一个实现了 org.apache.kafka.common.serialization.Serializer接口的类,生产者会使用这个类把键对象序列化为字节数组。这里拓展一下 Serializer 类

Serializer 是一个接口,它表示类将会采用何种方式序列化,它的作用是把对象转换为字节,实现了 Serializer 接口的类主要有 ByteArraySerializerStringSerializerIntegerSerializer,其中 ByteArraySerialize 是 Kafka 默认使用的序列化器,其他的序列化器还有很多,你可以通过 这里查看其他序列化器。要注意的一点: key.serializer 是必须要设置的,即使你打算只发送值的内容

  • value.serializer

与 key.serializer 一样,value.serializer 指定的类会将值序列化。

下面代码演示了如何创建一个 Kafka 生产者,这里只指定了必要的属性,其他使用默认的配置

private Properties properties = new Properties();
properties.put("bootstrap.servers","broker1:9092,broker2:9092");
properties.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
properties.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
properties = new KafkaProducer<String,String>(properties);

来解释一下这段代码

  • 首先创建了一个 Properties 对象
  • 使用 StringSerializer序列化器序列化 key / value 键值对
  • 在这里我们创建了一个新的生产者对象,并为键值设置了恰当的类型,然后把 Properties 对象传递给他。

Kafka 消息发送

实例化生产者对象后,接下来就可以开始发送消息了,发送消息主要由下面几种方式

简单消息发送

Kafka 最简单的消息发送如下:

ProducerRecord<String,String> record =
                new ProducerRecord<String, String>("CustomerCountry","West","France");

producer.send(record);

代码中生产者(producer)的 send()方法需要把 ProducerRecord的对象作为参数进行发送,ProducerRecord 有很多构造函数,这个我们下面讨论,这里调用的是

public ProducerRecord(String topic, K key, V value) {}

这个构造函数,需要传递的是 topic主题,key 和 value。

把对应的参数传递完成后,生产者调用 send() 方法发送消息(ProducerRecord对象)。我们可以从生产者的架构图中看出,消息是先被写入分区中的缓冲区中,然后分批次发送给 Kafka Broker。

发送成功后,send() 方法会返回一个 Future(java.util.concurrent)对象,Future 对象的类型是 RecordMetadata类型,我们上面这段代码没有考虑返回值,所以没有生成对应的 Future 对象,所以没有办法知道消息是否发送成功。如果不是很重要的信息或者对结果不会产生影响的信息,可以使用这种方式进行发送。

我们可以忽略发送消息时可能发生的错误或者在服务器端可能发生的错误,但在消息发送之前,生产者还可能发生其他的异常。这些异常有可能是 SerializationException(序列化失败)BufferedExhaustedException 或 TimeoutException(说明缓冲区已满),又或是 InterruptedException(说明发送线程被中断)

同步发送消息

第二种消息发送机制如下所示

ProducerRecord<String,String> record =
                new ProducerRecord<String, String>("CustomerCountry","West","France");

try{
  RecordMetadata recordMetadata = producer.send(record).get();
}catch(Exception e){
  e.printStackTrace();
}

这种发送消息的方式较上面的发送方式有了改进,首先调用 send() 方法,然后再调用 get() 方法等待 Kafka 响应。如果服务器返回错误,get() 方法会抛出异常,如果没有发生错误,我们会得到 RecordMetadata对象,可以用它来查看消息记录。

生产者(KafkaProducer)在发送的过程中会出现两类错误:其中一类是重试错误,这类错误可以通过重发消息来解决。比如连接的错误,可以通过再次建立连接来解决;无 错误则可以通过重新为分区选举首领来解决。KafkaProducer 被配置为自动重试,如果多次重试后仍无法解决问题,则会抛出重试异常。另一类错误是无法通过重试来解决的,比如 消息过大对于这类错误,KafkaProducer 不会进行重试,直接抛出异常。

异步发送消息

同步发送消息都有个问题,那就是同一时间只能有一个消息在发送,这会造成许多消息无法直接发送,造成消息滞后,无法发挥效益最大化。

比如消息在应用程序和 Kafka 集群之间一个来回需要 10ms。如果发送完每个消息后都等待响应的话,那么发送100个消息需要 1 秒,但是如果是 异步方式的话,发送 100 条消息所需要的时间就会少很多很多。大多数时候,虽然Kafka 会返回 RecordMetadata消息,但是我们并不需要等待响应。

为了在异步发送消息的同时能够对异常情况进行处理,生产者提供了回掉支持。下面是回调的一个例子

ProducerRecord<String, String> producerRecord = new ProducerRecord<String, String>("CustomerCountry", "Huston", "America");
        producer.send(producerRecord,new DemoProducerCallBack());


class DemoProducerCallBack implements Callback {

  public void onCompletion(RecordMetadata metadata, Exception exception) {
    if(exception != null){
      exception.printStackTrace();;
    }
  }
}

首先实现回调需要定义一个实现了 org.apache.kafka.clients.producer.Callback的类,这个接口只有一个 onCompletion方法。如果 kafka 返回一个错误,onCompletion 方法会抛出一个非空(non null)异常,这里我们只是简单的把它打印出来,如果是生产环境需要更详细的处理,然后在 send() 方法发送的时候传递一个 Callback 回调的对象。

生产者分区机制

Kafka 对于数据的读写是以 分区为粒度的,分区可以分布在多个主机(Broker)中,这样每个节点能够实现独立的数据写入和读取,并且能够通过增加新的节点来增加 Kafka 集群的吞吐量,通过分区部署在多个 Broker 来实现 负载均衡的效果。

上面我们介绍了生产者的发送方式有三种: 不管结果如何直接发送发送并返回结果发送并回调。由于消息是存在主题(topic)的分区(partition)中的,所以当 Producer 生产者发送产生一条消息发给 topic 的时候,你如何判断这条消息会存在哪个分区中呢?

这其实就设计到 Kafka 的分区机制了。

分区策略

Kafka 的分区策略指的就是将生产者发送到哪个分区的算法。Kafka 为我们提供了默认的分区策略,同时它也支持你自定义分区策略。

如果要自定义分区策略的话,你需要显示配置生产者端的参数 Partitioner.class,我们可以看一下这个类它位于 org.apache.kafka.clients.producer包下

public interface Partitioner extends Configurable, Closeable {
  public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster);

  public void close();
  
  default public void onNewBatch(String topic, Cluster cluster, int prevPartition) {}
}

Partitioner 类有三个方法,分别来解释一下

  • partition(): 这个类有几个参数: topic,表示需要传递的主题; key表示消息中的键值; keyBytes表示分区中序列化过后的key,byte数组的形式传递; value表示消息的 value 值; valueBytes表示分区中序列化后的值数组; cluster表示当前集群的原数据。Kafka 给你这么多信息,就是希望让你能够充分地利用这些信息对消息进行分区,计算出它要被发送到哪个分区中。
  • close() : 继承了 Closeable接口能够实现 close() 方法,在分区关闭时调用。
  • onNewBatch(): 表示通知分区程序用来创建新的批次

其中与分区策略息息相关的就是 partition() 方法了,分区策略有下面这几种

顺序轮询

顺序分配,消息是均匀的分配给每个 partition,即每个分区存储一次消息。就像下面这样

image.png

上图表示的就是轮询策略,轮训策略是 Kafka Producer 提供的默认策略,如果你不使用指定的轮训策略的话,Kafka 默认会使用顺序轮训策略的方式。

随机轮询

随机轮询简而言之就是随机的向 partition 中保存消息,如下图所示

实现随机分配的代码只需要两行,如下

List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
return ThreadLocalRandom.current().nextInt(partitions.size());

先计算出该主题总的分区数,然后随机地返回一个小于它的正整数。

本质上看随机策略也是力求将数据均匀地打散到各个分区,但从实际表现来看,它要逊于轮询策略,所以 如果追求数据的均匀分布,还是使用轮询策略比较好。事实上,随机策略是老版本生产者使用的分区策略,在新版本中已经改为轮询了。

按照 key 进行消息保存

这个策略也叫做 key-ordering策略,Kafka 中每条消息都会有自己的key,一旦消息被定义了 Key,那么你就可以保证同一个 Key 的所有消息都进入到相同的分区里面,由于每个分区下的消息处理都是有顺序的,故这个策略被称为按消息键保序策略,如下图所示

实现这个策略的 partition 方法同样简单,只需要下面两行代码即可:

List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
return Math.abs(key.hashCode()) % partitions.size();

上面这几种分区策略都是比较基础的策略,除此之外,你还可以自定义分区策略。

生产者压缩机制

压缩一词简单来讲就是一种互换思想,它是一种经典的用 CPU 时间去换磁盘空间或者 I/O 传输量的思想,希望以较小的 CPU 开销带来更少的磁盘占用或更少的网络 I/O 传输。如果你还不了解的话我希望你先读完这篇文章 程序员需要了解的硬核知识之压缩算法,然后你就明白压缩是怎么回事了。

Kafka 压缩是什么

Kafka 的消息分为两层:消息集合 和 消息。一个消息集合中包含若干条日志项,而日志项才是真正封装消息的地方。Kafka 底层的消息日志由一系列消息集合日志项组成。Kafka 通常不会直接操作具体的一条条消息,它总是在消息集合这个层面上进行 写入操作。

在 Kafka 中,压缩会发生在两个地方:Kafka Producer 和 Kafka Consumer,为什么启用压缩?说白了就是消息太大,需要 变小一点来使消息发的更快一些。

Kafka Producer 中使用 compression.type来开启压缩

private Properties properties = new Properties();
properties.put("bootstrap.servers","192.168.1.9:9092");
properties.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");
properties.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
properties.put("compression.type", "gzip");

Producer<String,String> producer = new KafkaProducer<String, String>(properties);

ProducerRecord<String,String> record =
  new ProducerRecord<String, String>("CustomerCountry","Precision Products","France");

上面代码表明该 Producer 的压缩算法使用的是 GZIP

有压缩必有解压缩,Producer 使用压缩算法压缩消息后并发送给服务器后,由 Consumer 消费者进行解压缩,因为采用的何种压缩算法是随着 key、value 一起发送过去的,所以消费者知道采用何种压缩算法。

Kafka 重要参数配置

在上一篇文章 带你涨姿势的认识一下kafka中,我们主要介绍了一下 kafka 集群搭建的参数,本篇文章我们来介绍一下 Kafka 生产者重要的配置,生产者有很多可配置的参数,在文档里( http://kafka.apache.org/docum...)都有说明,我们介绍几个在内存使用、性能和可靠性方面对生产者影响比较大的参数进行说明

key.serializer

用于 key 键的序列化,它实现了 org.apache.kafka.common.serialization.Serializer接口

value.serializer

用于 value 值的序列化,实现了 org.apache.kafka.common.serialization.Serializer接口

acks

acks 参数指定了要有多少个分区副本接收消息,生产者才认为消息是写入成功的。此参数对消息丢失的影响较大

  • 如果 acks = 0,就表示生产者也不知道自己产生的消息是否被服务器接收了,它才知道它写成功了。如果发送的途中产生了错误,生产者也不知道,它也比较懵逼,因为没有返回任何消息。这就类似于 UDP 的运输层协议,只管发,服务器接受不接受它也不关心。
  • 如果 acks = 1,只要集群的 Leader 接收到消息,就会给生产者返回一条消息,告诉它写入成功。如果发送途中造成了网络异常或者 Leader 还没选举出来等其他情况导致消息写入失败,生产者会受到错误消息,这时候生产者往往会再次重发数据。因为消息的发送也分为 同步异步,Kafka 为了保证消息的高效传输会决定是同步发送还是异步发送。如果让客户端等待服务器的响应(通过调用 Future中的 get()方法),显然会增加延迟,如果客户端使用回调,就会解决这个问题。
  • 如果 acks = all,这种情况下是只有当所有参与复制的节点都收到消息时,生产者才会接收到一个来自服务器的消息。不过,它的延迟比 acks =1 时更高,因为我们要等待不只一个服务器节点接收消息。

buffer.memory

此参数用来设置生产者内存缓冲区的大小,生产者用它缓冲要发送到服务器的消息。如果应用程序发送消息的速度超过发送到服务器的速度,会导致生产者空间不足。这个时候,send() 方法调用要么被阻塞,要么抛出异常,具体取决于 block.on.buffer.null参数的设置。

compression.type

此参数来表示生产者启用何种压缩算法,默认情况下,消息发送时不会被压缩。该参数可以设置为 snappy、gzip 和 lz4,它指定了消息发送给 broker 之前使用哪一种压缩算法进行压缩。下面是各压缩算法的对比

retries

生产者从服务器收到的错误有可能是临时性的错误(比如分区找不到首领),在这种情况下, reteis参数的值决定了生产者可以重发的消息次数,如果达到这个次数,生产者会放弃重试并返回错误。默认情况下,生产者在每次重试之间等待 100ms,这个等待参数可以通过 retry.backoff.ms进行修改。

batch.size

当有多个消息需要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次可以使用的内存大小,按照字节数计算。当批次被填满,批次里的所有消息会被发送出去。不过生产者井不一定都会等到批次被填满才发送,任意条数的消息都可能被发送。

client.id

此参数可以是任意的字符串,服务器会用它来识别消息的来源,一般配置在日志里

max.in.flight.requests.per.connection

此参数指定了生产者在收到服务器响应之前可以发送多少消息,它的值越高,就会占用越多的内存,不过也会提高吞吐量。把它设为1 可以保证消息是按照发送的顺序写入服务器。

timeout.ms、request.timeout.ms 和 metadata.fetch.timeout.ms

request.timeout.ms 指定了生产者在发送数据时等待服务器返回的响应时间,metadata.fetch.timeout.ms 指定了生产者在获取元数据(比如目标分区的首领是谁)时等待服务器返回响应的时间。如果等待时间超时,生产者要么重试发送数据,要么返回一个错误。timeout.ms 指定了 broker 等待同步副本返回消息确认的时间,与 asks 的配置相匹配----如果在指定时间内没有收到同步副本的确认,那么 broker 就会返回一个错误。

max.block.ms

此参数指定了在调用 send() 方法或使用 partitionFor() 方法获取元数据时生产者的阻塞时间当生产者的发送缓冲区已捕,或者没有可用的元数据时,这些方法就会阻塞。在阻塞时间达到 max.block.ms 时,生产者会抛出超时异常。

max.request.size

该参数用于控制生产者发送的请求大小。它可以指能发送的单个消息的最大值,也可以指单个请求里所有消息的总大小。

receive.buffer.bytes 和 send.buffer.bytes

Kafka 是基于 TCP 实现的,为了保证可靠的消息传输,这两个参数分别指定了 TCP Socket 接收和发送数据包的缓冲区的大小。如果它们被设置为 -1,就使用操作系统的默认值。如果生产者或消费者与 broker 处于不同的数据中心,那么可以适当增大这些值。

Kafka Consumer

应用程序使用 KafkaConsumer从 Kafka 中订阅主题并接收来自这些主题的消息,然后再把他们保存起来。应用程序首先需要创建一个 KafkaConsumer 对象,订阅主题并开始接受消息,验证消息并保存结果。一段时间后,生产者往主题写入的速度超过了应用程序验证数据的速度,这时候该如何处理?如果只使用单个消费者的话,应用程序会跟不上消息生成的速度,就像多个生产者像相同的主题写入消息一样,这时候就需要多个消费者共同参与消费主题中的消息,对消息进行分流处理。

Kafka 消费者从属于 消费者群组。一个群组中的消费者订阅的都是 相同的主题,每个消费者接收主题一部分分区的消息。下面是一个 Kafka 分区消费示意图

image.png

上图中的主题 T1 有四个分区,分别是分区0、分区1、分区2、分区3,我们创建一个消费者群组1,消费者群组中只有一个消费者,它订阅主题T1,接收到 T1 中的全部消息。由于一个消费者处理四个生产者发送到分区的消息,压力有些大,需要帮手来帮忙分担任务,于是就演变为下图

image.png

这样一来,消费者的消费能力就大大提高了,但是在某些环境下比如用户产生消息特别多的时候,生产者产生的消息仍旧让消费者吃不消,那就继续增加消费者。

image.png

如上图所示,每个分区所产生的消息能够被每个消费者群组中的消费者消费,如果向消费者群组中增加更多的消费者,那么多余的消费者将会闲置,如下图所示

image.png

向群组中增加消费者是横向伸缩消费能力的主要方式。总而言之,我们可以通过增加消费组的消费者来进行 水平扩展提升消费能力。这也是为什么建议创建主题时使用比较多的分区数,这样可以在消费负载高的情况下增加消费者来提升性能。另外,消费者的数量不应该比分区数多,因为多出来的消费者是空闲的,没有任何帮助。

Kafka 一个很重要的特性就是,只需写入一次消息,可以支持任意多的应用读取这个消息。换句话说,每个应用都可以读到全量的消息。为了使得每个应用都能读到全量消息,应用需要有不同的消费组。对于上面的例子,假如我们新增了一个新的消费组 G2,而这个消费组有两个消费者,那么就演变为下图这样

image.png

在这个场景中,消费组 G1 和消费组 G2 都能收到 T1 主题的全量消息,在逻辑意义上来说它们属于不同的应用。

总结起来就是如果应用需要读取全量消息,那么请为该应用设置一个消费组;如果该应用消费能力不足,那么可以考虑在这个消费组里增加消费者

消费者组和分区重平衡

消费者组是什么

消费者组(Consumer Group)是由一个或多个消费者实例(Consumer Instance)组成的群组,具有可扩展性和可容错性的一种机制。消费者组内的消费者 共享一个消费者组ID,这个ID 也叫做 Group ID,组内的消费者共同对一个主题进行订阅和消费,同一个组中的消费者只能消费一个分区的消息,多余的消费者会闲置,派不上用场。

我们在上面提到了两种消费方式

  • 一个消费者群组消费一个主题中的消息,这种消费模式又称为 点对点的消费方式,点对点的消费方式又被称为消息队列
  • 一个主题中的消息被多个消费者群组共同消费,这种消费模式又称为 发布-订阅模式

消费者重平衡

我们从上面的 消费者演变图中可以知道这么一个过程:最初是一个消费者订阅一个主题并消费其全部分区的消息,后来有一个消费者加入群组,随后又有更多的消费者加入群组,而新加入的消费者实例 分摊了最初消费者的部分消息,这种把分区的所有权通过一个消费者转到其他消费者的行为称为 重平衡,英文名也叫做 Rebalance。如下图所示

image.png

重平衡非常重要,它为消费者群组带来了 高可用性伸缩性,我们可以放心的添加消费者或移除消费者,不过在正常情况下我们并不希望发生这样的行为。在重平衡期间,消费者无法读取消息,造成整个消费者组在重平衡的期间都不可用。另外,当分区被重新分配给另一个消费者时,消息当前的读取状态会丢失,它有可能还需要去刷新缓存,在它重新恢复状态之前会拖慢应用程序。

消费者通过向 组织协调者(Kafka Broker)发送心跳来维护自己是消费者组的一员并确认其拥有的分区。对于不同不的消费群体来说,其组织协调者可以是不同的。只要消费者定期发送心跳,就会认为消费者是存活的并处理其分区中的消息。当消费者检索记录或者提交它所消费的记录时就会发送心跳。

如果过了一段时间 Kafka 停止发送心跳了,会话(Session)就会过期,组织协调者就会认为这个 Consumer 已经死亡,就会触发一次重平衡。如果消费者宕机并且停止发送消息,组织协调者会等待几秒钟,确认它死亡了才会触发重平衡。在这段时间里, 死亡的消费者将不处理任何消息。在清理消费者时,消费者将通知协调者它要离开群组,组织协调者会触发一次重平衡,尽量降低处理停顿。

重平衡是一把双刃剑,它为消费者群组带来高可用性和伸缩性的同时,还有有一些明显的缺点(bug),而这些 bug 到现在社区还无法修改。

重平衡的过程对消费者组有极大的影响。因为每次重平衡过程中都会导致万物静止,参考 JVM 中的垃圾回收机制,也就是 Stop The World ,STW,(引用自《深入理解 Java 虚拟机》中 p76 关于 Serial 收集器的描述):

更重要的是它在进行垃圾收集时,必须暂停其他所有的工作线程。直到它收集结束。 Stop The World这个名字听起来很帅,但这项工作实际上是由虚拟机在后台自动发起并完成的,在用户不可见的情况下把用户正常工作的线程全部停掉,这对很多应用来说都是难以接受的。

也就是说,在重平衡期间,消费者组中的消费者实例都会停止消费,等待重平衡的完成。而且重平衡这个过程很慢......

创建消费者

上面的理论说的有点多,下面就通过代码来讲解一下消费者是如何消费的

在读取消息之前,需要先创建一个 KafkaConsumer对象。创建 KafkaConsumer 对象与创建 KafkaProducer 对象十分相似 --- 把需要传递给消费者的属性放在 properties对象中,后面我们会着重讨论 Kafka 的一些配置,这里我们先简单的创建一下,使用3个属性就足矣,分别是 bootstrap.serverkey.deserializervalue.deserializer

这三个属性我们已经用过很多次了,如果你还不是很清楚的话,可以参考 带你涨姿势是认识一下Kafka Producer

还有一个属性是 group.id这个属性不是必须的,它指定了 KafkaConsumer 是属于哪个消费者群组。创建不属于任何一个群组的消费者也是可以的

Properties properties = new Properties();
        properties.put("bootstrap.server","192.168.1.9:9092");     properties.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");   properties.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");
KafkaConsumer<String,String> consumer = new KafkaConsumer<>(properties);

主题订阅

创建好消费者之后,下一步就开始订阅主题了。 subscribe()方法接受一个主题列表作为参数,使用起来比较简单

consumer.subscribe(Collections.singletonList("customerTopic"));

为了简单我们只订阅了一个主题 customerTopic,参数传入的是一个正则表达式,正则表达式可以匹配多个主题,如果有人创建了新的主题,并且主题的名字与正则表达式相匹配,那么会立即触发一次重平衡,消费者就可以读取新的主题。

要订阅所有与 test 相关的主题,可以这样做

consumer.subscribe("test.*");

轮询

我们知道,Kafka 是支持订阅/发布模式的,生产者发送数据给 Kafka Broker,那么消费者是如何知道生产者发送了数据呢?其实生产者产生的数据消费者是不知道的,KafkaConsumer 采用轮询的方式定期去 Kafka Broker 中进行数据的检索,如果有数据就用来消费,如果没有就再继续轮询等待,下面是轮询等待的具体实现

try {
  while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(100));
    for (ConsumerRecord<String, String> record : records) {
      int updateCount = 1;
      if (map.containsKey(record.value())) {
        updateCount = (int) map.get(record.value() + 1);
      }
      map.put(record.value(), updateCount);
    }
  }
}finally {
  consumer.close();
}
  • 这是一个无限循环。消费者实际上是一个长期运行的应用程序,它通过轮询的方式向 Kafka 请求数据。
  • 第三行代码非常重要,Kafka 必须定期循环请求数据,否则就会认为该 Consumer 已经挂了,会触发重平衡,它的分区会移交给群组中的其它消费者。传给 poll()方法的是一个超市时间,用 java.time.Duration类来表示,如果该参数被设置为 0 ,poll() 方法会立刻返回,否则就会在指定的毫秒数内一直等待 broker 返回数据。
  • poll() 方法会返回一个记录列表。每条记录都包含了记录所属主题的信息,记录所在分区的信息、记录在分区中的偏移量,以及记录的键值对。我们一般会遍历这个列表,逐条处理每条记录。
  • 在退出应用程序之前使用 close()方法关闭消费者。网络连接和 socket 也会随之关闭,并立即触发一次重平衡,而不是等待群组协调器发现它不再发送心跳并认定它已经死亡。
线程安全性

在同一个群组中,我们无法让一个线程运行多个消费者,也无法让多个线程安全的共享一个消费者。按照规则,一个消费者使用一个线程,如果一个消费者群组中多个消费者都想要运行的话,那么必须让每个消费者在自己的线程中运行,可以使用 Java 中的 ExecutorService启动多个消费者进行进行处理。

消费者配置

到目前为止,我们学习了如何使用消费者 API,不过只介绍了几个最基本的属性,Kafka 文档列出了所有与消费者相关的配置说明。大部分参数都有合理的默认值,一般不需要修改它们,下面我们就来介绍一下这些参数。

  • fetch.min.bytes

该属性指定了消费者从服务器获取记录的最小字节数。broker 在收到消费者的数据请求时,如果可用的数据量小于 fetch.min.bytes指定的大小,那么它会等到有足够的可用数据时才把它返回给消费者。这样可以降低消费者和 broker 的工作负载,因为它们在主题使用频率不是很高的时候就不用来回处理消息。如果没有很多可用数据,但消费者的 CPU 使用率很高,那么就需要把该属性的值设得比默认值大。如果消费者的数量比较多,把该属性的值调大可以降低 broker 的工作负载。

  • fetch.max.wait.ms

我们通过上面的 fetch.min.bytes告诉 Kafka,等到有足够的数据时才会把它返回给消费者。而 fetch.max.wait.ms则用于指定 broker 的等待时间,默认是 500 毫秒。如果没有足够的数据流入 kafka 的话,消费者获取的最小数据量要求就得不到满足,最终导致 500 毫秒的延迟。如果要降低潜在的延迟,就可以把参数值设置的小一些。如果 fetch.max.wait.ms 被设置为 100 毫秒的延迟,而 fetch.min.bytes 的值设置为 1MB,那么 Kafka 在收到消费者请求后,要么返回 1MB 的数据,要么在 100 ms 后返回所有可用的数据。就看哪个条件首先被满足。

  • max.partition.fetch.bytes

该属性指定了服务器从每个分区里返回给消费者的 最大字节数。它的默认值时 1MB,也就是说, KafkaConsumer.poll()方法从每个分区里返回的记录最多不超过 max.partition.fetch.bytes 指定的字节。如果一个主题有20个分区和5个消费者,那么每个消费者需要 至少4 MB的可用内存来接收记录。在为消费者分配内存时,可以给它们多分配一些,因为如果群组里有消费者发生崩溃,剩下的消费者需要处理更多的分区。max.partition.fetch.bytes 的值必须比 broker 能够接收的最大消息的字节数(通过 max.message.size 属性配置大), 否则消费者可能无法读取这些消息,导致消费者一直挂起重试。 在设置该属性时,另外一个考量的因素是消费者处理数据的时间。消费者需要频繁的调用 poll() 方法来避免会话过期和发生分区再平衡,如果单次调用poll() 返回的数据太多,消费者需要更多的时间进行处理,可能无法及时进行下一个轮询来避免会话过期。如果出现这种情况,可以把 max.partition.fetch.bytes 值改小,或者延长会话过期时间。

  • session.timeout.ms

这个属性指定了消费者在被认为死亡之前可以与服务器断开连接的时间,默认是 3s。如果消费者没有在 session.timeout.ms指定的时间内发送心跳给群组协调器,就会被认定为死亡,协调器就会触发重平衡。把它的分区分配给消费者群组中的其它消费者,此属性与 heartbeat.interval.ms紧密相关。heartbeat.interval.ms 指定了 poll() 方法向群组协调器发送心跳的频率,session.timeout.ms 则指定了消费者可以多久不发送心跳。所以,这两个属性一般需要同时修改,heartbeat.interval.ms 必须比 session.timeout.ms 小,一般是 session.timeout.ms 的三分之一。如果 session.timeout.ms 是 3s,那么 heartbeat.interval.ms 应该是 1s。把 session.timeout.ms 值设置的比默认值小,可以更快地检测和恢复崩愤的节点,不过长时间的轮询或垃圾收集可能导致非预期的重平衡。把该属性的值设置得大一些,可以减少意外的重平衡,不过检测节点崩溃需要更长的时间。

  • auto.offset.reset

该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下的该如何处理。它的默认值是 latest,意思指的是,在偏移量无效的情况下,消费者将从最新的记录开始读取数据。另一个值是 earliest,意思指的是在偏移量无效的情况下,消费者将从起始位置处开始读取分区的记录。

  • enable.auto.commit

我们稍后将介绍几种不同的提交偏移量的方式。该属性指定了消费者是否自动提交偏移量,默认值是 true,为了尽量避免出现重复数据和数据丢失,可以把它设置为 false,由自己控制何时提交偏移量。如果把它设置为 true,还可以通过 auto.commit.interval.ms属性来控制提交的频率

  • partition.assignment.strategy

我们知道,分区会分配给群组中的消费者。 PartitionAssignor会根据给定的消费者和主题,决定哪些分区应该被分配给哪个消费者,Kafka 有两个默认的分配策略 RangeRoundRobin

  • client.id

该属性可以是任意字符串,broker 用他来标识从客户端发送过来的消息,通常被用在日志、度量指标和配额中

  • max.poll.records

该属性用于控制单次调用 call() 方法能够返回的记录数量,可以帮你控制在轮询中需要处理的数据量。

  • receive.buffer.bytes 和 send.buffer.bytes

socket 在读写数据时用到的 TCP 缓冲区也可以设置大小。如果它们被设置为 -1,就使用操作系统默认值。如果生产者或消费者与 broker 处于不同的数据中心内,可以适当增大这些值,因为跨数据中心的网络一般都有比较高的延迟和比较低的带宽。

提交和偏移量的概念

特殊偏移

我们上面提到,消费者在每次调用 poll()方法进行定时轮询的时候,会返回由生产者写入 Kafka 但是还没有被消费者消费的记录,因此我们可以追踪到哪些记录是被群组里的哪个消费者读取的。消费者可以使用 Kafka 来追踪消息在分区中的位置(偏移量)

消费者会向一个叫做 _consumer_offset的特殊主题中发送消息,这个主题会保存每次所发送消息中的分区偏移量,这个主题的主要作用就是消费者触发重平衡后记录偏移使用的,消费者每次向这个主题发送消息,正常情况下不触发重平衡,这个主题是不起作用的,当触发重平衡后,消费者停止工作,每个消费者可能会分到对应的分区,这个主题就是让消费者能够继续处理消息所设置的。

如果提交的偏移量小于客户端最后一次处理的偏移量,那么位于两个偏移量之间的消息就会被重复处理

image.png

如果提交的偏移量大于最后一次消费时的偏移量,那么处于两个偏移量中间的消息将会丢失

515111/201911/1515111-20191128124627673-1587568519.png)

既然 _consumer_offset如此重要,那么它的提交方式是怎样的呢?下面我们就来说一下####提交方式

KafkaConsumer API 提供了多种方式来提交偏移量

自动提交

最简单的方式就是让消费者自动提交偏移量。如果 enable.auto.commit被设置为true,那么每过 5s,消费者会自动把从 poll() 方法轮询到的最大偏移量提交上去。提交时间间隔由 auto.commit.interval.ms控制,默认是 5s。与消费者里的其他东西一样,自动提交也是在轮询中进行的。消费者在每次轮询中会检查是否提交该偏移量了,如果是,那么就会提交从上一次轮询中返回的偏移量。

提交当前偏移量

auto.commit.offset设置为 false,可以让应用程序决定何时提交偏移量。使用 commitSync()提交偏移量。这个 API 会提交由 poll() 方法返回的最新偏移量,提交成功后马上返回,如果提交失败就抛出异常。

commitSync() 将会提交由 poll() 返回的最新偏移量,如果处理完所有记录后要确保调用了 commitSync(),否则还是会有丢失消息的风险,如果发生了在均衡,从最近一批消息到发生在均衡之间的所有消息都将被重复处理。

异步提交

异步提交 commitAsync()与同步提交 commitSync()最大的区别在于异步提交不会进行重试,同步提交会一致进行重试。

同步和异步组合提交

一般情况下,针对偶尔出现的提交失败,不进行重试不会有太大的问题,因为如果提交失败是因为临时问题导致的,那么后续的提交总会有成功的。但是如果在关闭消费者或再均衡前的最后一次提交,就要确保提交成功。

因此, 在消费者关闭之前一般会组合使用commitAsync和commitSync提交偏移量

提交特定的偏移量

消费者API允许调用 commitSync() 和 commitAsync() 方法时传入希望提交的 partition 和 offset 的 map,即提交特定的偏移量。

下面为自己做个宣传,欢迎关注公众号 Java建设者,号主是Java技术栈,热爱技术,喜欢阅读,热衷于分享和总结,希望能把每一篇好文章分享给成长道路上的你。
关注公众号回复 002 领取为你特意准备的大礼包,你一定会喜欢并收藏的。

文章参考:

Kafka史上最详细原理总结

《Kafka 权威指南》

https://kafka.apache.org/

http://kafka.apache.org/docum...

https://www.tutorialkart.com/...

https://dzone.com/articles/wh...

《极客时间 - Kafka 核心技术与实战》

终于有人把“TCC分布式事务”实现原理讲明白了! - JaJian - 博客园

$
0
0

之前网上看到很多写分布式事务的文章,不过大多都是将分布式事务各种技术方案简单介绍一下。很多朋友看了还是不知道分布式事务到底怎么回事,在项目里到底如何使用。

所以这篇文章,就用大白话+手工绘图,并结合一个电商系统的案例实践,来给大家讲清楚到底什么是 TCC 分布式事务。

首先说一下,这里可能会牵扯到一些 Spring Cloud 的原理,如果有不太清楚的同学,可以参考之前的文章: 《拜托,面试请不要再问我Spring Cloud底层原理!》

业务场景介绍

咱们先来看看业务场景,假设你现在有一个电商系统,里面有一个支付订单的场景。

那对一个订单支付之后,我们需要做下面的步骤:

  • 更改订单的状态为“已支付”
  • 扣减商品库存
  • 给会员增加积分
  • 创建销售出库单通知仓库发货

这是一系列比较真实的步骤,无论大家有没有做过电商系统,应该都能理解。

进一步思考

好,业务场景有了,现在我们要更进一步,实现一个 TCC 分布式事务的效果。

什么意思呢?也就是说,[1] 订单服务-修改订单状态,[2] 库存服务-扣减库存,[3] 积分服务-增加积分,[4] 仓储服务-创建销售出库单。

上述这几个步骤,要么一起成功,要么一起失败,必须是一个整体性的事务。

举个例子,现在订单的状态都修改为“已支付”了,结果库存服务扣减库存失败。那个商品的库存原来是 100 件,现在卖掉了 2 件,本来应该是 98 件了。

结果呢?由于库存服务操作数据库异常,导致库存数量还是 100。这不是在坑人么,当然不能允许这种情况发生了!

但是如果你不用 TCC 分布式事务方案的话,就用个 Spring Cloud 开发这么一个微服务系统,很有可能会干出这种事儿来。

我们来看看下面的这个图,直观的表达了上述的过程:

所以说,我们有必要使用 TCC 分布式事务机制来保证各个服务形成一个整体性的事务。

上面那几个步骤,要么全部成功,如果任何一个服务的操作失败了,就全部一起回滚,撤销已经完成的操作。

比如说库存服务要是扣减库存失败了,那么订单服务就得撤销那个修改订单状态的操作,然后得停止执行增加积分和通知出库两个操作。

说了那么多,老规矩,给大家上一张图,大伙儿顺着图来直观的感受一下:

落地实现 TCC 分布式事务

那么现在到底要如何来实现一个 TCC 分布式事务,使得各个服务,要么一起成功?要么一起失败呢?

大家稍安勿躁,我们这就来一步一步的分析一下。咱们就以一个 Spring Cloud 开发系统作为背景来解释。

TCC 实现阶段一:Try

首先,订单服务那儿,它的代码大致来说应该是这样子的:

public class OrderService {

    // 库存服务
    @Autowired
    private InventoryService inventoryService;

    // 积分服务
    @Autowired
    private CreditService creditService;

    // 仓储服务
    @Autowired
    private WmsService wmsService;

    // 对这个订单完成支付
    public void pay(){
        //对本地的的订单数据库修改订单状态为"已支付"
        orderDAO.updateStatus(OrderStatus.PAYED);

        //调用库存服务扣减库存
        inventoryService.reduceStock();

        //调用积分服务增加积分
        creditService.addCredit();

        //调用仓储服务通知发货
        wmsService.saleDelivery();
    }
}

如果你之前看过 Spring Cloud 架构原理那篇文章,同时对 Spring Cloud 有一定的了解的话,应该是可以理解上面那段代码的。

其实就是订单服务完成本地数据库操作之后,通过 Spring Cloud 的 Feign 来调用其他的各个服务罢了。

但是光是凭借这段代码,是不足以实现 TCC 分布式事务的啊?!兄弟们,别着急,我们对这个订单服务修改点儿代码好不好。

首先,上面那个订单服务先把自己的状态修改为:OrderStatus.UPDATING。

这是啥意思呢?也就是说,在 pay() 那个方法里,你别直接把订单状态修改为已支付啊!你先把订单状态修改为 UPDATING,也就是修改中的意思。

这个状态是个没有任何含义的这么一个状态,代表有人正在修改这个状态罢了。

然后呢,库存服务直接提供的那个 reduceStock() 接口里,也别直接扣减库存啊,你可以是冻结掉库存。

举个例子,本来你的库存数量是 100,你别直接 100 - 2 = 98,扣减这个库存!

你可以把可销售的库存:100 - 2 = 98,设置为 98 没问题,然后在一个单独的冻结库存的字段里,设置一个 2。也就是说,有 2 个库存是给冻结了。

积分服务的 addCredit() 接口也是同理,别直接给用户增加会员积分。你可以先在积分表里的一个预增加积分字段加入积分。

比如:用户积分原本是 1190,现在要增加 10 个积分,别直接 1190 + 10 = 1200 个积分啊!

你可以保持积分为 1190 不变,在一个预增加字段里,比如说 prepare_add_credit 字段,设置一个 10,表示有 10 个积分准备增加。

仓储服务的 saleDelivery() 接口也是同理啊,你可以先创建一个销售出库单,但是这个销售出库单的状态是“UNKNOWN”。

也就是说,刚刚创建这个销售出库单,此时还不确定它的状态是什么呢!

上面这套改造接口的过程,其实就是所谓的 TCC 分布式事务中的第一个 T 字母代表的阶段,也就是 Try 阶段。

总结上述过程,如果你要实现一个 TCC 分布式事务,首先你的业务的主流程以及各个接口提供的业务含义,不是说直接完成那个业务操作,而是完成一个 Try 的操作。

这个操作,一般都是锁定某个资源,设置一个预备类的状态,冻结部分数据,等等,大概都是这类操作。

咱们来一起看看下面这张图,结合上面的文字,再来捋一捋整个过程:

TCC 实现阶段二:Confirm

然后就分成两种情况了,第一种情况是比较理想的,那就是各个服务执行自己的那个 Try 操作,都执行成功了,Bingo!

这个时候,就需要依靠 TCC 分布式事务框架来推动后续的执行了。这里简单提一句,如果你要玩儿 TCC 分布式事务,必须引入一款 TCC 分布式事务框架,比如国内开源的 ByteTCC、Himly、TCC-transaction。

否则的话,感知各个阶段的执行情况以及推进执行下一个阶段的这些事情,不太可能自己手写实现,太复杂了。

如果你在各个服务里引入了一个 TCC 分布式事务的框架,订单服务里内嵌的那个 TCC 分布式事务框架可以感知到,各个服务的 Try 操作都成功了。

此时,TCC 分布式事务框架会控制进入 TCC 下一个阶段,第一个 C 阶段,也就是 Confirm 阶段。

为了实现这个阶段,你需要在各个服务里再加入一些代码。比如说,订单服务里,你可以加入一个 Confirm 的逻辑,就是正式把订单的状态设置为“已支付”了,大概是类似下面这样子:

public class OrderServiceConfirm {

    public void pay(){
        orderDao.updateStatus(OrderStatus.PAYED);
    }
}

库存服务也是类似的,你可以有一个 InventoryServiceConfirm 类,里面提供一个 reduceStock() 接口的 Confirm 逻辑,这里就是将之前冻结库存字段的 2 个库存扣掉变为 0。

这样的话,可销售库存之前就已经变为 98 了,现在冻结的 2 个库存也没了,那就正式完成了库存的扣减。

积分服务也是类似的,可以在积分服务里提供一个 CreditServiceConfirm 类,里面有一个 addCredit() 接口的 Confirm 逻辑,就是将预增加字段的 10 个积分扣掉,然后加入实际的会员积分字段中,从 1190 变为 1120。

仓储服务也是类似,可以在仓储服务中提供一个 WmsServiceConfirm 类,提供一个 saleDelivery() 接口的 Confirm 逻辑,将销售出库单的状态正式修改为“已创建”,可以供仓储管理人员查看和使用,而不是停留在之前的中间状态“UNKNOWN”了。

好了,上面各种服务的 Confirm 的逻辑都实现好了,一旦订单服务里面的 TCC 分布式事务框架感知到各个服务的 Try 阶段都成功了以后,就会执行各个服务的 Confirm 逻辑。

订单服务内的 TCC 事务框架会负责跟其他各个服务内的 TCC 事务框架进行通信,依次调用各个服务的 Confirm 逻辑。然后,正式完成各个服务的所有业务逻辑的执行。

同样,给大家来一张图,顺着图一起来看看整个过程:

TCC 实现阶段三:Cancel

好,这是比较正常的一种情况,那如果是异常的一种情况呢?

举个例子:在 Try 阶段,比如积分服务吧,它执行出错了,此时会怎么样?

那订单服务内的 TCC 事务框架是可以感知到的,然后它会决定对整个 TCC 分布式事务进行回滚。

也就是说,会执行各个服务的第二个 C 阶段,Cancel 阶段。同样,为了实现这个 Cancel 阶段,各个服务还得加一些代码。

首先订单服务,它得提供一个 OrderServiceCancel 的类,在里面有一个 pay() 接口的 Cancel 逻辑,就是可以将订单的状态设置为“CANCELED”,也就是这个订单的状态是已取消。

库存服务也是同理,可以提供 reduceStock() 的 Cancel 逻辑,就是将冻结库存扣减掉 2,加回到可销售库存里去,98 + 2 = 100。

积分服务也需要提供 addCredit() 接口的 Cancel 逻辑,将预增加积分字段的 10 个积分扣减掉。

仓储服务也需要提供一个 saleDelivery() 接口的 Cancel 逻辑,将销售出库单的状态修改为“CANCELED”设置为已取消。

然后这个时候,订单服务的 TCC 分布式事务框架只要感知到了任何一个服务的 Try 逻辑失败了,就会跟各个服务内的 TCC 分布式事务框架进行通信,然后调用各个服务的 Cancel 逻辑。

大家看看下面的图,直观的感受一下:

总结与思考

好了,兄弟们,聊到这儿,基本上大家应该都知道 TCC 分布式事务具体是怎么回事了!

总结一下,你要玩儿 TCC 分布式事务的话:首先需要选择某种 TCC 分布式事务框架,各个服务里就会有这个 TCC 分布式事务框架在运行。

然后你原本的一个接口,要改造为 3 个逻辑,Try-Confirm-Cancel:

  • 先是服务调用链路依次执行 Try 逻辑。
  • 如果都正常的话,TCC 分布式事务框架推进执行 Confirm 逻辑,完成整个事务。
  • 如果某个服务的 Try 逻辑有问题,TCC 分布式事务框架感知到之后就会推进执行各个服务的 Cancel 逻辑,撤销之前执行的各种操作。

这就是所谓的 TCC 分布式事务。TCC 分布式事务的核心思想,说白了,就是当遇到下面这些情况时:

  • 某个服务的数据库宕机了。
  • 某个服务自己挂了。
  • 那个服务的 Redis、Elasticsearch、MQ 等基础设施故障了。
  • 某些资源不足了,比如说库存不够这些。

先来 Try 一下,不要把业务逻辑完成,先试试看,看各个服务能不能基本正常运转,能不能先冻结我需要的资源。

如果 Try 都 OK,也就是说,底层的数据库、Redis、Elasticsearch、MQ 都是可以写入数据的,并且你保留好了需要使用的一些资源(比如冻结了一部分库存)。

接着,再执行各个服务的 Confirm 逻辑,基本上 Confirm 就可以很大概率保证一个分布式事务的完成了。

那如果 Try 阶段某个服务就失败了,比如说底层的数据库挂了,或者 Redis 挂了,等等。

此时就自动执行各个服务的 Cancel 逻辑,把之前的 Try 逻辑都回滚,所有服务都不要执行任何设计的业务逻辑。保证大家要么一起成功,要么一起失败。

等一等,你有没有想到一个问题?如果有一些意外的情况发生了,比如说订单服务突然挂了,然后再次重启,TCC 分布式事务框架是如何保证之前没执行完的分布式事务继续执行的呢?

所以,TCC 事务框架都是要记录一些分布式事务的活动日志的,可以在磁盘上的日志文件里记录,也可以在数据库里记录。保存下来分布式事务运行的各个阶段和状态。

问题还没完,万一某个服务的 Cancel 或者 Confirm 逻辑执行一直失败怎么办呢?

那也很简单,TCC 事务框架会通过活动日志记录各个服务的状态。举个例子,比如发现某个服务的 Cancel 或者 Confirm 一直没成功,会不停的重试调用它的 Cancel 或者 Confirm 逻辑,务必要它成功!

当然了,如果你的代码没有写什么 Bug,有充足的测试,而且 Try 阶段都基本尝试了一下,那么其实一般 Confirm、Cancel 都是可以成功的!

最后,再给大家来一张图,来看看给我们的业务,加上分布式事务之后的整个执行流程:

不少大公司里,其实都是自己研发 TCC 分布式事务框架的,专门在公司内部使用,比如我们就是这样。

不过如果自己公司没有研发 TCC 分布式事务框架的话,那一般就会选用开源的框架。

这里笔者给大家推荐几个比较不错的框架,都是咱们国内自己开源出去的:ByteTCC,TCC-transaction,Himly。

大家有兴趣的可以去它们的 GitHub 地址,学习一下如何使用,以及如何跟 Spring Cloud、Dubbo 等服务框架整合使用。

只要把那些框架整合到你的系统里,很容易就可以实现上面那种奇妙的 TCC 分布式事务的效果了。

下面,我们来讲讲可靠消息最终一致性方案实现的分布式事务,同时聊聊在实际生产中遇到的运用该方案的高可用保障架构。

最终一致性分布式事务如何保障实际生产中 99.99% 高可用?

上面咱们聊了聊 TCC 分布式事务,对于常见的微服务系统,大部分接口调用是同步的,也就是一个服务直接调用另外一个服务的接口。

这个时候,用 TCC 分布式事务方案来保证各个接口的调用,要么一起成功,要么一起回滚,是比较合适的。

但是在实际系统的开发过程中,可能服务间的调用是异步的。也就是说,一个服务发送一个消息给 MQ,即消息中间件,比如 RocketMQ、RabbitMQ、Kafka、ActiveMQ 等等。

然后,另外一个服务从 MQ 消费到一条消息后进行处理。这就成了基于 MQ 的异步调用了。

那么针对这种基于 MQ 的异步调用,如何保证各个服务间的分布式事务呢?也就是说,我希望的是基于 MQ 实现异步调用的多个服务的业务逻辑,要么一起成功,要么一起失败。

这个时候,就要用上可靠消息最终一致性方案,来实现分布式事务。

大家看上图,如果不考虑各种高并发、高可用等技术挑战的话,单从“可靠消息”以及“最终一致性”两个角度来考虑,这种分布式事务方案还是比较简单的。

可靠消息最终一致性方案的核心流程

①上游服务投递消息

如果要实现可靠消息最终一致性方案,一般你可以自己写一个可靠消息服务,实现一些业务逻辑。

首先,上游服务需要发送一条消息给可靠消息服务。这条消息说白了,你可以认为是对下游服务一个接口的调用,里面包含了对应的一些请求参数。

然后,可靠消息服务就得把这条消息存储到自己的数据库里去,状态为“待确认”。

接着,上游服务就可以执行自己本地的数据库操作,根据自己的执行结果,再次调用可靠消息服务的接口。

如果本地数据库操作执行成功了,那么就找可靠消息服务确认那条消息。如果本地数据库操作失败了,那么就找可靠消息服务删除那条消息。

此时如果是确认消息,那么可靠消息服务就把数据库里的消息状态更新为“已发送”,同时将消息发送给 MQ。

这里有一个很关键的点,就是更新数据库里的消息状态和投递消息到 MQ。这俩操作,你得放在一个方法里,而且得开启本地事务。

啥意思呢?如果数据库里更新消息的状态失败了,那么就抛异常退出了,就别投递到 MQ;如果投递 MQ 失败报错了,那么就要抛异常让本地数据库事务回滚。这俩操作必须得一起成功,或者一起失败。

如果上游服务是通知删除消息,那么可靠消息服务就得删除这条消息。

②下游服务接收消息

下游服务就一直等着从 MQ 消费消息好了,如果消费到了消息,那么就操作自己本地数据库。

如果操作成功了,就反过来通知可靠消息服务,说自己处理成功了,然后可靠消息服务就会把消息的状态设置为“已完成”。

③如何保证上游服务对消息的 100% 可靠投递?

上面的核心流程大家都看完:一个很大的问题就是,如果在上述投递消息的过程中各个环节出现了问题该怎么办?

我们如何保证消息 100% 的可靠投递,一定会从上游服务投递到下游服务?别着急,下面我们来逐一分析。

如果上游服务给可靠消息服务发送待确认消息的过程出错了,那没关系,上游服务可以感知到调用异常的,就不用执行下面的流程了,这是没问题的。

如果上游服务操作完本地数据库之后,通知可靠消息服务确认消息或者删除消息的时候,出现了问题。

比如:没通知成功,或者没执行成功,或者是可靠消息服务没成功的投递消息到 MQ。这一系列步骤出了问题怎么办?

其实也没关系,因为在这些情况下,那条消息在可靠消息服务的数据库里的状态会一直是“待确认”。

此时,我们在可靠消息服务里开发一个后台定时运行的线程,不停的检查各个消息的状态。

如果一直是“待确认”状态,就认为这个消息出了点什么问题。此时的话,就可以回调上游服务提供的一个接口,问问说,兄弟,这个消息对应的数据库操作,你执行成功了没啊?

如果上游服务答复说,我执行成功了,那么可靠消息服务将消息状态修改为“已发送”,同时投递消息到 MQ。

如果上游服务答复说,没执行成功,那么可靠消息服务将数据库中的消息删除即可。

通过这套机制,就可以保证,可靠消息服务一定会尝试完成消息到 MQ 的投递。

④如何保证下游服务对消息的 100% 可靠接收?

那如果下游服务消费消息出了问题,没消费到?或者是下游服务对消息的处理失败了,怎么办?

其实也没关系,在可靠消息服务里开发一个后台线程,不断的检查消息状态。

如果消息状态一直是“已发送”,始终没有变成“已完成”,那么就说明下游服务始终没有处理成功。

此时可靠消息服务就可以再次尝试重新投递消息到 MQ,让下游服务来再次处理。

只要下游服务的接口逻辑实现幂等性,保证多次处理一个消息,不会插入重复数据即可。

⑤如何基于 RocketMQ 来实现可靠消息最终一致性方案?

在上面的通用方案设计里,完全依赖可靠消息服务的各种自检机制来确保:

  • 如果上游服务的数据库操作没成功,下游服务是不会收到任何通知。
  • 如果上游服务的数据库操作成功了,可靠消息服务死活都会确保将一个调用消息投递给下游服务,而且一定会确保下游服务务必成功处理这条消息。

通过这套机制,保证了基于 MQ 的异步调用/通知的服务间的分布式事务保障。其实阿里开源的 RocketMQ,就实现了可靠消息服务的所有功能,核心思想跟上面类似。

只不过 RocketMQ 为了保证高并发、高可用、高性能,做了较为复杂的架构实现,非常的优秀。有兴趣的同学,自己可以去查阅 RocketMQ 对分布式事务的支持。

可靠消息最终一致性方案的高可用保障生产实践

背景引入

上面那套方案和思想,很多同学应该都知道是怎么回事儿,我们也主要就是铺垫一下这套理论思想。

在实际落地生产的时候,如果没有高并发场景的,完全可以参照上面的思路自己基于某个 MQ 中间件开发一个可靠消息服务。

如果有高并发场景的,可以用 RocketMQ 的分布式事务支持上面的那套流程都可以实现。

今天给大家分享的一个核心主题,就是这套方案如何保证 99.99% 的高可用。

大家应该发现了这套方案里保障高可用性最大的一个依赖点,就是 MQ 的高可用性。

任何一种 MQ 中间件都有一整套的高可用保障机制,无论是 RabbitMQ、RocketMQ 还是 Kafka。

所以在大公司里使用可靠消息最终一致性方案的时候,我们通常对可用性的保障都是依赖于公司基础架构团队对 MQ 的高可用保障。

也就是说,大家应该相信兄弟团队,99.99% 可以保障 MQ 的高可用,绝对不会因为 MQ 集群整体宕机,而导致公司业务系统的分布式事务全部无法运行。

但是现实是很残酷的,很多中小型的公司,甚至是一些中大型公司,或多或少都遇到过 MQ 集群整体故障的场景。

MQ 一旦完全不可用,就会导致业务系统的各个服务之间无法通过 MQ 来投递消息,导致业务流程中断。

比如最近就有一个朋友的公司,也是做电商业务的,就遇到了 MQ 中间件在自己公司机器上部署的集群整体故障不可用,导致依赖 MQ 的分布式事务全部无法跑通,业务流程大量中断的情况。

这种情况,就需要针对这套分布式事务方案实现一套高可用保障机制。

基于 KV 存储的队列支持的高可用降级方案

大家来看看下面这张图,这是我曾经指导过朋友的一个公司针对可靠消息最终一致性方案设计的一套高可用保障降级机制。

这套机制不算太复杂,可以非常简单有效的保证那位朋友公司的高可用保障场景,一旦 MQ 中间件出现故障,立马自动降级为备用方案。

①自行封装 MQ 客户端组件与故障感知

首先第一点,你要做到自动感知 MQ 的故障接着自动完成降级,那么必须动手对 MQ 客户端进行封装,发布到公司 Nexus 私服上去。

然后公司需要支持 MQ 降级的业务服务都使用这个自己封装的组件来发送消息到 MQ,以及从 MQ 消费消息。

在你自己封装的 MQ 客户端组件里,你可以根据写入 MQ 的情况来判断 MQ 是否故障。

比如说,如果连续 10 次重新尝试投递消息到 MQ 都发现异常报错,网络无法联通等问题,说明 MQ 故障,此时就可以自动感知以及自动触发降级开关。

②基于 KV 存储中队列的降级方案

如果 MQ 挂掉之后,要是希望继续投递消息,那么就必须得找一个 MQ 的替代品。

举个例子,比如我那位朋友的公司是没有高并发场景的,消息的量很少,只不过可用性要求高。此时就可以使用类似 Redis 的 KV 存储中的队列来进行替代。

由于 Redis 本身就支持队列的功能,还有类似队列的各种数据结构,所以你可以将消息写入 KV 存储格式的队列数据结构中去。

PS:关于 Redis 的数据存储格式、支持的数据结构等基础知识,请大家自行查阅了,网上一大堆。

但是,这里有几个大坑,一定要注意一下:

第一个,任何 KV 存储的集合类数据结构,建议不要往里面写入数据量过大,否则会导致大 Value 的情况发生,引发严重的后果。

因此绝不能在 Redis 里搞一个 Key,就拼命往这个数据结构中一直写入消息,这是肯定不行的。

第二个,绝对不能往少数 Key 对应的数据结构中持续写入数据,那样会导致热 Key 的产生,也就是某几个 Key 特别热。

大家要知道,一般 KV 集群,都是根据 Key 来 Hash 分配到各个机器上的,你要是老写少数几个 Key,会导致 KV 集群中的某台机器访问过高,负载过大。

基于以上考虑,下面是笔者当时设计的方案:

  • 根据它们每天的消息量,在 KV 存储中固定划分上百个队列,有上百个 Key 对应。
  • 这样保证每个 Key 对应的数据结构中不会写入过多的消息,而且不会频繁的写少数几个 Key。
  • 一旦发生了 MQ 故障,可靠消息服务可以对每个消息通过 Hash 算法,均匀的写入固定好的上百个 Key 对应的 KV 存储的队列中。

同时需要通过 ZK 触发一个降级开关,整个系统在 MQ 这块的读和写全部立马降级。

③下游服务消费 MQ 的降级感知

下游服务消费 MQ 也是通过自行封装的组件来做的,此时那个组件如果从 ZK 感知到降级开关打开了,首先会判断自己是否还能继续从 MQ 消费到数据?

如果不能了,就开启多个线程,并发的从 KV 存储的各个预设好的上百个队列中不断的获取数据。

每次获取到一条数据,就交给下游服务的业务逻辑来执行。通过这套机制,就实现了 MQ 故障时候的自动故障感知,以及自动降级。如果系统的负载和并发不是很高的话,用这套方案大致是没问题的。

因为在生产落地的过程中,包括大量的容灾演练以及生产实际故障发生时的表现来看,都是可以有效的保证 MQ 故障时,业务流程继续自动运行的。

④故障的自动恢复

如果降级开关打开之后,自行封装的组件需要开启一个线程,每隔一段时间尝试给 MQ 投递一个消息看看是否恢复了。

如果 MQ 已经恢复可以正常投递消息了,此时就可以通过 ZK 关闭降级开关,然后可靠消息服务继续投递消息到 MQ,下游服务在确认 KV 存储的各个队列中已经没有数据之后,就可以重新切换为从 MQ 消费消息。

⑤更多的业务细节

上面说的那套方案是一套通用的降级方案,但是具体的落地是要结合各个公司不同的业务细节来决定的,很多细节多没法在文章里体现。

比如说你们要不要保证消息的顺序性?是不是涉及到需要根据业务动态,生成大量的 Key?等等。

此外,这套方案实现起来还是有一定的成本的,所以建议大家尽可能还是 Push 公司的基础架构团队,保证 MQ 的 99.99% 可用性,不要宕机。

其次就是根据大家公司实际对高可用的需求来决定,如果感觉 MQ 偶尔宕机也没事,可以容忍的话,那么也不用实现这种降级方案。

但是如果公司领导认为 MQ 中间件宕机后,一定要保证业务系统流程继续运行,那么还是要考虑一些高可用的降级方案,比如本文提到的这种。

最后再说一句,真要是一些公司涉及到每秒几万几十万的高并发请求,那么对 MQ 的降级方案会设计的更加的复杂,那就远远不是这么简单可以做到的。

来源:【微信公众号】石杉的架构笔记

Javaer 运维指令合集(快餐版)

$
0
0


这年头,大家都喜欢吃快餐,虽不健康,但是奈何真香!这里我把经常使用的运维指令总结成一套指令快餐。之所以称之为快餐是因为每个指令都是简单的一句或者两句话的简介。注意很多指令用法奇多,文中不会一一列出。

系统相关

df
查看磁盘剩余空间。一般 df -h

free
查看内存使用情况。一般 free -h

du
查看文件占用磁盘空间。一般 du -h

top
查看系统整体情况,包括CPU,内存,负载等等。直接 top即可。也可以这样 top -H -p pid查看某个进程下的线程情况。

ps
查看系统进程。直接命令行输入 ps -ef或者 ps aux,将显示系统里面所有的进程。
一般我们会这样用 ps -ef|grep java|grep -v grep或者 ps aux|grep tomcat|grep -v tomcat。查找出所有java进程或者tomcat进程。

kill
杀死进程。一般 kill -9 pid强制杀死进程。也可以这样 ps -ef | grep java | grep -v grep | awk '{print $2}' | xargs kill -9
批量杀死所有Java进程。

pstree
查看一个进程下的线程数。一般 pstree -p pid |wc -l

mpstat
查看cpu 有关信息。直接命令行输入 mpstat,一般会这样使用, mpstat -P ALL 5 2,每5秒采集一次,采集2次。

vmstat
查看虚拟内存使用状态,及CPU等信息。用法和 mpstat类似, vmstat 2 1每两秒采集一次,一共采集一次。

pmap
查看一个进程是如何使用内存的。一般 pmap pid

iostat
查看机器IO情况,一般直接输入 iostat指令。

lsof
查看进程打开的文件及网络端口。一般 lsof filename查看哪个进程在使用该文件。 lsof -i tcp:8083列出哪个进程在使用8083端口

网络相关

curl
通过该指令可以访问URL地址。例如 curl http://url

wget
用于在命令行通过url下载文件,比如 wget http://url

ping
ping命令用来测试主机之间网络的连通性。 ping ip

telnet
一般用于检查服务端某个端口联通性,比如 telnet ip port

ss, netstat
都可以显示网络系统的状态信息。一般 netstat -anp|grep 8080或者 ss -lnp。ss的优势是比较快。当服务器链接数过多时,netstat会很慢。

ifconfig
查看本机IP及硬件信息。一般 ifconfig -a

tcpdump
网络抓包,分析网络链接的细节问题。需要有sudo权限。
监听通过网卡 eth0的网络包,并保存到 socketdump.dattcpdump -i eth0 tcp -w socketdump.dat 注意这个文件需要使用 wireshark来分析,你用肉眼是解读不出什么的。
监听某个主机某个端口 tcpdump tcp port 8089 and host hostname
监听某个网段的数据   tcpdump tcp -c 10 net 172.22

traceroute
用于追踪数据包在网络上的传输时的全部路径,它默认发送的数据包大小是40字节。一般 traceroute www.baidu.com

jvm相关

jps
查看运行中的java进程,一般的 jps -lv

jmap
查看JVM堆内存使用情况,包括GC算法等信息。一般 jmap -heap pid
dump出内存文件,用 jhat或者MAT进行分析。一般 jmap -dump:format=b,file=dumpFileName pid

jhat
分析堆内存文件。一般 jhat -J-Xmx2G -port 9998 dumpFileName然后在浏览器端通过9998端口访问。建议不要在线上服务器上操作,会很占用内存和CPU。

jstack
查看Java进程内的线程堆栈信息。一般的 jstack pid | grep "java.lang.Thread.state"| sort -nr| uniq -c。查看进程各个状态数量。注意 jstack显示的线程号都是16进制的。我们需要把进程号转换为16进制,命令行直接输入 printf '%x\n' 3528,然后查找对应线程信息, jstack pid | grep 'nid'

jstat
可以看出堆内存及GC情况,一般的 jstat -gcutil pid

结束

大家不要希望记住几个指令,就能解决线上所有问题,就能成为大牛。要想成为大牛还是要多实战,多看书。比如定位网络相关的问题,你最好多读几遍《TCP/IP详解》这本书,要想解决JVM相关的问题,你最好多去找几本JVM相关的书籍来看。快餐终究是快餐,解馋可以,对成长不利!


推荐阅读
你的内存够用吗
我是如何进行架构设计的


三年半Java后端面试经历 - codergoose‘s blog - SegmentFault 思否

$
0
0

经过半年的沉淀,加上对MySQL,redis和分布式这块的补齐,终于开始重拾面试信心,再次出征。

鹅厂

面试职位:go后端开发工程师,接受从Java转语言

都知道鹅厂是cpp的主战场,而以cpp为背景的工程师大都对os,network这块要求特别高,不像是Java这种偏重业务层的语言,之前面试Java的公司侧重还是在数据结构、网络、框架、数据库和分布式。所以OS这块吃的亏比较大

一面基础技术面

电话面试,随便问了些技术问题,最后还问了个LeetCode里面medium级别的算法题,偏简单

  1. redis有没有用过,常用的数据结构以及在业务中使用的场景,redis的hash怎么实现的,rehash过程讲一下和JavaHashMap的rehash有什么区别?redis cluster有没有了解过,怎么做到高可用的?redis的持久化机制,为啥不能用redis做专门的持久化数据库存储?
  2. 了不了解tcp/udp,说下两者的定义,tcp为什么要三次握手和四次挥手?tcp怎么保证有序传输的,讲下tcp的快速重传和拥塞机制,知不知道time_wait状态,这个状态出现在什么地方,有什么用?(参考quic)
  3. 知道udp是不可靠的传输,如果你来设计一个基于udp差不多可靠的算法,怎么设计?
  4. http与https有啥区别?说下https解决了什么问题,怎么解决的?说下https的握手过程。
  5. 看你项目里面用了etcd,讲解下etcd干什么用的,怎么保证高可用和一致性?
  6. 既然你提到了raft算法,讲下raft算法的基本流程?raft算法里面如果出现脑裂怎么处理?有没有了解过paxos和zookeeper的zab算法,他们之前有啥区别?
  7. 你们后端用什么数据库做持久化的?有没有用到分库分表,怎么做的?
  8. 索引的常见实现方式有哪些,有哪些区别?MySQL的存储引擎有哪些,有哪些区别?InnoDB使用的是什么方式实现索引,怎么实现的?说下聚簇索引和非聚簇索引的区别?
  9. 有没有了解过协程?说下协程和线程的区别?
  10. 算法题一个,剑指offer第51题,数组中的重复数字?

自己的回答情况,redis这块没啥问题,具体rehash有印象是渐进式的,但是具体原理可能答的有点出入。tcp的time_wait这块答的不是很好,之前没有了解过quic机制的实现,所以问可靠性udp的时候,基本上脑子里就照着tcp的实现在说。https这块没啥说的,之前项目里面有用到类似的东西,研究的比较清楚了。raft算法这个因为刚好在刷6.824(才刷到lab2。。。),答的也凑合,不过paxos和zab算法确实不熟悉,直接说不会。MySQL这块很熟了,包括索引,锁,事务机制以及mvcc等等,没啥说的,都已经补齐了。协程和线程,主要说了go程和Java线程的区别以及go程的调度模型。面试官提示没有提到线程的有内核态的切换,go程只在用户态调度。最后一个算法题,首先说使用HashMap来做,说空间复杂度能不能降到O(1),后面想了大概5min才想出来原地置换的思路。

二面项目技术面
  1. 主要针对自己最熟悉的项目,画出项目的架构图,主要的数据表结构,项目中使用到的技术点,项目的总峰值qps,时延,以及有没有分析过时延出现的耗时分别出现在什么地方,项目有啥改进的地方没有?
  2. 如果请求出现问题没有响应,如何定位问题,说下思路?
  3. tcp 粘包问题怎么处理?
  4. 问了下缓存更新的模式,以及会出现的问题和应对思路?
  5. 除了公司项目之外,业务有没有研究过知名项目或做出过贡献?

基本都没有啥问题,除了面试官说项目经验稍弱之外,其余还不错。

三面综合技术面

这面面的是阵脚大乱,面试官采用刨根问底的方式提问,终究是面试经验不够,导致面试的节奏有点乱。 举个例子:

其中有个题是go程和线程有什么区别?
答:1 起一个go程大概只需要4kb的内存,起一个Java线程需要1.5MB的内存;go程的调度在用户态非常轻量,Java线程的切换成本比较高。接着问为啥成本比较高?因为Java线程的调度需要在用户态和内核态切换所以成本高?为啥在用户态和内核态之间切换调度成本比较高?简单说了下内核态和用户态的定义。接着问,还是没有明白为啥成本高?心里瞬间崩溃,没完没了了呀,OS这块依旧是痛呀,支支吾吾半天放弃了。

后面所有的提问都是这种模式,结果回答的节奏全无,感觉被套路了。大多度都能回答个一二甚至是一二三,但是再往后或者再深入的OS层面就GG了。

后面问了下项目过程中遇到的最大的挑战,以及时怎么解决的?

后面还问了一个问题定位的问题,服务器CPU 100%怎么定位?可能是由于平时定位业务问题的思维定势,加之处于蒙蔽状态,随口就是:先查看监控面板看有无突发流量异常,接着查看业务日志是否有异常,针对CPU100%那个时间段,取一个典型业务流程的日志查看。最后才提到使用 top命令来监控看是哪个进程占用到100%。果然阵脚大乱,张口就来,捂脸。。。
本来正确的思路应该是先用 top定位出问题的进程,再用 top定位到出问题的线程,再打印线程堆栈查看运行情况,这个流程换平时肯定能答出来,但是,但是没有但是。还是得好好总结。

最后问了一个系统设计题目(朋友圈的设计),白板上面画出系统的架构图,主要的表结构和讲解主要的业务流程,如果用户变多流量变大,架构将怎么扩展,怎样应对?
这个答的也有点乱,直接上来自顾自的用了一个通用的架构,感觉毫无亮点。后面反思应该先定位业务的特点,这个业务明显是读多写少,然后和面试官沟通一期刚开始的方案的用户量,性能要求,单机目标qps是什么等等?在明确系统的特点和约束之后再来设计,而不是一开始就是用典型互联网的那种通用架构自顾自己搞自己的方案。

3天后收到短信被拒

总结
  1. tcp/udp,http和https还有网络这块(各种网络模型,已经select,poll和epoll)一定要非常熟悉
  2. 一定要有拿的出手的项目经验,而且要能够讲清楚,讲清楚项目中取舍,设计模型和数据表
  3. 分布式要非常熟悉
  4. 常见问题定位一定要有思路
  5. 操作系统,还是操作系统,重要的事情说三遍
  6. 系统设计,思路,思路,思路,一定要思路清晰,一定要总结下系统设计的流程
  7. 一点很重要的心得,平时blog和专栏看的再多,如果没有自己的思考不过是过眼云烟,根本不会成为自己的东西,就像内核态和用户态,平常也看过,但是没细想,突然要自己说,还真说不出来,这就很尴尬了。勿以浮沙筑高台,基础这种东西还是需要时间去慢慢打牢,多去思考和总结。

相关资料补充学习:

  1. 系统设计入门
  2. 系统设计典型问题的思考
  3. 协程的好处有哪些
  4. Golang的goroutine是怎么实现的?
  5. Context-Switch
  6. 从Java视角理解系统结构(一)CPU上下文切换

某东南亚互联网公司

一面技术面

先笔试算法题,LeetCode medium难度,然后综合问了下腾讯一面二面类似的问题+一个最熟悉的项目

二面研发经理综合面试

综合聊了下熟悉的技术,遇到了最难的问题,讲解一下项目的流程和架构

高手在民间,预测中国经济的雄文能神准到什么程度?

$
0
0
1

在开始这个话题之前,我首先要说的是我并不是一个阴谋论者,什么共济会掌握世界之类我是从来不信的。对于官员,我一贯的观点是如果有三个选择, 他们最后一定会选择最坏的那个,阴谋论这种东西实在太考验执行者的智商,而机构的智商历来都是不太够用。但是这事背后的蛛丝马迹又实在太过于匪夷所思,日本著名小学生曾经说过,当所有的可能全部被排除的时候,剩下的最后一个,不管看起来多么荒谬,那就是事实的真相,昨天深夜,我把所有的脉络理了一遍,来看看这个事情的过程,突然觉得很有意思。这些就当是路边社的呓语,我姑妄言之,你们姑妄听之.

1、老大哥的命门。今年最诡异的事情,莫过于楼市,往年楼市的回魂,都是有迹可循的,那就是一段时间内交易量放大,成交上涨以后,带来库存减少,房价上涨,但是这一轮楼市的爆发完全不同以往, 首先是价格飙升,带动市场恐慌后才赢来成交上涨,那只有形的手过于明显。有人说是因为一月的放水,但是不对,周期不对,市场传导的时间需要一个过程,就比如如果今天放水,反馈到市场上至少要几个月,带来价格飙升也应该在年底, 而这一波是一月放春节后立马立竿见影,时间节点不对。在开始这个话题之前,我们得先知道老大哥的命门是什么?相信很多人会异口同声的说, 楼市。没错,楼市的确是个大问题,而且已经彻底绑架了中国经济,如果楼市玩蛋,那是一个大地震,但是他绝对不会是一个能威胁到老大哥的问题, 金融市场的烂帐老大哥也是碰到不少,就比如世纪初,可谓是有丰富的经验,这是一个大问题,但是绝对不是一个生存问题。

老大哥真正的命门是外储。从八十年代后,老大哥已经彻底融入世界经济,生产低级必需品,然后换来美元,然后再从市场上购买各种石油,南美的大豆玉米等粮食产品,这是一个循环, 现在每年老大哥需要从市面上进口高达几千亿的石油,三分之一的粮食产品依赖进口,粮食自给是一个大问题,八十年代后,国人越吃越好,就是得益于全球经济,而不是金坷垃,像养猪养牛,从粮食到肉产品的转化都是一个固定的数据,你要养出多少吨的猪,必然要投入多少的饲料,所以你现在可以理解为什么外储才是一个生死存亡的问题。 手中有粮,心中不慌。08年前,每年外储增加多少都是要上新闻联播大书特书的事情,因为这个是硬需求,楼市塌了可以咬牙还贷,可以上信贷黑名单,可以苦难行军,但是吃不起饭,买不起石油呢?很多人没有意识到,或者完全没有想到这个问题有多严重: 过去中国的外储从高峰期下降了一万亿美金。

老大哥难道不知道现在一二线楼市有多荒谬?再加把火那不就更加骑虎难下了?房产市场的问题在于库存,最理想的结果当然是把大家赶去三四线买房,给国家解套了,但是问题是没人会那么傻,所以这个问题不会有最优解,那么,我们来换个思路呢, 既然我解决不了库存问题,那我放出风去让你知道我要保楼市。就像下围棋一样,我这里一条大龙看着很庞大,没人觉得我会弃子,但是我要是以这条大龙做掩护来做文章呢?其实围棋就是一个转换的游戏,如果弃掉大龙可以获得更多的利益,这并不是无法接受的事情。

在过去的一年里,外储为什么持续减少,绝不是离岸市场的问题,那里的弹药有限,外储一旦要爆也是爆在国内,大家如果持续换美元避险,外储绝对顶不住,但是这大半年时间内,有一个现象你可以观察: 那就是美元党的声音小了。外储一月到二月持续下降,但是到了二月三月就彻底站稳了,这出乎不少人的意料,因为按照道理说,汇市已经进入下行期,应该会越来越快才对,但是汇率虽然在节节败退,但是这半年时间内,他绝不是在溃败,外储一直稳定在32000亿左右,并没有减少,外汇持续贬值,但是央行并没有花掉太多弹药。外汇的表现和房产楼市的周期是完全一致的,彻底站稳的时间也是楼市起飞的时间,也就是说,这大半年时间内,这波楼市最大的成就就是彻底把社会游资全部锁在了一线房产, 你想换美元?你没钱了,你没钱了,你没钱了。

房产现在很难卖,要达到预期的价格,你看看成交量是多少,降价?降回原价你愿意吗?所以流动性彻底被锁死在一线市场,你还加了杠杆,你得还钱啊, 以后美联储加息,你也只能站着看,因为你没钱了。这就是所谓的灯下黑,大家一波炒作中国的楼市问题,把这个问题看得有多么严重,但是我用一个大问题掩盖另一个生死问题, 这用围棋上的术语,就叫交换。大家都觉得老大哥必须保楼市,这已经是宇宙真理了,但是你如果换个角度,假设这个大前提不存在呢?你仔细数数老大哥的历史,远的80前不提,近的比如90年代,这才过了多少年你就觉得老大哥改吃素的?历史上比这更严重的事情多了, 只有吃饭的问题才是本质问题。况且到现在你还买得起五万一平房的那也绝不是老大哥要同情的对象。

2、暧昧的美联储。我在很多论坛上看到一种论调,那就是中央会无限印钱,信贷猛增,所以未来一定是通胀,现在所有的人都抱着一种念头,就是疯狂抢购资产,等以后货币狂贬了之后扔一堆废纸给银行,问题是,未来真的会这样吗?这些话表面上看,似乎有点道理,无限印钞好像很符合我们过往对于ZF的印象,不就是缺钱,不就是欠钱,那就印啊。但是这件事有一个前提, 那就是中国必须是一个闭关锁国的国家,内外资本完全无法流通,否则你印钱好啊,那有什么可怕的,你印到人人工资一两万,我出国潇洒去啊,我出国购物去啊,楼市汇市只能保一个这么简单的道理你们都说的出来,为什么你们会觉得无限印钞这事可行?要是汇率不倒,印钞有啥可怕的,到时候人人用雅诗兰黛,人人开宝马,人人背LV都不是梦,你们怕啥?

问题是,这可能吗?

中国的外储顶不住啊。而且,这个事情本身有其客观规律,08年全球金融危机,大量避险资金进入中国,所以推高了资产价格,央行可以印的出钞,但是现在是2016年,去年发生了一件大事,就是美联储开始要进入加息的周期, 在加息周期用宽松政策?这是嫌命太长吗,这会直接刺破资产泡沫,导致资本进一步外流,你可以去翻翻央行过去的报告,看看外汇占款的趋势是什么?没记错的话,这个月又少了一千多亿,这就是大势。在这种情况下,为什么有人会认为这是一个通胀的周期,央行会无限印钱?

也许有人说,那怕什么,上手段啊, 但是中国是一个产品输出输入大国,他无法隔绝外部的联系,这个月IMF给中国加入SDR后出具了首份报告,肯定了中国的一些努力和指出一些问题,可以看出闭关锁国这绝不是高层的想法。话题回到美联储身上来,自从去年开始加息之后,美联储按兵不动,这一点让市场有点看不明白,有的人认为是经济表现不如预期,有人认为是英国意外脱欧导致了美联储不敢动。但是我觉得这看起来更像是一种等待,他在等什么?那我们来看看如果美联储在上半年加息国内市场会发生什么?由于社会游资,也就是筹码在市场手里,这时候对于资本外流会形成巨大压力,你只要想想过去几年释放了多少流动性就知道这冲击力会有多大。

很多人觉得美国就是一定想要干掉中国,但是这是不对的,美国的财团在中国有着巨大的利益,同时,中国外储使用的是美元,这代表着双方在资本市场的分歧并没有宣传的那么大。现在是一个全球的市场,除了三胖,大家谁也离不开谁, 实际上,这一轮新的全球金融危机策源地就在中国。

所以美联储绝对不会想要中国这个世界第二经济体一波跳贬直接跪了,在这点上,双方是有共同语言的。今年年初,美国高层还放话对于人民币贬值不满,可见一斑,同时中国最高领导人也在峰会上承诺稳定汇率,其实中国境内的美元资本全部回流这也绝非美联储的本意,一旦人民币跳贬,这会导致这波加息变成夹生饭 ,进一步加大美国国内压力,最后变成一个比烂的游戏。美联储最理想的是人民币挺住,剪完羊毛就行,绝不是想要这羊带着人肉炸弹直奔家里来。不管美联储为什么不加息,但是在这个事情上的表现,双方体现出了一致的默契,至于这是无意的还是沟通过的就不得而知。 现在中国市场上的社会资金,影子银行全部都给套在一线房产里了,而且套牢盘十分稳固,要知道,这一波暴涨直接翻了一倍啊,你想想这套牢盘能不稳吗?可以说,在加息风暴中,至少老大哥已经把篱笆扎起来了,后面扛几波风暴会不会伤筋动骨不知道,但是至少比原来强多了

3、吹响的集结号。今年八月,信贷数据一出来,大大出乎市场的意料,很多人乐观的以为央行很快会霜降放水,但是这次央行是出乎意料的严厉,8月4日,发改委文件里出现了降息降准马上秒删,央行再次重申稳健的货币政策,8月11日,新华社撰文,中国绝不采取刺激宽松的货币政策,语气十分强硬。为什么会有这样的转变?

因为你们的钱全部套在房产上了,这时候我不需要和你们讲道理了,你们挺住就是了。这就像那部电影,当集结号吹响的时候,没有援军了,老大哥已经撤了,你们好好的站最后一班岗吧。在八月的数据,有一条十分重要,但是容易被很多人忽略, 那就是M2增速降到10.2%,这是历年新低,实际上已经标志着货币政策开始真正转向,按照市场传导,这一波会在年底前反应出来,时间大概是11到12月,今年的六月,就是中国地产的极值。如果我们来重新梳理下路径,差不多就是这样的: 中国一线房产6月达到极值,开始转入稳健货币政策周期,年底楼市开始松动,因为有聪明人开始准备跑路,这时候的表现就一个词:震荡,同时经济开始准备全面迫降,估计会在明年全面着陆,同时美联储加速进入加息周期,人民币汇率贬值窗口关闭,不会再贬了,老大哥在两三波加息后压力全面增大,也会开始跟随美联储的步伐,这标志着新周期的彻底建立。这个周期钱会非常值钱,同时,个人背负债务会十分痛苦。以上就属于个人的一点脑洞大开的产物,如果未来央行继续霜降,你就可以把他们当路边社报道, 说明老大哥的智商的确不足以负担这么大的阴谋,但是如果央行坚决执行稳定货币政策,同时美联储开始加速进入加息周期,你也许就该重新考虑下个人选择了。


2

另外,现在的市场是在通胀还是通缩,有个很直观的数据,PPI和CPI,这两个数据近年来都处于连续的低位运转, CPI更是连续两个月处于1时代,这说明连放水都拉不起物价了。这绝非是很多人说的钱都去楼市了, 而是货币政策已经失效的前兆。实际上,按照经济学的观点,适当的,轻微的通胀是有利于经济发展的。但是从各方面的数据来看,现在是连放水都刺激不起物价。

换美元我觉得没必要, 因为只有一种情况下,换美元是大赚的,那就是彻底崩溃,以老大哥的能力和目前的态势,不至于到那一步。你如果只在本国内流通,美元的流通性太差,而且收益并不高。

对于大多数人的情况其实各有不同,所以不好给出具体建议,但是有一点我认为是可以说的 :那就是不要欠债。现在的经济形势有点类似于上世纪90年代末,但是那时候大多数人外债都少,所以体会的并不明显, 但是这一轮危机会给很多人补上这一课。而且 那时候中国是在风暴外围,而这一次是在风暴中心

这个区别最大的问题就是一切以阴谋来看待问题,经济运行,世界运转是有其客观规律的,在我看来, 目前中国的经济一切都还是在规则内运行,他的所有行为都是符合现有规则的,都是可以解释的。最后谁会成为倒霉蛋不是国家决定的,而是经济规律决定的。中国历史上不是没有不讲规矩的时期,80年代高层懂经济的人少,不讲了一次规矩,结果引爆了危机,吓得朱上来以后,直接把央行不准在一级市场购买国债写进了法律,现在高层懂经济的人多,至少技术官僚都是喝过墨水的,那样的时代早就过了 不会太迟,美元只要接着加息楼市肯定扛不住,最多不会超过两波加息,我预计是一波都扛不住,聪明的资金会想从楼市冲出来冲击汇市,但是楼市本身就是流动性很差的项目,所以大体上只要第一波释放掉,楼市的钱就全埋里面了。

房产现在实质已经塌了,除了个别城市,大部分城市根本没市场, 市场上的新闻很多都是房托放出来的,你要测试你本地的楼市很简单,你周围亲戚看看有没有要真卖的,对比今年的价格挂低两成出去,成交不了,说明你们当地的繁荣全是虚假的,要是你按去年前年的价格出还是卖不掉,说明已完。今年国家搞了个大动作,叫营改增, 实际上就是把财权上缴,逐步从地方收到上级。过去扩张时期,各地有钱了,队伍就膨胀起来,养了太多人员,现在日子不好过,支出肯定得减少,我估计以后的动作就是上面统筹, 保证你基层不会出现以前90年代有的县城出现欠薪的情况,但是支出是有定额的,所以当初外围膨胀起来的人员应该会被逐步调整。按照惯例,好城市的公务员肯定会好过些,小县城的公务员日子又要回到90年代。

中石油40块一股的时候,你会想出无数的理由解释他值40块,现在他只卖7块钱你买吗? 你们还年轻,上海房子只要4000块一平还送户口的年代你们没经历过罢了。楼市的问题说白了就是资金的问题,他是一个资金密集产业 ,只要资金一收紧,他就难以维系,今年这一波本质上是实业挤出的资金和社会游资在一线大搞配资。你只要看资金面一收紧,这个就得完,美元加息会导致外资进一步流出,楼市就面临风险就是这个原理。而且今年从5月以后,央行的政策就是收缩,这一波会在年底传导出来。大通胀要符合市场逻辑, 央行变不出钱,基础货币在缩水,他选择扩大货币乘数不过是在对冲罢了,想印钱也是得符合市场规则的。

不符合规则,只有彻底变废纸一条路,这是不可能的, 因为货币本身就是政权存续的标志,还有控制力的情况下,没有任何政权会容许自己的货币变废纸,所有一切东西在触及这个底线的时候都是可以抛弃的。战争的确是条去产能解决通缩的路子,这也是上个世纪初那时候的大国选择的办法,但是现在这条路子风险太大,不确定因素太多,我认为不至于。更何况现在是全球通缩,大家的问题都一样,这时候第一个跳出来是当雷锋当靶子,牺牲自己造福其他人。


3

拭目以待吧,我开始相信掌握世界的的确是共济会了其实说到店铺,你可以关注下商业地产,也就是俗称的商铺之类的东西,价格已经完全崩了,前几年商铺投资火热,现在大部分基本没人要, 这个曲线就跟现在的一线房产是一样,只是商铺破在前面而已,现在你可以去问投资者谁买商铺?当年还说一铺养几代人。

那些说房子下跌就会被抢购的人,完全不知道房产价格一旦破裂,那就一文不值。当年的四万亿是以美元流入为基础印出来的。美元流入,地方愿意借,这才有后来的结果,那四万亿不是凭空出现的,恰恰就是符合了货币原理最近几年债务猛增,那是因为近年内滚动的M2实质大头全是旁氏借贷带来的债务增长, 现在的债务杠杆是经济最大的问题,与产能过剩并列,甚至我觉得这个才是优先度最高的问题,楼市在这两个问题面前都不是问题没有居民贷款,这个月M2已经下降了,这几个月趋势就是企业降杠杆,居民加杠杆, 老乡冲锋在前不可能的,这要是能循环,世界上所有国家都这么搞了,你要刺激生产,借出去大量的钱,这些债务最后一定得合理,现在的周期就是已经进入了旁氏借贷, 这些债务连借钱还利息都快给不出来了,而且也没银行敢给了,央行从8月开始,一定会收缩货币政策, 因为再放下去,银行就得全部领便当了,银行破产比国企破产严重多了,这些我会在下一个章节说明,这也是我说的,为什么说货币政策的拐点来了。 货币政策的拐点一定是楼市的拐点,以后是要过苦日子还债了。经济进入通缩阶段,公司破产,就业岗位减少, 你不能按现在的收入去预估,我只能这么告诉你,以后就是投资啥都不赚钱, 能有工作就是硬道理。另外,我的建议是, 不要急于买房。今年上半年所有的资金已经全部进楼市了,绝不要相信楼市还能涨,那是房托吹出来的。你再拿一段,说不定拐点就来了, 这个时间绝不会太长,到时候你就看明白了。最典型的比如商业地产,就是商铺那类的, 前几年的口号是一铺养几代人,现在你看商铺还有人要吗

金融属性越强的产品,一旦跌价决没有任何人接手,直到跌回他本来的居住价值属性。这句话的意思也就是说, 现在炒的最狠的一线二线全得暴跌。

反而那些本来就没炒过的中西部小县城相对安全在讲述为什么今年地王频出这个原因之前,我要插播一个小新闻,那就是标普前不久刚刚下调了美国某家房地产公司的信用评级到B-级,原因是这家公司的息前利润连覆盖利息都勉强,这家美国公司刚刚在资本市场上大发神威,参与并购某家龙头企业的股票,在当时,我看到坛子里有人说了一句,大意是: XX买股票是因为股票价格低,这家公司买股票是因为股票价格高。

这位同志是真懂行的人。如果你能看明白这句话,你就知道为什么地王频出。如果你还不知道,我建议你去看看海曼明斯基我说的以上故事是发生在美国,与中国无关,大家不要过分解读。以下是中国的故事。

2010年,国资委宣布了所谓的退房令,也就是部分央企还能参与房地产,其他企业将逐步退出。在那个时段,也是一个地王制造的十分疯狂的年代,09年中化40亿拿下广渠地块,疯狂的让败北的潘石屹大喊,他就是盖房子不花钱他也得赔。是中化方兴的领导比业内多年的领袖之一潘石屹更懂大势, 更懂房地产,所以以那个价格拿地?当然不是,这个中原因你可以细细品味。


4

我首先要声明的是,大家都是成年人,基于自己的情况,对于自己的投资理财的方向要有自己的判断,不是说我卖房你们也必须跟着卖,因为个人的情况是完全不同的,就比如我前面说的不建议你们抢购美金,但是你的个人资产如果在几千万,那么适当配置美元资产是很合理的,把所有的资产全部放一个篮子里那赌博的意味太大,但是你要是全部身家就几万人民币,那换美元没啥意义。前面有的朋友不明白为什么外汇贬值的窗口会关闭,这个道理其实并不深奥。 贬值对谁有利?

当然是中国,因为贬值有利于释放国内的资金压力,刺激出口,但是这个过程不能太快,一旦跳贬,那会引发资金大规模出逃, 这个道理就像高压锅一样,缓慢的释放是有利的,但是你直接掀盖就炸了。那么贬值对中国有利,对什么国家不利?当然是世界大经济里的其他国家,你这等于是薅别的国家的羊毛。所以在对待跳贬这件事情上中美是有共同利益的,同时美方希望人民币汇率挺住,不要贬值再破坏脆弱的世界经济秩序。中方的态度会如何,其实已经很明显,年中的峰会上,相关最高领导人已经承诺要稳定汇率,中美财长这一年曾经多次通话,最近的一次就在昨晚,关系远远比很多人想的密切。

因为中国现在是秩序的维护者,受益者,他一年从其中获取了几千亿美金的顺差,他怎么可能会想去破坏这个秩序。同时做生意最重要的就是讲信用,你像三胖一样直接杀羊,下次别人就不和你一起玩了,这个是很简单的道理,中方一直是负责的大国所以在堵上国内的缺口以后,中国一定会关闭外汇贬值的窗口,这个目前进度来看,已经为时不远。同时这会产生一个问题,内外压力不一致,既然无法从外部解决,那一定会把内部的气压放掉,现在筹码已经转移了,释放掉这个压力的大条件基本都成熟


5

说完了其他的话题,我们再来说说钱的事,我不止一次听人说M2要爆表啊,所以要通胀啊等等等,在开始这个话题之前,你要知道钱的本质是什么,否则你只会被人忽悠的满街跑被人卖了还替人数钱。

其实M2并不是真实印出来的钞票数,央行真实印出来的钱在央行公布的资产负债表里,有一栏叫储备货币,差不多29万亿左右,其中23.5万亿外汇占款,5万亿本币,数量是不是少的让你意外?这就是所谓的基础货币。

那么这29万亿是怎么变出149万亿M2的呢?很简单,假设银行准备金是17%,放出去贷款后进入流通领域又变成现金进入银行,这样反复运作,最后一块变五块, 这五块就是货币乘数(以上描述肯定不严谨,为了让你们易懂简单说说,其实准备金率并不直接影响货币乘数,因为贷不贷是下头决定,同时还有各种其他因素)所以你只要明白了这个原理,一切就明白了,过去的十几年,中国的外汇占款飞速增长,这其中有很大一部分是中国干血汗工厂换回来的,08以后,外来资本的流入又带进来了很大一部分,一直到14年达到了顶峰29万亿的外汇占款,所以,因为有这些外来资本的迅速流入,中国的M2迅猛增长,尽管央行的存款准备金率一路从07年的9%,上调到12年的顶峰21%,但是因为输入的外汇占款的猛增,所以可以在市场上投放出天量的输出,加上12以后央行开始降准,所以你们感受中的物价飞涨基本来源于这段时期。而这一切在这两年形势彻底转变,首先是外汇开始流失,这代表什么?

代表每流失一元美金央行就得注销6块多人民币,外汇占款的飞速下降虽然不代表基础货币就一定会暴跌,但是时间长了,你就知道这长期趋势一定会是国内的流动性开始收紧, 这时候央行开始降准,就是为了对冲这部分损失,放出更多的信贷来弥补流动性的不足。所以结论绝对出乎你们的意料,大概是从14后15年开始,央行并不是在印钱制造通胀,而是钱已经快没了, 他在注入流动性来拯救市场。你是不是觉得这两年时间钱越来越难赚了,企业越活越难,这就是市场在通缩的表象,因为大家都没钱了,没人敢花钱。央行想要放水刺激出通胀,但是无果。那么为什么有的人觉得在通胀?这就是这个货币政策的问题所在。欧美长时间救市之后,总结出一个经验,就是长期使用货币政策后,货币政策会出现失效效应。怎么理解?

当你制造出一个长效通胀周期之后,产能严重过剩,这时候市场需求不足,你再注入流动性,他不会流通到你想要的地方去,而是开始在某个领域空转,因为资本是逐利的,这就是前文说的,你降准后,贷不贷,怎么贷是下头的银行决定的,现在做实业的全部亏钱,他放贷必然不会给实业公司,实业也不想借,最后大家一起借钱去赌博空对空。所以,七月的数据出来,新增贷款102%为房贷,这实质上就是货币政策接近失效的反应。那么回到前文中来,现在的大趋势是什么?外来资本继续流失,同时美元进入加息阶段,这个大趋势是不会变的,外汇占款这个月降到了23.5万亿,而且是连续下降,所以我说,在美元的加息周期,是不可能有通胀的,你钱都没了你怎么通胀?

央行的货币政策使用并不是在制造通胀,他的每个释放流动性的举动不过是在弥补市场的损失罢了,而且因为货币政策接近失效,恐怕很长一段时间内他也不敢继续再有大动作,毕竟放水也是有成本的,呆死坏账多了,到时别说兑美金,光是人民币提款就能威胁银行的生存,所以从去年开始就有一句话, 叫堤防系统性金融风险,你想想什么叫系统性金融风险?其实这些报告都是很有价值的,你得学会看懂这些报告。

人类往往是会倒霉在自己的经验上,就像那句老话,参谋们永远在为上一场战争做准备,过去三十年都是通胀,他以后怎么可能不是通胀?但是其实这个世界是在永远变化的。我发这个帖子不是为了证明什么,现实里科幻的情节多了,很多国家的选择也往往是非理性的,但是以后每次你在做选择的时候,你可以多想想这件事背后的原理是什么,总比被人一忽悠就去接盘好多了。这几天没有大事就到这里。


6

如果你看清楚了我以上所有的发言,而且看懂了所有的话,你怎么会不明白我的意思呢?

拿稳你的钱,不要去做任何冲动投资。我们不谈宏观大层面的经济和原理,你看看你的周围,所谓的有钱人和中产, 他们手头还有多少人有现钱?

人人都觉得人民币要变草纸了,人人争着投资, 个个欠了一屁股债,说起来财产全是不动产,以房子计价都是千万富翁亿万富翁,如果真的实现了,那大概世界大同就要来了。再来看看你周围的经济环境,今天这家公司倒闭,明天那家公司欠薪, 经济下行越来越明显,失业人员越来越多,工地停工,公司停摆,连互联网三巨头都不招人了,开始裁员了,你说未来大家是挣到钱的机会多了还是少了?所有人都在用房子对冲风险,杠杆加上去,负债越来越多,最后消灭流动性,这时候手里有钱的人变成了稀缺资源,钱又值钱了,房子不值钱了,最后的路径大抵就是这样的。

这一波财富洗牌会是历史上又一波起起落落的机会,首当其冲的就是手头有大负债的人,简单的说就是富人和中产,因为只有他们有资源加杠杆,特别是底层的小富豪,他们已经习惯了负债经营,习惯了加杠杆,他们的发迹得益于历史大潮,但是他们知其然不知其所以然,这一次会把很多人打回原形,甚至更惨。

人人都觉得今年的房产是财富最后的直通车,其实这是一班地狱直通车。拿好你的钱, 不要想着去股市搏命,理财千万别买,退潮的时候,这些东西跟P2P不会有太大的区别,一百万最好分三个银行去存着, 这时候你就是狗皮膏药,只要你牢牢的粘在历史的大车上,不要被甩下车,下一波大潮来临的时候,你就是最先复苏的人。记得, 下落的过程中会有无数的跳升,这都是陷阱,都是假阳,别去抄底,抄在半山腰上的人不值得同情。什么是真正的复苏? 中国的走势是紧随美国的,当美国开始复苏,开始对外输出美元,对内输入商品来压制美国国内的通胀的时候,这就是真正的机会。

我说了这么多,但是你要明白一点,成年人要有基础的判断力,你的家庭环境是什么,你对资金是否有自主支配的权利,你能承受多大的损失和压力,你的家庭和谐不和谐,这些都是外人不知道的,你要有自己的判断能力,你做出判断,自己承担责任,以后赚了是自己的,亏了也是自己的。

我经常在网上看到有人说, XX老师,给我推荐下股票。其实这就是在推卸自己的责任,成年人只有负担自己的责任才是成年人中国的这一波房产潮,大体上和香港是差不多的,他们从来没有经历过楼灾,不懂得什么叫敬畏,香港人是不幸的又是幸运的,上次香港房产泡沫破裂,给无数人上了一课,后来得益于中国大陆楼市带动,无数眼看着要被坑一辈子的人又解套了,你看看后来香港房产再次飙升,多少本地人去买?都是大陆人在买,因为本地人已经懂得敬畏,而大陆人不知道,他们没上过这一课,所以香港人是不幸的又是幸运的,而以中国的体量,一旦房产套牢了,不会有人给他们解套了,这辈子咬牙还贷吧。


7

这里有的朋友说到印钞,似乎是个很火热的话题,那么我们就加个番外,来谈谈印钞,以上两个预定章节就顺延了。 楼上有无数的朋友说ZF会无限印钞,那么我要先强调下,至少在目前为止,我看到的情况, ZF印钞都是有理论依据的。上次没有理论依据印金圆券的那位现在还在流亡桃花岛呢,所以 这是关系到权利的大事,经济的问题再严重也没权利严重。那么我们就来说说印钞。

第一, 首先是外生货币,这个很好理解,你干出口血汗工厂拿回来外汇,或者别人来投资,带来一美元,你给他兑成六块五人民币,这样,央行就能有了六块五人民币的本钱,然后再通过货币乘数,让下面的商业银行通过放贷把水放到市面上去,这个放贷工具里最好用的就是房地产和上下游相关产业, 所以说房产是过去中国最大的印钞机这点的确没错,但是注意了, 外储是因,房产是果,因果因果,没有因就没有果。中国目前的经济秩序,本质上是朱一手打造的,不管风评如何,但是他的确是一位中堂一样的人物,实业和财政方面的先不提,在金融上,他制定了银行法,规定了现在央行运转的大框架,加入WTO,带来源源不断的美元,这是市面繁荣的基础,而同时,最重要的是他给人民币找到了美元这个最重要的锚点,这是一整个的体系。

人民币在市面上号称小美元,你就可以知道他的特点,最主要的是找到美元这个锚点后,他可以彻底借用美元信用,所以外储是老大哥的根并不仅仅是我开篇说的那么简单,在这个章节你会发现更多深层次的东西,因为很多历史和政治因素,特别是80年代经济上的表现, 人民币有一个锚点,取得信赖是十分重要的(额,自己体会,某些原因我就不展开了),这不仅仅是武力可以提供的, 你只有有信用,别人才会来投资,才会相信你不是三胖一样的东西,你看三胖今天说开放就能开放吗?不可能啊,我要你那些废纸干啥。(当然,也许哪天三胖也聪明了,以人民币为锚弄币改,说不定他还真能吸引中国资本)。

所以这个锚就是一切的基础,大海上船只没有锚就会飘走,这个体系居然可以在朱之后十多年没有大修改的情况下独立运行,可见他的强大之处,而且从这个基础你也可以看出中美在经济上是多么的紧密, 双方实际上是一个利益共同体,这个利益共同点是双方不会爆发冲突的真正基础,什么俄国中东那都是扯淡的路边社说法,但是有一个问题,在美国强势周期,美国需要对外输出美元,对内输入商品压制通胀的情况下,这个体系是完美的, 但是一旦美元需要收缩,你这个就很碍眼了,对双方都形成了杀伤,首先是中方高达几千亿的顺差,简直不能忍,所以你要再继续贬值,美国也不同意,第二是美元回流, 导致人民币的锚开始松动,所以中国前段时间开始储备黄金,实际就是为了稳定本币,要知道,美元之前,甚至包括美元都是金本位(有某些时间黄金不够了,以白银印钞),所以黄金的确可以起到一定的锚的作用,最大问题就是数量太少,少到跟流通货币比起来可以忽略不计,所以这就进入第二个章节,内生货币。

第二,内生货币,这个理论的研究在西方真正的开始成系统,来源于凯恩斯,没错,从这个章节开始,你会看到越来越多熟悉的词汇,实际上,老大哥过去几年的动作直指币改,就是打算开始用内生货币来弥补外生货币的流失,一堆水货经济学家只会跟着瞎参合,写出来的东西没有一篇能看的,弄一大堆故弄玄虚的词汇来忽悠人,连老大哥真正的目的都看不出来,这完全是摆在桌子上的。 我一直觉得经济学就是要让人简单易懂,四个字能说明的不要写5000字,而市面上的文章,一堆数据,经常是七八十个图表都不知道他在谈什么,观点朝令夕改。OK,回到这个话题上来,国内关于这方面的著作,目前比较权威系统的有《中央银行与货币供给》,作者是盛*松成,翟春。盛*松成,现任央行调查统计司长。于是,你可以看到越来越多重要人物开始浮出水面,然后把这些珠子串成一条线,主要脉络就出来了。那么内生货币是怎么产生的?你把上面那本书读明白了就有系统的认识,当然了,我知道大多数人不读书,可能你直接翻也读不懂,那我直接放结论: 金融创新推动货币内生性。还是看不懂?OK,再缩成两个字:股市。

在西方,股市是一个十分重要的工具,就是因为他不仅仅是一个融资场所那么简单,同时他还是货币的源头之一,所以你可以想象为什么我们说现代经济学里凯恩斯主义这么重要,大家看经济里,股市是个重要指标,就是来自于此。

这和中国股市有着本质区别, 中国股市是一堆国有企业烂帐没法解决,所以干脆上市融资,坑股民的钱,所以中国发生股灾那叫赌场失火,重新装修就能开业了,美国发生超大股灾,那就是经济危机,性质和你炸了美联储差不多,所以美股必须讲究公平公正公开,监管的十分严厉就是在此,曾经有香港某些人还以为那跟港股一样,想去内幕交易,然后被FBI叫去喝茶原因就在于此。

那么他的原理是什么呢?这是很难直接描述的,大体上,就比如特斯拉在股市上市了,业绩冲天,但是他现在产能不足,于是要扩大生产,就去找银行信贷,银行觉得你业绩好,没有偿付压力,给出贷款,于是特斯拉拿钱去买厂房造汽车拉动经济, 于是这时候,货币的内生性就产生了,银行对自己的贷款负责,一旦出事,本金出了问题,被挤兑了,那就破产,于是去年国内也出台了银行破产法,破产后你的存款最多只赔50万,你可以看出这是一环跟一环的,所以美股的数据必须真实,一旦虚假,出现大面积的银行倒闭,整个国民体系就会出问题,所以在对数据造假上,监管机构十分严厉。

同时,这还会让银行出现很大不同,那就是中国现在银行十分粗放,简单说就是吃利差,毫无技术含量,因为太复杂的东西,中国银行业人员素质完全跟不上,但是一旦这些体系打造起来,对从业者要求会增高。中国的银行之前必须高存准,因为他的体系十分原始,从业人员素质不高,还有体制内的因素, 所以准备金就必须高。那么回到那个话题上来,具体到股市,有什么变化呢?

注册制。

又一个熟悉的词。这就是要进行货币的内生性改革, 彻底把一个赌场变成印钞厂,所以你可以知道老大哥这两年在股市的动作是什么意思,但是随着去年的那次股灾,短时间来看,要再启动恐怕有难度,所以你可以理解肖*传雄这么黯淡离职的原因,要知道,这个级别很少会这么不体面…嗯,我们就不多说了。所以从这个章节,我们可以看到过去几年中国经济改革的一些主线脉络,嗯,今天就到这里。


8

这几天连续的写,感觉有点疲劳,开篇还是解释下前面的话题,我们所说的货币,也就是那张红纸,那是必须要有载体的, 没有载体,那下场就跟大明宝钞和金圆券一个样,不是你想印就能印,你必须在逻辑上能够自洽,不开玩笑的说,这楼内要是有人能找出一套全新的印钞办法,不同学界以往的路子,诺贝尔经济学奖就是你的了,虽然现在排队领奖的多了,但是你插队绝对没争议。

那么多国家因为最后实在印不出钱了债务压身,只好选择对外扩张,难道他们不知道接着印,印个没完来赖账吗?这原因当然不是因为他们太善良,不够流氓,你想想是什么原因让他们宁愿铤而走险也不走这条路?到底是什么样的东西会比战争更可怕?你仔细琢磨琢磨这句话。

我们前面说的是为什么没法印,印不出了,这个章节要说的是不敢印。这个章节我删减了好几次,保证现在就是洁本了。首先这要从一个现象说起,从08年以后,M2一直猛增到八九十万亿一百万亿那个阶段,反应在市面上的就是物价飞涨,人工飙升,今天你去菜市场买把菜五毛,下个月也许就变成一块了,一个馒头从一两毛升到个把块,市面上什么都在涨,我工人干活的工资也得涨啊,你不涨我活不下去啊,还打什么工,我不如回家种田,什么?你叫我爱干干不干滚,那行,我真滚了,反正市面上的公司工厂多了去了。

你不加钱就真找不到人,因为整个社会的成本在上升,各行各业都在扩张,这时候整个社会需求都是在上升的,这才是最典型的通胀。物价迅猛飞升,工人全部失业那不叫正常通胀,那叫恶性通胀,是社会秩序崩溃的表现,你们是不是南美委内瑞拉看多了觉得这是正常情况?这才是最不正常的。

这种情况你还考虑毛的资产能保值, 买把菜刀防身才是对的,哦,也许那时买菜刀都得用以物易物了。这种极端情况不太可能会出现,因为极端的情况必然是极端的条件引起的,比如外储耗尽,进出口停滞, 市面上的钱就失去了锚点,就会爆发

请注意,这种极端情况是最糟糕的,任何国家都会极力避免的,这也是我开头说的,什么东西是比战争更可怕的东西的原因。我记得那时候有个笑话,就是小时候的梦想是工资两千块,坐办公室,现在终于实现了。

这句话背后反映了两个真实的场景,第一个,通胀的年代,第二,中国曾经经历过通缩。通胀的年代你们的年纪相信是经历过的我就不讲了,通缩的年代是什么样的呢?就是那句话里描述的,好工作难找,有份好工作就是人工最大的理想。

这个时代大概是90年代末,对应的是下岗分流,大家都是朝不保夕,那时候上海房子一平3000送户口没人要,为什么,我得先吃饭啊,我还得养家糊口啊。所有的人都在储备过冬,一拿到钱就收起来,根本不敢花,这就是通缩,什么房子,在生存面前不值一提。

好了,言归正传,回到那个话题上来,那个时段M2猛增,反应到市面上就是通胀,那么,从M2猛增到149万亿的这个阶段,为什么你们没经历过这样的场景呢?我要说你们早在通缩里了,你们肯定会说我胡说八道,这两年房租都在涨,怎么可能是通缩?

但是其实人的思维是有惯性的,房租我们后面会再详细的说,这里先略过,但是只要你注意, 你就会发现这两年除了房租,物价整体是平稳的,除了个别因为供需关系和季节关系引起的价格波动,大宗商品,各类零售产品的价格不止是平稳,甚至可以说是下降的,你关注的只是季节性的供需性的涨价,你以为那是通胀了,其实不是,当他们回落的时候,你并没有注意到。我知道你现在思维的本能想抵抗这句话,不要紧,你一年后再来看,你会发现我是对的。

当然,我们现在有分歧,但是至少你会认同,这两年没有出现当年那么大的涨幅,物价整体趋于平稳。那么是什么造成了这种情况,按理说,M2的增幅一点都不少,50万亿啊,不应该直接在物价上反应出来吗?

这里要先介绍一个人,海曼明斯基,他是一个至少值得一个诺贝尔奖的学者,但是当世界发现他的价值的时候,他已经去世了。诺贝尔经济学奖获得者 保罗克鲁德曼在13年就表示中国已经进入了明斯基时刻,不得不说,他水平高一些,13年就看明白了,我水平低一点,后面才看明白的。

那么什么叫明斯基时刻?

简单的说, 资本的三个阶段,第一阶段是正常的经营,企业可以偿本付息,第二阶段,信贷大为扩张,激进的投资之后,企业发现自己的净利润只能够还利息,本金还不起了,第三阶段,危机阶段,这时候债务快全面爆发了,因为连利息的窟窿都快补不上了,企业必须不断的拆借,夸大自己的资产来延缓债务爆发,这叫旁氏借贷。

也就是说,其实, 大量的社会融资都已经被用来进入旁氏借贷的循环当中,市面上的资金越来越紧张,这时候的宽松,其实钱滚到市面上的并不多,所以当然不可能冲击物价。

这个数是多少?彭博社估计去年15.6万亿里至少一半多进入了这个循环。那么这个循环有没有可能进行下去,当然不可能, 信贷借出去的钱都是得还的,我存本金在你那,你拿出去贷款,最后我上门要提钱,你给不出来,这时候银行就有破产的风险,所以银行放出去的钱不是白放的,一定得保障安全, 至少坏账率要在合理水平,所以再松下去,银行有全面破产的可能,所以接着放,央行绝对不敢,接下去一定是收紧货币。

这就是第一个大问题,债务问题,其实现在既然决定要紧货币,那不如先美联储快一步加息,这也会阻止资本外流,但是这会刺破债务问题, 在中国没人敢承担这个责任,所以最后一定是拖到没法解决了才有动作,等美元加息了再甩锅美帝。

第二个问题叫产能过剩,也许后面会详细说说,这里简单的说一下,中国现在的问题就是做啥都不赚钱,这就是严重的产能过剩, 就是因为产能过剩,所以放出去的信贷只会变成坏账。

你做什么赔什么,最后只能拿到村头赌钱,这可不就是坏账吗。现在和08年的时候最大区别也就是在这里,那时候是社会需求扩张,所以价格每次上涨都是真实的,能支撑产能上涨,但是你现在是需求下降,价格上涨是去产能去出来的,这有本质区别,所以每次价格回升都是假阳, 因为价格上涨,信贷支持,产能恢复,价格立马回落,表现出来的特点是每轮涨价绝对涨不过前期顶部。一到点他就得往下。

但是同时,你注意了,就是因为中国是世界工厂,所以这也是不会发生恶性通胀的原因, 像委内瑞拉,石油一回落,啥都得进口,没钱买他就得破产,但是中国可是世界最大工厂,所有种类一应具起,你涨价?太好了,哥们上,马上就给你做的没利润了。中国没法做的东西不多,CPU那玩意日子紧一紧就完了,又不是必需品。

所以只要人民币保持稳定,外储没见底,世界工厂绝对不会发生恶性通胀。我相信这道理随便一说很多人都懂。所以以后的日子就是回到紧缩的日子,去产能去债务,考虑到现在庞大的债务和产能,这会是一个长期过程。

其实回头想一想,一 切的根源在当初就已经决定了,当初人民币应该一口气涨到5,这样就不会有太多输入性膨胀,太贪,结果输入多了,大举举债,产能扩张,到最后做出来的卖不出去,市场通缩,陷入旁氏借贷,最后产能债务一起压顶,其实一切的根源,如果当初重来走另一条路会完全不一样。

最后,我跟你们说个段子,你们不要对银行的债务太悲观:最近两年P2P理财线上金融飞速发展,反正我也不知道为啥,满大街突然就出现了这东西,而且规模大的好像也没人抓,还能上央视打广告。

最后,你惦记别人的利息,别人惦记你的本金,钱飞了,本金利息全没了,P2P跑路了。那这里钱干啥去了呢?一部分当然是骗钱的拿去挥霍了, 但是这些骗子其实当初还是有远大理想的,想要快速做强做大,反正要通胀嘛,18%利息算毛,以后都不是钱,反正这钱是到处流,最后都当了接盘侠,套住了,没法变现,跑路吧。那你仔细一想想,他接的是谁的盘呢?反正世事就是这么巧合,当然我们不能说这是设计好的,反正这种事情就是各取所需吧,愿赌服输。


9

我跟你们说, 大多数学者都是没发迹的时候是水平最高的时候,像郎咸平现在写的东西还能看吗?郭德纲天桥说书的时候绝对是他相声生涯的巅峰,现在就只能卖于谦屁股了, 再过十年,爬上去以后我肯定再也写不出这样的东西了,现在想想,还是挺伤感的。今天就到这了。 这世道,钱离了手就回不来了。

中国的理财产品你得等他跑路了你才知道他拿去干啥了,招标书上根本没用。这些东西说白了就是风险太大,银行不愿意接,挂卖的。更别提其他风险更大的东西这类玩意你在经济上行期违约可能较小,但是一旦到了退潮期,那就千万别碰。其实就我个人来说,以后那个时期屌丝日子还是可以的,反正你本来也没啥可以失去的,只要别有负债日子不会和以前差太多,说不定还会更舒服, 这波苦的就是乱上杠杆的。要是净资产高,那就更好了。

其实人的一辈子碰上这种拐点的机会不会多,也就两三次,抓住一次机会这辈子就转折了,但是切忌不要贪,都是平头百姓别想一口吃成马云,你能上去一个阶层就不错了。外汇占款每下降一千亿,乘以乘数就是五千亿的缺口,现在不敢降准了,只能靠mlf逆回购补充流动性,聊胜于无,恰恰说明央行不敢动了。 央行手里还有武器,但是那是留给加息用的,不是拿给赌徒去炒房的黄金的价格周期由美元决定,黄金强美元弱,美元弱黄金强,一旦美联储开始加息, 美元走强,你想想会是什么结果。

如果做短期,无非就是赌不加息来获利,这是赌博行为,我不提供意见关于美联储加息的问题,首先你要先明确一点, 这一次是美元从0利率回归正常利率,而不是从正常利率走高,来压制国内过热,耶伦其实早就告诉你了,美联储为什么要加息, 就是因为如果美联储不加息,未来一旦再发生危机,那美元就裸奔了。

也就是说,现在加息是为了以后有空间降息,否则美联储没有武器抵御下一轮危机。这和现在的世界大经济形势是一样的, 大家都没有走出泥潭,所以才要继续准备过冬。

所以虽然美元处于加息阶段, 但是这一次的美元相比于历史处于弱美元阶段,而不是真正的强势美元,所以即使加息,美元指数应该也不会太高,同时, 正是由于经济没有恢复,所以加息的机会转瞬即逝,所以这一波美元的加息速度一定会让你瞠目结舌。所以我的判断和格林斯潘一样, 美联储接下来一定会迅猛加息,毕竟现在夜长梦多,现在这世道谁也不知道会出啥意外。

东北经济问题最先爆发,最先出问题,同时群众底子薄弱,给点工程大家都有饭吃, 有饭吃就不会有大问题其他东部地区人民还挺富裕的, 存款坚持一段没问题,这是危机的应对路子。天要下雨娘要嫁人,老大哥也有走麦城的时候。

这个事例正好告诉你, 国家不是万能的我的判断是从世界上看,2019年这个周期结束,中国国内的情况看去产能和去债务进度,这种东西只能走一步看一步,三年时间太长,在这种转换周期什么都可能发生。 能活下来的都是好企业。不要增加太多应收账款,保持流动性,宁愿少利润也要尽量缩短帐期没人说得清楚, 因为货币政策一定是保密的,否则被人知道了时间套利了,这责任谁也负不起,美联储这次是不是要加息,这个答案值1000亿美金。所以再如何,也只能估摸个大方向,美联储要的就是别人看不懂。我的预判是今年两次(没有任何数据和理论支持,性质跟买双色球差不多)


10

这几天,有很多人问我房子的问题,有的我没有回答,因为我不知道该怎么回答你们。这里,有很多人是刚需,你们的苦恼我很理解,你说,让你下定决心卖房去睡马路,这个肯定不现实。不过既然是自住,一两套的也没必要折腾, 只要你不是作死炒楼的,贷款一般也不过几十万,熬一熬,几年就过去了,下一个周期来临了以后日子就会轻松一点。

所以过一段时间,如果我说的这些东西应验了,你也不必灰心,曙光就在前面,当你绝望的时候你想一想就有盼头了。

其实在我看来,这个周期的前奏已经过了, 现在已经进入中期,是危机全面爆发的阶段,在过个两三年就进入尾声了。很多时候普通人就是这样的,不知不觉的,当你发现事情发生的时候,其实已经快结尾了,但是你还以为才刚开始。

如果你不知道这是为什么的话,你只会感受到,物价渐渐开始平稳了,市面上的生意开始不好做了,越来大家越没钱了,都在借钱,然后资产泡沫全破了, 你看玉石文玩古董红木这些泡沫这两年有没炸的吗就剩一二线房产了,等那个炸了你就信了。然后企业发不出钱了,慢慢大家都开始裁员减薪下岗了,最后,所有人慢慢开始达成共识,新周期来了。

错了,这时候这个周期已经快走进底部了,马上再过段时间又是新周期了,这时候满地的资产,但是大家只要钱。所以普通人的人生就是这样浑浑噩噩的过了,几个潮起潮落,一辈子就过去了。 所以你们其实是幸运的,这个世界上,正常人都会经历几个大转折周期,但是很少有人会完整的体验他的整个过程,往往是等他们发现的时候就已经结束了,所以你要好好的看,你看明白了整个过程,你的人生以后就不会吃大亏。

而且大多数人看问题只会看表象,看不到事物的核心。什么叫事物的核心?就举个例子:

前几天还是个把月前我在步行街上看到一个报道,有人雇人买了一堆蛇去放生,最后那蛇跑的整个村子都是,太缺德了,而且怎么会有人这么傻,大体上评论都是这样的。

但是问题是这事不符合逻辑,中国人傻吗?当然不傻,中国人最大的问题就是太精明。所以买蛇放生,特别是一大堆蛇,我不信。我把这事修改了下,你们看看这样是不是更符合逻辑:

这个村子在山里,附近有个养蛇场,现在销路不太好,继续饲养,那是赔钱的,而且不知道还要赔多少,你杀掉嘛,自己吃数量太多,储存啊,人工又是一笔开支,所以场主及时止损,找两人给你放生了,你看看这事是不是符合逻辑了?而且这事背后又可以找出更多东西。

蛇的用途主要有肉,对应高端餐饮,有皮革,对应皮具加工厂,次要用途还有医药等,所以这事的背后,是高端餐饮和皮具这两个产业链不行了。

过去一年,中国各种稀奇古怪的放生事件频发,本质上反应的是中国的养殖业正在走入萧条,而且下游产业正在破灭。

所以最后记者写啥你们信啥,他说通胀你们说对,今天肉涨价了,但是跌价的时候他不说,你看年初我买猪肉一斤20一堆人吵翻天,现在一斤都快跌破10块有人关注吗?最后忽悠得你们都以为还在通胀。国家不能告诉你们正在通缩,要不没人消费就麻烦了,但是你们自己得去找这些道理。所以这就是表象和核心

那么房地产的核心是什么呢?有人说是户口,是房子附带的资源,是人口的涌入, 其实这都是表象。房地产的核心就一个字,钱。

房产问题就是资金问题,房产现象就是资金现象。有钱进来他就涨,没钱进来他就跌,其实这道理很多人也慢慢总结出来了,但是他们分析不出钱以后会多还是少。所以外汇飞速增长,央行释放流动性全进房地产的时期,他绝对不可能跌,他还得涨,你再调控有个鬼用,下面火这么旺你不把他扑灭了你指望上头凉?

反过来, 外汇占款飞速下降,央行全面紧缩你指望他能涨,他能拉的住价格,这可能吗?现在一个月信贷就得几千亿,加上居民存款消耗,你怎么一个月也得一万亿,一年十几万亿才能拉住房产,你以后有这么多钱吗? 你弄两百个地王出来有啥用,最后还是钱说话,所以这事很简单,你们盯死钱就行,外汇占款继续下降,央行继续紧缩,他就涨不起来,而且从四五月开始央行全面收缩,按照时间的传导规律,年底前房地产就得接受考验。所以今天就这样了,以上段落搭配张雨生的《大海》一起听效果更佳。


11

1、发改委:进一步引导利率下行空间依然很大 政策仍须进一步着力像这种新闻你们要学会看,各部门的利益是不一样的,FGW的意思是赶紧放,不放下面的都快死了

2、央行副行长易*纲:现在流动性充裕 可以支持各个市场正文:14天期逆回购操作给了市场更多的选择,会继续保持市场流动性的充裕。现在银行间市场的流动性充裕,完全可以支持各个市场。刚才楼上有人说这个新闻,我看了下,新闻其实是这样的。恰恰相反,说的是现在流动性充裕,我们不用放了。逆回购从7天变成14天,其实是提高了资金的使用成本,这是收紧的标志。所以你们要是看不懂这样的新闻就会被忽悠。

另外不管是MLF逆回购这些,都是短期货币工具,不能带来派生货币,目的是保持央行手下各个正规军商业银行的流动性,不是在放水,而且到期还得置换。 这种工具用的越多,越说明央行现在不敢放水,所以必须用短期工具来保证正规军的流量不枯竭。你们别被媒体忽悠了, 现在是金融绑架媒体倒逼央行放水,什么三天两头开发商跳楼,开发商死了多少那全是在造势。

从耶伦的发言看,加息的脚步是越来越近,中美这要没一腿我都不信了,大半年的中国刚好用楼市套死流动性,美联储就要开始动作了,世界上哪有这么多巧合的事情。

现在就是退潮了, 谁能带着自己的钱撤退,谁就是上轮经济周期的大赢家,然后负债的那部分人,就是这个周期的牺牲品,他们来负责买单。每个周期都会有牺牲品,还记得当年的下岗吗,就是那样的。所以现在的主题就是看好你自己的钱投资的楼盘都是要有资金成本的,时间长了负担不起,没有资金滚进来,这个旁氏骗局就继续不下去。所以你要是自住的比较多的城市我认为反而抛压不大,但是一二线这些热炒的不行,就是因为我知道这些城市已经全是热钱滚动的重灾区,所以我才会说到时候这些城市的抛压会非常大,而且杠杆加的多,那时候肯定刷新你的三观。

是的,以后这个国家要回到老实工作,劳动创造财富的年代了,无贷款很好,你以后会是受益者。

现在市面上的繁荣都是欠钱欠出来的,08年以后,债务大举扩张,创造出了太多不属于市面上的消费能力,我在一个三线县城看到的路虎都比美国一个月看到的多,这就像透支信用卡,其实中国人目前的赚钱能力是支持不了这种消费的。 现在就是债务无法持续,一切到了重新清算的地步了,以后该你还信用卡了。这些天量的债务,一部分ZF打包,低息置换,烂账以后慢慢还,一部分就是转嫁到了其他人手里,就是房奴,借贷的这部分人。现在还保有大量现金的人,等于收到了这个周期的虚假繁荣的好处,又没有负担成本,因为全让别人承担了,所以他们是幸运儿。

不会有奇迹的,我就是看明白了才敢这么说,昨天明晰的信号都出来了,从现在开始,房地产的周期进入倒计时了。今天外面的楼托已经疯了,他们知道我说的是对的,其实炒房的人远远比你们想象的聪明, 但是在大时代面前,这点聪明是不够用的。慢慢看,接下来这几年会是人类历史奇迹。现在已经不是资金流入楼市了,你没看到一二线城市关门的信号都出来了,市面上没有这么多资金了,也差不多了,该收网了。


12

因为现在是美元加息周期, 全世界的美元要回到美国那里去,所以流动性谁都缺,就跟涨潮落潮一样,然后美联储顺势完成缩表。

你回复里也说了,资本是要赚钱的,现在的问题就是中国现在干啥都不赚钱,所以资本要离开中国,一旦美联储开始加息,利差缩小,中国肯定也得跟着加息留住资本,这会刺破债务泡沫,现在其实本来就该加息来留住资本了,但是央行恨不得负利率,就是因为债务太庞大了,所以仔细分析,你就会发现要保住外汇这是必然选择。

人民币国际化是要配套很多改革的,首先是要自由浮动,不再由政府操控,第二是财富要有载体,你现在地皮是国家的,矿是国家的,外国人拿人民币什么都买不了,总不能拿着来一二线炒楼吧,你会想去莫斯科炒楼吗?所以这会是一个长期改革的过程。不改革这个国家就看不到前途。

关门的标志就是限制信贷,提高首付,这是最简单的,现在一套房五六百万上千万,不加杠杆谁买得起?只要把首付一抬起来,就没人买得起,这是最狠的,直接关门。

个人借贷对于银行,现在肯定属于优质资产,你别看有的人说房价跌了就甩楼给银行, 真的跌了敢不还钱试试?让失信者寸步难行,你以为这句话是说谁的?中国没有个人破产,你要是贷了200万,最后断供了,拍楼卖了100万,剩下的100万还得接着还。

相比起来,企业大多都是有限责任,直接破产你还真没什么办法,更别提有的不要脸的特权单位还敢提 债转股

银行对这些企业没什么办法,对个人花样多了,催收是干啥的。国情就是这样的。

现在国内的基础货币一直流失,带来的货币乘数扩张也已经达到了极限, 未来央行不仅不可能像过去那样宽松,还会收紧货币政策,从而刺破房地产这样的泡沫。

这个楼里的人的状态其实我看的很清楚,对于刚需,我同情你,但是有的人欠了一屁股债,然后又天真的以为钞票会无限的印下去,最后印的纸币作废,从而自己也可以赖账,对于此,不要多想了,下半辈子老实还债去吧。

房价一旦下跌,房子就会变成风险资产,银行就会开始惜贷,现在房价前几的城市不用杠杆根本撬不动。房贷都是长期贷款,现在就是水龙头快干了,银行已经放不出那么多水。 现在信贷扩张已经到头了,我估计以后有贷款额度也会先给一手房解套吧,你想想要是二手房收紧信贷会是什么情况,按现在的房价,就算是首付六七成,谁能一口气拍出几百万现金?不开玩笑,这跟全款区别不大,买得起的就买得起,剩下只付得起首付的也接不了盘。其实还是有一些迹象可以看得出来的,比如前不久风传的房贷利息抵个税,现在最老实纳税的就是体制内了,还有一些规范的外企,这样如果你真是刚需,这部分也能挽回一点损失。

其实这应该算是给体制内的一些隐形福利,总不能到时候真让体制内都没钱干活吧?所以我说,你们别被有的人忽悠了。

1、我前面已经说了,中国是世界最大工厂,俄罗斯那是卖石油的,就是我提到的跟委内瑞拉差不多的货色,拿俄罗斯来比,简直似是而非,中国现在面临的危机就两个, 债务压顶,产能严重过剩,俄罗斯有产能过剩吗? 俄罗斯有这么严重的债务问题? 中国一旦加息,债务危机就得炸

2、基础货币投放,你们只要看外汇占款就行,外汇储备那是随汇率波动的,不减少不代表基础货币不减少。 而且随着美联储一旦开始加速加息,外汇会面临巨大的考验。

3、 现在的信贷扩张就是已经到极限,再放下去,银行有破产的风险,我这些前面已经说的很清楚,以后人家来提款,你取不出钱怎么办?而且现在放水有啥用?一放出来全部拿去楼市赌博,以后美联储加息后,这些钱还会去冲击汇市,国内不赚钱了,那行,资本就出国。国家最怕的就是这个,想指望央行放水来给你们解套?做梦。

4、 中国楼市现在是银河系第一大泡沫,这个国情其他国家有?一旦下落,那就是无穷无尽的抛盘,几年都甩不完5、中国不加息?别做梦了, 一旦美联储开始加息,国内外利差迅速减少,人家资本是自由的,你想不让人出国就得给好处。这轮加息周期美联储的目标预计是3%,你想想要达到这个目标,中国的利率要加到多少才能保持利差。


13

我打个比方,你本来的基础货币是100万,货币乘数5.1,外汇占款大幅度下降,这时候,你是不是要降准来补充市面上的流动性?

但是,中国现在面临着一个问题,就是产能严重过剩,所以本来假设你要降准补充的是60个行业,其他59个行业全部亏损,这时候必然导致一个情况,降准的钱全部跑到一个行业去。

所以最后就是造成了一个行业的泡沫严重,其他行业加剧通缩的情况。这也是我前面说的,产能过剩,大家都不赚钱,钱来了就全跑到村头赌钱。这就是货币政策已经失效的表现。

所以我特地提到两大问题,债务危机,产能过剩危机,就是这样的。现在不是央行想不想放的问题, 他敢接着放,出来的全会转成不良贷款,债务会越滚越大,银行会破产的,中国股市和楼市差不多,要赚钱就是两早,买的早和卖的早, 筹码没兑现之前就是纸上富贵。你说的部分内容如果在时间充裕的情况下是可以考虑的,但是问题就是没时间了,你看看美联储等了多久了?前不久刚有人发言说中国市场已经没问题了,你想想这句话含义是什么。耶伦的发言意思很明显了,数据到了就加,而且一定是狂风暴雨的加,房产会消灭消费能力,一人买房,全家出钱,每一套刚需盘都代表一个家庭未来十几年的购买力全被消灭。 一业兴而百业衰就是这样的。

当初这条路从一开始就是不归路。最后再强调一遍,事物的客观规律不是以ZF的意志为转移的 ,很多人老是意淫因为XXX很严重,所以ZF一定不会让他发生。你想多了,从50后开始,违背客观规律的是什么后果应验过不止一次了。我通篇讲的就是现在已经到了那个时刻了, 印不出,不敢印,没法印。

楼市泡沫被刺破只不过是这个规律下必然的结果罢了,不要在意淫因为XX很严重,所以ZF不会让他发生。 现在已经是XX正在发生,ZF要怎么补救的问题。

数据一致性检测的应用场景与最佳实践-云栖社区-阿里云

$
0
0

随着业务规模的扩张,企业系统变得越来越复杂,在这种复杂的分布式系统架构下,难免会出现远程调用失败,消息发送失败,并发 bug 等等问题,这些问题最终会导致系统间的数据不一致,导致用户体验受损,用户利益受损,对平台来说就是产生资损。因此如何持续保障系统的业务稳定性对于企业来说是一个很重要的课题,本文旨在介绍一些常见业务应用场景下的业务数据一致性保障最佳实践。

离线or在线,事前or事后

应对业务数据不一致问题的常规操作是,配置定时任务,在每个固定时间点去拉取历史一段时间的数据出来进行比对,判断是否有数据故障出现,比如利用hadoop做一些批处理MapReduce作业,这种离线计算的方式时效性比较差,对于电商系统或者对于实时性要求较高的系统来说,问题发现的越晚损失也就越大,所以我们需要一种在线的校验模式来实时发现数据不一致问题。

在线的校验模式指的是每出现一笔数据就进行一次比对,这种比对方式还可以分为事前和事后比对。

  • 事前比对是一种业务强耦合的校验方式,我们在业务系统代码中进行类似 AOP 的操作,横插一段校验代码,如果校验发现问题,则阻断这次业务操作,这种模式虽然时效性很高,能够保证每一笔数据的正确性,但是因为和业务耦合的太重,很容易出现一些灾难性的问题,比如校验代码的性能差或者异常处理不正确,会直接导致业务操作受阻,影响正常业务活动。
  • 事后校验严格上来说不能算是实时校验,因为校验的时间点滞后于真实的业务动作发生时间点,这算是一种准实时校验,这种校验的好处在于,可以和业务解耦,不阻断业务的正常进行,还能较为"实时"的发现数据不一致问题,并且在一些特殊场景下(比如异步业务,下面会介绍)只能使用事后校验,缺点也很明显,就是时效性相比于事前校验来说会比较差。

这里在啰嗦一句,可能读到这里,有些人会问,既然是业务动作发生之后再进行校验,它的意义还有多大呢?的确相比于事前校验来说,他并不能保证每一笔数据都正确,但是在实际操作中,像电商这种场景下,我们进行业务功能迭代,会经过日常环境 -> 预发环境 -> Beta测试 -> 线上环境的流程,尤其是在预发环境和 Beta 测试的情况下,一般会进行一些线上引流或者模拟数据测试,特点是量小,即使发生问题也只是局部不会引起灾难,那在这种场景下,事后校验的意义就显得很大,可以提前验证功能和数据的正确性,又不会对线上造成强耦合的影响;在功能完全上线后,事后校验的作用在于及时发现数据不一致问题,避免问题的进一步扩散。

综上所述,对于业务数据校验时效性不是那么高的场景下,离线校验是一种比较合适的方式,开发接入成本都较低,对于业务数据校验时效性有一些要求的场景下,事后校验是一种比较适合的方式,对于业务校验时效性要求非常严格,并且能够投入较多资源的情况下,事前校验比较适合。

数据一致性检测实践案例

案例一:会员系统

某店铺会员入会业务,需要结合店铺系统、打标系统、会员系统进行入会退会操作,如下图所示:

lADPDgQ9rMQbY_fNARPNAgU_517_275_jpg_620x10000q90g

在这个业务场景中,买家在店铺会员页发起入会申请,入会成功对外发送会员入会metaq消息,下游业务系统根据这个metaq消息,为该用户打上一个标签,用户在下单的时候就根据这个标签判断是否有优先购买的权利。既然有入会就有退会,退会同样发起metaq消息给用户进行去标操作。所以不管入会还是退会,业务上要求店铺系统的会员状态(入会还是退会)必须和用户系统的标签状态一致(有或者没有),一旦发现数据不一致,一个已经退会的用户如果还有用户会员标签,该用户就可以购买这个限购商品,这样就会造成商家资损。因此必须有对账业务对数据一致性进行强保证,一旦发现数据不一致,必须要通知相关人员进行数据核对,如有问题则进行数据订正。

这个案例在对账系统的选择上有如下几个要求:

  1. 实时:必须当天尽快处理。
  2. 可以报警
  3. 必须支持不同领域模型。
  4. 接口调用需要有一定的延迟,以便下游系统处理完所有流程之后再校验。
  5. 由于入会、退会metaq可能会有丢失或者乱序的情况,因此不可以根据该消息进行对账。

在这个业务场景下,我们可以看到,业务是异步的,会员系统发起入会操作后,并不是立刻就能在用户系统打标的,所以实时的事前校验并不适合这个场景,因为在会员系统发起入会操作的时候在用户系统中还查不到这个打标状态,需要延迟一段时间去查,所以只能用事后校验来做。

我们在这个场景的做法是:拉取店铺会员数据库的实时binlog日志数据,给到校验系统,校验系统解析日志数据拿到要打标的会员id,并且延时一段时间后去会员系统查询这个会员的入会状态,和日志中的状态进行一致性比对,发现不一致则进行告警。

案例二:新老库迁移

当新老系统需要进行更替的时候,经常会涉及到数据迁移,由于数据量非常大,而且不允许停机,所以迁移一定是一个循序渐进的过程,整个过程会分成两个部分,第一个部分是双写,保证新增数据两边同步。第二步是开始做存量数据迁移,通过后台任务慢慢跑。在这个过程中可能会出现部分字段没有同步,更新数据顺序错乱导致数据内容不一致的问题,所以需要对迁移进行数据的一致性检查,及时发现数据问题进行订正或者bug修复。

由于我们的目的是将数据迁移到新系统,所以数据校验触发条件就是新系统有数据写入,这里可能有人会问如果老系统同步失败呢,那么新系统就不会有数据写入,就触发不了校验。这里就存在校验边界的问题,即我们假设同步系统是一定会同步成功的,如果同步失败的话不允许跳过会一直尝试重试同步,所以这里如果发生同步失败,同步会暂停并且打印出同步错误日志,这个就不是校验系统的问题了,我们会通过同步的进度或者同步日志来观察到这个现象。

所以我们在这个场景的做法是:接收新库的数据库变更binlog日志数据,解析日志内容,通过这条数据id去查询旧库的对应数据,进行数据内容的比对。由于双写的存在,一条数据可能会变更多次,这里就要求我们的校验必须是较为实时的进行,否则就会出现拿到的日志数据内容是旧的(这条数据又发生了更新),导致查询老库的数据出现不一致的问题,其实算是一种误报。

lADPDgQ9rMQbY_rNAZvNAcU_453_411_jpg_620x10000q90g

推荐工具&产品

  • AHAS —— 阿里云应用高可用服务,提供企业级的流量控制、故障评测等高可用能力
  • APDS —— 阿里云应用发现服务,支持自动资产盘点,帮助企业实现快速上云搬站

作者信息:高超,花名龙多,高可用架构技术专家,集团业务检验平台负责人,参与了4次双十一大促系统保障工作,多年业务数据对账和资损防控相关经验。

别让自己“墙”了自己

$
0
0

这一两周与几个朋友聊天,有年轻的90后,也有大叔级的70后,这些人在我看来都是很有能力的人,但是一些喜好过于强烈,让我不经意地回顾了我工作20年来身边的人,有发展得好的,也有发展的不好的,有些人是很可惜的,因为限制他们的不是其它人,也不是环境,而是自己,所以,很想写下这篇文章。(注:这篇文章可能会是一篇说教的文章,所以,可能会让你看着犯困,所以,我会尽量地短一些,而且尽可能多讲故事,少道理,这里的故事,全是真实发生的)

几个故事

2019年年初,我面试了一个很年轻的小伙子(93/94年出生),这个小伙子特别有灵性,也很聪明,计算机专业出生,也很喜欢技术,基础和学习能力也很好。在我这20年来认识的人中,如果他能呆在北京、上海、深圳这样的城市,我保证不出三年,他会成为他们同龄人中非常出色的技术人员,如果有个好的舞台有一个好的团队带他,他的未来会非常成功。然而,这个小伙子有两大喜好:1)只愿呆在一个毫无IT的环境的三/四线城市,2)对技术有非常大的偏好,只喜欢Go语言,非常不喜欢其它的语言,比如:Java(离开Java的世界,基本上离开了做架构的世界)。

他的这两个喜好,足以让一个未来会很优秀的人毁掉,因为,这个时代没有限制他,他的能力也没有限制他,但是他的意识完完全全地限制了他。

  • 他把自己最宝贵的青春放在了很烂的项目上,就算能用一些新的技术,他也只能算是自娱自乐,在实验室中玩玩具罢了。
  • 他把自己的技术栈封闭起来,而直接放弃了这个时代最具工业化的技术Java,对于一个好的程序员来说,同时掌握几门语言和技术完全是没什么问题,但是自己封闭了自己的视野。

实在是非常可惜,我本来是可以为他介绍到一些很不错的公司的,但是他这样的习性,等于自己把自己未来的门给关上了,虽然我跟他长谈过,但是我也没有办法叫醒不想醒的人……

  • 视野、环境和舞台,对一个人的限制是非常大的。井蛙不知道大海,被空限维度所限制;夏虫不知道冬天,是被时间维度所限制;圈奍的动物没有斗志,是被自己意识所限制。
  • 偏见和不开放,对一个人的限制是真正有毁灭性的。主动让自己成为一个瞎子和聋子,主动把自己的能力阉割掉,这是一件令人痛心的事。想想大清的闭关锁国是如何让世界第一的北洋水师给毁掉的……

我还有个同学,他的技术并不差,就算呆在昆明这种很落后的地方,他也非常地好学,学习英文,学习各种新技术,对技术没有任何的偏好,喜欢C/C++/Java/Python/Shell,同样喜欢前端Javascript,对基础知识非常地踏实,他在技术上没有限制自己的潜力,有什么就学什么。后来,我带他玩Docker/Go/K8S……分布式架构,他也上手的很快……像他这样的人,技术能力完全没得说,比我还大一岁,44岁了,还是一样的天天追代码细节,看Youtube的各种大会,翻github里的各种issue和pull request……

我同学这人,拥有了成为一个技术牛人几乎的条件:基础知识过硬,细节扎得深,面很广,学习能力强,有英文能力,逻辑思维能力不错,非常的自律,执行力也很强,抓得住重点……然而,只有一个小问题,就是没有到大公司历练过,我三番五次叫他从昆明出来,但是最终他都呆在昆明这个城市没有出来,因为有所谓的家庭约束。然而,我身边还有好些人,把自己家从北京搬到上海,从上海搬到深圳,从厦门搬到深圳……这样的人大有人在……像他这样的能力,在哪个公司都会是主力和骨干,对于一个公司的主力和骨干来说,家庭上的这些问题都是小问题都是有很多解的……

另外,我这个同学还是一个比较悲观的人,任何事情都是先想到不好的事,他关注负面的东西会胜于正面的东西,而且他还有一定的社交恐惧,怕与人相处和交流,时间越长越害怕,甚至有时候直接跟我说,“我就是不想改变”这样的话……其实,我以前也是一个很害怕与人交流的人,面试的时候,我根本不敢正眼看面试官一眼,也不知道与人怎么交流。但是,我与他不一样,我努力克服,不断地面试,与人面对面的交流,到一线技术客服接用户的电话,在公司里做分享,慢慢地到外面分享……3-5年就完全克服掉了。

其实,很多事情,完全是有解的,也没有必要担心,自己的心理障碍也是可以克服的,重点就是自己愿不愿意,只要愿意完成了一半,接下来就是不断的摸爬滚打坚持了。

  • 不限制自己的人,会穷举各种方法来解决问题,限制自己的人,只会找各式各样的问题或借口。
  • 不限制自己的人,会努力改变自己的问题和缺陷,限制自己的人,会放任自己。

另外几个故事

我还有另外几个故事(活到四十多,能看到好多人十几年的发展过程,感觉有点上帝视角了)

我还有一个以前团队里的一个小伙,人是很聪明,但就完全就是野路子,他对技术没有什么偏好,一个PHP程序员,做那个Discuz!论坛,公司被并购了,转成Java,开始研究Java的各种细节,对技术从来没有什么偏见,有什么就玩什么,每做一个项目,就算是一样的他都要用新的技术做一遍,然后跟着我做云计算,我教他TCP,教他C/C++,后来一起玩Docker/Go,等等,反正是一点就通,他是我见过学习能力最强的人。但是,有一个事他一直与我的想法不一样,就是我希望他先把软件设计好,再写代码,他非常不能理解,他习惯于直接动手开干,然后有什么问题就整什么问题,我也很难教育他。

有一天,他电话面了一下Facebook,电话面了15分钟后对方就放弃了,他受到了严重的打击。然后,他就开始找菲利宾人练英文口语了,我也让他做算法题,然后,他才发现,一道连算法都不是的纯编程题都提交几次都过不了,等他做完了Leetcode最初的那151道题后,整个人都改变了,写代码前认认真真地在纸上把程序的状态,处理时序以及可能遇到的一些条件先罗列出来,然后,进行逻辑设计后,再写,从此,他就开启他更大的天地了。我后来把他推荐给了微软,先在中国的Bing,在中国升好2-3级,然后去了美国的Azure,现在听说他准备要跟 k8s 的 co-founder Brendan Burns混了(虽然,他现在还在印度人手下,但是,我真的不知道他未来能玩多大,因为今年他才33岁,而且非常聪明)

他以前是把自己封闭起来的,我叫他出来,他也不出来,后来因为一些办公室政治的原因不得不来找我,于是我就带着他玩了两年,跟他讲了很多外面的世界是怎么玩的,他这个人也是一个相当不善于社交的人,但是心是开放的,愿意接受新的东西,虽然对技术也有一定偏见,比如不喜欢Windows,但是也不会不喜欢到完全封闭。后来我跟他说,微软的技术相当的强的,你看到的技术只是表面,深层次的东西都是相通的,直到他到了微软后发现各种牛逼的东西,对微软系统的技术的态度也有了改变,而且我让他跟我说很多微软那边的事,我发现,他对技术了解的维度已经是越来越高级的了……

还是我以前团队的一个小伙,他是一个前端,他说前端的东西没什么意思,想来找我做后端,我也一点点带他……后来,我说,你如果想要玩得好,你必需来北京,无论现在你觉得过得有多好,你都要放弃掉,然后,尽最大可能出去经历一下世界最顶尖的公司,我甚至跟他说,如果他女朋友不跟来的话,就先分开一段时间,先自己立业,他来北京的时候,他之前的同事都等着看他的笑话,我说,那些人连想都不敢想,不必管他们。于是,他去了Amazon,再过了一年去了西雅图,我跟他说,接下来就是去AWS,然后,如果有足够的野心,用自己的年轻这个资本去硅谷创业公司赌一把……未来他怎么样我不知道,但至少他没有限制自己,他的未来不会有封顶……

也是我的同学,我跟他在大学是上下铺,后来他去了人民大学读计算机博士,大学的时候做国产数据库kingbase,然后去了一家外企,天天被派到用户那边做数据分析,后来,他想回科研单位做国产数据库,我说,别啊,你的技术比我好太多,还有博士理论加持,你不去国外顶尖公司玩玩,你不知道自己有多强的,于是他跟公司申请去了国外做核心,后来因为Hadoop的原因,公司的产品最终成为了历史,于是我说,你来了美国么,你一定要去AWS,于是他就去了AWS的Aurora团队,成为了AWS明星级产品的中坚力量,天天在改MySQL的核心源码,干了两年,被提升为Principle Software Engineer ……

这里我到不是说出国有多牛,也许你只关注能挣多少钱,但是我想说,他们之所以能有这样的际遇,除了他们本来就有实力,还更因为他们从来不给自己设制什么限制,就是那种“艺多不压身”,有什么就学什么,有更高的就去向更高的迈进,其它的像家庭什么的问题其实都是会有解的,真的不必担心太多……

 别限制了自己

上面的这些故事,也许你能看得懂,也许你看得不一定能懂,这里,让我来做个总结吧

  • 做有价值的事。这个世界对计算机人才的要求是供不应求的,所以,不要让自己为自己找各式各样的借口,让自己活在“玩玩具”、“搬砖”和“使蛮力加班”的境地。其实,我发现这世界上有能力的人并不少,但是有品味的人的确很少。 所谓的有价值,就是,别人愿付高价的,高技术门槛的,有创造力的,有颠覆性的……
  • 扩大自己的眼界,开放自己的内心。人要变得开放,千万不要做一个狭隘的民族主义者,做一个开放的人,把目光放在全人类这个维度,不断地把自己融入到世界上,而不是把自己封闭起来,这里, 你的英文语言能力对你能不能融入世界是起决定性的作用。开放自己的心态,正视自己的缺点,你才可能往前迈进。 你的视野决定了你的知不知道要去哪,你的开放决定了你想不想去
  • 站在更高的维度。面的维度会超过点的维点,空间的维度会超过面的维度,在更高维度上思考和学习,你会获得更多。 整天在焦虑那些低维度的事(比如自己的薪水、工作的地点、稳不稳定、有没有户口……),只会让你变得越来越平庸,只要你站在更高的维度(比如: 眼界有没有扩大、可能性是不是更多、竞争力是不是更强、能不能解决更大更难的问题、能创造多大的价值……),时间会让你明白那些低维度的东西全都不是事儿。技术学习上也一样,站在学习编程语法特性的维度和站在学习编程范式、设计模式的维度是两种完全不一样的学习方式。
  • 精于计算得失。很多人其实不是很懂计算。绝大多数人都是在算计自己会失去多少,而不会算会得到多少。而一般的人也总是在算短期内会失去什么,优秀则总是会算我投入后未来会有什么样的回报,前者在算计今天,目光短浅,而后者则是舍在今天,得在明天,计算的是未来。 精于计算得失的,就懂得什么是投资,不懂的只会投机。对于赚钱,你可以投机,但是对于自己最好还是投资。
  • 勇于跳出传统的束缚。有时候,跳出传统并不是一件很容易的事,因为大多数人都会对未知有恐惧的心理。比如:我看到很多人才都被大公司垄断了,其实,有能力的人都不需要加入大公司,有能力的人是少数,这些少数的人应该是所有的公司share着用的,这样一来,对于所有的人都是利益最大化的。这样的事现在也有,比如:律师、设计师……。但是,绝大多数有能力的技术人员是不敢走出这步。我在2015年到2016年实践过一年半,有过这些实践,做“鸡”的比“二奶”好多了,收入也好很多很多(不好意思开车了)……

庄子说过几句话——

井蛙不可以语于海者,拘于虚也;//空间局限

夏虫不可以语于冰者,笃于时也;//时间局限

曲士不可以语于道者,束于教也。//认识局限

别自己墙了自己,人最可悲的就是自己限制自己,想都不敢想,共勉!

(全文完)


关注CoolShell微信公众账号和微信小程序

(转载本站文章请注明作者和出处 酷 壳 – CoolShell,请勿用于任何商业用途)

——=== 访问 酷壳404页面寻找遗失儿童。 ===——

分布式应用框架 Dapr

$
0
0

微服务架构已成为构建云原生应用程序的标准,微服务架构提供了令人信服的好处,包括可伸缩性,松散的服务耦合和独立部署,但是这种方法的成本很高,需要了解和熟练掌握分布式系统。为了使用所有开发人员能够使用任何语言和任何框架轻松地构建便携式微服务应用程序,无论是开发新项目还是迁移现有代码

Dapr 介绍

Github:  https://github.com/dapr/dapr

Dapr是一种可移植的,事件驱动的,无服务器运行时,用于构建跨云和边缘的分布式应用程序。

Distributed Application Runtime. An event-driven, portable runtime for building microservices on cloud and edge.

其中提到了多语言和多开发者框架,我认为这是他选择的通过通信共享信息,即  HTTP 和  GRPC 支持多语言等特性。微软想通过这个设定一个构建微服务应用的规则。从根本上确立你开发的每一个应用的独立性。赋能每个开发者,为了使Dapr对于不同的语言更加方便,它还包括针对Go,Java,JavaScript,.NET和Python的语言特定的SDK。这些SDK通过类型化的语言API(而不是调用http / gRPC API)公开了Dapr构建块中的功能,例如保存状态,发布事件或创建actor。这使开发人员可以使用他们选择的语言编写无状态和有状态功能以及参与者的组合。并且由于这些SDK共享Dapr运行时,您甚至可以获得跨语言的actor和功能支持!

Dapr还可以与任何开发人员框架集成。例如,在Dapr .NET SDK中,您将找到ASP.NET Core集成,该集成带来了可响应其他服务的发布/订阅事件的状态路由控制器,从而使ASP.NET Core成为构建微服务Web应用程序的更好框架。  

不过需要注意的是Dapr目前正处于Alpha阶段, 今天刚发布了0.2版本。在v1.0稳定版本发布之前,建议不要用于生产环境。 

下面进行一个 QuickStart

环境

  1. Install Docker(微服务已经离不开容器化了)

  2. Install Dapr CLI

  3. Install .Net Core SDK 3.0

在Windows 上通过Powershell 安装:

powershell -Command "iwr -useb https://raw.githubusercontent.com/dapr/cli/master/install/install.ps1 | iex"
然后把 c:\dapr 添加到环境变量 PATH
运行dapr命令,检查输出是否正常

C:\workshop\Github\dotnet-sdk>dapr --help

         __
     ____/ /___ _____  _____
    / __  / __ '/ __ \/ ___/
   / /_/ / /_/ / /_/ / /    
   \__,_/\__,_/ .___/_/
               /_/

======================================================
A serverless runtime for hyperscale, distributed systems

Usage:
   dapr [command]

Available Commands:
   help        Help about any command
   init        Setup dapr in Kubernetes or Standalone modes
   list        List all dapr instances
   publish     publish an event to multiple consumers
   run         Launches dapr and your app side by side
   send        invoke a dapr app with an optional payload
   stop        Stops a running dapr instance and its associated app
   uninstall   removes a dapr installation

Flags:
   -h, --help      help for dapr
       --version   version for dapr

Use "dapr [command] --help" for more information about a command.


执行初始化(会启动 docker 容器)
dapr init
Making the jump to hyperspace...
Downloading binaries and setting up components
Success! Dapr is up and running
下载.NET SDk代码
https://github.com/dapr/dotnet-sdk ,里面有.NET Core的多个示例代码:
示例描述
1. ActorDemonstrates creating virtual actors that encapsulate code and state. Also see docs in this repo for a tutorial.
2. ASP.NET CoreDemonstrates ASP.NET Core integration with Dapr by create Controllers and Routes.
3. gRPC client

The gRPC client sample shows how to make Dapr calls to publish events, save state, get state and delete state using a gRPC client.

我们一起来看下ASP.NET Core的Demo;

例子中主 我们使用  Dapr 的交互。Dapr通过  Runtime

  • 提供 Dapr API 给多语言调用。

  • 提供 状态管理 By state stores


/// <summary>
  /// Sample showing Dapr integration with controller.
  /// </summary>
  [ApiController]
  public class SampleController : ControllerBase
  {
      /// <summary>
      /// Gets the account information as specified by the id.
      /// </summary>
      /// <param name="account">Account information for the id from Dapr state store.</param>
      /// <returns>Account information.</returns>
      [HttpGet("{account}")]
      public ActionResult<Account> Get(StateEntry<Account> account)
      {
          if (account.Value is null)
          {
              return this.NotFound();
          }

         return account.Value;
      }


     /// <summary>
      /// Method for depositing to account as psecified in transaction.
      /// </summary>
      /// <param name="transaction">Transaction info.</param>
      /// <param name="stateClient">State client to interact with dapr runtime.</param>
      /// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
      [Topic("deposit")]
      [HttpPost("deposit")]
      public async Task<ActionResult<Account>> Deposit(Transaction transaction, [FromServices] StateClient stateClient)
      {
          var state = await stateClient.GetStateEntryAsync<Account>(transaction.Id);
          state.Value ??= new Account() { Id = transaction.Id, };
          state.Value.Balance += transaction.Amount;
          await state.SaveAsync();
          return state.Value;
      }


     /// <summary>
      /// Method for withdrawing from account as specified in transaction.
      /// </summary>
      /// <param name="transaction">Transaction info.</param>
      /// <param name="stateClient">State client to interact with dapr runtime.</param>
      /// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
      [Topic("withdraw")]
      [HttpPost("withdraw")]
      public async Task<ActionResult<Account>> Withdraw(Transaction transaction, [FromServices] StateClient stateClient)
      {
          var state = await stateClient.GetStateEntryAsync<Account>(transaction.Id);

         if (state.Value == null)
          {
              return this.NotFound();
          }

         state.Value.Balance -= transaction.Amount;
          await state.SaveAsync();
          return state.Value;
      }
  }


这里重点是状态存储,即将   state通过 StateClient 存储在   Dapr中,我们通过状态转移在   Dapr里实现了   stateless。
 
Dapr 运行.NET 应用程序

演示Dapr的服务调用,在终端中切换到项目目录,然后使用dapr启动应用


C:\workshop\Github\dotnet-sdk\samples\AspNetCore\ControllerSample>dapr run --app-id routing --app-port 5000 dotnet run    
Starting Dapr with id routing. HTTP Port: 61102. gRPC Port: 61103
You're up and running! Both Dapr and your app logs will appear here.


注意: 以上dapr run命令,通过app-id指定了应用的ID,通过app-port指定了应用的端口(webapi默认使用5000作为http端口),后跟dotnet run命名启动当前项目。可参考Dapr文档服务调用


后台运行的  CLI 命令,这里是前台打印的日志, 注意到 .NET  App 在指定的  5000 端口运行,同时还有状态存储的  redis 在  6379 端口运行


== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="starting Dapr Runtime -- version 0.2.0 -- commit c75b11
1-dirty"
     == DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="log level set to: info"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="standalone mode configured"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="dapr id: routing"
== DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="loaded component messagebus (pubsub.redis)"        
     == DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="loaded component statestore (state.redis)"
     == DAPR == time="2019-11-16T18:33:22+08:00" level=info msg="application protocol: http. waiting on port 5000"
     == APP == info: Microsoft.Hosting.Lifetime[0]
     == APP ==       Now listening on: http://localhost:5000
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP ==       Application started. Press Ctrl+C to shut down.
     == APP == info: Microsoft.Hosting.Lifetime[0]
== APP ==       Hosting environment: Development
== APP == info: Microsoft.Hosting.Lifetime[0]
== APP ==       Content root path: C:\workshop\Github\dotnet-sdk\samples\AspNetCore\ControllerSample
     == DAPR == time="2019-11-16T18:33:31+08:00" level=info msg="application discovered on port 5000"
     == DAPR == 2019-11-16 18:33:32.029764 I | redis: connecting to localhost:6379
     == DAPR == 2019-11-16 18:33:32.036316 I | redis: connected to localhost:6379 (localAddr: [::1]:61164, remAddr: 
[::1]:6379)
     == DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actor runtime started. actor idle timeout: 1h0m0s. 
actor scan interval: 30s"
     == DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: starting connection attempt to placement se
rvice at localhost:6050"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="http server is running on port 61102"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="gRPC server is running on port 61103"
     == DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="local service entry announced"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 917
6.5164ms"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: established connection to placement service
  at localhost:6050"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement order received: lock"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement order received: update"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement tables updated"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="actors: placement order received: unlock"
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP ==       Start processing HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP ==       Sending HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP ==       Received HTTP response after 2228.2998000000002ms - OK     
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]       
== APP ==       End processing HTTP request after 2257.3405000000002ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP ==       Start processing HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP ==       Sending HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
     == APP ==       Received HTTP response after 67.46000000000001ms - Created
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP ==       End processing HTTP request after 68.0343ms - Created
     == APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
     == APP ==       Start processing HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP ==       Sending HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP ==       Received HTTP response after 5.8247ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP ==       End processing HTTP request after 6.268400000000001ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP ==       Start processing HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP ==       Sending HTTP request POST http://localhost:61102/v1.0/state
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP ==       Received HTTP response after 4.5181000000000004ms - Created
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP ==       End processing HTTP request after 4.6208ms - Created
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[100]
== APP ==       Start processing HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[100]
== APP ==       Sending HTTP request GET http://localhost:61102/v1.0/state/17
== APP == info: System.Net.Http.HttpClient.state.ClientHandler[101]
== APP ==       Received HTTP response after 20.2967ms - OK
== APP == info: System.Net.Http.HttpClient.state.LogicalHandler[101]
== APP ==       End processing HTTP request after 20.691100000000002ms – OK

为了同时实现可移植性和与现有代码的轻松集成,Dapr通过http或gRPC提供了标准API。Dapr端口可从Dapr启动日志中获取,如以下日志表示Dapr公开的HTTP端口为61102(通过Dapr也可使用gRPC方式进行服务调用)

== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="http server is running on port 61102"
== DAPR == time="2019-11-16T18:33:32+08:00" level=info msg="gRPC server is running on port 61103"

我们可通过以下地址来调用示例方法,根据Dapr服务调用API规范,其代理调用规则为:

POST/GET/PUT/DELETE http://localhost:<Dapr端口>/v1.0/invoke/<id>/method/<method-name>

直接调用:GET http://localhost:5000/17

通过Dapr服务调用: GET http://localhost:61102/v1.0/invoke/routing/method/17
 
注意:Dapr的服务调用是有dapr sidecar来实现的,在被调用的服务中无需注入任何与dapr相关的代码。


Viewing all 15893 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>