一、Tomcat重复加载war包问题
有同事开发了一个demo服务,服务包含前端页面和ServiceComb开发的REST后端服务两部分。打成war包部署在Tomcat中,发现这个服务会在服务中心注册两个地址相同的实例。观察日志,发现Spring Context加载了两遍,并且Tomcat的webapps目录下存在一个与war包同名的目录和一个ROOT目录,两个目录中的内容是相同的。
Tomcat在启动时会将webapps目录下的war包解压到一个同名目录下,将其作为一个context加载。而在Tomcat的conf/server.xml文件中,又额外定义了一个Context:
于是该war包会作为root context 又被加载一次。
删掉conf/server.xml文件中定义的context,把拷贝到webapps目录下的war包改名为ROOT.war。Tomcat启动时会自动将war包解压作为root context加载。
二、CSE/ServiceComb JAVA SDK如何配置业务处理线程池
CSE JAVA SDK的线程池模型比较复杂,对于tomcat场景,以及Edge Service的vert.x场景,都有一个业务处理线程池(Edge Service的场景默认在reactive模式,是没有业务线程池的)。CSE设置的默认线程池的线程个数为2 * CPU个数。如果部分服务处理比较慢(比如评价时延>50ms),那么建议要设置一个较大的业务线程池,以提升吞吐量。如果所有接口都处理的很快,则不需要设置非常大的业务线程池,过多线程反而会因为线程调度,增加处理时延。如果一个微服务,有少量的几个接口处理非常耗时,需要考虑将这些接口放到独立的线程池执行(线程池隔离),防止访问慢的接口,影响访问快的接口。
线程池配置 (可以适当的抽时间学习下线程模型:https://bbs.huaweicloud.com/blogs/0a1a862f412611e89fc57ca23e93a89f)
通过配置项servicecomb.executors.default给业务接口制定执行线程池。配置项的的缺省值为servicecomb.executor.groupThreadPool,这个值是Bean的ID,CSE缺省的几个Bean ID如下:
1 2 3 4 5 |
|
2. 通过servicecomb.executors.Provider.[servicename.schemaid.operationId]给某个具体的接口指定不同的线程池。
三、服务端定义一个空schema,客户端抛出异常
错误异常如下:
2019-01-05 11:17:33.882 [pool-1-thread-1] ERROR o.a.s.s.consumer.MicroserviceVersions Line:163 ----------------> Failed to setInstances, appId=ippc, microserviceName=service-acceptance-dhc-quyq.
java.lang.NullPointerException: null
at org.apache.servicecomb.swagger.SwaggerUtils.validateSwagger(SwaggerUtils.java:80)
at org.apache.servicecomb.core.definition.SchemaUtils.parseSwagger(SchemaUtils.java:49)
at org.apache.servicecomb.core.definition.schema.ConsumerSchemaFactory.loadSwagger(ConsumerSchemaFactory.java:77)
at org.apache.servicecomb.core.definition.schema.ConsumerSchemaFactory.createSchema(ConsumerSchemaFactory.java:54)
at org.apache.servicecomb.core.definition.schema.ConsumerSchemaFactory.createSchema(ConsumerSchemaFactory.java:33)
at org.apache.servicecomb.core.definition.schema.AbstractSchemaFactory.getOrCreateSchema(AbstractSchemaFactory.java:65)
at org.apache.servicecomb.core.definition.schema.ConsumerSchemaFactory.createConsumerSchema(ConsumerSchemaFactory.java:47)
at org.apache.servicecomb.core.definition.MicroserviceVersionMeta.
at org.apache.servicecomb.core.definition.MicroserviceVersionMetaFactory.create(MicroserviceVersionMetaFactory.java:38)
at org.apache.servicecomb.serviceregistry.consumer.MicroserviceVersions.lambda$setInstances$0(MicroserviceVersions.java:182)
at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
at org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx.computeIfAbsent(ConcurrentHashMapEx.java:56)
at org.apache.servicecomb.serviceregistry.consumer.MicroserviceVersions.setInstances(MicroserviceVersions.java:180)
at org.apache.servicecomb.serviceregistry.consumer.MicroserviceVersions.safeSetInstances(MicroserviceVersions.java:160)
at org.apache.servicecomb.serviceregistry.consumer.MicroserviceVersions.pullInstances(MicroserviceVersions.java:155)
at org.apache.servicecomb.serviceregistry.consumer.MicroserviceVersions.submitPull(MicroserviceVersions.java:127)
at org.apache.servicecomb.serviceregistry.consumer.MicroserviceManager.lambda$getOrCreateMicroserviceVersions$0(MicroserviceManager.java:55)
at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
at org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx.computeIfAbsent(ConcurrentHashMapEx.java:56)
at org.apache.servicecomb.serviceregistry.consumer.MicroserviceManager.getOrCreateMicroserviceVersions(MicroserviceManager.java:53)
at org.apache.servicecomb.serviceregistry.consumer.MicroserviceManager.getOrCreateMicroserviceVersionRule(MicroserviceManager.java:79)
at org.apache.servicecomb.serviceregistry.consumer.AppManager.getOrCreateMicroserviceVersionRule(AppManager.java:59)
at org.apache.servicecomb.core.provider.consumer.ReferenceConfig.
at org.apache.servicecomb.core.provider.consumer.ConsumerProviderManager.createReferenceConfig(ConsumerProviderManager.java:56)
at org.apache.servicecomb.core.provider.consumer.ConsumerProviderManager.createReferenceConfig(ConsumerProviderManager.java:87)
at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
at org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx.computeIfAbsent(ConcurrentHashMapEx.java:56)
at org.apache.servicecomb.core.provider.consumer.ConsumerProviderManager.getReferenceConfig(ConsumerProviderManager.java:91)
at org.apache.servicecomb.core.SCBEngine.getReferenceConfigForInvoke(SCBEngine.java:293)
at org.apache.servicecomb.provider.pojo.Invoker.findReferenceConfig(Invoker.java:119)
at org.apache.servicecomb.provider.pojo.Invoker.createInvokerMeta(Invoker.java:88)
at org.apache.servicecomb.provider.pojo.Invoker.invoke(Invoker.java:129)
at com.sun.proxy.$Proxy170.findAllByApplyId(Unknown Source)
at com.dhc.ippc.state.controller.sync.SyncCntrController.getCntrNAndBlNs(SyncCntrController.java:316)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.servicecomb.swagger.engine.SwaggerProducerOperation.doInvoke(SwaggerProducerOperation.java:160)
at org.apache.servicecomb.swagger.engine.SwaggerProducerOperation.syncInvoke(SwaggerProducerOperation.java:148)
at org.apache.servicecomb.swagger.engine.SwaggerProducerOperation.invoke(SwaggerProducerOperation.java:115)
at org.apache.servicecomb.core.handler.impl.ProducerOperationHandler.handle(ProducerOperationHandler.java:40)
at org.apache.servicecomb.core.Invocation.next(Invocation.java:151)
问题原因:
服务端定义了一个接口, 没有任何方法,比如:
@RestSchema(schemaid="hello")
public class Hello{
}
解决方法:删除这个接口。或者增加一个REST接口定义。
四、CSE JAVA SDK: java.lang.IllegalStateException: Response is closed 原因和定位
当有些业务处理比较耗时的时候,日志里面经常打印如下异常:
2019-01-09 10:42:22,890 [ntloop-thread-0] ERROR Unhandled exception [io.vertx.core.impl.ContextImpl.lambda$wrapTask$2(ContextImpl.java:345)]
java.lang.IllegalStateException: Response is closed
at io.vertx.core.http.impl.HttpServerResponseImpl.checkValid(HttpServerResponseImpl.java:548)
at io.vertx.core.http.impl.HttpServerResponseImpl.end0(HttpServerResponseImpl.java:401)
at io.vertx.core.http.impl.HttpServerResponseImpl.end(HttpServerResponseImpl.java:319)
at org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse.internalFlushBuffer(VertxServerResponseToHttpServletResponse.java:122)
at org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse.lambda$flushBuffer$0(VertxServerResponseToHttpServletResponse.java:112)
at io.vertx.core.impl.ContextImpl.lambda$wrapTask$2(ContextImpl.java:339)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:463)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:745)
这个异常表示的是在给请求返回结果的时候,写结果发现连接已经关闭。这种情况通常发生在业务处理比较长的情况。这个日志在服务端打印(服务端是相对的)。和这个问题相关的配置由如下几个:
客户端:
cse.rest.client.connection.idleTimeoutInSeconds(缺省值30s)
服务端:
cse.rest.server.connection.idleTimeoutInSeconds(缺省值30s)
如果业务处理时间超过这两个值的最小值,那么服务端就会报告这个异常。