Java系统中拆分同步和异步详解

 更新时间:2019年06月14日 10:18:16   作者:Petr Bouda  
这篇文章主要给大家介绍了关于Java系统中拆分同步和异步的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

很多开发人员说,将应用程序切换到异步处理很复杂。因为他们有一个天然需要同步通信的Web应用程序。在这篇文章中,我想介绍一种方法来达到异步通信的目的:使用一些众所周知的库和工具来设计他们的系统。 下面的例子是用Java编写的,但我相信它更多的是基本原理,同一个应用程序可以用任何语言来重新写。

所需的工具和库:

  • Spring Boot
  • RabbitMQ

1.Web应用程序

一个用Spring MVC编写的Web应用程序并运行在Tomcat上。 它所做的只是将一个字符串发送到一个队列中 (异步通信的开始) 并等待另一个队列中的消息作为HTTP响应发送回来。

首先,我们需要定义几个依赖项,然后等待Spring Boot执行所有必要的自动配置。

<dependencies>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-amqp</artifactId>
 </dependency>
 <dependency>
  <groupId>com.thedeanda</groupId>
  <artifactId>lorem</artifactId>
 </dependency>
</dependencies>
@SpringBootApplication
public class BlockingApplication {
 public static void main(String[] args) {
  SpringApplication.run(BlockingApplication.class, args);
 }
 @RestController
 public static class MessageController {
  private final RabbitTemplate rabbitTemplate;
  public MessageController(CachingConnectionFactory connectionFactory) {
   this.rabbitTemplate = new RabbitTemplate(connectionFactory);
  }
  @GetMapping("invoke")
  public String sendMessage() {
   Message response = rabbitTemplate.sendAndReceive("uppercase", null, request());
   return new String(response.getBody());
  }
  private static Message request() {
   Lorem LOREM = LoremIpsum.getInstance();
   String name = LOREM.getFirstName() + " " + LOREM.getLastName();
   return new Message(name.getBytes(), new MessageProperties());
  }
 }
 @Bean
 public CachingConnectionFactory connectionFactory() {
  CachingConnectionFactory factory = new CachingConnectionFactory();
  factory.setAddresses("localhost:5672");
  factory.setUsername("admin");
  factory.setPassword("admin");
  return factory;
 }
}

2.消费端应用程序

第二个应用程序仅仅是一个等待消息的RabbitMQ的消费端,将拿到的字符串转换为大写,然后将此结果发送到输出队列中。

<dependencies>
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-amqp</artifactId>
 </dependency>
</dependencies>
@SpringBootApplication
public class ServiceApplication {
 public static void main(String[] args) {
  SpringApplication.run(ServiceApplication.class, args);
 }
 public static class MessageListener {
  public String handleMessage(byte[] message) {
   Random rand = new Random();
   // Obtain a number between [0 - 49] + 50 = [50 - 99]
   int n = rand.nextInt(50) + 50;
   String content = new String(message);
   try {
    Thread.sleep(n);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   return content.toUpperCase();
  }
 }
 @Bean
 public CachingConnectionFactory connectionFactory() {
  CachingConnectionFactory factory = new CachingConnectionFactory();
  factory.setAddresses("localhost:5672");
  factory.setUsername("admin");
  factory.setPassword("admin");
  return factory;
 }
 @Bean
 public SimpleMessageListenerContainer serviceListenerContainer() {
  SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
  container.setConnectionFactory(connectionFactory());
  container.setConcurrentConsumers(20);
  container.setMaxConcurrentConsumers(40);
  container.setQueueNames("uppercase_messages");
  container.setMessageListener(new MessageListenerAdapter(new MessageListener()));
  return container;
 }
}

3.底层如何执行的?

程序启动并首次调用sendMessage()方法后,我们可以看到Spring AMQP支持自动创建了一个新的回复队列并等待来自我们的服务应用程序的响应。

2019-05-12 17:23:21.451  INFO 4574 --- [nio-8080-exec-1] .l.DirectReplyToMessageListenerContainer : Container initialized for queues: [amq.rabbitmq.reply-to]
2019-05-12 17:23:21.457  INFO 4574 --- [nio-8080-exec-1] .l.DirectReplyToMessageListenerContainer : SimpleConsumer [queue=amq.rabbitmq.reply-to, consumerTag=amq.ctag-VF-iqD9rLEuljIBstbCI1A identity=10e58093] started

如果我们在消费端应用程序中查看消息,我们可以看到Spring自动传播有关回复队列的信息以及**相关ID,**用于将其传递回Web应用程序以便能够将请求和响应配对在一起。

这就是发生魔术的地方。 当然,如果您想使其更复杂,您可以在协作中包含更多服务,然后将Web应用程序的最终响应放入与自动生成的队列不同的队列中, 该队列只具有正确的关联ID。 另外,不要忘记设置合理的超时。

这个解决方案还有一个很大的缺点 - 应用程序吞吐量。 我故意这样做,以便我可以跟进这篇文章,进一步深入调查AsyncProfiler! 但是目前,我们使用Tomcat作为主HTTP服务器,默认为200个线程,这意味着我们的应用程序无法同时处理200多条消息,因为我们的服务器线程正在等待RabbitMQ 回复队列的响应,直到有消息进入或发生超时。

感谢您阅读本文,敬请关注后续内容! 如果您想自己尝试一下,请查看我的GitHub存储库

原文链接:dzone.com/articles/ho

作者:Petr Bouda

译者:KeepGoingPawn

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。

相关文章

  • Java中向文件写入数据的几种常见方式分享

    Java中向文件写入数据的几种常见方式分享

    在日常开发中,肯定离不开要和文件打交道,今天就简单罗列一下平时比较常用的创建文件并向文件中写入数据的几种方式,文中有详细的代码示例供大家参考,具有一定的参考价值,需要的朋友可以参考下
    2023-10-10
  • 关于JAVA 数组的使用介绍

    关于JAVA 数组的使用介绍

    本篇文章小编将为大家介绍,关于JAVA 数组的使用介绍,有需要的朋友可以参考一下
    2013-04-04
  • MyBatis-Plus拦截器对敏感数据实现加密

    MyBatis-Plus拦截器对敏感数据实现加密

    做课程项目petstore时遇到需要加密属性的问题,而MyBatis-Plus为开发者提供了拦截器的相关接口,本文主要介绍通过MyBatis-Plus的拦截器接口自定义一个拦截器类实现敏感数据如用户密码的加密功能,感兴趣的可以了解一下
    2021-11-11
  • springboot中的dockerfile使用

    springboot中的dockerfile使用

    这篇文章主要介绍了springboot中的dockerfile使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java接口和抽象类有什么区别

    Java接口和抽象类有什么区别

    这篇文章主要介绍了Java接口和抽象类有什么区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • Spring5新功能@Nullable注解及函数式注册对象

    Spring5新功能@Nullable注解及函数式注册对象

    这篇文章主要为大家介绍了Spring5新功能详解@Nullable注解及函数式注册对象,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • 深入浅析Spring-boot-starter常用依赖模块

    深入浅析Spring-boot-starter常用依赖模块

    这篇文章主要介绍了Spring-boot-starter常用依赖模块及spring boot的两大优点,需要的朋友可以参考下
    2018-01-01
  • Java+mysql本地图片上传数据库及下载示例

    Java+mysql本地图片上传数据库及下载示例

    本篇文章主要介绍了Java+mysql本地图片上传数据库及下载示例,具有一定的参加价值,有兴趣的可以了解一下。
    2017-01-01
  • JAVA实现长连接(含心跳检测Demo)

    JAVA实现长连接(含心跳检测Demo)

    这篇文章主要介绍了JAVA实现长连接(含心跳检测Demo),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • Spring @Value如何通过${}、#{}注入不同类型的值

    Spring @Value如何通过${}、#{}注入不同类型的值

    这篇文章主要介绍了Spring @Value如何通过${}、#{}注入不同类型的值问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05

最新评论