本文介绍如何使用Ali-Tomcat将使用HSF框架开发的应用迁移为Dubbo框架开发的应用。
迁移方案
迁移的最终目标是从HSF+EDAS注册中心迁移到Dubbo+Nacos。目前有两种方案:
- 两步迁移
- 将HSF+EDAS注册中心迁移到Dubbo+EDAS注册中心。
- 将EDAS注册中心迁移到Nacos。
优势是相对比较稳定,适合小步迭代。缺点是需要应用发布两次。
- 直接迁移
将HSF+EDAS注册中心直接迁移到Dubbo+Nacos。
目前HSF尚不支持Nacos,需要额外开发。
如果应用想快速迁移到Dubbo并上线,建议采用第一种方案,从稳定性角度考虑也推荐第一种方案。下文将介绍如何进行两步迁移。
迁移架构图
Dubbo服务在服务注册的时候,同时注册成HSF和Dubbo的格式,保证HSF的服务消费者也发现Dubbo服务。Dubbo服务消费者在订阅的时候,同时订阅HSF和Dubbo格式的数据,保证Dubbo的消费者也能发现HSF的服务。
前提条件
迁移过程中需要依赖以下组件:
- 启动轻量级配置及注册中心
- Dubbo 2.7.3
- EDAS-container V3.5.5
- edas-dubbo-extension 2.0.6
├── pom.xml
├── src
│ └── main
│ └── java
│ └── com
│ └── alibaba
│ └── edas
│ └── DemoService.java
迁移服务提供者
假设待迁移的HSF应用为edas-hsf-demo-provider-war,主要包含以下文件:
pom.xml
:应用的模块之间依赖关系的配置文件。DemoServiceImpl.java
:DemoService的实现。hsf-provider-beans.xml
:HSF的Spring Bean声明文件。web.xml
:用于WAR包部署的描述符。
edas-hsf-demo-provider-war
的目录结构如下: ├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── alibaba
│ │ │ └── edas
│ │ │ └── hsf
│ │ │ └── provider
│ │ │ └── DemoServiceImpl.java
│ │ ├── resources
│ │ │ └── hsf-provider-beans.xml
│ │ └── webapp
│ │ └── WEB-INF
│ │ └── web.xml
迁移服务消费者
基于edas-hsf-demo-consumer-war进行迁移,迁移为edas-dubbo-demo-consumer-war。
将应用部署到EDAS并验证
本文以ECS集群部署为例,在EDAS中创建4个应用:
- edas-dubbo-demo-consumer:迁移后的服务消费者应用,运行环境为Apache Tomcat 7.0.91。
- edas-dubbo-demo-provider:迁移后的服务提供者应用,运行环境为Apache Tomcat 7.0.91。
- edas-hsf-demo-consumer:迁移前的服务消费者应用,运行环境EDAS-Container v3.5.4。
- edas-hsf-demo-provider:迁移前的服务提供者应用,运行环境为EDAS-Container v3.5.4。
FAQ
- Dubbo服务消费者启动后,提示找不到服务提供者地址。
问题现象
java.lang.IllegalStateException: Failed to check the status of the service com.xxxx.xxxxx.service.xxxxxConfigService. No provider available for the service HSF/com.xxxxx.xxxxx.service.xxxxxxxxxxService:1.0.0 from the url edas://127.0.0.1:8080/org.apache.dubbo.registry.RegistryService?application=xxxx-flow-center-bj&dubbo=2.0.2&group=HSF&interface=com.xxxx.xxxxxx.service.xxxxxxxxxxService&lazy=false&methods=queryConfigs,getConfig,saveConfig&pid=11596®ister.ip=xxx.xx.xx.xxx&release=2.7.3&revision=1.0.1-SNAPSHOT&side=consumer&sticky=false&timeout=2000×tamp=1564242421194&version=1.0.0 to the consumer xxx.xx.xx.xxx use dubbo version 2.7.3
可能原因
注册中心的地址推送为异步推送,启动过程中Dubbo默认会检查服务提供者是否有可用地址。如果没有,则会抛出该异常。
解决方案
在Dubbo的
<dubbo:reference>
标签中增加check="false"
配置:<dubbo:reference id="demoService" interface="com.alibaba.edas.DemoService" group="HSF" version="1.0.0" check="false"/>
该参数表示Dubbo启动过程中不会去检查提供者地址是否可用。但是,如果业务初始化逻辑里面有需要调用Dubbo服务的话,这种情况下业务可能会失败。
- HSF服务消费者调用Dubbo服务异常。
问题现象
2019-07-28 23:07:38.005 [WARN ] [cf67433d1e7a44412a518bd190100d176-node401] [NettyServerWorker-6-1] [o.a.d.r.exchange.codec.ExchangeCodec:91] | [DUBBO] Fail to encode response: Response [id=343493, version=HSF2.0, status=20, event=false, error=null, result=AppResponse [value=FlowControlDto(postWeightDtoHashMap={614215325=PostWeightDto(postId=614215325, weight=1.0, postSourceType=null)}), exception=null]], send bad_response info instead, cause: For input string: "", dubbo version: 2.7.3, current host: xxx.xx.xx.xxx java.lang.NumberFormatException: For input string: "" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.lang.Integer.parseInt(Integer.java:592) at java.lang.Integer.parseInt(Integer.java:615) at org.apache.dubbo.common.Version.parseInt(Version.java:133) at org.apache.dubbo.common.Version.getIntVersion(Version.java:118) at org.apache.dubbo.common.Version.isSupportResponseAttachment(Version.java:102) at org.apache.dubbo.rpc.protocol.dubbo.DubboCodec.encodeResponseData(DubboCodec.java:195) at org.apache.dubbo.remoting.exchange.codec.ExchangeCodec.encodeResponse(ExchangeCodec.java:283) at org.apache.dubbo.remoting.exchange.codec.ExchangeCodec.encode(ExchangeCodec.java:71) at org.apache.dubbo.rpc.protocol.dubbo.DubboCountCodec.encode(DubboCountCodec.java:40) at org.apache.dubbo.remoting.transport.netty4.NettyCodecAdapter$InternalEncoder.encode(NettyCodecAdapter.java:70) ...
可能原因
Dubbo升级到2.7后,HSF对Dubbo的协议兼容性出现问题。
解决方案
升级EDAS-container到V3.5.5,该版本的HSF已经修复了该问题。
- Dubbo服务消费者调用HSF服务提供者失败。
问题现象
java.lang.Exception: [HSF-Provider-192.168.0.46] Error log: [HSF-Provider] App [xxxxxxx-3b6f-42d3-xxxx-0ad2434xxxxx] failed to verify the caller signature [null] for [com.alibaba.edas.DemoService:1.0.0] [sayHello] from client [192.168.XX.XX] com.taobao.hsf.io.remoting.dubbo2.Dubbo2PacketFactory.serverCreate(Dubbo2PacketFactory.java:284) com.taobao.hsf.io.stream.AbstractServerStream.write(AbstractServerStream.java:25) com.taobao.hsf.io.RpcOutput.flush(RpcOutput.java:37) com.taobao.hsf.remoting.provider.ProviderProcessor$OutputCallback.operationComplete(ProviderProcessor.java:155) com.taobao.hsf.remoting.provider.ProviderProcessor$OutputCallback.operationComplete(ProviderProcessor.java:130) com.taobao.hsf.util.concurrent.AbstractListener.run(AbstractListener.java:18) com.taobao.hsf.invocation.AbstractContextAwareRPCCallback.access$001(AbstractContextAwareRPCCallback.java:12) com.taobao.hsf.invocation.AbstractContextAwareRPCCallback$1.run(AbstractContextAwareRPCCallback.java:27) com.taobao.hsf.util.concurrent.WrappedListener.run(WrappedListener.java:34) com.taobao.hsf.invocation.AbstractContextAwareRPCCallback.run(AbstractContextAwareRPCCallback.java:36) com.google.common.util.concurrent.MoreExecutors$DirectExecutor.execute(MoreExecutors.java:456) com.google.common.util.concurrent.AbstractFuture.executeListener(AbstractFuture.java:817) com.google.common.util.concurrent.AbstractFuture.addListener(AbstractFuture.java:595) com.taobao.hsf.util.concurrent.DefaultListenableFuture.addListener(DefaultListenableFuture.java:32) com.taobao.hsf.remoting.provider.ProviderProcessor.handleRequest(ProviderProcessor.java:55) com.taobao.hsf.io.remoting.dubbo2.message.Dubbo2ServerHandler$1.run(Dubbo2ServerHandler.java:65) java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) java.lang.Thread.run(Thread.java:748)
可能原因
HSF开启了调用鉴权,而Dubbo暂时不支持鉴权。
解决方案
在HSF服务端增加参数
-DneedAuth=false
,关闭调用鉴权。 - Dubbo服务消费者调用HSF服务提供者失败。
问题现象
2019-08-02 17:17:15.187 [WARN ] [cf67433d1e7a44412a518bd190100d176-node401] [NettyClientWorker-4-1] [o.a.d.r.p.dubbo.DecodeableRpcResult:91] | [DUBBO] Decode rpc result failed: null, dubbo version: 2.7.3, current host: xxx.xx.xx.xxx java.lang.StackOverflowError: null at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:57) at sun.reflect.UnsafeByteFieldAccessorImpl.setByte(UnsafeByteFieldAccessorImpl.java:98) at java.lang.reflect.Field.setByte(Field.java:838) at com.alibaba.com.xxxxxx.hessian.io.JavaDeserializer$ByteFieldDeserializer.deserialize(JavaDeserializer.java:452) at com.alibaba.com.xxxxxx.hessian.io.JavaDeserializer.readObject(JavaDeserializer.java:276) at com.alibaba.com.xxxxxx.hessian.io.JavaDeserializer.readObject(JavaDeserializer.java:203) at com.alibaba.com.xxxxxx.hessian.io.SerializerFactory.readObject(SerializerFactory.java:532) at com.alibaba.com.xxxxxx.hessian.io.Hessian2Input.readObjectInstance(Hessian2Input.java:2820) at com.alibaba.com.xxxxxx.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2743) at com.alibaba.com.xxxxxx.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2278) at com.alibaba.com.xxxxxx.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2080) at com.alibaba.com.xxxxxx.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2074) at com.alibaba.com.xxxxxx.hessian.io.JavaDeserializer$ObjectFieldDeserializer.deserialize(JavaDeserializer.java:406) at com.alibaba.com.xxxxxx.hessian.io.JavaDeserializer.readObject(JavaDeserializer.java:276) at com.alibaba.com.xxxxxx.hessian.io.JavaDeserializer.readObject(JavaDeserializer.java:203) at com.alibaba.com.xxxxxx.hessian.io.SerializerFactory.readObject(SerializerFactory.java:532) at com.alibaba.com.xxxxxx.hessian.io.Hessian2Input.readObjectInstance(Hessian2Input.java:2820) at com.alibaba.com.xxxxxx.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2743) at com.alibaba.com.xxxxxx.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2278) at com.alibaba.com.xxxxxx.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2080) at com.alibaba.com.xxxxxx.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2074) at com.alibaba.com.xxxxxx.hessian.io.JavaDeserializer$ObjectFieldDeserializer.deserialize(JavaDeserializer.java:406) ...
可能原因
HSF服务提供和依赖的hessian-lite版本较低,不支持JDK 8的LocalDateTime的序列化。
解决方案
升级HSF服务端的EDAS-Container的版本到v3.5.5。