ServiceComb/CSE常见问题处理(1811)

一、operation with url /rest/cbc/cbcbianalysisservice/v1/applications/, method GET is duplicated.

这个错误一般发生在development环境。 在development情况下,CSE会检查契约是否有变化,有变化会覆盖、或者新增schema信息,但是删除的schema不会从服务中心删除。 当用户将Schema重命名,而且没有修改版本号,没有清理服务中心老版本历史数据的情况下就会可能出现这个异常。

在production环境(默认),不会出现这个问题,因为只要契约变化,production环境服务启动就会失败,不会将错误遗漏到运行环节。 

 

2018-11-26 08:27:20,530 [ERROR] [transport-vert.x-eventloop-thread-15] [org.apache.servicecomb.serviceregistry.consumer.MicroserviceVersions.safeSetInstances(MicroserviceVersions.java:163)] Failed to setInstances, appId=dev_alpha_auto, microserviceName=CBCMockBIAnalysisService.
org.apache.servicecomb.foundation.common.exceptions.ServiceCombException: operation with url /rest/cbc/cbcbianalysisservice/v1/applications/, method GET is duplicated.
        at org.apache.servicecomb.common.rest.locator.MicroservicePaths.addStaticPathResource(MicroservicePaths.java:81)
        at org.apache.servicecomb.common.rest.locator.MicroservicePaths.addResource(MicroservicePaths.java:57)
        at org.apache.servicecomb.common.rest.locator.ServicePathManager.addResource(ServicePathManager.java:121)
        at org.apache.servicecomb.common.rest.locator.ServicePathManager.addSchema(ServicePathManager.java:88)
        at org.apache.servicecomb.common.rest.RestEngineSchemaListener.onSchemaLoaded(RestEngineSchemaListener.java:54)
        at org.apache.servicecomb.core.definition.loader.SchemaListenerManager.notifySchemaListener(SchemaListenerManager.java:59)
        at org.apache.servicecomb.core.definition.loader.SchemaListenerManager.notifySchemaListener(SchemaListenerManager.java:48)
        at org.apache.servicecomb.core.definition.MicroserviceVersionMeta.(MicroserviceVersionMeta.java:46)
        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:67)
        at org.apache.servicecomb.edge.core.EdgeInvocation.findMicroserviceVersionMeta(EdgeInvocation.java:85)
        at org.apache.servicecomb.edge.core.EdgeInvocation.edgeInvoke(EdgeInvocation.java:68)
        at com.huawei.cbc.edgeservice.EdgeDispatcher.onRequest(EdgeDispatcher.java:98)
        at io.vertx.ext.web.impl.RouteImpl.handleContext(RouteImpl.java:219)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:120)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:133)
        at org.apache.servicecomb.transport.rest.vertx.RestBodyHandler$BHandler.doEnd(RestBodyHandler.java:270)
        at org.apache.servicecomb.transport.rest.vertx.RestBodyHandler$BHandler.end(RestBodyHandler.java:251)
        at org.apache.servicecomb.transport.rest.vertx.RestBodyHandler.lambda$handle$0(RestBodyHandler.java:91)
        at io.vertx.core.http.impl.HttpServerRequestImpl.handleEnd(HttpServerRequestImpl.java:417)
        at io.vertx.core.http.impl.Http1xServerConnection.handleEnd(Http1xServerConnection.java:482)
        at io.vertx.core.http.impl.Http1xServerConnection.processMessage(Http1xServerConnection.java:456)
        at io.vertx.core.http.impl.Http1xServerConnection.handleMessage(Http1xServerConnection.java:144)

 

二、ServiceComb/CSE重试和隔离如何判断错误?

简单的描述:

1. 网络错误 + 503错误码等情况可以触发重试。

2. 网络错误 + 503错误码 + 超时等错误可以触发隔离。

细节内容可以参考代码:

Retry: https://github.com/apache/servicecomb-java-chassis/blob/master/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/DefaultRetryExtensionsFactory.java

Isolation: https://github.com/apache/servicecomb-java-chassis/blob/master/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadbalanceHandler.java

   参考isFailedResponse

关于隔离恢复:https://github.com/apache/servicecomb-java-chassis/blob/master/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/IsolationDiscoveryFilter.java

   参考allowVisit

内部线程只会触发快速隔离,不会触发恢复,参考: https://github.com/apache/servicecomb-java-chassis/blob/master/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombLoadBalancerStats.java

  里面的Timer线程

 

三、cse.request.timeout 怎么配置operation/schema级别的超时

CSE支持给每个接口调用单独设置超时时间。 格式为:

  cse.request.[microserviceName.[schemaid.[operationid.]]].timeout

比如: 

cse.request.timeout 所有接口

cse.request.testSerivce.timeout testService的所有接口

 

四、没修改代码,服务重启失败: The difference in local schema

问题现象:服务上次可以启动成功,重启后启动失败,或者换了一个机器启动失败,报告The difference in local schema

问题分析:这个是由于服务重启后生成的schema与上次启动不同。 这个问题比较隐蔽,通常发生在如下情况下:

业务定义了一个model,里面有些方法,比如isSucess, getIndex等,这些方法没有对应的属性,而只是用于控制的。 JVM在加载属性顺序是固定的,但是加载方法不固定,顺序可能会变化。 由于json会解析model里面的getter/setter方法,也把他们当成model。CSE会根据model生成契约,并检查契约的差异,不同的时候,必须修改版本号才能成功。 所以启动失败 。

对于这种场景,应该修改业务代码的model,将不是属性的字段什么味@JsonIgnore,或者进行适当的代码重构,服务的model尽可能指包含属性和getter/setter方法(POJO)。

还有一些情况,可能导致这种问题。业务以为定义的model的属性,但是由于属性不符合java bean规范,造成了额外的getter/setter方法。 

比如:

 private boolean isSuccess;

  public boolean isSuccess() {return isSuccess;}

这个例子将属性声明为isSuccess,实际应该为success。 

再比如这里的例子: https://bbs.huaweicloud.com/forum/thread-11107-1-1.html

一些特殊的属性名,生成的getter/setter不符合java bean规范。

 关于从Pojo提取契约的说明

规则
1.通用规则
http://download.oracle.com/otndocs/jcp/7224-javabeans-1.01-fr-spec-oth-JSpec/
实践上来说,简单地满足下面的规则就ok了:
(下面的用法有的是合法的,但是为了简单清晰,最好也不要用)
a.boolean不要以is开头,比如isEnable,应该直接命名enable
b.使用驼峰命名,但是不使用“奇特”的用法,比如pName/iValue/Name/NAME等等
2.CSE规则
主要针对:没有field对应的getter/setter
a.如果不属于契约范围,则需要使用@JsonIgnore标记为忽略
  不考虑CSE契约,普通的RESTful,如果不加忽略标记,看似能跑,但是网络上传输了不期望的数据,这也是一个不健康的状态
b.如果属于契约范围,则需要使用@JsonPropertyOrder指定这些虚拟properties的顺序
  以避免因为java反射获取method列表的无序性导致契约不稳定
  
CSE规则的背景
契约是CSE的核心,这一块不再重复描述
1.如果业务未提供契约,则CSE需要根据业务Schema类生成契约
  生成契约直接依赖swagger从Pojo提取契约model的能力
  swagger依赖jackson从Pojo提取properties的能力
  jackson以及业界很多组件,将public field/getter/setter均看作是properties
2.根据java规范,反射获取method列表的顺序是未定义的,每次获取的结果可能不相同
  所以对于没有field,仅仅只有getter/settter的这些虚拟properties,jackson提取出来的顺序是不确定的
3.CSE要求同一个微服务的同一个版本,其契约是一致的,否则无论从哪个层面的语义上都是说不通的
  这不是仅仅是一个序列化问题,而是对外呈现的契约是否一致的问题

对于getter/setter定义错误的情况,比如:

private boolean isSuccess;

public boolean isSuccess() {return isSuccess;}

这个问题在开发调试的可能比较难于发现,因为一台机器的顺序可能是不变的。当换一台测试机器的时候,就容易暴露出来。

 

五、如何使用拦截器

原来的Spring MVC代码使用如下方式使用拦截器

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

@Configuration

public class DemoConfig extends WebMvcConfigurerAdapter {

 

    @Override

    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(new HandlerInterceptor() {

 

            public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

                System.out.println("11111");

                return true;

            }

 

            public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

 

            }

 

            public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

 

            }

 

        });

    }

 

}

切换CSE以后不生效。

这种方式是Spring MVC的方式,CSE提供了类似的方式,CSE有Handler、HttpServerFilter等机制可以替换使用。https://bbs.huaweicloud.com/blogs/02de5f11cb6e11e8bd5a7ca23e93a891介绍了Spring MVC的运行时和CSE的运行时差异. Handler, HttpServerFilter机制开发指南参考: https://docs.servicecomb.io/java-chassis/zh_CN/general-development/http-filter.html 和 https://docs.servicecomb.io/java-chassis/zh_CN/references-handlers/intruduction.html

 

六、not allowed multi path for com.huawei.iam.cache.controller.AgencyController:getAgency

java.lang.Error: generate operation swagger failed, com.huawei.iam.cache.controller.AgencyController:getAgency

 

generate operation swagger failed, com.huawei.iam.cache.controller.AgencyController:getAgency

 

java.lang.Error: not allowed multi path for com.huawei.iam.cache.controller.AgencyController:getAgency

 

说明: 

CSE不允许一个接口定义多个path,比如:

1

2

3

4

5

@RequestMapping(value = {"/v3-ext/agencies""/v3-huawei/agencies""/v3.0/OS-AGENCY/agencies"},

                method = RequestMethod.GET, produces = "application/json;charset=UTF-8")

public Map listAgency(@RequestParam("domain_id") String domainId,

                                      @RequestParam(value = "name", required = false) String name,

                                      HttpServletRequest request) throws BadRequestException {

CSE不允许一个接口定义多个path,因为每个接口必须存在唯一的operation id。 这种情况需要将接口拆分为3个接口,比如listAgency1, listAgency2, listAgency3. 

 

 

七、如何在微服务之间配置和使用会话粘滞策略

场景描述:

一个客户登录后,始终把请求发送到某一个实例。当第一次访问这个实例的时候,需要记录会话ID对应的实例是A,这个会话下次发起同样的请求的时候,仍然访问A。

CSE提供的会话粘滞:

CSE并没有提供上述场景的会话粘滞策略实现。因为一个系统的会话管理是多样的,CSE框架并不知道如何标记一个请求属于哪个会话。

CSE提供了一个简化的会话粘滞实现,保证对于一个接口的访问,尽可能发送到上一次访问的实例,负载均衡策略名字为SessionStickiness,修改负载均衡策略参考https://docs.servicecomb.io/java-chassis/zh_CN/references-handlers/loadbalance.html。

如果用户需要实现“场景描述”里面的会话粘滞策略,需要考虑自定义负载均衡策略,实现相关接口即可。详细可以参考SessionStickiness、RoundRobin等CSE自带的负载均衡的策略实现。

 八、连接服务中心websocket接口失败

错误日志:

2018-11-06 07:46:59,746 ERROR [registry-vert.x-eventloop-thread-2][][http.ServiceRegistryClientImpl 688] watcher connect to service center server failed, microservice 19a50f38e0f411e8b1c30255ac105523, Websocket connection attempt returned HTTP status code 404

问题原因:

在公有云环境下,服务调用是经过网关的,不支持使用websoket,在这种场景下,使用pull机制来更新实例。 需要设置如下参数:

cse:
 service:
   instance:
     watch: false #使用API网关访问,只能使用PULL模式

公有云环境连接服务中心的说明参考:

https://huaweicse.github.io/cse-java-chassis-doc/servicecomb-using-cse/connect-service-center.html

你可能感兴趣的:(精品案例)