草庐IT

SpringBoot 同步和异步网络请求

wtcl_wtcl 2024-07-14 原文

关于

本文主要介绍springboot下的一些网络请求方式,包含同步请求和异步请求。

正文

1. ResTemplata方式

这种方式是同步请求。

此处再次介绍同步和异步的区别:

同步,是指一个任务接着一个任务,当上一个任务完成时,才可以开启下一个任务。在网络请求中是指,当向网络服务器发送网络请求时,服务器会对请求进行处理,处理结束后会响应对应的内容。在接收到响应内容前,是不能继续发送请求的,需要等待接收到网络响应才可以继续。

异步,是指可以开启一个任务队列,可以满足多个任务同时进行,当队列中有任务结束,就可以继续添加新任务。当向网络服务器发送网络请求后,只要在等待响应的任务数没有超过设定的限额,就可以继续进行发送任务。例如,1000人参加只有100台设备的考试,首先可以让100人参加,如果其中有人提前完成,就可以让第101人进入考试。这样的方式,可以提高效率,达到减少网络请求时间的目的。

具体实现代码如下:

Map<String, Object> map = new HashMap<>();
// map中存放我想要发送的json键值对
map.put("a", "123456789");
map.put("b", 1);

LinkedMultiValueMap<String, String> newmap = new LinkedMultiValueMap<>();
// newmap是对发送json字符串进行打包的map包
newmap.add("data",  JSONObject.toJSONString(map));

// 构造请求
HttpHeaders httpHeaders = new HttpHeaders();
MediaType type = MediaType.parseMediaType("application/x-www-form-urlencoded;charset=UTF-8");
// 这串字符的请求类型,这里为表单形式
httpHeaders.setContentType(type);
HttpEntity<LinkedMultiValueMap<String, String>> httpEntity = new HttpEntity<>(newmap, httpHeaders);
// 打包成发送的实体数据
ResponseEntity<String> apiResponse = restTemplate.postForEntity("http://127.0.0.1:8081/placesearch",httpEntity,String.class);
此处发送请求,此处会一直阻塞等待,直到请求结果返回,然后才继续执行下面的部分
String result = apiResponse.getBody();

这里的数据处理方式其实是有点过于冗杂了(我代码写得太烂了)。此处发送的请求是给另一个springboot的接口,由于不明的原因,设置了很多种形式,对面的接口都无法正常接收到发送的数据,然后我就找啊找啊找,最后在某次不起眼的尝试后,终于发送数据成功了,对面接收到数据了。

接收端的代码:

@RequestMapping(value = "/placesearch", method = RequestMethod.POST)
@ResponseBody
public String placesearch(String data){
    System.out.println(data);
    // 这里的data就是我上面要发送的map键值对,形式为字符串,需要转为json格式
}

由于功能实现了,所以就没有研究有没有其它的数据打包方式,有兴趣的童鞋可以继续研究研究。

补充一个restTemplate网络请求的具体配置:

// restTemplate网络请求设置
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(10*100);
requestFactory.setReadTimeout(10*1000);
RestTemplate client = new RestTemplate(requestFactory);
HttpHeaders httpHeaders = new HttpHeaders();
MediaType type = MediaType.parseMediaType("application/x-www-form-urlencoded;charset=UTF-8");
httpHeaders.setContentType(type);

2. WebClient方式

这是一种异步的请求方式。在我的项目中,用的是这种方式,因为可以减少程序运行时间,加快任务完成速度。

Map<String, ArrayList<List<String>>> rawMap = new HashMap<>();
rawMap.put("data", dis);
// 此处rawMap仍然是一个map类型的数据

WebClient client = WebClient.create();
@SuppressWarnings("rawtypes")
Flux<Map> mapFlux = client.post().uri("http://127.0.0.1:8080/api").contentType(MediaType.APPLICATION_FORM_URLENCODED).body(BodyInserters.fromFormData("data",JSONObject.toJSONString(rawMap))).retrieve().bodyToFlux(Map.class);

mapFlux.collectList().subscribe(maps -> {
    String result = (String) maps.get(0).get("res");
    // api接口返回的是一个json数据,如{"res":"1"};
    System.out.println(result);
});

这里的请求是异步的,即发送请求后,不会一直阻塞在请求语句,会继续执行后面的语句,而每次返回的结果都用mapFlux进行收集和处理,我在上面对结果进行了打印,这个任务是在收到返回结果后进行,与整体的任务进程无关。

如果需要在短期内实现大量上面的请求,就需要配置线程池大小,具体代码如下:

ConnectionProvider connectionProvider = ConnectionProvider.builder("myConnectionPool").maxConnections(100000).pendingAcquireMaxCount(100000).build();
// 这里将线程池增加到100000,基本上不会出现线程池爆满
ReactorClientHttpConnector clientHttpConnector = new ReactorClientHttpConnector(HttpClient.create(connectionProvider));

Map<String, String> res = new HashMap<>();
// mapAll是多个键值对,值为发送内容,键为记录内容
for(String key: mapAll.keySet()){
    WebClient client = WebClient.builder().clientConnector(clientHttpConnector).build();
        
    @SuppressWarnings("rawtypes")
     Flux<Map> mapFlux = client.post().uri("http://127.0.0.1:8080/api").contentType(MediaType.APPLICATION_FORM_URLENCODED).body(BodyInserters.fromFormData("data",JSONObject.toJSONString(mapAll.get(key)))).retrieve().bodyToFlux(Map.class);
     mapFlux.collectList().subscribe(maps -> {
         String result = (String) maps.get(0).get("res");
         System.out.println(result);
     });
}

配置线程池大小的目的是为了防止加入的请求数量超过设定的最大数量,导致线程阻塞。一开始我没有设置数量,所以出现了问题,经过线程池数量设置后,完美解决了这个问题。

3. httpAsyncClient方式

这个方法没有具体研究,直接上代码:

// 使用httpAsyncClient实现异步请求,效果和webClient差不多,但是代码更加复杂
CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients.createDefault();

httpAsyncClient.start();

String requestPath = "http://127.0.0.1:8080/api";

HttpPost post = new HttpPost(requestPath);

List<NameValuePair> list = new ArrayList<>();

list.add(new BasicNameValuePair("data", JSONObject.toJSONString(rawMap)));

post.setEntity(new UrlEncodedFormEntity(list, "utf-8"));

httpAsyncClient.execute(post, new Back());

Back()方法代码:

class Back implements FutureCallback<HttpResponse>{
        Back(){}

        @Override
        public void completed(HttpResponse httpResponse) {
            try{
                org.apache.http.HttpEntity responseEntity = httpResponse.getEntity();
                System.out.println(EntityUtils.toString(responseEntity));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void failed(Exception e) {
            System.out.println("failed");
        }

        @Override
        public void cancelled() {

        }
    }

使用方式较为复杂,所以不太推荐使用。

总结

本文介绍了三种网络访问请求方式,第一种比较适合用于同步请求,第二种比较适合用于异步请求,第三种仅供学习参考,用起来还是不太方便。

对于需要在springboot下进行网络访问请求的童靴,可以参考学习。

有关SpringBoot 同步和异步网络请求的更多相关文章

  1. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  2. ruby-on-rails - Rails HTML 请求渲染 JSON - 2

    在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这

  3. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  4. jquery - 我的 jquery AJAX POST 请求无需发送 Authenticity Token (Rails) - 2

    rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送

  5. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  6. 网络编程套接字 - 2

    网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识

  7. ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成? - 2

    在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.

  8. ruby - HTTP 请求中的用户代理,Ruby - 2

    我是Ruby的新手。我试过查看在线文档,但没有找到任何有效的方法。我想在以下HTTP请求botget_response()和get()中包含一个用户代理。有人可以指出我正确的方向吗?#PreliminarycheckthatProggitisupcheck=Net::HTTP.get_response(URI.parse(proggit_url))ifcheck.code!="200"puts"ErrorcontactingProggit"returnend#Attempttogetthejsonresponse=Net::HTTP.get(URI.parse(proggit_url)

  9. ruby-on-rails - 获取并发布相同匹配项的请求 - 2

    在我的路线文件中我有:match'graphs/(:id(/:action))'=>'graphs#(:action)'如果是GET请求(工作)或POST请求(不工作),我想匹配它我知道我可以使用以下方法在资源中声明POST请求:post'/'=>:show,:on=>:member但是我怎样才能为比赛做到这一点呢?谢谢。 最佳答案 如果你同时想要POST和GETmatch'graphs/(:id(/:action))'=>'graphs#(:action)',:via=>[:get,:post]编辑默认值可以设置如下match'g

  10. ruby - 检查网络文件是否存在,而不下载它? - 2

    是否可以在不实际下载文件的情况下检查文件是否存在?我有这么大的(~40mb)文件,例如:http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm这与ruby​​不严格相关,但如果发件人可以设置内容长度就好了。RestClient.get"http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm",headers:{"Content-Length"=>100} 最佳答案

随机推荐