esb服务总线返回数据异常「存储器总线是什么」

互联网 2023-02-02 18:53:36

今天给大家普及一下esb服务总线返回数据异常「存储器总线是什么」相关知识,最近很多在问esb服务总线返回数据异常「存储器总线是什么」,希望能帮助到您。

这篇文章基于实际的ESB服务总线集成项目中接口服务日志的采集存储,日志查询实践总结。该方法和思路也同样适用于微服务架构下通过API网关进行的日志采集存储以及后续的查询分析。

在前面文章已经谈到,在通过ESB总线或API网关实现的中心化架构下,一个是可以通过插件化的方式很方便的配置实现,类似安全,限流熔断,负载均衡和路由等能力。另外一个关键点就是可以拦截接口服务消费的报文日志信息,通过对这些接口服务运行日志的采集和存储,可以进一步的来分析和优化接口服务,提升服务治理管控水平。

日志采集和存储的基本思路

简单来讲,在ESB总线架构下可以通过增加日志拦截插件的方式来采集消息报文日志信息,对消息输入和消息返回输出分别进行日志插件拦截。

对于采集的日志不能实时同步的写入到文件或数据库中,一个是考虑到持久化存储本身的可靠性问题,一个是实时同步写库本身会对数据库造成巨大的性能压力,同时也直接影响到接口服务实时调用的性能。

因此一般的处理方式都是通过消息中间件来实现解耦,即采集到的日志信息先写入到消息中间件,然后管控平台再通过订阅接口异步获取消息并持久化到数据库中。

日志写入可靠性的问题

在最早进行ESB集成平台的整体架构设计的时候,就考虑了对于ESB总线引擎和SOA治理管控平台要实现彻底的解耦,简单来说就是管控治理平台宕机不应该影响到ESB平台的运行。在实现这个设计目标的时候实际需要做两个方面的事情。

其一就是ESB引擎运行和启动的时候,需要将管控平台DB数据库的配置元数据全部装载到内存中,管控平台定时去刷新缓存数据。

其二就是ESB总线涉及到的数据朝管控平台的写入,都必须采用异步的方式进行。

JMS消息中间件异常

如果JMS消息中间件出现异常,如何确保日志信息不丢失。在这里我们进一步做了可靠性和冗余设计,即ESB在写入数据到消息中间件的时候,如果异常,则直接将日志信息存储到本地磁盘文件,等JMS集群恢复后再重试写入。

日志表按时间分区

在进行架构设计之初,我们就考虑到日志表数据存储量巨大。

因此需要对接口服务调用实例日志头表和实例日志详细的输入输出信息表进行拆分,形成1对2的主从表结构。

同时预估每个月1000万条数据,当前实例表差不多20个字段,数据容量在3到4G左右。因此可以按月进行表分区。而在系统试运行期间发现接口服务运行实例日志峰值一天的调用量就接近1000万条,因此又修改按天进行表分区。

具体分区表的创建可参考类似如下脚本:

//按天创建分区表create table Log_Instance( INSTANCE_ID NUMBER(20) not null,SERVICE_NAMEVARCHAR2(100),REQ_APP_NAME VARCHAR2(50),REQ_APP_IP VARCHAR2(50), REMARK VARCHAR2(1000), START_DATE DATE,END_DATE DATE)//分区信息PARTITION BY RANGE (START_DATE ) INTERVAL (NUMTODSINTERVAL(1, 'day'))(partition part_t01 values less than(to_date('2019-01-01', 'yyyy-mm-dd')));//创建主键alter table Log_Instance add constraint test_pk primary key (INSTANCE_ID ) using INDEX;

为何创建分区表?

分区表不仅仅是提升了数据查询速度,更加重要的就是进行接口服务日志的备份和清理方便。比如你是按天进行分区的,那么要清理特定的某天接口日志数据的时候,实际是整个分区删除,速度相当快。

其次,如果查询的是同一天的接口服务日志信息,那么实际在查询的时候只需要查询该特定分区表即可,性能也得到更快提升。

但是分区表仍然无法解决模糊查询的时候跨多天情况。比如我们基于一个服务名称查询最近1个月的接口服务调用日志,那么这个时候仍然相当慢。

特定服务大并发调用

如果存在某个特定查询服务大并发调用,比如1天调用超过500万次,而这个接口服务日志实际本身没有太多记录的必要。

因此在管控平台设计的时候,可以针对每一个服务配置是否记录详细报文日志,还是只记录接口服务运行实例头日志信息。但是1天500万次的调用量,即使只记录调用头信息也对整体日志采集和存储造成巨大的压力。因此后续的管控在优化的时候,又增加了头信息是否记录的配置。

也就是在整体优化后,日志的配置存在三种:

全部不记录只记录服务日志头信息记录详细的日志头 输入输出报文信息

通过这种配置方式的修改,可以更加灵活的对日志信息的采集和记录进行管控。

Solr日志关键字查询

在最初的接口服务实例日志查询中,仅仅能做到通过服务运行实例号,服务名称,消费方系统,服务调用时间等信息进行接口服务日志查询。

但是实际的业务场景中会出现基于接口服务消息报文中的关键字进行实例日志查询。比如一个采购订单导入接口服务,我们希望能够更加订单发货地址信息进行关键字查询,而在原来的架构设计实现中是很难实现的。

而Solr是当前特别是互联网和电商使用的比较多的一个全文检索引擎,包括一些电商网站的商品模糊查询功能也在使用Solr进行模糊查询,应用相对广泛。

Solr的基本介绍

Solr 是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展,并对索引、搜索性能进行了优化。

Solr可以独立运行,运行在Jetty、Tomcat等这些Servlet容器中,Solr 索引的实现方法很简单,用 POST 方法向 Solr 服务器发送一个描述 Field 及其内容的 XML 文档,Solr根据xml文档添加、删除、更新索引 。

Solr 搜索只需要发送 HTTP GET 请求,然后对 Solr 返回Xml、json等格式的查询结果进行解析,组织页面布局。Solr不提供构建UI的功能,Solr提供了一个管理界面,通过管理界面可以查询Solr的配置和运行情况。

Solr体系架构

以下是Solr的主要构建块(组件)说明:

请求处理程序(Request Handlers):发送到Solr的请求由这些请求处理程序处理。请求可以是查询请求或索引更新请求。根据这些请示的要求来选择请求处理程序。为了将请求传递给Solr,通常将处理器映射到某个URI端点,并且它将为指定的请求提供服务。

搜索组件(Search Components):搜索组件是Solr中提供的搜索类型(功能)。它可能是拼写检查、查询、构面、命中突出显示等。这些搜索组件被注册为搜索处理程序。多个组件可以注册到搜索处理程序。

查询解析器(Query Parser):Solr查询解析器解析传递给Solr的查询,并验证查询的语法是否有错误。解析查询后,将它们转换为Lucene理解的格式。

响应写入器(Response Writers):Solr中的响应写入器是为用户查询生成格式化输出的组件。Solr支持XML、JSON、CSV等响应格式。对每种类型的响应都有不同的响应写入。

分析器/分词器(Text Analysis):Lucene以令牌的形式识别数据。Solr分析内容,将其分成令牌,并将这些令牌传递给Lucene。Solr中的分析器检查字段的文本并生成令牌流。分词器将分析器准备的令牌流分解成令牌。

更新请求处理器(Update Handlers):每当向Solr发送更新请求时,请求都通过一组称为更新请求处理器的插件(签名、日志记录、索引)运行。这个处理器负责修改,例如删除字段、添加字段等。

服务日志和Solr结合

注意Solr的底层数据库本身就是一种key-value结构的非结构化数据,而对于value值一般又是以Xml结构进行存储,这种结构天然的适合对服务运行日志信息进行存储和查询。

对于服务运行,每一次运行ESB都会生成一个独立的UUID值,而这个值就是存储的Key值,同时对于服务运行生成的输入和输出则形成一个大的XML消息文本放入Document对象里面即可。同时可以看到日志信息本身不需要改写,而是只读性质。

对于Solr库本身提供Http Rest接口间索引的创建,包括查询功能,因此后续使用也相当简单。

首先我们看下如何通过Solr进行服务日志的实时查询能力,在原来我们的方案里面是对于服务调用日志信息会先送入到JMS消息管道里面,然后异步的方式通过Java程序读取到后送入到DB数据库中。那么现在就有两种方法来做,即:

将日志信息写入到两个JMS队列,或一个消息Topic中,日志消息同时分发到DB数据库和Solr库中,这样就可以保证Solr库中的索引和文本数据始终都是实时和最新的。

对于Solr的查询,本身也天然的支撑xml结构,特别是根据关键字段的关键字查询能力。比如对于如下的一个xml结构的Document对象。

我们来看,常见的一些查询能力。

//根据一个element关键字段来进行关键值的查询/solr/select?q=name:"A Clash of Kings"//使用通配符查询/solr/select?q=name:"*Kings*"//根据时间段进行模糊查询/solr/select?q=date:[201507 TO 201510] and name:"*Kings"

而这正是服务日志根据业务关键字查询的关键业务场景需求,比如我们可以根据采购订单号来查询服务日志,可以根据采购订单名称来查询服务日志等。也可以根据名称和时间段来组合模糊查询。

分布式日志存储选型

对于当前,我们的服务实例日志是存在在结构化数据库中,其中包括了两个部分的内容。其一是服务运行实例头信息,主要记录了服务实例号,调用时间开始和结束,报文数据量,消费方IP和系统,服务英文名称等关键信息。其二是服务实例详细的输入和输出报文信息。

对于这两部分内容,我们当前仍然是采用分开两个表存储,一个是服务实例运行Header表,一个是服务运行实例详细报文明细表,在报文明细表中采用Blob字段进行输入和输出报文的存储。

在服务运行次数很大的企业,已经对服务实例历史日志存储周期要求较长的企业,我们都可以看到这个数据量相当大,服务实例头表在1年不到往往就达到20亿条左右的规模。而在这个规模下报文明细的存储量往往更大,仅仅服务日志存储往往就需要上100T的存储空间,而且如果采用类似IP SAN等集中化存储方案的时候本身也是很大的成本和资源投入。

同时可以看到在这个报文日志数据量下,为了提供服务日志查询速度,以及方便后续的服务实例日志清理,我们会对服务实例表建立分区表。原来我们按月建立分区表,但是仍然发现可能一天的数据量就在上千万条,因此后面改为按天建立分区表。

在当前这种模式下,我们定时的对服务实例日志进行清理,加上服务分区表设置本身基本能够应对当前的需求,但是仍然带来了如下问题。

其一是服务实例查询慢,特别是多条件跨天的模糊查询,整体查询效率很低。其二是需要定期清理日志,无法做到一个比较长周期的服务存储,同时存储扩展也不容易。

基于上面两个问题,我们才需要考虑如何将服务运行实例日志的存储从当前的结构化数据库迁移出来。这就涉及到分布式数据库的使用,而当前主流的分布式数据库,我们先看下我网上摘录的一些选择思路如下:

如果你对数据的读写要求极高,并且你的数据规模不大,也不需要长期存储,选redis;如果你的数据规模较大,对数据的读性能要求很高,数据表的结构需要经常变,有时还需要做一些聚合查询,选MongoDB;如果你需要构造一个搜索引擎或者你想搞一个看着高大上的数据可视化平台,并且你的数据有一定的分析价值或者你的老板是土豪,选ElasticSearch;如果你需要存储海量数据,连你自己都不知道你的数据规模将来会增长多么大,那么选HBase。

而对于分布式数据库的选择,初步来看实际上可以分为三类

其一是偏基于Hadoop体系架构和分布式存储的,类似HDFS库和HBase数据库,也包括中间类型MongoDB;其二是偏内存和缓存类的,类似Redis库。其三是偏全文检索类和数据分析类的,类似ElasticSearch和Solr库。

那么基于当前服务实例日志的存储和查询需求,我们可以看到:

对于服务实例头信息也可以转移到分布式数据库,选择MongoDB比HBase在模糊查询上支持更好。而对于大的报文输入和输出的存储,最好是选择HBase库或直接采用HDFS分布式存储。如果需要对报文输入和输出做关键字的全文检索,那么需要采用ElasticSearch或Solr库。

但是我们也看到,如果对所有的报文输入和输出都进行全文检索,那么ElasticSearch或Solr库建立的索引会消耗大量的存储空间。同时这种存储更多的是为查询分析服务,而不能作为一个标准的持久化存储解决方案。

Hbase Solr方案

图片来自网络

对于日志分布式存储重点需要考虑解决两个问题,第一个就是引入分布式数据库或分布式存储,通过NFS等来替代SAN集中化存储并实现弹性水平扩展;第二个就是解决当前服务实例信息模糊查询速度慢的问题。

在前面提到了将服务运行实例头信息存储到Solr库中,而将明细信息存储到HBASE分布式数据库中。而经过后续讨论,整个方案变化为将服务实例头和明细信息都写入到HBASE数据库中,但是这样的话对于服务实例查询仍然是对全部扫描HBASE库表,性能仍然上不来。

因此新方案为:

所有数据全部入HBASE数据库 基于HBASE数据库构建Solr二级索引。

这是一个当前主流的对于大表数据存储和查询的一个推荐解决方案。

这个方案属于典型的以空间换时间,通过二级索引的建立,将每次查询请求快速的定位到一个更小的索引集中,以实现高性能检索。当然也是强烈不建议直接采用HBASE索引进行多条件模糊查询,这种全部扫描对内存和计算资源消耗都很大。

图片来自网络

对于基于Solr来构建HBASE的二级索引可以参考下面这篇文章:

https://blog.csdn.net/jediael_lu/article/details/76576897

为hbase表建倒排索引,重新索引回hbase中,以标签作rowkey,以用户id作值使用coprocessor将数据索引至solr使用solr-index等开源工具将数据索引至solr。

CDH有一个hbase-solr的模块,它是基于开源项目hbase-indexer的。问题是hbase-indexer基于0.94与0.98的,不清楚cdh是否有改进,没文档说明。但一般而言,它与CDH5.6同时发布,应该是不存在兼容性问题的。

另外一篇文章参考:

https://blog.csdn.net/u014091123/article/details/73322563

对于二级索引构建方式:表索引、列索引、全文索引

表索引: 是将索引数据单独存储为一张表,通过 HBase Coprocessor 生成并访问索引数据。

列索引: 是将索引数据与源数据存储在相同的 Region 里,索引数据定义为一个单独的列族,也是利用 Coprocessor 来生成并访问索引数据。对于表索引,源数据表与索引表的数据一致性很难保证,访问两张不同的表也会增加 IO 开销和远程调用的次数。对于列索引,单表的数据容量会急剧增加,对同一 Region 里的多个列族进行 Split 或 Merge 等操作时可能会造成数据丢失或不一致。

全文索引:以CDH5中的Lily HBase Indexer服务实现,其使用SolrCloud存储HBase的索引数据,Indexer索引和搜索不会影响HBase运行的稳定性和HBase数据写入的吞吐量,因为索引和搜索过程是完全分开并且异步的。Lily HBase Indexer在CDH5中运行必须依赖HBase、SolrCloud和Zookeeper服务。

CDH5.4中的Key-Value Indexer使用的是Lily HBase NRT Indexer服务,Lily HBase Indexer是一款灵活的、可扩展的、高容错的、事务性的,并且近实时的处理HBase列索引数据的分布式服务软件。它是NGDATA公司开发的Lily系统的一部分,已开放源代码。

Lily HBase Indexer使用SolrCloud来存储HBase的索引数据,当HBase执行写入、更新或删除操作时,Indexer通过HBase的replication功能来把这些操作抽象成一系列的Event事件,并用来保证写入Solr中的HBase索引数据的一致性。并且Indexer支持用户自定义的抽取,转换规则来索引HBase列数据。Solr搜索结果会包含用户自定义的columnfamily:qualifier字段结果,这样应用程序就可以直接访问HBase的列数据。而且Indexer索引和搜索不会影响HBase运行的稳定性和HBase数据写入的吞吐量,因为索引和搜索过程是完全分开并且异步的。

Lily HBase Indexer在CDH5中运行必须依赖HBase、SolrCloud和Zookeeper服务。

对原有日志存储和日志模糊查询功能的改造

在原来文章里面我就谈到过,对于已经上线的生产系统在后续变更和优化中最佳方案就是要尽量对已有的功能影响最小,对已有的功能无侵入。这样即使新功能或优化功能上线出现问题或Bug也能够将影响控制到最小范围。因此原来我们准备是对日志存储和模糊查询功能做大改造。

但是新方案修改为:

完全保留当前日志存储和查询功能,再新增加一个归档服务日志查询功能。

对于归档日志查询功能采用定时任务的方式将当前结构化数据库中的日志存储同步到HBASE数据库中,并对同步到HBASE数据库中的数据再进行Solr二级索引的建立。这样对于在线库只需要保存3到6个月实例日志数据,其它服务日志都定时同步到HBASE归档库中,并基于HBASE库新增加一个归档日志查询功能即可。

采用这种方式可以最大限度避免对已有日志存储和查询功能的影响。