365bet地区异步框架

365bet地区365bet地区异步库

4.0.0

介绍

使用具有多个内核的现代硬件,许多编程语言都在添加异步并行编程API,Groovy也不例外

365bet地区添加了365bet地区,并且由于是外部项目,365bet地区的异步功能旨在简化框架内的并发编程,并包括Promises和统一事件模型的概念

承诺

Promise是许多并发框架所接受的概念,它们类似于java util并发Future实例,但包括更用户友好的异常处理模型,有用的功能,例如链接和附加侦听器的功能

365bet地区要使用365bet地区 Promise抽象,您应该在异步的您的插件建立gradle文件

建立gradle
运行时组织grails插件异步

承诺基础

365bet地区在365bet地区中使异步承诺类提供Promise API的入口点

进口 静态的 使异步承诺

要创建承诺,您可以使用任务返回一个实例的方法使异步Promise接口

定义p任务2 * 2 }
定义p任务4 * 4 }
定义p任务8 * 8 }
断言 [4,16,64等待全部

waitAll方法等待同步阻塞当前线程以完成所有并发任务,并返回结果

如果您不希望阻止当前线程,则可以使用onComplete方法

onComplete p p p清单结果断言 [4,16,64结果

waitAll如果执行诺言之一时发生错误,则该方法将引发异常。将引发原始异常。onComplete但是,如果发生异常,则该方法将不会简单地执行传递的闭包。onError侦听器,如果您希望处理异常而不阻塞

onError p p p可投掷t println"发生错误${消息}"
}

如果您只有一个长期承诺,那么使异步Promise接口在promise本身上提供了类似的API,例如

进口 静态的 java有用的并发TimeUnit
进口 静态的 使异步承诺承诺任务长期运行的任务p onError可投掷错误打印"发生错误${错误消息}"p on完成结果打印"承诺退货$结果"
}
阻塞直到结果被调用
定义结果p得到封锁指定的时间
定义结果p得到1分钟

PromiseFactory界面

默认情况下承诺静态方法使用的实例无极工厂这个无极工厂接口具有多种实现方式,默认实现是CachedThreadPoolPromiseFactory使用线程池将根据需要创建线程,该线程池与java util并发执行器newCachedThreadPool)

365bet地区但是,365bet地区 Promise框架的设计是这样的,您可以将基础实现替换为自己的实现或预先支持的实现之一。例如,使用RxJava x只需将RxJava依赖项添加到建立gradle:

建立gradle
运行时组织grails grails异步rxjava

通过以上操作,将使用RxJava x创建诺言实例

下表总结了可用的实现以及应添加以激活它们的依赖项

表承诺工厂实现
构架 相依性 实现类

GPar x

365bet地区异步gpars

组织grails异步工厂gpars GparsPromiseFactory

RxJava x

365bet地区异步rxjava

组织grails异步工厂rxjava RxPromiseFactory

RxJava x

365bet地区异步rxjava

组织grails异步工厂rxjava RxPromiseFactory

您也可以覆盖365bet地区异步PromiseFactory使用的类承诺通过设置promiseFactory静态场

365bet地区一个常见的用例是单元测试,通常您不希望诺言在单元测试期间异步执行,因为这会使测试更难编写。为此,365bet地区附带了一个组织grails异步工厂SynchronousPromiseFactory实例,更容易测试承诺

进口 组织grails异步工厂
进口 365bet地区异步承诺promiseFactory同步承诺工厂

使用无极工厂365bet地区从理论上讲,可以将其他并发库插入365bet地区框架中。为此,您需要覆盖两个接口使异步Promise365bet地区异步PromiseFactory.

承诺链

可以将多个promise链接起来,然后使用然后方法

最后抛光最后转变最后保存最后通知Promise Promise任务长期运行的任务承诺然后抛光然后转换然后保存然后通知最终结果
}

如果在链中的任何点发生异常,它将被传播回调用方,并且不会调用链中的下一步

承诺清单和地图

365bet地区365bet地区异步API还具有承诺列表和映射的概念,它们由365bet地区异步PromiseList异步PromiseMap分别上课

创建承诺清单或地图的最简单方法是通过任务的方法承诺

进口 静态的 使异步承诺

定义promiseList任务2 * 2 }, { 4 * 4}, { 8 * 8 }])

断言 [4,16,64promiseList得到

任务传递闭包列表时的方法返回一个承诺清单您也可以构造一个承诺清单手动地

进口 365bet地区异步

定义清单PromiseList列表2 * 2清单4 * 4清单8 * 8列出完整清单结果断言 [4,16,64结果
承诺清单类不实现Java util List接口,而是从get方法返回一个Java util List

与...合作承诺图实例非常相似同样,您可以使用任务方法

进口 静态的 使异步承诺

定义promiseList任务:{ 2 * 2 },
                        :{ 4 * 4},
                        :{ 8 * 8 }

断言 [:4,:16,:64promiseList得到

或构造一个承诺图手动地

进口 365bet地区异步

定义地图PromiseMap地图''] = { 2 * 2地图''] = { 4 * 4地图''] = { 8 * 8映射完成地图结果断言 [:4,:16,:64结果

委托异步转换

要求同时使用同一个API的同步版本和异步版本是很常见的,因为同时开发异步API会简单地委派给同步版本,这两者都会导致维护问题。

委托异步转换旨在通过将任何同步API转换为异步API来缓解此问题

例如考虑以下服务

 图书服务 {
    清单<findBooks标题实作
    }
}

findBooks方法在与调用方相同的线程中同步执行。要制作此API的异步版本,您可以定义另一个类,如下所示

进口 365bet地区异步

 AsyncBookService {
   委托异步BookService bookService

委托异步转换将自动向该对象添加一个类似于以下内容的新方法AsyncBookService

诺言清单<findBookstitle承诺任务bookService findBooks title

如您所见,转换添加了返回Promise并异步执行的等效方法

AsyncBookService然后可以注入到其他控制器和服务中,并按以下方式使用

AsyncBookService asyncBookService定义 findBooks(标题asyncBookService findBooks标题onComplete清单结果打印"图书${结果}"
       }
}

大事记

365bet地区365bet地区引入了一个新的Events API,该API取代了以前基于Reactor x的实现,该实现不再维护和弃用

365bet地区在365bet地区和更高版本中事件总线已经引入了抽象无极工厂这个概念有一些实现事件总线通用异步框架(如GPars和RxJava)的接口

365bet地区要使用365bet地区事件抽象,您应该在大事记您的插件建立gradle文件

建立gradle
运行时组织grails插件事件

365bet地区如果类路径中不存在异步框架,则默认情况下,365bet地区将基于当前活动框架创建一个EventBus无极工厂默认实现是CachedThreadPoolPromiseFactory使用线程池将根据需要创建线程,该线程池与java util并发执行器newCachedThreadPool).

如果您希望使用流行的异步框架(例如RxJava)作为事件总线实现,那么您将需要添加适当的依赖关系,例如RxJava x

建立gradle
运行时组织grails grails事件rxjava

下表总结了异步框架支持和必要的依赖关系

表EventBus实现
构架 相依性 实现类

GPar x

365bet地区事件gpars

组织grails事件gpars ActorEventBus

RxJava x

365bet地区事件rxjava

组织grails事件rxjava RxEventBus

RxJava x

365bet地区事件rxjava

组织grails事件rxjava RxEventBus

活动发布

触发事件通知的最简单方法是通过发行人注解

例如

SumService groovy
进口 grails事件注释SumService类发行人
    整型整型一种整型a

上面所做的是获取方法的返回值并使用事件发布事件ID与方法名称相同

如果您想更改活动ID您可以为发行人:

发行人('myEvent')

如果您想要更大的灵活性,可以通过编写以下代码来模拟注释的行为

SumService groovy
进口 365bet地区事件SumService类实施事件发布整型整型一种整型b整型结果a b通知""结果返回结果

注意上面的例子中事件发布特质被明确实现

尽管通常建议使用注释方法,因为它更简单,更简洁,事件发布trait确实提供了更大的灵活性,因为它能够以一种方法发布多个事件,依此类推

订阅活动

消耗事件有几种方法。建议的方法是使用订户注释请注意,使用此注释的类必须是Spring bean

例如

TotalService规则
进口 grails事件注释TotalService类原子整数 原子整数(0)
    订户
    虚空Onsum整型addAndGet总数

在此示例中,每次事件发生,将调用订户

再次默认使用方法名称作为事件ID,尽管它可以以单词on开头。换句话说,方法名称为要么Onsum适用于上述示例,或者您可以提供事件ID进行订阅

订户('myEvent')

如果您希望动态订阅事件或需要更大的灵活性,那么另一种选择是与事件总线直接例如

TotalService规则
进口 grails事件总线EventBusAware
进口 javax注释PostConstructTotalService类实施EventBusAware原子整数 原子整数(0)

    后构造
    虚空初始化eventBus订阅"") { 整型addAndGet总数

在这个例子中全面服务来电订阅并在称为在里面在里面方法带有注释后构造所以在事件总线已由Spring注入,以确保仅调用一次且事件已正确订阅

反应堆弹簧注释

365bet地区在早于365bet地区的365bet地区版本中,使用了Reactor x,它提供了消费者选择器注释以监听事件,例如

进口 反应堆弹簧上下文注释

消费者
 我的服务 {
        选择器('myEvent')
        虚空myEventListener宾语数据打印"GOT事件$数据"
        }
}

grails事件兼容性365bet地区依赖365bet地区以及以上版本的这些注释可以桥接并为升级到365bet地区的应用程序提供兼容性,但是这些注释被认为已弃用,并且订户应该使用注解代替它提供等效的功能

GORM活动

GORM定义了有用事件数你可以听的

要订阅活动,只需定义一个订户接收事件类型作为参数

进口 grails事件注释采集insertEvents并发链接平台订户
虚空beforeInsert PreInsertEvent事件insertEvents添加事件
这些事件是异步触发的,因此无法取消或操纵持久性操作

如果您希望定义一个同步侦听器,则应改用听众注解

进口 grails事件注释gorm
...
听众
虚空tagFunnyBooks PreInsertEvent事件标题事件getEntityAccess getPropertyValue"标题")
    如果标题包含"滑稽"事件getEntityAccess setProperty"标题", "幽默${标题}"toString
如果您打算修改要插入的实体的属性,请使用实体访问如上所示的对象如果直接在实体上设置属性,它将被标记为脏,这将导致发布另一个更新

春季活动

Spring还会触发许多有用的事件org springframework使用弹簧这些事件默认情况下被禁用,因为它们会增加性能开销

启用S​​pring Event转换集春季事件真正yml应用程序在定义订户之前

例如

进口 grails事件注释
进口 组织springframework网站上下文支持
进口 org springframework启动上下文事件
...

大事记命名空间"弹簧")
 我的服务 {

    订户
    虚空applicationStarted ApplicationStartedEvent事件应用启动时触发
    }

    订户
    虚空servletRequestHandled RequestHandledEvent事件每次处理请求时触发
    }
}

配置默认事件总线

如果您包含上述混凝土之一事件总线RxJava或GPars的实现,则只需配置适当的实现即可

使用RxJava x可以做到RxJava挂钩与RxJava x与RxJava插件并与GParsGParsConfig

如果您使用默认实现,则可以覆盖由事件总线通过在中注册适当的beangrails app conf春季资源groovy:

grails app conf春季资源groovy
进口 组织grails事件总线
进口 Java有用的并发bean eventBus ExecutorEventBus执行者newFixedThreadPool5))
}

异步GORM

365bet地区由于365bet地区 GORM具有异步编程模型,该模型可在所有受支持的数据存储区,Hibernate MongoDB等中使用

尽管GORM异步执行持久性操作,但由于基础数据库驱动程序不是异步的,因此这些操作仍会阻塞。

AsyncEntity特性

365bet地区由于365bet地区的GORM异步部分是可选的,要启用它,您首先需要添加grails数据存储gorm异步依赖建立gradle:

建立gradle
编译"org grails grails数据存储gorm异步"

然后在您希望允许异步处理的域类中,应使用异步实体特征

进口 365bet地区 Gorm异步

 我的实体 实施异步实体 {
   ...
}

异步命名空间

异步实体实体特征提供了异步的以异步方式公开所有GORM方法的名称空间

例如,以下代码清单异步地从数据库读取对象

进口 静态的 使异步承诺

定义p人员异步获取)
定义p人员异步获取)
定义p人员异步获取)
定义结果waitAll p p p

使用异步的命名空间,所有常规GORM方法,甚至是动态查找器都可用,但不是同步执行,而是在后台运行查询,并且诺言实例返回

以下代码清单显示了异步执行GORM查询的一些常见示例

进口 静态的 使异步承诺人员异步列表onComplete清单结果打印"得到了人${结果}"
}
定义p人员异步getAll, , )
清单结果p定义p人员异步findByFirstName"荷马")
定义p人员异步findByFirstName"巴特")
定义p人员异步findByFirstName"巴尼"结果waitAll p p p

异步与会话

使用GORM异步时,每个诺言都会在不同的线程中执行由于Hibernate会话不是并发安全的,因此每个线程都绑定了一个新会话

当使用GORM异步(尤其是将Hibernate作为持久性引擎)时,这是一个重要的考虑因素。异步查询返回的对象将是分离的实体

这意味着您必须先将异步查询返回的对象重新合并到会话中,然后才能保存它们。例如,以下操作将不起作用

定义承诺人异步findByFirstName"荷马")
定义人许诺得到人名"巴特"人保存

相反,您需要将对象与绑定到调用线程的会话进行合并。上面的代码需要编写为

定义承诺人异步findByFirstName"荷马")
定义人承诺得到人合并人名字"巴特"

注意首先被调用是因为它可能会从缓存或数据库中刷新对象,从而导致更改丢失。通常,不建议在不同线程中读写对象,除非绝对必要,否则应避免使用此技术

最后,分离对象的另一个问题是关联延迟加载将不会工作,你会遇到LazyInitializationException如果这样做会出错,如果您打算访问异步查询返回的对象的关联对象,则应使用急切查询,无论如何建议这样做,以避免出现N个问题

多个异步GORM调用

如上一节所述,您应避免在不同的线程中读写对象,因为合并往往效率低下

但是,如果您希望异步执行更复杂的GORM工作,则GORM异步命名空间提供了任务使之成为可能的方法例如

定义承诺人与事务异步任务定义人员findByFirstName"荷马"人名"巴特"人保存齐平:真正人员更新人员承诺获得

请注意,GORM任务方法不同于静态承诺任务方法,它为您处理了将新会话绑定到异步线程的问题。如果您不使用GORM版本并与GORM进行异步工作,则需要手动执行此操作

进口 静态的 使异步承诺

定义承诺任务具有NewSession的人你的逻辑在这里
    }
}

异步DetachedCriteria

分离标准该类还支持异步的例如,您可以执行以下操作

DetachedCriteria查询Person的姓氏在哪里"辛普森"
}

定义承诺查询异步列表

RxJava支持

365bet地区由于可以使用365bet地区RxJava的365bet地区在365bet地区控制器中编写反应性逻辑,以利用基础容器的异步处理功能

要开始使用,只需在中声明对插件的依赖建立gradle:

建立gradle
依赖项编译'org grails插件rxjava'
}

然后您可以返回rx可观察365bet地区作为任何控制器的返回值,365bet地区将自动应用以下步骤

  1. 创建一个新的异步请求

  2. 产生一个订阅可观察对象的新线程

  3. 当可观察对象发出结果时,使用响应方法

有关如何使用RxJava插件的更多详细说明,请参见用户指南文档对于插件

服务器发送事件

服务器发送的事件SSE是一种技术,浏览器通过HTTP连接从服务器接收自动更新。服务器发送的事件EventSource API被W C标准化为HTML的一部分。

RxJava插件365bet地区向365bet地区添加了对SSE的支持,从而简化了编写与JavaScript客户端保持连续的非阻塞通信的控制器

例如

定义 指数接收流观察者观察者(1)
       对于一世 (0..5)) {
           如果一世2 == 0观察者onNext rx渲染"") (2)
               )
           }
           其他观察者onNext rx渲染"ock"睡觉1000 (3)观察者onCompleted(4)
   }
}
1 致电传递接受一个闭包的方法rx订户开始发送事件
2 使用发射一个或多个项目onNext
3 呼叫睡觉模拟缓慢的请求
4 呼叫onCompleted完成请求

有关如何使用SSE和RxJava插件的详细说明,请参见用户指南文档对于插件

RxGORM

RxGORM是GORM的新实现,它具有以下目标

  • 反应性

  • 不阻塞

  • 无状态

  • 简单

RxGORM不像异步GORM实现的目的是真正做到不阻塞驱动程序级别

以下是运行中的RxGORM的示例

取得ID订阅 打印"标题${标题}"
}

您可以将RxGORM与RxJava的365bet地区插件,用于实现365bet地区控制器的反应式响应,例如

定义 节目() {
        返回一个rx Observable
    获取参数id toString

有关如何使用RxGORM的更多信息,请参见RxGORM用户指南.

异步请求处理

如果要部署到Tomcat和更高版本的Servlet容器,则可以异步处理响应

通常,对于快速执行的控制器动作,异步处理请求几乎没有好处,但是对于长时间运行的控制器动作来说,这是非常有益的

原因是使用异步非阻塞响应时,一个线程一个请求一个响应关系被破坏了。容器可以使客户端响应保持打开和活动状态,同时将线程返回到容器中以处理另一个请求,从而提高了可伸缩性。

例如,如果您有可用的容器线程,并且如果未以非阻塞方式执行操作,则该操作需要一分钟才能完成,所有线程被占用且容器无法响应的可能性非常高,因此您应考虑异步请求处理

365bet地区由于365bet地区 365bet地区具有简化的API,可用于创建基于诺言先前讨论的机制

该实现基于Servlet异步,因此要启用异步功能,您需要在应用程序yml中将Servlet目标版本设置为

:
    小服务程序:
        : 3.0

异步模型

365bet地区365bet地区控制器中的典型活动是生成一个模型,该模型包含可以由视图呈现的键值对映射

365bet地区如果模型需要一段时间才能生成,则服务器可能会到达阻塞状态,从而影响可伸缩性。您可以通过返回a来指示365bet地区异步构建模型。异步PromiseMap通过承诺任务方法

进口 静态的 365bet地区异步Web WebPromisesdef索引任务图书: 异步列表totalBooks: 异步计数otherValue: {
           努力工作
         }
}

365bet地区365bet地区将在呈现视图之前异步处理响应,等待诺言完成。上面的等效同步操作是

定义 指数() {
    定义otherValue图书: 清单totalBooks: 计数otherValueotherValue

您甚至可以通过传递承诺图模型的属性渲染方法

进口 静态的 异步WebPromisesdef索引渲染视图:"我的看法", 模型任务:{ 2 * 2 },
                                       :{ 3 * 3 } )
}

异步响应渲染

365bet地区您也可以使用365bet地区及更高版本中的promise异步写入响应

进口 静态的 异步WebPromises
 库存控制员 {

    定义 股票(股票任务代码'高格'
           定义网址 网址("http下载金融yahoo com d报价csv s${股票代码}在csv上的nsl")
           价格网址文字分割',')[-1]  渲染"股票代码$股票代码价钱\$价钱"
       }
    }
}

上面的示例使用Yahoo Finance查询异步执行的股票价格,并且仅在获得结果后才呈现响应。这是通过返回a诺言控制器动作的实例

如果Yahoo URL无响应,则原始请求线程将不会被阻止,容器也不会无响应

Servlet异步

365bet地区除了本节前面讨论的高级异步功能之外,您还可以从365bet地区应用程序访问原始Servlet异步API。

Servlet异步渲染

为此,您应该首先实施365bet地区异步Web AsyncController控制器中的特征

进口 365bet地区异步网站类BookController实施异步控制器

您可以通过调用以下内容以异步方式呈现内容模板二进制数据等startAsync返回Servlet实例的方法异步上下文一旦您引用了异步上下文365bet地区您可以使用365bet地区常规渲染方法来渲染内容

定义 指数() {
    定义ctx startAsync ctx开始 (标题:"展台"保存渲染模板:"图书", 模型:[图书:列出ctx完成

请注意,您必须致电完成终止连接的方法

恢复异步请求

您可以继续处理异步请求,例如使用以下命令委托查看渲染调度的方法异步上下文

定义 指数() {
    定义ctx startAsync ctx开始做工作
        ...
        渲染视图ctx调度