shenyu2.5.0报错Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive; at path $

时间:2022-12-06 12:58:30

一、环境

shenyu:2.5.0 业务服务接入方式: http shen-admin的数据数据同步方式:websocket

二、异常描述

当shenyu-admin启动或者业务服务注册到admin时,报错

2022-10-12 00:06:10 [main] ERROR org.apache.shenyu.admin.service.manager.impl.DocManagerImpl - getDocInfo error={}
com.google.gson.JsonSyntaxException: Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive; at path $
at com.google.gson.internal.bind.TypeAdapters$34$1.read(TypeAdapters.java:920)
at com.google.gson.Gson.fromJson(Gson.java:991)
at com.google.gson.Gson.fromJson(Gson.java:956)
at com.google.gson.Gson.fromJson(Gson.java:905)
at com.google.gson.Gson.fromJson(Gson.java:876)
at org.apache.shenyu.common.utils.GsonUtils.fromJson(GsonUtils.java:135)
at org.apache.shenyu.admin.service.manager.impl.DocManagerImpl.getDocInfo(DocManagerImpl.java:101)
at org.apache.shenyu.admin.service.manager.impl.DocManagerImpl.addDocInfo(DocManagerImpl.java:85)
at org.apache.shenyu.admin.service.manager.impl.ServiceDocManagerImpl.pullApiDocument(ServiceDocManagerImpl.java:72)
at java.lang.Iterable.forEach(Iterable.java:75)
at org.apache.shenyu.admin.service.manager.impl.ServiceDocManagerImpl.pullApiDocument(ServiceDocManagerImpl.java:53)
at org.apache.shenyu.admin.service.manager.impl.LoadServiceDocEntryImpl.loadApiDocument(LoadServiceDocEntryImpl.java:100)
at org.apache.shenyu.admin.listener.ApplicationStartListener.onApplicationEvent(ApplicationStartListener.java:49)
at org.apache.shenyu.admin.listener.ApplicationStartListener.onApplicationEvent(ApplicationStartListener.java:33)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378)
at org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle.start(WebServerStartStopLifecycle.java:46)
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178)
at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54)
at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356)
at java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155)
at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:745)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:420)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1317)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
at org.apache.shenyu.admin.ShenyuAdminBootstrap.main(ShenyuAdminBootstrap.java:36)

三、问题定位

当服务注册或者admin启动时,会向业务服务请求获取swagger的元数据

@Service
public class ServiceDocManagerImpl implements ServiceDocManager {

private static final String SWAGGER_V2_PATH = "/v2/api-docs";

// 向业务服务拉取swagger的元数据
public void pullApiDocument(final UpstreamInstance instance) {
String clusterName = instance.getClusterName();
if (!canPull(instance)) {
LOG.info("api document has been pulled and cannot be pulled againl,instance={}", JsonUtils.toJson(instance));
return;
}
String url = getSwaggerRequestUrl(instance);
try {
String body = HTTP_UTILS.get(url, Collections.EMPTY_MAP);
docManager.addDocInfo(
clusterName,
body,
callback -> LOG.info("load api document successful,clusterName={}, iPandPort={}",
clusterName, instance.getIp() + ":" + instance.getPort())
);
CLUSTER_LASTSTARTUPTIME_MAP.put(clusterName, instance.getStartupTime());
} catch (Exception e) {
LOG.error("add api document fail. url={} error={}", url, e);
}
}

// 拼接Swagger的元数据请求地址
private String getSwaggerRequestUrl(final UpstreamInstance instance) {
return "http://" + instance.getIp() + ":" + instance.getPort() + SWAGGER_V2_PATH;

}

}

当业务服务的注册http的配置为

server:
port: 8080
servlet:
context-path: /spvr

shenyu:
register:
registerType: nacos #zookeeper #etcd #nacos #consul
serverLists: ${spring.cloud.nacos.config.server-addr} #localhost:2181 #http://localhost:2379 #localhost:8848
props:
username: nacos
password: nacos
nacosNameSpace: ${spring.cloud.nacos.discovery.namespace}
client:
http:
props:
contextPath: /demo
isFull: true
appName: ${spring.application.name}

因为服务默认带着context-path故请求到demo服务时,报出404异常信息, 使用gson无法解析,故报错

四、解决

  1. 去除server.servlet.context-path