我正在Spring Boot 2中创建一个端点,并使用Spring webflux。在此端点中,我将从呼叫者处获取纬度和经度,并将基于此返回状态。为了获取状态,我正在调用弹性搜索API来获取数据。
我能够从Elastic搜索API中获得如下响应:
{
"took": 11,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"hits": {
"total": 117252,
"max_score": null,
"hits": [
{
"_index": "geolocation",
"_type": "geolocationdata",
"_id": "AWt0m6GJqkN7DgSP9Lsd",
"_score": null,
"_source": {
"network": "117.254.200.0/22",
"geonameId": 1262062,
"registeredCountrygeonameId": 1269750,
"representedCountrygeonameId": "",
"postalCode": "370655",
"location": "23.2667,68.8333",
"accuracyRadius": 100,
"localecode": "en",
"continentcode": "AS",
"continentname": "Asia",
"countryisocode": "IN",
"countryname": "India",
"subdivision1isocode": "GJ",
"subdivision1nname": "Gujarat",
"subdivision2isocode": "",
"subdivision2nname": "",
"cityName": "Naliya",
"metroCode": "",
"timeZone": "Asia/Kolkata"
},
"sort": [
6986.775031169917
]
}
]
}
}
一旦有了此JSON,我想仅从其中获取必要的字段,并构建一个API所需的模型,该模型将返回给调用者。
这是我如何使用Elastic search API并获取结果
private WebClient webClient;
@PostConstruct
public void init() {
this.webClient = WebClient.builder()
.baseUrl("http://172.24.5.162:9200/geolocation")
.defaultHeader(
HttpHeaders.CONTENT_TYPE,
MediaType.APPLICATION_JSON_VALUE)
.build();
}
public String getGeoname(String latitude, String longitude) throws Exception {
try {
String req = "{\"from\": 0,\"size\": 1,\"sort\": {\"_geo_distance\": {\"location\": {\"lat\": " + latitude
+ ",\"lon\": " + longitude
+ "},\"order\": \"asc\",\"unit\": \"km\",\"distance_type\": \"plane\"}}}";
final String test;
//result from Elastic search API
Mono<String> result = webClient.post()
.uri("/_search")
.body(Mono.just(req), String.class)
.retrieve().bodyToMono(String.class);
} catch (Exception ex) {
log.error("Exception while sending request to Elastic search Lat: " + latitude + " Long: " + longitude, ex);
return gson.toJson(new ErrorModel(ErrorCodes.BAD_INPUT, "Bad Input"));
}
return "";
}
在结果变量中,我具有上面显示为Mono的JSON。如果我将对结果变量使用block()方法来获取JSON IWant字符串,则它将阻塞主线程并变为阻塞状态。我的需求是消耗此Mono,以便可以进行以下操作(基本上是在构建我的模型的GeoLocation)
String hits = "";
JSONObject jsonObject = new JSONObject(o);
if (jsonObject.has("hits") && jsonObject.getJSONObject("hits").has("hits")) {
hits = jsonObject.getJSONObject("hits")
.getString("hits");
hits = hits.substring(1);
JSONObject hitsJson = new JSONObject(hits);
JSONObject source = new JSONObject();
if (hitsJson.has("_source")) {
source = hitsJson.getJSONObject("_source");
GeoLocation geolocation = new GeoLocation(source.getString("continentname"),
source.getString("countryname"),
source.getString("subdivision1nname"),
source.getString("cityName"));
geoLocationResponse = Mono.just(gson.toJson(geolocation));
如何以非阻塞方式进行上述操作并将结果返回给端点调用者?我正在考虑从我的RestController返回Mono。
答案 0 :(得分:0)
首先,您不要在反应式环境中使用try catch,我们创建Mono.error
,然后在信号链中稍后对这些错误采取行动。
您应该真正查看一些教程或尝试一下反应堆项目文档中的基本入门。它将对您有很大帮助。
您应该利用Spring Boot中捆绑的jackson,而不要使用JSONObject和Gson。
public Mono<GeoLocation> getGeoname(String latitude, String longitude) {
final String req = "{\"from\": 0,\"size\": 1,\"sort\": {\"_geo_distance\": {\"location\": {\"lat\": " + latitude
+ ",\"lon\": " + longitude
+ "},\"order\": \"asc\",\"unit\": \"km\",\"distance_type\": \"plane\"}}}";
return webClient.post()
.uri("/_search")
.body(Mono.just(req), String.class)
.exchange()
.flatMap(clientResponse -> {
// Check status if not 200 create a mono error that we will act upon later
if(clientResponse.rawStatusCode() != 200)
return Mono.error(RuntimeException::new);
return clientResponse.bodyToMono(String.class);
}).map(body -> {
// if you want to work with strings then do your mapping here
// otherwise replace bodyToMono(FooBar.class) the FooBar class with a
// representation of the returned body and jackson will map to it.
return new GeoLocation();
}).doOnError(throwable -> {
// handle the mono error we created earlier here, and throw an exception.
// Then handle the exception later in a global exception handler
throw new RuntimeException(throwable);
});
}