由于 request.getSession() 为空,使用 webClient 时出现随机 NullPointerException / onErrorDropped
random NullPointerException / onErrorDropped using webClient, due to request.getSession() being null
我有一个 Spring Boot (2.5) 应用程序,我需要在其中对远程系统(我存储非规范化视图的 Solr 实例)进行 REST 调用,我可以在其中创建或更新记录。
我真的不关心我得到的响应(有时远程系统响应很慢),所以我在 createIndexForTicket
/ updateIndexForTicket
中进行这样的异步调用:
public MyService(WebClient webClient, String solrUpdateUrl) {
this.webClient = webClient;
this.solrUpdateUrl = solrUpdateUrl;
}
public void createIndexForTicket(TicketIndex ticketIndex) {
// build the request
var createRequest = webClient.post()
.uri(solrUpdateUrl);
triggerRequest(createRequest, ticketIndex,"creation");
log.info("payload sent, creating index for ticket {} : {}",ticketIndex.getUserFriendlyTicketId(),ticketIndex);
}
public void updateIndexForTicket(TicketIndex ticketIndex) {
// build the request
var updateRequest = webClient.put()
.uri(solrUpdateUrl + "/" + ticketIndex.getInternalTicketId());
triggerRequest(updateRequest, ticketIndex,"update");
log.info("payload sent, updating index for ticket {} : {}",ticketIndex.getUserFriendlyTicketId(),ticketIndex);
}
private static void triggerRequest(RequestBodySpec requestToSolr,
TicketIndex ticketIndex,
String action) {
requestToSolr.bodyValue(ticketIndex)
.retrieve()
.onStatus(HttpStatus::is2xxSuccessful,
resp -> logSuccess(ticketIndex,action))
.bodyToMono(String.class)
.doOnError(t ->
log.error("problem while performing a "+action+", "
+ "calling Solr for ticket "+ticketIndex.getUserFriendlyTicketId(),t))
.subscribe();
}
它在大多数情况下工作正常。但我注意到我有时会收到 Operator called default onErrorDropped
错误,堆栈跟踪如下:
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.NullPointerException
Caused by: java.lang.NullPointerException: null
at org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizedClientRepository.saveAuthorizedClient(HttpSessionOAuth2AuthorizedClientRepository.java:63)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ Request to PUT https://myRemoteSolrSystem/services/v2/tickets/dGlja2V0aW5nLXNlcnZpY2UxNDEzNzM1 [DefaultWebClient]
Stack trace:
at org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizedClientRepository.saveAuthorizedClient(HttpSessionOAuth2AuthorizedClientRepository.java:63)
at org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository.saveAuthorizedClient(AuthenticatedPrincipalOAuth2AuthorizedClientRepository.java:92)
at org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager.lambda$new[=12=](DefaultOAuth2AuthorizedClientManager.java:126)
at org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager.authorize(DefaultOAuth2AuthorizedClientManager.java:184)
at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.lambda$authorizeClient(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:552)
at reactor.core.publisher.MonoSupplier.call(MonoSupplier.java:86)
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:227)
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68)
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
查看源代码,我发现这导致 spring-security-oauth2-client
5.5.1,在 HttpSessionOAuth2AuthorizedClientRepository.saveAuthorizedClient
@Override
public void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal,
HttpServletRequest request, HttpServletResponse response) {
Assert.notNull(authorizedClient, "authorizedClient cannot be null");
Assert.notNull(request, "request cannot be null");
Assert.notNull(response, "response cannot be null");
Map<String, OAuth2AuthorizedClient> authorizedClients = this.getAuthorizedClients(request);
authorizedClients.put(authorizedClient.getClientRegistration().getRegistrationId(), authorizedClient);
request.getSession().setAttribute(this.sessionAttributeName, authorizedClients);
}
l.63,出现异常的地方是最后一个:
request.getSession().setAttribute(this.sessionAttributeName, authorizedClients);
所以它看起来像 request.getSession()
returns 空...但我不知道为什么,我找不到模式。有时我从同一个线程连续触发 2 个调用,一个成功而另一个不成功。有时都失败,有时都成功。还有一次,我只触发了一个调用,但它失败了,而另一个线程同时或多或少地做了类似的事情,并且它起作用了。
被注入的 webClient 是这样构建的:
@Bean
@Primary
WebClient servletWebClient(ClientRegistrationRepository clientRegistrations,
OAuth2AuthorizedClientRepository authorizedClients) {
var oauth = new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, authorizedClients);
oauth.setDefaultClientRegistrationId("keycloak");
return WebClient.builder()
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.apply(oauth.oauth2Configuration())
.build();
}
关于我做错了什么,或者我可以尝试更好地理解正在发生的事情的任何提示?
谢谢
这是似乎有效的解决方法:
声明一个线程执行器:
private final ExecutorService solrRequestExecutor = Executors.newSingleThreadExecutor();
然后,通过它进行异步调用:
private void triggerRequest(RequestBodySpec requestToSolr,
TicketIndex ticketIndex,
String action) {
// performing calls to Solr asynchronously
solrRequestExecutor.submit(
() ->
requestToSolr.bodyValue(ticketIndex)
.retrieve()
.onStatus(HttpStatus::is2xxSuccessful,
resp -> logSuccess(ticketIndex,action))
.bodyToMono(String.class)
.doOnError(t ->
log.error("problem while performing a "+action+", "
+ "calling Solr for ticket "+ticketIndex.getUserFriendlyTicketId(),t))
.block());
}
由于这不再在同一线程中执行,因此必须正确配置 webClient,否则我们会收到 servletRequest cannot be null
错误。见
我仍然不确定为什么原始代码会随机失败...它是否仅在远程端点也是反应性端点时才有效?
我有一个 Spring Boot (2.5) 应用程序,我需要在其中对远程系统(我存储非规范化视图的 Solr 实例)进行 REST 调用,我可以在其中创建或更新记录。
我真的不关心我得到的响应(有时远程系统响应很慢),所以我在 createIndexForTicket
/ updateIndexForTicket
中进行这样的异步调用:
public MyService(WebClient webClient, String solrUpdateUrl) {
this.webClient = webClient;
this.solrUpdateUrl = solrUpdateUrl;
}
public void createIndexForTicket(TicketIndex ticketIndex) {
// build the request
var createRequest = webClient.post()
.uri(solrUpdateUrl);
triggerRequest(createRequest, ticketIndex,"creation");
log.info("payload sent, creating index for ticket {} : {}",ticketIndex.getUserFriendlyTicketId(),ticketIndex);
}
public void updateIndexForTicket(TicketIndex ticketIndex) {
// build the request
var updateRequest = webClient.put()
.uri(solrUpdateUrl + "/" + ticketIndex.getInternalTicketId());
triggerRequest(updateRequest, ticketIndex,"update");
log.info("payload sent, updating index for ticket {} : {}",ticketIndex.getUserFriendlyTicketId(),ticketIndex);
}
private static void triggerRequest(RequestBodySpec requestToSolr,
TicketIndex ticketIndex,
String action) {
requestToSolr.bodyValue(ticketIndex)
.retrieve()
.onStatus(HttpStatus::is2xxSuccessful,
resp -> logSuccess(ticketIndex,action))
.bodyToMono(String.class)
.doOnError(t ->
log.error("problem while performing a "+action+", "
+ "calling Solr for ticket "+ticketIndex.getUserFriendlyTicketId(),t))
.subscribe();
}
它在大多数情况下工作正常。但我注意到我有时会收到 Operator called default onErrorDropped
错误,堆栈跟踪如下:
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.NullPointerException
Caused by: java.lang.NullPointerException: null
at org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizedClientRepository.saveAuthorizedClient(HttpSessionOAuth2AuthorizedClientRepository.java:63)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ Request to PUT https://myRemoteSolrSystem/services/v2/tickets/dGlja2V0aW5nLXNlcnZpY2UxNDEzNzM1 [DefaultWebClient]
Stack trace:
at org.springframework.security.oauth2.client.web.HttpSessionOAuth2AuthorizedClientRepository.saveAuthorizedClient(HttpSessionOAuth2AuthorizedClientRepository.java:63)
at org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository.saveAuthorizedClient(AuthenticatedPrincipalOAuth2AuthorizedClientRepository.java:92)
at org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager.lambda$new[=12=](DefaultOAuth2AuthorizedClientManager.java:126)
at org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager.authorize(DefaultOAuth2AuthorizedClientManager.java:184)
at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.lambda$authorizeClient(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:552)
at reactor.core.publisher.MonoSupplier.call(MonoSupplier.java:86)
at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:227)
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68)
at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
查看源代码,我发现这导致 spring-security-oauth2-client
5.5.1,在 HttpSessionOAuth2AuthorizedClientRepository.saveAuthorizedClient
@Override
public void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal,
HttpServletRequest request, HttpServletResponse response) {
Assert.notNull(authorizedClient, "authorizedClient cannot be null");
Assert.notNull(request, "request cannot be null");
Assert.notNull(response, "response cannot be null");
Map<String, OAuth2AuthorizedClient> authorizedClients = this.getAuthorizedClients(request);
authorizedClients.put(authorizedClient.getClientRegistration().getRegistrationId(), authorizedClient);
request.getSession().setAttribute(this.sessionAttributeName, authorizedClients);
}
l.63,出现异常的地方是最后一个:
request.getSession().setAttribute(this.sessionAttributeName, authorizedClients);
所以它看起来像 request.getSession()
returns 空...但我不知道为什么,我找不到模式。有时我从同一个线程连续触发 2 个调用,一个成功而另一个不成功。有时都失败,有时都成功。还有一次,我只触发了一个调用,但它失败了,而另一个线程同时或多或少地做了类似的事情,并且它起作用了。
被注入的 webClient 是这样构建的:
@Bean
@Primary
WebClient servletWebClient(ClientRegistrationRepository clientRegistrations,
OAuth2AuthorizedClientRepository authorizedClients) {
var oauth = new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, authorizedClients);
oauth.setDefaultClientRegistrationId("keycloak");
return WebClient.builder()
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.apply(oauth.oauth2Configuration())
.build();
}
关于我做错了什么,或者我可以尝试更好地理解正在发生的事情的任何提示?
谢谢
这是似乎有效的解决方法:
声明一个线程执行器:
private final ExecutorService solrRequestExecutor = Executors.newSingleThreadExecutor();
然后,通过它进行异步调用:
private void triggerRequest(RequestBodySpec requestToSolr,
TicketIndex ticketIndex,
String action) {
// performing calls to Solr asynchronously
solrRequestExecutor.submit(
() ->
requestToSolr.bodyValue(ticketIndex)
.retrieve()
.onStatus(HttpStatus::is2xxSuccessful,
resp -> logSuccess(ticketIndex,action))
.bodyToMono(String.class)
.doOnError(t ->
log.error("problem while performing a "+action+", "
+ "calling Solr for ticket "+ticketIndex.getUserFriendlyTicketId(),t))
.block());
}
由于这不再在同一线程中执行,因此必须正确配置 webClient,否则我们会收到 servletRequest cannot be null
错误。见
我仍然不确定为什么原始代码会随机失败...它是否仅在远程端点也是反应性端点时才有效?