本文主要介绍springboot下的一些网络请求方式,包含同步请求和异步请求。
这种方式是同步请求。
此处再次介绍同步和异步的区别:
同步,是指一个任务接着一个任务,当上一个任务完成时,才可以开启下一个任务。在网络请求中是指,当向网络服务器发送网络请求时,服务器会对请求进行处理,处理结束后会响应对应的内容。在接收到响应内容前,是不能继续发送请求的,需要等待接收到网络响应才可以继续。
异步,是指可以开启一个任务队列,可以满足多个任务同时进行,当队列中有任务结束,就可以继续添加新任务。当向网络服务器发送网络请求后,只要在等待响应的任务数没有超过设定的限额,就可以继续进行发送任务。例如,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);
这是一种异步的请求方式。在我的项目中,用的是这种方式,因为可以减少程序运行时间,加快任务完成速度。
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);
});
}
配置线程池大小的目的是为了防止加入的请求数量超过设定的最大数量,导致线程阻塞。一开始我没有设置数量,所以出现了问题,经过线程池数量设置后,完美解决了这个问题。
这个方法没有具体研究,直接上代码:
// 使用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下进行网络访问请求的童靴,可以参考学习。