1. 程式人生 > 程式設計 >springboot2中使用dubbo的三重境界

springboot2中使用dubbo的三重境界

在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 return
Str = "Hello "+str; return returnStr; } }複製程式碼

然後執行DubbodemoApplication就能釋出這個服務了。

同樣的,在消費者那邊,我們直接用@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的介面類。

這樣就能愉快的呼叫了

是不是很簡單,是不是很容易。但是這個世界是複雜的,我們看下pom檔案可以看到,裡面引用的dubbo版本是2.6.0

但現在很多地方用的並不是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,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檔案

<?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原始碼