问题描述
有时候我们需要在spring cloud gateway中进行一些远程接口的调用,比如网关的鉴权,需要调用rpc接口查询用户信息,如果直接调用feign接口,会出现如下报错:
java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
问题原因
这是因为新版的 gateway 采用的是非阻塞式编程,但是在获取注册中心实例时,调用了block()方法阻塞式获取实例,只要你敢 block,就会
throw new IllegalStateException("block()/blockFirst()/blockLast() are blocking, which is not supported in thread " + Thread.currentThread().getName());
public <T> ServiceInstance choose(String serviceId, Request<T> request) {
ReactiveLoadBalancer<ServiceInstance> loadBalancer = this.loadBalancerClientFactory.getInstance(serviceId);
if (loadBalancer == null) {
return null;
} else {
Response<ServiceInstance> loadBalancerResponse = (Response)Mono.from(loadBalancer.choose(request)).block();
return loadBalancerResponse == null ? null : (ServiceInstance)loadBalancerResponse.getServer();
}
}
final T blockingGet() {
if (Schedulers.isInNonBlockingThread()) {
throw new IllegalStateException("block()/blockFirst()/blockLast() are blocking, which is not supported in thread " + Thread.currentThread().getName());
}
if (getCount() != 0) {
try {
await();
}
catch (InterruptedException ex) {
dispose();
throw Exceptions.propagate(ex);
}
}
Throwable e = error;
if (e != null) {
RuntimeException re = Exceptions.propagate(e);
//this is ok, as re is always a new non-singleton instance
re.addSuppressed(new Exception("#block terminated with an error"));
throw re;
}
return value;
}
解决方式
问题找到了,解决起来就简单了,我们只需要重写获取实例的方法就好了,上代码
@Bean
public BlockingLoadBalancerClient blockingLoadBalancerClient(ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerClientFactory,
DiscoveryClient discoveryClient) {
return new BlockingLoadBalancerClient(loadBalancerClientFactory) {
@Override
public <T> ServiceInstance choose(String serviceId, Request<T> request) {
List<ServiceInstance> instanceList = discoveryClient.getInstances(serviceId);
return loadBalancerInstance(instanceList);
}
};
}
private static ServiceInstance loadBalancerInstance(List<ServiceInstance> instanceList) {
if (CollUtil.isEmpty(instanceList)) {
return null;
}
if (instanceList.size() == 1) {
return instanceList.get(0);
}
// 随机负载
int index = RandomUtil.randomInt(0, instanceList.size());
return instanceList.get(index);
}
这里的负载均衡是随机负载的,各位可以根据自己的机器数量,设置不同的负载均衡策略(我这生产其实只有一个节点,负载不负载无所谓)