springboot2中使用dubbo的三重境界

Easter79
• 阅读 967

在springboot中使用dubbo,本来是件挺简单的事情,但现实的世界就是如此的复杂,今天我用一个亲身经历的跳坑和填坑的事来讲在spring boot中使用高版本dubbo(当当的魔改版)的三重境界。

1、看山是山,使用官方starter

简单的使用dubbo starter集成进spring boot还是非常简单的。

在springboot2的pom.xml中引入dubbo的starter

    <dependency>
        <groupId>com.alibaba.spring.boot</groupId>
        <artifactId>dubbo-spring-boot-starter</artifactId>
        <version>2.0.0</version>
    </dependency>

在启动类里面加上注解@EnableDubboConfiguration

@EnableDubboConfiguration
@SpringBootApplication
public class DubbodemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DubbodemoApplication.class, args);
    }

}

在application.properties里面写dubbo的相关配置

spring.dubbo.application.name=mydubbostarterdemo
spring.dubbo.server=true
spring.dubbo.protocol.name=dubbo
spring.dubbo.registry.address=zookeeper://127.0.0.1:2181

作为服务的提供者,我们发布一个服务

public interface IHelloService {
    public String hello(String str);
}

import com.alibaba.dubbo.config.annotation.Service;
//这个service的注解是dubbo的service,不是spring的service注解
@Service(interfaceClass = IHelloService.class)
@Component
public class HelloServiceImpl implements IHelloService {

    @Override
    public String hello(String str) {
        String returnStr = "Hello "+str;
        return returnStr;
    }

}

然后运行DubbodemoApplication就能发布这个服务了。

springboot2中使用dubbo的三重境界

同样的,在消费者那边,我们直接用@Reference注解来引用这个服务即可

    @Reference
    private IHelloService helloService;
    @GetMapping("/hello/{name}")
    public String hello(@PathVariable("name") String name) {
       String str = helloService.hello(name);
       System.out.println(str);
       return str;
    }

记得在消费者的工程里面要加上IHelloService的接口类。

这样就能愉快的调用了

springboot2中使用dubbo的三重境界

是不是很简单,是不是很容易。但是这个世界是复杂的,我们看下pom文件可以看到,里面引用的dubbo版本是2.6.0

springboot2中使用dubbo的三重境界

但现在很多地方用的并不是dubbo,而是当当改过的dubbo,最新的版本已经到了2.8.4,如果服务提供者用这个版本,那么消费者还用这种方法调用,就会报错

2019-12-05 11:51:28.094 ERROR 17808 --- [nio-8088-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.alibaba.dubbo.rpc.RpcException: Failed to invoke the method hello in the service com.skyblue.dubbodemo.service.IHelloService. Tried 3 times of the providers [192.168.1.103:20880] (1/1) from the registry 115.29.199.6:2181 on the consumer 192.168.1.103 using the dubbo version 2.6.0. Last error is: Failed to invoke remote method: hello, provider: dubbo://192.168.1.103:20880/com.skyblue.dubbodemo.service.IHelloService?anyhost=true&application=mydubbostarterdemo&check=false&dubbo=2.8.4a&generic=false&interface=com.skyblue.dubbodemo.service.IHelloService&methods=hello&pid=17808&register.ip=192.168.1.103&remote.timestamp=1575517827193&side=consumer&timestamp=1575517879746, cause: Fail to decode request due to: RpcInvocation [methodName=hello, parameterTypes=null, arguments=null, attachments={path=com.skyblue.dubbodemo.service.IHelloService, input=223, dubbo=2.6.0, version=0.0.0}]] with root cause

com.alibaba.dubbo.remoting.RemotingException: Fail to decode request due to: RpcInvocation [methodName=hello, parameterTypes=null, arguments=null, attachments={path=com.skyblue.dubbodemo.service.IHelloService, input=223, dubbo=2.6.0, version=0.0.0}]
    at com.alibaba.dubbo.remoting.exchange.support.DefaultFuture.returnFromResponse(DefaultFuture.java:218) ~[dubbo-2.6.0.jar:2.6.0]
    at com.alibaba.dubbo.remoting.exchange.support.DefaultFuture.get(DefaultFuture.java:137) ~[dubbo-2.6.0.jar:2.6.0]
    at com.alibaba.dubbo.remoting.exchange.support.DefaultFuture.get(DefaultFuture.java:111) ~[dubbo-2.6.0.jar:2.6.0]
    at com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker.doInvoke(DubboInvoker.java:95) ~[dubbo-2.6.0.jar:2.6.0]
    at com.alibaba.dubbo.rpc.protocol.AbstractInvoker.invoke(AbstractInvoker.java:142) ~[dubbo-2.6.0.jar:2.6.0]
    ......

同样,提供服务端也是报错

2019-12-05 12:00:55.144  WARN 7412 --- [w I/O worker #7] c.a.d.r.p.dubbo.DecodeableRpcInvocation  :  [DUBBO] Decode rpc invocation failed: expected integer at 0x12 java.lang.String (Ljava/lang/String;), dubbo version: 2.8.4a, current host: 192.168.1.103

com.alibaba.com.caucho.hessian.io.HessianProtocolException: expected integer at 0x12 java.lang.String (Ljava/lang/String;)
    at com.alibaba.com.caucho.hessian.io.Hessian2Input.error(Hessian2Input.java:2720) ~[dubbo-2.8.4a.jar:2.8.4a]
    at com.alibaba.com.caucho.hessian.io.Hessian2Input.expect(Hessian2Input.java:2691) ~[dubbo-2.8.4a.jar:2.8.4a]
    at com.alibaba.com.caucho.hessian.io.Hessian2Input.readInt(Hessian2Input.java:773) ~[dubbo-2.8.4a.jar:2.8.4a]
    at com.alibaba.dubbo.common.serialize.support.hessian.Hessian2ObjectInput.readInt(Hessian2ObjectInput.java:58) ~[dubbo-2.8.4a.jar:2.8.4a]
    at com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:106) ~[dubbo-2.8.4a.jar:2.8.4a]

显然,因为版本差异造成了向下兼容有问题,那怎么办呢,我首先想到的是把消费端的dubbo starter也升级到2.8.4,但这个版本不是阿里做的,所以阿里官方的starter还是稳稳的停留在2.0.0的最新版本,只支持dubbo 2.6.0的版本,那怎么办呢?

2、看山不是山,回归传统,不用starter

我其实首先尝试的是starter直接强行引用2.8.4的版本

       <dependency>
            <groupId>com.alibaba.spring.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.0.0</version>
            <exclusions>
                <exclusion>
                    <groupId>com.alibaba</groupId>
                    <artifactId>dubbo</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.8.4</version>
        </dependency>

可惜可耻的失败了,启动消费端的时候就报错了

12:12:01.733 [main] ERROR org.springframework.boot.SpringApplication - Application run failed
java.lang.NoClassDefFoundError: com/alibaba/dubbo/qos/server/DubboLogo
    at com.alibaba.dubbo.spring.boot.context.event.DubboBannerApplicationListener.buildBannerText(DubboBannerApplicationListener.java:49)
    at com.alibaba.dubbo.spring.boot.context.event.DubboBannerApplicationListener.onApplicationEvent(DubboBannerApplicationListener.java:39)
    at com.alibaba.dubbo.spring.boot.context.event.DubboBannerApplicationListener.onApplicationEvent(DubboBannerApplicationListener.java:23)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
    at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:76)
    at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:53)
    at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:345)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
    at com.skyblue.dubbodemo.DubbodemoClientApplication.main(DubbodemoClientApplication.java:13)

这里有一个DubboLogo的报错,后面我们会讲到。显然,starter里面用到的dubbo2.6.0的代码在dubbo2.8.4里面做了改动,已经调不了了,怎么办,只能抛弃starter,使用传统的dubbo调用方法了。

        //不使用starter
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.8.4</version>
        </dependency>

在resources/spring目录下添加一个dubbo-consumer.xml文件

springboot2中使用dubbo的三重境界

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <dubbo:application name="mydubbodemo"/>

    <!-- 注册中心配置,使用zookeeper注册中心暴露服务地址 -->
    <dubbo:registry id="registry1" address="zookeeper://127.0.0.1:2181" timeout="60000" />

    <dubbo:consumer timeout="120000" retries="0" check="false"/>
    <dubbo:reference interface="com.skyblue.dubbodemo.service.IHelloService" registry="registry1" id="helloService"/>
</beans>

然后在消费端的启动文件里面加上这个xml文件

@SpringBootApplication
@ImportResource("classpath:spring/dubbo-consumer.xml")
class DubbodemoClientApplication{

    public static void main(String[] args) {
        SpringApplication.run(DubbodemoClientApplication.class, args);
    }

}

调用服务的类改成传统的引入方式

   @Autowired
    private IHelloService helloService;

这样就能使用2.8.4版本的dubbo了。

但是,这种方法需要在xml里面显式的申明我需要调用的服务,和直接用注解@Reference就调用服务比方便性差太多了,虽然现在可以使用了,但我还是深深的想念starter...

3、看山还是山,就要用starter

看过我之前文章的朋友应该清楚,其实starter是一个很简单的规范,可以让开发人员来自定义自己的starter,既然官方不支持,那我们就自己改一个starter不就行了,我就是拿2.0.0版本的官方dubbo starter改的。先clone一份官方的版本,这里

首先要改的就是把pom.xml的dubbo版本升级到2.8.4,但改完后就会看到有报错。报错就是这个类DubboBannerApplicationListener

bannerTextBuilder.append(DubboSpringBootStarterConstants.LINE_SEPARATOR).append(DubboLogo.dubbo)
        .append(" :: Dubbo ::        (v").append(Version.getVersion()).append(")")
        .append(DubboSpringBootStarterConstants.LINE_SEPARATOR);

里面的DubboLogo这个类在dubbo后面版本里面被去掉了,所以报错,但这个其实是在后台的console 出现一个dubbo的标志,不要也无所谓,去掉就好了。上面我们在消费端强行升级dubbo版本,启动时报的其实也是这个错。

然后我们再次改pom.xml文件,把spring boot和jdk的版本也升一下

<properties>
...
        <java.version>1.8</java.version>
        <spring-boot.version>2.2.1.RELEASE</spring-boot.version>
        <dubbo.version>2.8.4a</dubbo.version>
...
</properties>

这样,一个可以在2.8.4a版本下使用的dubbo starter就改造完成了。

4、用起来

虽然改完了,但怎么用却还是一个问题,第一种方法是直接用mvn package生成dubbo-spring-boot-starter-2.8.4a.jar,然后copy到项目中去。但这种方法在现在的软件开发中已经不常用了,大部分用的方法是发布到maven的nexus私服。首先要把pom.xml改一下

<distributionManagement>
        <snapshotRepository>
            <id>snapshots</id>
            <url>http://私服ip:port/nexus/content/repositories/snapshots</url>
        </snapshotRepository>
        <repository>
            <id>releases</id>
            <url>http://私服ip:port/nexus/content/repositories/releases</url>
        </repository>
    </distributionManagement>

另外本地maven软件的setting.xml文件也要配置一下:

<servers>
      <server>
            <id>releases</id>
            <username>账号</username>
            <password>密码</password>
        </server>
        <server>
            <id>snapshots</id>
            <username>admin</username>
            <password>admin123</password>
        </server>
</servers>

然后执行 mvn deploy就能发布到私服上去了。如果deploy有报这个错

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-gpg-plugin:1.6:sign (sign-artifacts) on project dubbo-spring-boot-starter: Unable to execute gpg command: Error while executing process. Cannot run program "gpg.exe": CreateProcess error=2, 系统找不到指定的文件。 -> [Help 1]

是因为deploy的时候调用了gpg的签名,我们私服不见得需要,我直接把它从pom.xml里面去掉了

<plugin>
                <artifactId>maven-gpg-plugin</artifactId>
                <version>${maven-gpg-plugin.version}</version>
                <configuration>
                    <skip>false</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>sign-artifacts</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>sign</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

就是这段,去掉后,发布成功就能够直接在项目的pom.xml中引用了。

修改后的starter源代码

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k