最近线上上午8:30左右,gateway会cpu告警,因为我们的服务可以自动伸缩,访问服务也正常,没有太在意这个问题。
上班路上突然收到项目经理和客户线上问题反馈记录。部分三方分享链接打开显示如下内容。
```
{"code":1021,"msg":"网关内部异常.","timestamp":"1575616043978","result":null,"success":false}
```
第一反应,再次打开app分享一个链接到微信,打开没问题。初步判断是网关告警时出了问题。
到公司赶紧排查了一下日志,发现扩容节点存在部分请求失败,异常信息如下。
```
FilterProcessor.java:193)\n\t... 72 common frames omitted\nCaused by: com.netflix.client.ClientException: Load balancer does not have available server for client: USER-CENTER\n\tat com.netflix.loadbalancer.LoadBalancerContext.getServerFromLoadBalancer(LoadBalancerContext.java:483)\n\tat com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:184)\n\tat com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:180)\n\tat rx.Observable.unsafeSubscribe(Observable.java:10327)\n\tat rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:94)\n\tat rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:42)\n\tat rx.Observable.unsafeSubscribe(Observable.java:10327)\n\tat rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber$1.call(OperatorRetryWithPredicate.java:127)\n\tat rx.internal.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.enqueue(TrampolineScheduler.java:73)\n\tat rx.internal.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.schedule(TrampolineScheduler.java:52)\n\tat rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:79)\n\tat rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:45)\n\tat rx.internal.util.ScalarSynchronousObservable$WeakSingleProducer.request(ScalarSynchronousObservable.java:276)\n\tat rx.Subscriber.setProducer(Subscriber.java:209)\n\tat rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:138)\n\tat rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:129)\n\tat rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)\n\tat rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)\n\tat rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)\n\tat rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)\n\tat rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)\n\tat rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)\n\tat rx.Observable.subscribe(Observable.java:10423)\n\tat rx.Observable.subscribe(Observable.java:10390)\n\tat rx.observables.BlockingObservable.blockForSingle(BlockingObservable.java:443)\n\tat rx.observables.BlockingObservable.single(BlockingObservable.java:340)\n\tat com.netflix.client.AbstractLoadBalancerAwareClient.executeWithLoadBalancer(AbstractLoadBalancerAwareClient.java:112)\n\tat org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.execute(LoadBalancerFeignClient.java:63)\n\t... 88 common frames
```
由于网关ErrorFilter中我把所有的异常都处理成了json返回,导致访问html时也返回了json字符串。至于为什么扩容服务出现了服务不可用,后面会详细讨论。
下面写一下处理办法。
###在gatewany中整合thymeleaf
* pom.xml 追加依赖
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
```
* 添加静态资源路径
```
mvc:
throw-exception-if-no-handler-found: true
static-path-pattern: /static/**
resources:
add-mappings: false
```
* 通过configuration 配置一下路径(我们用的2.0.4,发现仅配置文件不好使)
```
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
}
```
* 添加一个ErrorController 用来跳转异常页(因为resources 下资源不可以直接访问)
```
@Controller
public class ErrorController {
@RequestMapping("404.html")
public String go404(){
return "error/404";
}
@RequestMapping("500.html")
public String go500(){
return "error/500";
}
}
* 过滤器中针对html访问失败,跳转500页面
```
if(isStaticResourceRequest(uri) && !response.isCommitted()){
response.sendRedirect("/500.html")
return null;
}
boolean isStaticResourceRequest(String url){
String reg = ".+(.html)\$"
Pattern pattern = Pattern.compile(reg)
Matcher matcher = pattern.matcher(url)
return matcher.find()
}
```
配置这些就ok了,如果微服务不可用,且访问的是html页面,那么就会跳转到一个异常页;如果访问不是页面,依然还是json格式的错误内容。