精华内容
下载资源
问答
  • 来自Stitch Fix团队的工程副总裁Randy ShoupQCon纽约2017会议上讨论了如何在基于微服务的应用中管理数据和隔离持久化。他还介绍了将事件(Event)作为微服务的第一类构造。他介绍自己的团队将机器学习技术应用到了...

    来自Stitch Fix团队的工程副总裁Randy Shoup在QCon纽约2017会议上讨论了如何在基于微服务的应用中管理数据和隔离持久化。他还介绍了将事件(Event)作为微服务的第一类构造。他介绍自己的团队将机器学习技术应用到了业务的各个组成部分,比如购买、库存管理以及风格推荐等。

    个性化推荐会基于库存运行机器学习,从而创建出推荐的算法。这些推荐算法随后会被全国范围内的设计师所监管,从而形成个性化风格的推荐。

    微服务架构是渐进演化的。像eBay、Twitter和Amazon这样的组织都经历了一些架构的迭代,从单体应用转换成了微服务。

    微服务除了具有单一的目的、定义良好的接口、模块化和独立性之外,还需要负责隔离持久化。Shoup讨论了一些持久化的方式,比如操作自己的数据存储或使用持久化服务。在第一种方式中,会将数据存储在自己的数据库实例中,这个实例是由服务团队拥有并进行运维的。而如果采用持久化服务的话,我们会将数据存储在数据库的一个单独的模式中,由其他团队或第三方供应商以服务的方式进行运维。数据应该是与服务的其他消费者相隔离的。

    事件是微服务架构中的第一类构造。它们代表了现实世界是如何运作的并且保证应用符合一定的领域要求,比如在金融方面。事件是服务接口的重要组成部分,它应该包含服务产生的所有事件以及服务消费的所有事件。

    从单体共享数据库抽取微服务一般涉及到如下几个步骤:

    创建服务:服务边界应该包含服务本身以及它所面对的数据库;应用要使用服务:通过使用新创建的服务,让应用与共享数据库解耦;将数据转移至私有数据库:然后将数据从共享数据库转移至新的私有数据库。这不会对客户端应用造成任何影响,因为它们已经不直接依赖于数据库;重复操作:为应用中需要抽取成独立微服务的其他业务功能采用相同的过程。

    Shoup还讨论到微服务用例涉及到共享数据(Shared Data)、连接(join)以及事务。

    共享数据:创建的服务是单一系统记录(System of Record,SoR)的并且拥有自己的所有数据。数据的其他副本是只读的,只是非权威(non-authoritative)的缓存。为了访问共享数据,我们有三个可选方案:同步查找(一个服务调用另外的服务来获取数据)、具有缓存的异步事件或共享元数据库;

    连接:将数据切分为单独的服务会让连接变得很困难。在微服务领域中,我们可以在客户端应用中进行数据连接,或者是通过监听两个服务的事件,创建“物化视图(Materialized Views)”,并在本地存储中维护非规格化(denormalized)的两个数据集的连接结果。

    事务:在单体应用中,事务管理非常容易,但是在微服务架构中却非常困难,因为数据被拆分到了多个不同的服务中了。我们可以将事务实现为一种工作流,它会按照倒序使用补偿操作从而形成一种回滚机制。很多现实系统已经采用了这种方式,比如支付处理和费用审批的系统。这些流程也是采用功能即服务(Serverless架构)的理想候选方案。 


    本文作者:Srini Penchikala

    来源:51CTO

    展开全文
  • 1. 传统应用的事务管理1.1 本地事务传统单机应用使用一个RDBMS作为数据源,应用开启事务,进行CRUD,提交或回滚事务,统统发生本地事务,由资源管理器(RM)直接提供事务支持。...

    1. 传统应用的事务管理

    1.1 本地事务

    传统单机应用使用一个RDBMS作为数据源,应用开启事务,进行CRUD,提交或回滚事务,统统发生在本地事务中,由资源管理器(RM)直接提供事务支持。数据的一致性在一个本地事务中得到保证。

    1.2 分布式事务

    1.2.1 两阶段提交(2PC)

    当应用逐渐扩展,出现一个应用使用多个数据源的情况,这个时候本地事务已经无法满足数据一致性的要求。由于多个数据源的同时访问,事务需要跨多个数据源管理,分布式事务应运而生。其中最流行的就是两阶段提交(2PC),分布式事务由事务管理器(TM)统一管理。

    两阶段提交分为准备阶段和提交阶段。

    两阶段提交-commit

    两阶段提交-rollback

    然而两阶段提交也不能完全保证数据一致性问题,并且有同步阻塞的问题,所以其优化版本三阶段提交(3PC)被发明了出来。

    1.2.2 三阶段提交(3PC)

    三阶段提交

    然而3PC也只能保证绝大多数情况下的数据一致性。

    具体分布式事务2PC和3PC的详细介绍请见  关于分布式事务、两阶段提交协议、三阶提交协议
    。分布式事务不是本文的重点,故不展开。

    2. 微服务下的事务管理

    那么,分布式事务2PC或者3PC是否适合于微服务下的事务管理呢?答案是否定的,原因有三点:

    1. 由于微服务间无法直接进行数据访问,微服务间互相调用通常通过RPC(dubbo)或Http API(SpringCloud)进行,所以已经无法使用TM统一管理微服务的RM。

    2. 不同的微服务使用的数据源类型可能完全不同,如果微服务使用了NoSQL之类不支持事务的数据库,则事务根本无从谈起。

    3. 即使微服务使用的数据源都支持事务,那么如果使用一个大事务将许多微服务的事务管理起来,这个大事务维持的时间,将比本地事务长几个数量级。如此长时间的事务及跨服务的事务,将为产生很多锁及数据不可用,严重影响系统性能。

    由此可见,传统的分布式事务已经无法满足微服务架构下的事务管理需求。那么,既然无法满足传统的ACID事务,在微服务下的事务管理必然要遵循新的法则--BASE理论。

    BASE理论由eBay的架构师Dan
    Pritchett提出,BASE理论是对CAP理论的延伸,核心思想是即使无法做到强一致性,应用应该可以采用合适的方式达到最终一致性。BASE是指基本可用(Basically
    Available)、软状态( Soft State)、最终一致性( Eventual Consistency)。

    基本可用 :指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。

    软状态
    :允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。

    最终一致性 :最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。

    BASE中的 最终一致性
    是对于微服务下的事务管理的根本要求,既基于微服务的事务管理无法达到强一致性,但必须保证最重一致性。那么,有哪些方法可以保证微服务下的事务管理的最终一致性呢,按照实现原理分主要有两类,事件通知型和补偿型,其中事件通知型又可分为可靠事件通知模式及最大努力通知模式,而补偿型又可分为TCC模式、和业务补偿模式两种。这四种模式都可以达到微服务下的数据最终一致性。

    3. 实现微服务下数据一致性的方式

    3.1 可靠事件通知模式

    3.1.1 同步事件

    可靠事件通知模式的设计理念比较容易理解,即是主服务完成后将结果通过事件(常常是消息队列)传递给从服务,从服务在接受到消息后进行消费,完成业务,从而达到主服务与从服务间的消息一致性。首先能想到的也是最简单的就是同步事件通知,业务处理与消息发送同步执行,实现逻辑见下方代码及时序图。

    public void trans() {
            try {
            // 1. 操作数据库
                bool result = dao.update(data);// 操作数据库失败,会抛出异常
            // 2. 如果数据库操作成功则发送消息
                if(result){
                    mq.send(data);// 如果方法执行失败,会抛出异常
                }
            } catch (Exception e) {
                roolback();// 如果发生异常,就回滚
            }
        }
    

    上面的逻辑看上去天衣无缝,如果数据库操作失败则直接退出,不发送消息;如果发送消息失败,则数据库回滚;如果数据库操作成功且消息发送成功,则业务成功,消息发送给下游消费。然后仔细思考后,同步消息通知其实有两点不足的地方。

    1. 在微服务的架构下,有可能出现网络IO问题或者服务器宕机的问题,如果这些问题出现在时序图的第7步,使得消息投递后无法正常通知主服务(网络问题),或无法继续提交事务(宕机),那么主服务将会认为消息投递失败,会滚主服务业务,然而实际上消息已经被从服务消费,那么就会造成主服务和从服务的数据不一致。具体场景可见下面两张时序图。

    image

    1. 事件服务(在这里就是消息服务)与业务过于耦合,如果消息服务不可用,会导致业务不可用。应该将事件服务与业务解耦,独立出来异步执行,或者在业务执行后先尝试发送一次消息,如果消息发送失败,则降级为异步发送。

    3.1.2 异步事件

    3.1.2.1 本地事件服务

    为了解决3.1.1中描述的同步事件的问题,异步事件通知模式被发展了出来,既业务服务和事件服务解耦,事件异步进行,由单独的事件服务保证事件的可靠投递。

    异步事件通知-本地事件服务

    当业务执行时,在同一个本地事务中将事件写入本地事件表,同时投递该事件,如果事件投递成功,则将该事件从事件表中删除。如果投递失败,则使用事件服务定时地异步统一处理投递失败的事件,进行重新投递,直到事件被正确投递,并将事件从事件表中删除。这种方式最大可能地保证了事件投递的实效性,并且当第一次投递失败后,也能使用异步事件服务保证事件至少被投递一次。

    然而,这种使用本地事件服务保证可靠事件通知的方式也有它的不足之处,那便是业务仍旧与事件服务有一定耦合(第一次同步投递时),更为严重的是,本地事务需要负责额外的事件表的操作,为数据库带来了压力,在高并发的场景,由于每一个业务操作就要产生相应的事件表操作,几乎将数据库的可用吞吐量砍了一半,这无疑是无法接受的。正是因为这样的原因,可靠事件通知模式进一步地发展-外部事件服务出现在了人们的眼中。

    3.1.2.2 外部事件服务

    外部事件服务在本地事件服务的基础上更进了一步,将事件服务独立出主业务服务,主业务服务不在对事件服务有任何强依赖。

    异步事件通知-外部事件服务

    业务服务在提交前,向事件服务发送事件,事件服务只记录事件,并不发送。业务服务在提交或回滚后通知事件服务,事件服务发送事件或者删除事件。不用担心业务系统在提交或者会滚后宕机而无法发送确认事件给事件服务,因为事件服务会定时获取所有仍未发送的事件并且向业务系统查询,根据业务系统的返回来决定发送或者删除该事件。

    外部事件虽然能够将业务系统和事件系统解耦,但是也带来了额外的工作量:外部事件服务比起本地事件服务来说多了两次网络通信开销(提交前、提交/回滚后),同时也需要业务系统提供单独的查询接口给事件系统用来判断未发送事件的状态。

    3.1.2.3 可靠事件通知模式的注意事项

    可靠事件模式需要注意的有两点,1. 事件的正确发送;2. 事件的重复消费。
    通过异步消息服务可以确保事件的正确发送,然而事件是有可能重复发送的,那么就需要消费端保证同一条事件不会重复被消费,简而言之就是保证事件消费的 幂等性

    如果事件本身是具备幂等性的状态型事件,如订单状态的通知(已下单、已支付、已发货等),则需要判断事件的顺序。一般通过时间戳来判断,既消费过了新的消息后,当接受到老的消息直接丢弃不予消费。如果无法提供全局时间戳,则应考虑使用全局统一的序列号。

    对于不具备幂等性的事件,一般是动作行为事件,如扣款100,存款200,则应该将事件id及事件结果持久化,在消费事件前查询事件id,若已经消费则直接返回执行结果;若是新消息,则执行,并存储执行结果。

    3.2 最大努力通知模式

    相比可靠事件通知模式,最大努力通知模式就容易理解多了。最大努力通知型的特点是,业务服务在提交事务后,进行有限次数(设置最大次数限制)的消息发送,比如发送三次消息,若三次消息发送都失败,则不予继续发送。所以有可能导致消息的丢失。同时,主业务方需要提供查询接口给从业务服务,用来恢复丢失消息。最大努力通知型对于时效性保证比较差(既可能会出现较长时间的软状态),所以对于数据一致性的时效性要求比较高的系统无法使用。这种模式通常使用在不同业务平台服务或者对于第三方业务服务的通知,如银行通知、商户通知等,这里不再展开。

    3.3 业务补偿模式

    接下来介绍两种补偿模式,补偿模式比起事件通知模式最大的不同是,补偿模式的上游服务依赖于下游服务的运行结果,而事件通知模式上游服务不依赖于下游服务的运行结果。首先介绍业务补偿模式,业务补偿模式是一种纯补偿模式,其设计理念为,业务在调用的时候正常提交,当一个服务失败的时候,所有其依赖的上游服务都进行业务补偿操作。举个例子,小明从杭州出发,去往美国纽约出差,现在他需要定从杭州去往上海的火车票,以及从上海飞往纽约的飞机票。如果小明成功购买了火车票之后发现那天的飞机票已经售空了,那么与其在上海再多待一天,小明还不如取消去上海的火车票,选择飞往北京再转机纽约,所以小明就取消了去上海的火车票。这个例子中购买杭州到上海的火车票是服务a,购买上海到纽约的飞机票是服务b,业务补偿模式就是在服务b失败的时候,对服务a进行补偿操作,在例子中就是取消杭州到上海的火车票。

    补偿模式要求每个服务都提供补偿借口,且这种补偿一般来说是 不完全补偿
    ,既即使进行了补偿操作,那条取消的火车票记录还是一直存在数据库中可以被追踪(一般是有相信的状态字段“已取消”作为标记),毕竟已经提交的线上数据一般是不能进行物理删除的。

    业务补偿模式最大的缺点是软状态的时间比较长,既数据一致性的时效性很低,多个服务常常可能处于数据不一致的情况。

    3.4 TCC/Try Confirm Cancel模式

    TCC模式是一种优化了的业务补偿模式,它可以做到 完全补偿
    ,既进行补偿后不留下补偿的纪录,就好像什么事情都没有发生过一样。同时,TCC的软状态时间很短,原因是因为TCC是一种两阶段型模式(已经忘了两阶段概念的可以回顾一下1.2.1),只有在所有的服务的第一阶段(try)都成功的时候才进行第二阶段确认(Confirm)操作,否则进行补偿(Cancel)操作,而在try阶段是不会进行真正的业务处理的。

    TCC模式

    TCC模式的具体流程为两个阶段:

    1. Try,业务服务完成所有的业务检查,预留必需的业务资源

    2. 如果Try在所有服务中都成功,那么执行Confirm操作,Confirm操作不做任何的业务检查(因为try中已经做过),只是用Try阶段预留的业务资源进行业务处理;否则进行Cancel操作,Cancel操作释放Try阶段预留的业务资源。

    这么说可能比较模糊,下面我举一个具体的例子,小明在线从招商银行转账100元到广发银行。这个操作可看作两个服务,服务a从小明的招行账户转出100元,服务b从小明的广发银行帐户汇入100元。

    服务a(小明从招行转出100元):

    try: update cmb_account set balance=balance-100, freeze=freeze+100 where
    acc_id=1 and balance>100;
    
    confirm: update cmb_account set freeze=freeze-100 where acc_id=1;
    
    cancel: update cmb_account set balance=balance+100, freeze=freeze-100 where
    acc_id=1;
    

    服务b(小明往广发银行汇入100元):

    try: update cgb_account set freeze=freeze+100 where acc_id=1;
    
    confirm: update cgb_account set balance=balance+100, freeze=freeze-100 where
    acc_id=1;
    
    cancel: update cgb_account set freeze=freeze-100 where acc_id=1;
    

    具体说明

    • a的try阶段,服务做了两件事,1:业务检查,这里是检查小明的帐户里的钱是否多余100元;2:预留资源,将100元从余额中划入冻结资金。

    • a的confirm阶段,这里不再进行业务检查,因为try阶段已经做过了,同时由于转账已经成功,将冻结资金扣除。

    • a的cancel阶段,释放预留资源,既100元冻结资金,并恢复到余额。

    • b的try阶段进行,预留资源,将100元冻结。

    • b的confirm阶段,使用try阶段预留的资源,将100元冻结资金划入余额。

    • b的cancel阶段,释放try阶段的预留资源,将100元从冻结资金中减去。

    从上面的简单例子可以看出,TCC模式比纯业务补偿模式更加复杂,所以在实现上每个服务都需要实现Cofirm和Cancel两个接口。

    3.5 总结

    下面的表格对这四种常用的模式进行了比较:

    四种常用的模式比较

    展开全文
  • 虽说 Java EE提供了一个强大的平台,供我们创建、部署和管理企业级微服务,但本文,我将展示如何创建一个尽可能小的 RESTful微服务。放心,这个过程,我们不会浪费时间精力去重复做些数据处理之类的事情。...
    揭秘:RESTEasy如何完美支持JAVA 微服务中的多种数据格式

    作者 | Lyndsey Padget

    译者 |月满西楼

    本文来自公众号“EAWorld”,ID:eaworld

    今天我们来聊聊 Java中的微服务。虽说 Java EE提供了一个强大的平台,供我们创建、部署和管理企业级微服务,但在本文中,我将展示如何创建一个尽可能小的 RESTful微服务。

    放心,在这个过程中,我们不会浪费时间精力去重复做些数据处理之类的事情。我们会通过 JBoss RESTEasy来进行搭建。而确保该微服务的轻量级,目的是为了向大家展示,在一个全新或者现存的微服务前端,建立一个 RESTful接口,真的非常简单。

    与此同时,我会进一步证明,通过 RESTEasy构建的微服务具备很大的灵活性,不仅可以兼容包括 JSON,XML在内的多种数据传输格式,还支持将其部署到 Apache Tomcat[1]服务器而非 JBoss企业应用平台 (EAP)[2]上。诚然,每个工具都有自己的优势,但是我认为先在 KISS原则 [3]下探讨技术可用性会很有帮助,然后才是根据软件的长期目标和需求来决定应该为服务架构添加哪些特性。

    本文中提到的代码示例都可以在 GitHub[4]上查阅,包括“starter” 和 “final”这两个分支。下面是我采用的环境,当然你的实际情况可能有所不同:

    • Java Development Kit[5] (JDK)1.8.0-131 (amd64)

    • apache Tomcat[6] 9

    • apache Maven[7] 3.5.0

    • Eclipse Java EE IDE[8] 4.7.0 (Oxygen)

    • Linux Mint[9] 18.2 (Sonya)64位

    就技术而言...

    微服务 [10]是一种体积小、更为精炼的服务,其目标是“做好一件事”。微服务之间通过一些接口进行交互是很普遍的现象。如果该接口可以通过 web访问 (使用 HTTP),那么它就是一个 web服务。部分 web服务是基于 RESTful这种架构风格的,另一些则不是。注意,微服务并不都是 web服务,web服务并不都是 RESTful web服务,RESTful web服务也并不都是微服务!

    揭秘:RESTEasy如何完美支持JAVA 微服务中的多种数据格式

    REST和 XML……能否共存?

    如果你此前在使用 RESTful web服务时,没用过除 JSON 以外的文本数据交换格式 [11]来进行内容传输,那么你可能会认为二者是不相关的。但是回想下,REST是定义 API的一种架构风格,REST和 JSON这两者又碰巧一起流行起来 (注意,这并非偶然)。XML多年的发展使其拥有大量的客户群,能够接纳和提供 XML数据传输格式的 RESTful web服务, 不管是对那些已经依赖于这类内容交互系统的组织,还是对仅仅是更熟悉 XML的用户来说,都非常有用。当然,通常情况下,JSON依然是首选,因为其消息体更小,但有时 XML只是一个更简单的“sell”。拥有一个能同时支持这两种格式的 RESTful微服务是最理想的 ;从部署的角度来说,它不仅简洁,具备可扩展性,还有足够的灵活性,可以支持不同类型的内容,从而满足那些其他有调用需求的应用程序。

    为什么选择 RESTEasy?

    RESTEasy[12]是 Jboss的一个框架,可以用来构建 RESTful web服务。通过 RESTEasy构建的 RESTful web服务,可以根据四个函数库来实现对 XML和 JSON这两种数据传输格式的支持:

    • resteasy-jaxrs,实现了 JAX-RS 2.0 (用于 RESTful Web服务的 Java API) [13]

    • resteasy-jaxb-provider,其 JAXB[14]绑定能有效支持 XML

    • resteasy-jettison-provider,用 Jettison[15]将 XML转换为 JSON

    • resteasy-servlet-initializer,将服务部署到 Servlet 3.0容器 (在 Tomcat服务器上)

    首先,创建一个内含 pom.xml数据包的 web服务项目:

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.lyndseypadget</groupId> <artifactId>resteasy</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>resteasy</name> <repositories> <repository> <id>org.jboss.resteasy</id> <url>http://repository.jboss.org/maven2/</url> </repository> </repositories> <dependencies> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-jaxrs</artifactId> <version>3.1.4.Final</version> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-jaxb-provider</artifactId> <version>3.1.4.Final</version> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-jettison-provider</artifactId> <version>3.1.4.Final</version> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-servlet-initializer</artifactId> <version>3.1.4.Final</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.0.2</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> <finalName>resteasy</finalName> </build> </project>

    (左右滑动可查看全部代码,下同)

    这些数据库的大小大概在 830KB。当然,这些直接的依赖环境(dependency),运用 Maven一起构建项目也会带来部分传递性依赖。

    揭秘:RESTEasy如何完美支持JAVA 微服务中的多种数据格式

    接下来,我将用“Maven方法”来构建这个项目,例如在 src/main/java中,使用 Maven构建命令等,不想用 Maven的话,你也可以直接从下载页面 [16]下载 RESTEasy jar数据包。下载的时候不用理会 RESTEasy站点上弹出的这个提示:JBoss仅仅是在尝试引导你采用更“企业化”的方法。你只需点击“继续下载”,来开展后面的操作。

    揭秘:RESTEasy如何完美支持JAVA 微服务中的多种数据格式

    项目设计

    下面这个微服务可以用非常简单的方法来演示一些基本概念。如下图所示,它包括 5个等级。

    揭秘:RESTEasy如何完美支持JAVA 微服务中的多种数据格式

    此处,FruitApplication是微服务的切入点。FruitService提供了主要的路径 (/fruits),同时它也充当了路由器的功能。苹果和水果都是模型;水果包含一些抽象的功能,苹果则会具体地扩展它的功能。

    和你设想一致的是,FruitComparator可以提供比较功能。不熟悉 Java comparator的读者,可以在这篇文章中了解一下对象的等同性和比较,这里我用字符来取代。虽然 FruitComparator不是一个模型,但我更喜欢将比较器与它想要比较的对象类型保持相类似的命名。

    模型

    让我们从 Fruit这级开始

    package com.lyndseypadget.resteasy.model; import javax.xml.bind.annotation.XmlElement; public abstract class Fruit { private String id; private String variety; @XmlElement public String getId { return id; } public void setId(String id) { this.id = id; } @XmlElement public String getVariety { return variety; } public void setVariety(String variety) { this.variety = variety; } }

    然后 Apple这级对其展开:

    package com.lyndseypadget.resteasy.model; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "apple") public class Apple extends Fruit { private String color; @XmlElement public String getColor { return color; } public void setColor(String color) { this.color = color; } }

    以上并不是什么特别惊人的代码,你可能会觉得都不值得拿出来炫耀,就是一个 Java继承的简单实例。但重点在于这两个注释 @XmlElement 和 @XmlRootElement,它们定义了 XML apple结构的样子:

    <apple> <id>1</id> <variety>Golden delicious</variety> <color>yellow</color> </apple>

    因为没有约定明显的构造函数:Java使用了隐式的、无参数的默认构造函数,所以一些更微妙的事情在发生。这个无参数的构造函数对 JAXB 施展魔法般效果的工作是十分必要的(本文解释了这一点,以及必要的话,如何用 XMLAdapter来让它工作)。

    现在我们有了一个对象:被定义的苹果。它有三个属性: ID、多样性和颜色。

    服务

    FruitService 被用来作为与微服务交互的主要路径 (/fruits)。在本例中,我使用 @path注释直接在该层级中定义了第一个路径,/fruits/apples。随着 RESTful微服务的扩展,你可能希望在自己的层级中定义多个最终路径 (例如 /apples, /bananas, /oranges)。

    package com.lyndseypadget.resteasy; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import com.lyndseypadget.resteasy.model.Apple; import com.lyndseypadget.resteasy.model.FruitComparator; @Path("/fruits") public class FruitService { private static Map<String, Apple> apples = new TreeMap<String, Apple>; private static Comparator comparator = new FruitComparator; @GET @Path("/apples") @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public List getApples { List retVal = new ArrayList(apples.values); Collections.sort(retVal, comparator); return retVal; } }

    这张苹果的地图帮助我们根据 id跟踪苹果的数据,从而模拟某些类型的数据持久层。利用 getApples方法(常用的 HTTP请求方式)将会返回地图跟踪到的相关苹果数据。GET /apples route是用 @GET和 @path注释定义的,它可以生成数据传输格式 XML或 JSON的内容。

    这个方法需要返回一个 List< apple >对象,然后用这个比较器按品种属性来对列表进行排序。

    FruitComparator看起来是像这样的:

    package com.lyndseypadget.resteasy.model; import java.util.Comparator; public class FruitComparator implements Comparator { public int compare(F f1, F f2) { return f1.getVariety.compareTo(f2.getVariety); } }

    注意,如果想要对苹果的一个特定属性进行排序,比如颜色,我们就必须创建一个新的比较器去取代,并取个名字,比如 AppleComparator。

    应用程序

    在 RESTEasy3.1.x中, 你需要定义一个扩展应用的层级。RESTEasy示例文档说明这是一个单例模式注册表(singleton registry),如下所示:

    package com.lyndseypadget.resteasy; import javax.ws.rs.core.Application; import java.util.HashSet; import java.util.Set; public class FruitApplication extends Application { HashSet singletons = new HashSet; public FruitApplication { singletons.add(new FruitService); } @Override public Set<Class> getClasses { HashSet<Class> set = new HashSet<Class>; return set; } @Override public Set getSingletons { return singletons; } }

    如果仅为了说明本例,就不需要对这个层级做太多工作,但是我们需要在 web.xml文件中将它连接起来,这会在后面的章节“web服务连接”中进行介绍。

    对象的构建集合

    GET /apples调用将返回如下数据:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <collection> <apple> <id>1</id> <variety>Golden delicious</variety> <color>yellow</color> </apple> </collection>
    [ { "apple": { "id": 1, "variety": "Golden delicious", "color": "yellow" } } ]

    但是,我们可以将数据更改成看起来稍有点不同:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <apples> <apple> <id>1</id> <variety>Golden delicious</variety> <color>yellow</color> </apple> </apples>
     { "apples": { "apple": { "id": 1, "variety": "Golden delicious", "color": "yellow" } } }

    第二个选项在 XML中看起来更好一些,但是对 JSON产生了不太好的影响。如果你喜欢这个结构,可以用它自己的类型打包 List< Apple >,并修改 FruitService.getApples方法来返回这种类型:

    package com.lyndseypadget.resteasy.model; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name = "apples") public class Apples { private static Comparator comparator = new FruitComparator; @XmlElement(name = "apple", type = Apple.class) private List apples; public List getApples { Collections.sort(apples, comparator); return apples; } public void setApples(Collection apples) { this.apples = new ArrayList(apples); } }

    这些注释有效地“重新标记”了根元素,即 collection/list。通过读取用于 javax.xml.bind.annotation的 javadoc文档,你可以尝试用它和不同的 XML Schema映射注释。

    当然,如果实在不能搞定一般的方法签名(method signature),则可以编码写入不同的方法——一个用于 XML,另一个用于 JSON。

    一些 web服务连接

    从将该服务部署到 Tomcat开始,我用一个放在 src/main/webapp/web inf/web.xml的 web应用部署描述符文件。它所包含的内容如下:

    <?xml version="1.0"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <display-name>resteasy</display-name> <context-param> <param-name>javax.ws.rs.core.Application</param-name> <param-value>com.lyndseypadget.resteasy.FruitApplication</param-value> </context-param> <context-param> <param-name>resteasy.servlet.mapping.prefix</param-name> <param-value>/v1</param-value> </context-param> <listener> <listener-class> org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap </listener-class> </listener> <servlet> <servlet-name>Resteasy</servlet-name> <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class> </servlet> <servlet-mapping> <servlet-name>Resteasy</servlet-name> <url-pattern>/v1/*</url-pattern> </servlet-mapping> </web-app>

    没错,“servlet-name”表示 servlet(即 Service)的名称是 Resteasy。servlet-mapping url-pattern (/v1/*)要求 Tomcat服务器将包含该模式的传入请求传输到 Resteasy服务。关于如何建立这个文件的更多信息,以及可用的不同选项,请参阅 Tomcat的应用程序部署文档 [17]。

    构建及部署

    从项目的根目录中,可以运行以下内容来构建 WAR(web application resource,web应用程序资源)文件:

    mvn clean install

    这将在 target文件夹中创建一个包含 WAR文件的新文件夹。虽然用 Maven或其他工具来部署该文件也可以,但我只用一个简单的复制命令就可以。需要注意的是,每次将 WAR重新部署到 Tomcat服务器时,应该首先暂停服务器运行,并删除服务应用程序文件夹 (在本例中,是这个文件夹:< tomcatDirectory >/webapps/resteasy)和旧的 WAR文件 (< tomcatDirectory >/webapps/resteasy.war)。

    [sudo] cp target/resteasy.war <tomcatDirectory>/webapps/resteasy.war

    如果此时 Tomcat服务器正在运行,那么会即刻部署 web服务。如果不是,下次服务器启动时,该服务也会被自动部署上去。然后,就可以通过如下地址访问 web服务:http://< tomcatHost >:< tomcatPort >/resteasy/v1/fruits/apples。我的范例中是这个地址 http://localhost:8080/resteasy/v1/fruits/apples。

    通过“内容协商(Content negotiation)”测试服务

    内容协商(Content negotiation)是一种机制,它可以提供不同资源 (URI)的表现形式。最基本的,这意味着可以:

    • 详细设置 Accept header,以指示希望从服务中接受的内容类型

    • 详细设置 Content-Type header,以指示发送给服务的内容类型

    要获取更多关于内容协商(Content negotiation)和 header的信息,请参阅 RFC 2616[18]的第 12和 14章。在本例中,你真正需要了解的是:

    • @Produces annotation(注释)指明了该方法能够生成哪些内容 (这将尝试匹配请求上的 Accept header)。

    • @Consumes annotation(注释)指明了该方法能够使用哪些内容 (这将尝试匹配请求的 content-type header)。

    如果您试图对一个有效端点进行 HTTP调用,但是内容不能被协商,这意味着没有 @Produces匹配该 Accept数据,或者没有 @Consumes匹配 Content-Type数据,将被返回 HTTP状态码 415:不支持的数据传输格式。

    返回常见数据传输格式的 GET调用实际上可以直接进入浏览器。对于 GET /apples这样的调用,默认情况下您将获得 XML:

    揭秘:RESTEasy如何完美支持JAVA 微服务中的多种数据格式

    不过,使用像 Postman[19]这类工具可能会更有帮助,因为它明确地指定 Accept header作为 application/xml:

    揭秘:RESTEasy如何完美支持JAVA 微服务中的多种数据格式

    这两种方法都返回了一些有效但没有多大意义的 XML,即一个空的苹果列表。但是这里有一些很酷的东西。将 Accept header更改为 application/json,太好了,瞧!JSON*生效*了:

    揭秘:RESTEasy如何完美支持JAVA 微服务中的多种数据格式

    不只是“读取”

    你可能会发现,很多 RESTful web服务的例子,都是只读的,部分也不会有进一步的提示,比如如何去创建、更新和删除这些操作。虽然我们现在已经有了 web服务的框架,但这是一个不能更改的空列表,这并没多大意义。所以我们应该运用一些其他方法,将苹果添加到这个列表中或从列表中将其删除。

    package com.lyndseypadget.resteasy; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import com.lyndseypadget.resteasy.model.Apple; import com.lyndseypadget.resteasy.model.FruitComparator; @Path("/fruits") public class FruitService { private static Comparator comparator = new FruitComparator; private static Map apples = new TreeMap; private static int appleCount = 0; @GET @Path("/apples") @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public List getApples { List retVal = new ArrayList(apples.values); Collections.sort(retVal, comparator); return retVal; } @GET @Path("/apples/{id}") @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public Response getApple(@PathParam("id") String id) { Apple found = apples.get(id); if(found == ) { return Response.status(404).build; } return Response.ok(found).build; } @DELETE @Path("/apples/{id}") public Response deleteApple(@PathParam("id") String id) { apples.remove(id); return Response.status(200).build; } @POST @Path("/apples") @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) public Response createApple(Apple apple) { String newId = Integer.toString(++appleCount); apple.setId(newId); apples.put(newId, apple); return Response.status(201).header("Location", newId).build; } }

    如此,就增加了一些新的功能:

    • 通过 id检索苹果数据 (如果在地图中没有找到,则返回状态代码 404)

    • 通过 id删除苹果数据

    • 创建新的苹果数据 (如果成功的话,返回状态代码 201)

    这些方法完善了很多功能,确保了服务可以按照预期工作。更新苹果 (使用 @PUT和 /或 @PATCH),以及更多的关于端点、逻辑和管理持久性方面的功能操作,都留给读者你们来练习吧。

    当我们再次进行构建和部署时会发现 (如果用 Maven或者 Tomcat来进行设置,请参阅上文“构建和部署”),现在已经可以在服务中创建、检索和删除苹果了。而且即使不在服务器上做任何重新配置,也可以在 XML和 JSON之间进行选择性调用。

    来创建一个拥有“application/json”内容类型和 JSON主体的苹果,如下图所示:

    揭秘:RESTEasy如何完美支持JAVA 微服务中的多种数据格式

    这是另一个例子:创建一个具有“application/xml”内容类型和 XML主体的苹果。

    揭秘:RESTEasy如何完美支持JAVA 微服务中的多种数据格式

    在 XML中检索所有的苹果数据:

    揭秘:RESTEasy如何完美支持JAVA 微服务中的多种数据格式

    在 JSON中通过 id检索 apple 2的数据:

    揭秘:RESTEasy如何完美支持JAVA 微服务中的多种数据格式

    通过 id删除 apple 1的数据:

    揭秘:RESTEasy如何完美支持JAVA 微服务中的多种数据格式

    在 JSON中检索所有苹果的数据:

    揭秘:RESTEasy如何完美支持JAVA 微服务中的多种数据格式

    小结

    在此我们已经探讨了 RESTEasy架构如何在 Java web服务中无缝支持 XML和 JSON数据传输格式。我还解释了 REST、Media type(数据传输格式)、web服务和微服务之间的技术差异,因为在这些术语中有很多容易混淆的灰色地带。

    我这里列举的例子可能有点勉强,生活中我其实从来没有真正需要过水果相关的数据,我也没有在食品行业工作过。之所以用水果来举例,是因为我觉得这个“规模”能有助于大家理解微服务的概念,你也可以想象其他例如蔬菜,罐头或海鲜这样的微服务是如何共同构成一个食物分配系统。现实世界中,食品杂货店的食物分配系统实际上非常复杂,它必须考虑到包括销售、优惠券、过期日期、营养信息等各方面的问题。

    当然,你可以选择其他方式去对系统进行分割,但当你需要一种快速高效、轻量级工具来支持多种数据格式时,RESTEasy真的是个非常不错的选择。

    参考地址:

    [1]、[2]、[6] https://tomcat.apache.org/

    [3] https://en.wikipedia.org/wiki/KISS_principle

    [4] https://github.com/lyndseypadget/resteasy-demo

    [5] http://www.oracle.com/technetwork/java/javase/downloads/index.html

    [7] https://maven.apache.org/

    [8] https://www.eclipse.org/downloads/

    [9] https://linuxmint.com/edition.php?id=237

    [10] https://stackify.com/what-are-microservices/

    [11] https://www.iana.org/assignments/media-types/media-types.xhtml

    [12] http://resteasy.jboss.org/

    [13] https://jcp.org/aboutJava/communityprocess/final/jsr339/index.html

    [14] http://www.oracle.com/technetwork/articles/javase/index-140168.html

    [15] https://github.com/jettison-json/jettison

    [16] http://resteasy.jboss.org/downloads

    [17] https://tomcat.apache.org/tomcat-9.0-doc/appdev/deployment.html

    [18] https://www.ietf.org/rfc/rfc2616.txt

    [19] https://www.getpostman.com/

    期望得到更多优质技术干货,欢迎加入 EAWorld社区,与近万名技术人一起成长。入群暗号:328

    展开全文
  • 物流向导:“物流向导”是一种端到端的智能供应链管理解决方案,展示了如何在现实世界执行混合云,微服务和预测性数据分析
  • 在微服务架构,每个微服务都有自己私有的数据集。不同微服务可能使用不同的SQL或者NoSQL数据库。尽管数据库架构有很强的优势,但是也面对数据分布式管理的挑战。第一个挑战就是如何在多服务之间维护业务数据一致性...
  • 图片来自 Pexels本文,我们将先介绍数据网格(Data Grid)的基本概念、属性、以及能够提供的服务,然后讨论如何设计可扩展的数据网格,以满足实际场景的业务需求。什么是数据网格?数据网格是一组能够提供共享数据...

    【51CTO.com原创稿件】在本文中,我们先介绍数据网格的基本概念、属性、以及能够提供的服务,然后讨论了如何设计可扩展的数据网格,以满足实际场景的业务需求。

    87b1aa7ab01da26655034a33d02e8670.png

    图片来自 Pexels

    在本文中,我们将先介绍数据网格(Data Grid)的基本概念、属性、以及能够提供的服务,然后讨论如何设计可扩展的数据网格,以满足实际场景的业务需求。

    什么是数据网格?

    数据网格是一组能够提供共享数据管理的服务,它可以通过网格状的结构,去访问源自各种应用程序与服务的异构数据。

    在技术实现上,我们通常可以采用功能强大的中间件应用程序和服务,实现对于源于各种应用请求的数据输入与查询。

    网格中的数据往往可以通过诸如 REST、以及 JSON 格式的 API 被访问到。这些数据既可以被保存到磁盘上,也能够备份到另一个数据库里。

    不同的服务可以将 JSON 格式的数据保存到网格之中,并在不到一毫秒的时间内实现数据查询(类似于缓存)。

    以下便是数据网格的基本属性:

    • 使用 API(基于 REST 的 JSON 格式)从网格进行数据访问。
    • 其本质上具有真正的弹性,即:可以水平缩放而没有上限。
    • 能够支持任何体量的数据。
    • 具有耐用性,可应对各种宕机和系统故障。
    • 提供低延迟的响应。

    它的选配属性则包括:

    • 可以利用诸如:JWT、TSL 客户端验证等方案,对网格中的每一种数据请求进行授权。
    • 能够清除数据,并为更多相关数据留出空间。
    • 能够将数据持久地保存到磁盘上。
    • 能够从诸如:RDBMS 或 NoSQL 存储等其他数据源,进行数据的热加载(hot-load)。

    数据网格的使用

    在一个真正的微服务架构系统中,每一项服务都拥有自己的私有数据库(即:每个服务模型都配有一个数据库)。

    如果其中的任何一项服务需要横跨多个服务获取数据的话,那么我们就需要以诸如:JSON、XML 或二进制格式,来处理这些服务的响应。

    而有些请求既可能使用的是 REST 标准的 HTTP(S)请求,也可能使用 SOAP 请求,还有可能使用 RPC 等请求。

    不过,真正的挑战并非在技术上,而是在处理诸如安全异常、数据验证、握手、网络、数据解析等失败的情况下,微服务将如何应对。

    在实际应用中,我们常常会碰到高度依赖性的问题。也就是说:生产者(producer)服务中的任何变更都可能会更改响应的结构,而消费者(consumer)服务也可能需要跟着适应此类变更。

    如果消费者服务仅从其他服务中查询数据(而非请求任何计算结果),那么该方式则可能无效。

    为了解决上述问题,我们引入了数据网格的方法,该方法几乎能够提供任意数量的自定义数据存储,并且具有高度可扩展性和易于维护的低延迟响应。

    在此,我们将 Apache Ignite(https://ignite.apache.org/,以下简称为 Ignite)作为数据网格设计中的主要组件之一,由它提供具有持久性、弹性和分布式的内存平台。

    此外,Ignite 还提供了多种缓存选项,可连接 RDBMS 和 NoSQL 存储,以及计算服务等功能。

    数据定义

    通常,若要为基础架构构建数据网格,所有的微服务都应当发布各自写入网格的数据格式。

    例如:用户服务(即:管理某个系统中所有用户信息的服务)应当发布所有具有 upsert 和 delete 操作的用户信息,以及用户数据结构的定义。

    同时,此类数据定义应当能够支持版本控制,以便任何新的服务都可以查询到特定的最新版本。

    据此,所有相关的消费者服务也都可以从“数据网格”中查询到数据定义,进而构建相应的服务功能。

    以下是一个已发布的用户数据结构(版本 1)的代码示例。其对应的 URL 为:

    https:///grid/datadefinition&type=user&version=1.

    047007e762c9ba72daf9d72921a8ab9e.png

    如下是对于用户数据定义版本 2 的查询代码,其对应的 URL 为:

    https:///grid/datadefinition&type=user&version=2.

    577997d76dc184bda42cebd4bab8fa0c.png

    高级设计

    我们可以使用某个在线购物网站为例,来展示数据网格的系统设计。该购物网站是采用各种微服务(例如:用户服务、订单服务、产品目录服务、以及其他服务)来构建的。

    这些微服务有助于实现从各种目录中订购产品,并最终将其交付给客户。下图是数据网格的完整工作流程:

    b649cc96338c094bb783228c3ff15a30.png

    各个组件服务

    数据层

    这是数据网格的核心,其中部署了 Apache Ignite 的服务器端模式设置,并构成了“Ignite 服务器群集”。

    在此,Ignite 提供了如下可用于构建可扩展网格的功能:

    • 通过内存中缓存,实现低延迟的响应。
    • 分布式的持久存储。
    • 弹性,即:通过添加节点,实现水平扩展。
    • 容错,即:数据复制,以及在节点出现故障时的自动负载均衡。
    • 针对磁盘或数据库的数据复制和持久性。

    Ignite 也可以在无主控的架构上工作,并通过拆分其他节点,只向群集组中添加额外的内存内(in-memory)缓存空间。

    另外,通过 Ignite 提供的各种缓存配置,您可以按需对其进行调整和增强。此类配置包括:数据持久性选项、缓存的逐出策略、以及数据复制等方面。

    数据网格的 API 网关

    该网关可以将查询请求路由到适当服务器上。同时,多个服务也可以被注册到该网关上,以便根据真实的负载,来处理和调节各种请求。

    查询服务与更新服务

    这是一些大规模的应用服务,可用于查询数据,或将数据更新并添加到数据层,也就是“Ignite 服务器群集”上(有关数据层的可视化,请参见上图)。

    查询服务设置将使用 Ignite 的客户端库(即:配置为客户端模式)连接到 Ignite 服务器群集,并成为 Ignite 群集拓扑中的一部分。

    如果这些服务并不会被作为 Ignite 的客户端节点加入群集拓扑,那么我们可以使用 Ignite 的瘦客户端(如:Java Thin Client 或 Node.js Thin Client)去连接到 Ignite 服务器集群,并执行各种缓存操作。

    而且,每个服务都能够更新 Ignite 服务器群集中的一到多个缓存。

    将数据推送到数据网格虽然会产生开销,但是我们可以通过使用异步机制,或者将数据推送到某些 Kafka 的 topic 上来解决。

    在此类 topic 中,数据网格的更新服务(Data Grid Update Service)会将其推送到 Ignite 的服务器群集之中。

    注意:应用服务会使用 Ignite 的客户端库,来进行各项缓存操作。在默认情况下,它们通过加入 Ignite 服务器群集拓扑,来充当服务器节点,以参与缓存任务。

    当然,这并不是必需的。我们需要在 Ignite 的配置文件中启用客户端模式标志(即:设置为 true),或者在应用服务的初始化时,调用某个类似的 Ignite API。

    有关 Ignite 客户端和服务器设置的更多信息,请参见:https://apacheignite.readme.io/docs/clients-vs-servers

    使用数据网格的示例

    在上图中,最左侧的组件是微服务,其中每个服务都有自己的数据库。在传统的非数据网格方法中,上例中的订购服务需要针对用户服务,去查询用户的相关信息(例如:用户的电子邮件与地址等)。

    而在圣诞节、感恩节等销售旺季,此类订购服务可能会遇到大量的交易请求。那么此类订购服务就必须调用相应的用户服务,以获取与交易数量成比例的用户相关信息。

    当然,订购服务可以缓存用户的信息,以避免多个网络的调用。或者,为了满足不断增加的用户服务负载,我们还可以向集群添加更多的用户服务节点,以处理各种读取请求。不过,总的说来,数据网格更适合于处理此类业务场景。

    当某个微服务有数据更新时,该数据将会被数据网格更新服务推送到数据网格之中。Ignite 服务器进而根据缓存配置将数据插入到缓存里。

    此外,由于 Ignite 具有持久性,因此我们可以添加任意数量的节点,以支持来自各种服务的大型数据集。

    这些 Ignite 服务器群集既可以通过原生持久性来启用,也可以连接到数据库上,以便保留各种缓存数据。

    当某个微服务需要访问特定的数据时,它会通过传递必要的查询参数,来使用数据网格的查询服务。

    由于查询服务连接着 Ignite 服务器,因此它可以从缓存中查询到数据。当然,如果数据不在缓存中,却已经启用了持久性,那么 Ignite 则可以从持久性存储中加载相应的数据。

    在极端情况下,如果缓存和持久性存储中的数据都不可用,那么查询服务则可以通过内置的逻辑,将请求重新路由到相应的微服务上,以获取数据并将其插入到缓存中。

    同时,该响应也会将请求发送给消费者服务,以便在下一个请求到来时,直接从数据网格本身获取对应的数据。

    由于插入到缓存中的数据是基于更新服务来部署缓存的,因此它确保了在任何微服务中的更新数据,都会在数据网格中可用。

    此外,由于 Ignite 具有持久性,因此我们可以添加任意数量的节点,以支持来自各种服务的大型数据集。

    总结

    本文提供了将消费者服务与生产者服务相解耦的思路,进而让用户能够灵活地向微服务群添加更多的服务,以构建和部署新的功能集。

    【51CTO原创稿件,合作站点转载请注明原文作者和出处为51CTO.com】

    展开全文
  • 图片来自 Pexels本文,我们将先介绍数据网格(Data Grid)的基本概念、属性、以及能够提供的服务,然后讨论如何设计可扩展的数据网格,以满足实际场景的业务需求。什么是数据网格?数据网格是一组能够提供共享数据...
  • 在本文,我们将介绍在微服务之间共享DTO的方法。 使用微服务管理表示应用程序域的模型。域模型的关注点与DTO不同,我们将它们与DAO层数据模型分开。 这样做的主要原因是我们不想通过服务向客
  • 您可以以下链接找到用于微服务数据管理的模式:[http://microservices.io/patterns/data/database-per-service.html。]顺便说一下,对于微服务架构而言,这是一个非常有用的博客。对于您的情况,您更喜欢按...
  • 本文,我们先介绍数据网格的基本概念、属性、以及能够提供的服务,然后讨论了如何设计可扩展的数据网格,以满足实际场景的业务需求。图片来自 Pexels本文,我们将先介绍数据网格(Data Grid)的基本概念、...
  • 微服务处理数据

    千次阅读 2017-04-13 11:03:46
    在微服务系统,具有混合持久性的环境时,有必要让数据处理保持可管理。为了解释如何实现此目标,本章会简要介绍微服务在数据处理方面的特征,然后介绍如何使用基于 Java 的微服务实现此目的。 微服务特定于数据的...
  • Java创建微服务

    2021-02-25 18:50:34
    本章将重点介绍如何识别和创建组成应用程序的微服务,特别是如何将识别的候选服务转换为RESTfulAPI,然后Java实现它。我们使用两个示例应用程序来帮助解释相关概念和提出观点:在线零售店是一个讨论应用程序...
  • 微服务原则:去中心化数据管理

    千次阅读 2019-01-29 10:55:14
    传统的整体式软件设计方法,我们通常使用整体式的数据存储,例如包含诸多表格(Table)的单个数据库的 SQL 服务器。这种中央数据库作为全体数据的持久性引擎而被使用,并且通常应用程序逻辑的一部分以使用复杂...
  • 举一个例子, 用户管理一个服务, 账务管理一个服务 这里要显示每一个用户信息和余额, 各个服务数据是独立的, 原来关系型表通过联很好实再, 但划分微服务后,一般如何处理?
  • 相关阅读: 基于X-admin2.2的后台管理系统登录实现 Spring Boot整合Java web项目 Spring Boot 踩坑系列之Error resolving template ...最近写基于Spring Boot+JPA的微服务进行数据更新时,发现始终无法通过**sa
  • 分布式数据管理(CRUD)解决方案之前,有必要介绍下CAP原理和最终一致性相关概念。CAP原理(CAPTheorem)足球比赛里,一个球员一场比赛中进三个球,称之为帽子戏法(Hat-trick)。分布式数据系统,也有一个帽子...
  • 在本文,我们将介绍在微服务之间共享DTO的方法。 2. 将域对象发布为DTO 使用微服务管理表示应用程序域的模型。域模型的关注点与 DTO 不同,我们将它们与DAO层数据模型分开。 这样做的主要原因是我们不想通过...
  • 微服务如何实现联合查询

    万次阅读 2019-07-24 15:48:56
    微服务架构下,用户信息、机构信息这种基础数据多个系统中都会用到,数据库设计该怎么搞?比如我们现在有个核心系统放代理人信息,要再开发一个代理人管理子系统,里面对于代理人的名称和所属机构两边都要用,...

空空如也

空空如也

1 2 3 4 5 ... 15
收藏数 286
精华内容 114
关键字:

在微服务中如何管理数据