react 命令_OO功能命令式React式与头等程序编织在一起

react 命令

这是分两部分的文章的第一部分,讨论“头等程序”,这是我用来最好地描述该概念的术语。

尽管函数式编程通过一流的函数提供组合,而对象定向通过对象提供组合,但对我而言,一流的过程是这两个概念的融合。 我发现对象定向遭受隐式对象引用的问题(例如,想要一根香蕉和让大猩猩进入丛林),而函数编程(和OO)则遭受隐式线程的问题。 头等程序解决了这些问题,因此组成,状态和执行都是单独的关注点。

现在要写关于一流程序的完整说明已经超出了一篇文章。 有许多模式一起使用,以通过一流的程序进行合成。 因此,我将分两部分介绍一流的过程:

  • 本文通过工作代码演示如何使用一流的过程进行灵活灵活的组合
  • 下一篇文章将提供一个与头等舱程序如何演变成其当前理解的理论更接近的解释

我们将以一些简单的示例开始,然后将一流的过程进行更有趣的编织。

头等舱程序

简单事件循环

以下第一类过程为REST请求提供服务。 这将在HTTP套接字事件循环线程上运行。

 public void service(ObjectResponse<ServicedThreadResponse> response) { 
     response.send( new ServicedThreadResponse(Thread.currentThread().getName(), "Event" , System.currentTimeMillis()));  } 

每个请求简单的线程

以下第一类过程通过从数据库中提取一个值并将其发送到响应中来服务REST请求。 这将由单独的线程池运行。

 public void service(ServicedThreadRequest request, ThreadPerRequestRepository repository, ObjectResponse<ServicedThreadResponse> response) { 
     int identifier = request.getIdentifier() % 10 ; 
     ThreadPerRequest entity = repository.findById(identifier).get(); 
     response.send( new ServicedThreadResponse(Thread.currentThread().getName(), entity.getName(), System.currentTimeMillis()));  } 

使用线程的区别将在后面讨论。 但是,现在请注意,每个请求线程的一流过程仅使用Spring Repository。

头等程序编织在一起

好的,上面有点无聊。 我们之前已经在Web应用程序服务器中看到了这一点。 给我们展示一些有趣的东西!

为了展示更多有趣的东西,我们将编织一流的过程,以实现以下目标:

  1. 验证请求(在套接字事件循环线程上)。
  2. 启动事务并将请求注册到数据库中。 这将在另一个线程上,以避免暂停套接字事件循环线程。
  3. 进行响应式调用以从其他服务中提取数据。
  4. 运行一些功能代码以计算出维修时间的标准偏差。
  5. 采取替代流程来处理特殊情况(包括处理异常)。 然后,如果没有引起回滚的异常,则将结果存储在数据库中。 这也是在另一个线程上,以不占用响应事件循环线程。
  6. 提交事务后发送响应

我们将按照上面的顺序处理每个头等程序。

验证请求(在套接字事件循环上)

这是对请求是否正确的简单验证。 由于它是直接逻辑,因此我们使用套接字事件循环的线程。 这样,我们不必支付线程上下文切换的开销和拒绝无效请求的线程开销。 代码如下:

 const HttpException = Java.type( "net.officefloor.server.http.HttpException" );  const Integer = Java.type( "java.lang.Integer" )  function validate(identifier, requestIdentifier) { 
     if (Number(identifier) <= 0 ) { 
         throw new HttpException( 422 , "Invalid identifier" ); 
     } 
     requestIdentifier.set(Integer.valueOf(identifier))  }  validate.officefloor = [ 
     { httpPathParameter: "identifier" }, 
     { out: Integer }, 
     { next : "valid" }  ]; 

请注意,验证是用JavaScript编写的。 这样一来,客户端JavaScript验证规则可以重新用于验证请求,以确保客户端和服务器之间的一致性。

添加到该函数的officefloor属性提供元数据。 这是必要的,因为JavaScript无法提供一流过程所需的强类型信息。

必须在数据库中注册请求

验证之后,请求标识符将在数据库中注册。 这还将基于数据库中的IDENTITY列为请求创建一个唯一编号。

 @Next ( "registered" )  public static void registerRequest( @Val int requestIdentifier, WeavedRequestRepository repository, Out<WeavedRequest> weavedRequest) { 
     WeavedRequest entity = new WeavedRequest(requestIdentifier); 
     repository.save(entity); 
     weavedRequest.set(entity);  } 

React性

接下来是一些Reactive代码,以同时调用本文开头详细介绍的两个REST端点(简单事件循环和每个请求简单线程)。 因为我们使用的是Reactive,所以我们可以同时调用它们以提高性能。

请注意,在等待响应时,该流实际上是空闲的,线程为其他功能提供服务。 这是异步处理,因此线程不必等待。 一旦两组结果都返回,它们就会通知相应的异步流以继续进行处理。

 private final static String URL = " http://localhost:7878/ {path}" ;  @Next ( "useData" )  public static void retrieveData(WebClient client, 
         AsynchronousFlow eventLoopFlow, @EventLoopResponse Out<ServicedThreadResponse> eventLoopResponse, 
         @Val WeavedRequest request, AsynchronousFlow threadPerRequestFlow, @ThreadPerRequestResponse Out<ServicedThreadResponse> threadPerRequestResponse) { 
     Flux.range( 1 , 10 ) 
         .map((index) -> client.get().uri(URL, "event-loop" ).retrieve().bodyToMono(ServicedThreadResponse. class )) 
         .flatMap((response) -> response).collectList().subscribe((responses) -> eventLoopFlow.complete( 
             () -> eventLoopResponse.set(responses.stream().toArray(ServicedThreadResponse[]:: new )))); 
     Flux.range( 1 , 10 ) 
         .map((index) -> client.post().uri(URL, "thread-per-request" ).contentType(MediaType.APPLICATION_JSON) 
             .syncBody( new ServicedThreadRequest(request.getId())).retrieve() 
             .bodyToMono(ServicedThreadResponse. class )) 
         .flatMap((response) -> response).collectList().subscribe((responses) -> threadPerRequestFlow.complete( 
             () -> threadPerRequestResponse.set(responses.stream().toArray(ServicedThreadResponse[]:: new ))));  } 

现在,您可能已经注意到Out / @ Val组合了。 这就是将值从一个一级过程传递到另一个一级过程的方式。 请注意,如果不同值的类型相同,则可以使用限定符来区分它们。 其余参数由依赖项注入(在本例中为Spring)提供。

功能性

接下来,将React性响应提供给Scala功能代码,以确定服务时间的标准偏差。

 def mean(timestamps: Iterable[Long]): Double = timestamps.sum.toDouble / timestamps.size  def variance(timestamps: Iterable[Long]): Double = { 
     val avg = mean(timestamps) 
     timestamps.map(timestamp => math.pow(timestamp.toDouble - avg, 2 )).sum / timestamps.size  }  def stdDev(timestamps: Iterable[Long]): Double = math.sqrt(variance(timestamps))  @Next ( "use" )  def standardDeviation( @EventLoopResponse @Val eventLoopResponses: Array[ServicedThreadResponse], @ThreadPerRequestResponse @Val threadPerRequestResponses: Array[ServicedThreadResponse]): Double = 
     stdDev((eventLoopResponses ++ threadPerRequestResponses).map(response => response.getTimestamp)) 

请注意,可以使用库来减少此代码。 但是,我们这样做是为了演示如何将功能代码集成到一流的过程中。

流量控制

下一个一流的过程触发处理特殊情况的流程。 如果特殊情况没有问题,则将标准偏差存储在数据库中。

 @FlowInterface  public static interface Flows { 
     void handleSpecialCases(FlowSuccessful callback); 
     void stored();  }  public static void store( @Parameter double standardDeviation, Flows flows, @Val WeavedRequest request, WeavedRequestRepository repository, Out<RequestStandardDeviation> stDevOut) { 
     flows.handleSpecialCases(() -> { 
         request.setRequestStandardDeviation( new RequestStandardDeviation(standardDeviation, request)); 
         repository.save(request); 
         stDevOut.set(request.getRequestStandardDeviation()); 
         flows.stored(); 
     });  } 

特殊情况的处理是通过以下一流程序进行的。

 public static void handleSpecialCase( @Val WeavedRequest request) throws WeavedRollbackException, WeavedCommitException { 
     switch (request.getRequestIdentifier()) { 
         case 3 : 
             throw new WeavedRollbackException(request); 
         case 4 : 
             throw new WeavedCommitException(request); 
     }  } 

触摸异常处理

两种异常处理一流的过程如下。

 public static void handle( @Parameter WeavedRollbackException exception, ObjectResponse<WeavedErrorResponse> response) { 
     WeavedRequest request = exception.getWeavedRequest(); 
     response.send( new WeavedErrorResponse(request.getRequestIdentifier(), request.getId()));  }  public static void handle( @Parameter WeavedCommitException exception, WeavedRequestRepository repository, ObjectResponse<WeavedErrorResponse> response) { 
     WeavedRequest request = exception.getWeavedRequest(); 
     request.setWeavedError( new WeavedError( "Request Identifier (" + request.getRequestIdentifier() + ") is special case" , request)); 
     repository.save(request); 
     response.send( new WeavedErrorResponse(request.getRequestIdentifier(), request.getId()));  } 

第二个处理程序在事务中工作,因此包括存储在数据库中的其他数据。

请注意,由于一流的过程组合不需要调用方捕获异常,因此包含了检查的异常。 我们认为检查异常对于流程组成非常有用。 但是,区别在于它不应该是呼叫者的关注,而应该是流程的关注。 对我来说,这是一个很大的不同,并且可以停止catch和log异常处理问题。 现在,异常处理是一个单独的问题,可以在以后进行编码。

成功回应

成功将请求详细信息存储在数据库中后,以下第一类过程将发送响应。

 public void send( @Val WeavedRequest request, @Val RequestStandardDeviation standardDeviation, @EventLoopResponse @Val ServicedThreadResponse[] eventLoopResponse, 
         @ThreadPerRequestResponse @Val ServicedThreadResponse[] threadPerRequestResponse, ObjectResponse<WeavedResponse> response) { 
     response.send( new WeavedResponse(request.getRequestIdentifier(), request.getId(), eventLoopResponse, threadPerRequestResponse, standardDeviation.getStandardDeviation()));  } 

Kotlin面向对象

哦,还有一点多语言的乐趣,下面是用于表示JSON请求/响应的OO对象。

 @HttpObject  data class ServicedThreadRequest(val identifier: Int)  data class ServicedThreadResponse(val threadName: String, val lookupName: String, val timestamp: Long)  data class WeavedErrorResponse(val requestIdentifier: Int, val requestNumber: Int)  data class WeavedResponse(val requestIdentifier: Int 
         , val requestNumber: Int 
         , val eventLoopResponses: Array 
         , val threadPerRequestResponses: Array 
         , val standardDeviation: Double) 

证明它可行

以下是确认一流程序服务流程请求的测试。

 public static final SpringRule spring = new SpringRule();  public static final OfficeFloorRule officeFloor = new OfficeFloorRule();  @ClassRule  public static final RuleChain ordered = RuleChain.outerRule(spring).around(officeFloor);  @Rule  public final HttpClientRule client = new HttpClientRule();  private static final ObjectMapper mapper = new ObjectMapper();  static { 
     mapper.registerModule( new KotlinModule());  }  @Test  public void confirmWeavedTogether() throws Exception { 
     HttpResponse response = this .client.execute( new HttpPost( this .client.url( "/weave/1" ))); 
     assertEquals( "Should be successful" , 200 , response.getStatusLine().getStatusCode()); 
     WeavedResponse body = mapper.readValue(EntityUtils.toString(response.getEntity()), WeavedResponse. class ); 
     WeavedRequest entity = spring.getBean(WeavedRequestRepository. class ).findById(body.getRequestNumber()).get(); 
     assertNotNull( "Should have standard deviation stored" , entity.getRequestStandardDeviation());  } 

编织在一起

下图是将上面的一流过程编织在一起的配置。

头等程序

这是将一流的过程组合在一起的唯一配置/代码。 注意,这些名称表示第一类过程名称及其各自的元数据。

这意味着,检查所有呼叫和测试上的端口。 是的,您在上面看到的所有内容都在一个端口上运行。 是的,您不必在仅提供每个请求线程数或单线程事件循环的框架之间进行选择。 这是由于一流过程的线程注入所提供的执行策略。

螺纹注射

实际上,线程配置如下:

 <teams> 
     <team source= "net.officefloor.frame.impl.spi.team.ExecutorCachedTeamSource" type= "org.springframework.data.repository.CrudRepository" />  </teams> 

在这里,我们标记所有需要由线程池执行Spring Repository的过程。 记住我说过要注意使用Spring Repository。 好了,上面的配置具有任何需要由配置的线程池执行的Spring Repository的一流过程。 请注意,由于来自Office的一流过程的建模起源,线程池被称为团队。

因此,再次查看流程,线程执行如下:

  1. 验证使用套接字侦听器事件循环的线程
  2. 注册请求使用一个Spring Repository,因此执行被交换到已配置线程池中的线程
  3. 这个线程进行异步React调用
  4. 然后,React式事件循环线程将调用回调。 由于Scala代码可以快速执行,因此响应事件循环线程会继续执行Scala纯函数。 这里认为线程上下文切换的开销太大,仅调用高度优化的Scala纯函数会更有效。 但是,如果要将Scala函数分离到不同的线程池,则可以在不同的线程池中进行配置(通常通过对第一类过程的标记依赖性)。
  5. 剩余的命令性代码可以从已配置的线程池切换回线程,具体取决于Spring存储库。 此外,线程之间的线程局部变量会传播到每个使用的线程,因此不会丢失Spring Repository事务(即,事务在事务范围内对所有第一类过程都是活动的)。
  6. 然后发送响应。

现在,以上所有内容都可以通过Thread Injection配置。 例如,如果我们有多个同步数据存储,则可以创建一个线程池与每个数据存储进行交互,以避免一个慢速数据存储占用应用程序的所有线程。

这也意味着您可以为不同的环境配置不同的线程,而不必更改任何代码。

免责声明

在现实世界的应用程序中,我将尽量避免使用上述许多编程语言。 我会尝试将它们简化为几个,以避免涉及过多的技能,从而增加了应用程序的维护成本(并且减少了混合编译的问题)。 这只是演示如何将OO,功能,命令式和React式代码与一流的过程结合在一起。 此外,它演示了如何在抽象之前编写具体的解决方案

同样,如您所见,在每个编程范例中我们都必须涵盖很多广度。 如果代码不能很好地表示范例,那么我们很乐意从更熟悉特定范例的人员那里获得有关改进的反馈。

如果我们错过了重要的范例,请让我知道,以便我们可以考虑将其包括在内。 在编码方面,我们感谢多样性为开发人员提供了选择。 我们正试图打破范式之间的界限,以建立一个幸福的大编码家族。

摘要

我们已经展示了一流的过程如何将以不同范例编写的多语言代码编织在一起以服务请求。 本文上面概述的代码是该应用程序所需的所有代码。 无需其他编织代码。

此外,为了避免它仅在我的机器上起作用的问题(在本文中),可以在此处获得以上代码。 请参阅自述文件 ,了解如何运行它。

要进一步了解正在发生的事情,请参阅教程其他文章 ,尤其是下一篇文章

翻译自: https://www.javacodegeeks.com/2019/05/oo-functional-imperative-together-first-class-procedures.html

react 命令

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值