Dubbo 3 于 Spring MVC 下使用注解配置
Posted sp42a
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dubbo 3 于 Spring MVC 下使用注解配置相关的知识,希望对你有一定的参考价值。
入门
Dubbo 是做 RPC 的,基于 Socket + 高性能协议,肯定比 HTTP 调用快多。我当期架构逐渐向分布式靠近,——其实也不是最赶什么微服务的潮流,只是觉得写好的代码,如果不独立,都是依附在某个某个项目中(即“单体”)的话,则很不稳定,有点变化的不好维护。还是独立部署运行比较稳定可用。另外一点好处是,微服务其实可以减少代码重复!你想想看,各个模块哦都独立运行了,变成一个个项目,就是把这模块高度抽象提炼,最大限度复用当期的服务或者组件。
项目是 Spring MVC 5,没有也没必要使用 Spring Boot,但使用注解下配置几乎是一样的。网上很多教程都是说 Dubbo v2.X 于 Spring MVC 下 xml 配置的,本文完全抛弃 xml,采用 Java 注解来配置。
直连方式
同时本文使用最简单的直连方式,无须配置什么 ZooKeeper 注册中心。直连方式是配置 url 指向提供者,将绕过注册中心,支持多个 url。
顺便科普一下,注册中心是什么:
就像发送http请求一样,消费端只要知道服务提供方的 ip 地址和端口号,就可以发起远程 RPC 调用,但如果服务提供方服务器迁移或者增加节点,又或者某个节点故障维修,那会导致消费端不可用,所以需要一个注册中心来统一管理。消费端从注册中心拿到正常提供服务的所有服务提供者,服务提供者向注册中心注册,并保持心跳,当某个提供者不可用时,将提供者剔除,并通知所有消费者,或者消费者定时询问注册中心。Monitor 暂时不讨论。
用法
还记得 Java RMI 吗?定义接口,序列化数据,远程请求,很神奇,就像在本地调用一样。当前 Dubbo 就是 RMI 加强版。跟 RMI 一样,客户端(消费端)只需要写接口,不用写实现,即可执行返回结果,实现的逻辑由服务端提供并返回。
定义一个简单的 GreetingService
接口,里面只有一个简单的方法 sayHello
向调用者问好。这个接口无论消费端还是服务端都需要的,你可以把它放在一个公共的项目/包。
public interface GreetingService
String sayHello(String name);
服务端:服务实现
实现 GreetingService
接口,并通过 @DubboService
来标注其为 Dubbo 的一个服务。
import org.apache.dubbo.config.annotation.DubboService;
@DubboService
public class AnnotatedGreetingService implements GreetingService
public String sayHello(String name)
System.out.println("::::::::::::" + name);
return "hello, " + name;
注意 AnnotatedGreetingService
实现了接口 GreetingService
。
配置服务端
像普通 Bean 那样配置 Dubbo 的相关配置。注意 scanBasePackages
要配置到能扫描到上述的业务类 AnnotatedGreetingService
。
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.ProviderConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableDubbo(scanBasePackages = "com.ajaxjs")
public class ProviderConfiguration
@Bean // #2
public ApplicationConfig applicationConfig()
ApplicationConfig app = new ApplicationConfig();
app.setName("dubbo-annotation-provider");
return app;
@Bean // #1
public ProviderConfig providerConfig()
ProviderConfig provider = new ProviderConfig();
provider.setTimeout(1000);
return provider;
@Bean // #3
public RegistryConfig registryConfig()
RegistryConfig registry = new RegistryConfig();
registry.setAddress("N/A"); // 直连
return registry;
@Bean // #4
public ProtocolConfig protocolConfig()
ProtocolConfig protocol = new ProtocolConfig();
protocol.setName("dubbo");
protocol.setPort(20880);
return protocol;
至此服务端的配置就 OK 了。运行 Spring 就会启动 Dubbo 服务,运行 Tomcat 也是会。或者你写个单测也行,不需要调用什么,Spring 注入 Dubbo bean 时候就启动了。单测例子如下。
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@ContextConfiguration(locations = "classpath*:applicationContext.xml" )
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public class TestRpcServer
@Test
public void test() throws Exception
System.out.println("Dubbo Server Running!");
// Thread.sleep(100000);
System.in.read(); // #4
成功运行日志显示如下。
配置消费端
与服务端的类似。
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ConsumerConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableDubbo(scanBasePackages = "com.ajaxjs.cms")
public class DubboConsumerConfiguration
@Bean
public ApplicationConfig applicationConfig()
ApplicationConfig app = new ApplicationConfig();
app.setName("dubbo-annotation-consumer");
return app;
@Bean
public ConsumerConfig consumerConfig()
ConsumerConfig con = new ConsumerConfig();
con.setTimeout(3000);
return con;
/**
* 注册中心配置
*
* @return
*/
@Bean
public RegistryConfig registryConfig()
RegistryConfig registry = new RegistryConfig();
registry.setAddress("N/A"); // 直连
return registry;
消费端服务定义
这个就是你要调用的定义,不需要写实现,就是简单的接口。
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Component;
import com.ajaxjs.rpc.GreetingService;
@Component
public class GreetingServiceConsumer
@DubboReference(url = "dubbo://127.0.0.1:20880")
private GreetingService greetingService;
public String doSayHello(String name)
return greetingService.sayHello(name);
比较重要的是 @DubboReference
注解,这里指定 Dubbo 服务端地址。开始我就是没配置这里,搞了很久。
循例写单测:
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import com.ajaxjs.cms.GreetingServiceConsumer;
@ContextConfiguration(locations = "classpath*:applicationContext.xml" )
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public class TestRpcConsumer
@Autowired
private GreetingServiceConsumer greetingServiceConsumer;
@Test
public void test()
String hello = greetingServiceConsumer.doSayHello("World!"); // #4
System.out.println("result: " + hello); // #5
assertNotNull(hello);
使用技巧
- 单测时候不想加载 Dubbo,加快速度,把注解
@EnableDubbo
注释掉就可以。
使用 @DubboReference
注解有个问题,就是写死了配置,不能可配置化。对此我们可以抛弃注解方式,改用以下 API 的方式:
@Value("$Dubbo.server")
private String dubboServer;
/**
* 配置要使用的 RPC 服务
*
* @return RPC 服务,使用 get() 返回业务对象
*/
@Bean
public ReferenceConfig<IDataDictService> ReferenceConfig()
ReferenceConfig<IDataDictService> ref = new ReferenceConfig<>();
ref.setUrl(dubboServer);
ref.setInterface(IDataDictService.class);
return ref;
使用业务类的话,变成:
@Autowired
ReferenceConfig<IDataDictService> ref;
@Test
public void test2()
IDataDictService service = ref.get();
List<DataDict> dataDict = service.getDataDict(2L);
assertNotNull(dataDict);
System.out.println(dataDict.get(0));
点对点直连要注意:
// 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
ReferenceConfig<DemoService> reference = new ReferenceConfig<DemoService>();
// 如果点对点直连,可以用reference.setUrl()指定目标地址,设置url后将绕过注册中心,
// 其中,协议对应provider.setProtocol()的值,端口对应provider.setPort()的值,
// 路径对应service.setPath()的值,如果未设置path,缺省path为接口名
reference.setUrl("dubbo://10.20.130.230:20880/com.xxx.DemoService");
参见《Dubbo 的几种启动方式》、《Dubbo 直连代码示例》。
注册中心
注册中心作用,还是这位老铁说得好:
开篇首先想思考一个问题,没有注册中心 Dubbo 还能玩下去吗? 当然可以,只要知道服务提供者地址相关信息,消费者配置之后就可以调用。如果只有几个服务,这么玩当然没问题。但是生产服务动辄成千上百,如果每个服务都需要手写配置信息,想象一下是多么麻烦。
好吧,如果上面的问题都不是事的话,试想一下如果一个服务提供者在运行过程中宕机,消费者怎么办?消费者不知情,所以它还会不断把请求发往服务提供者,然后不断失败。这个时候唯一的办法就是修改服务地址信息,然后重启服务。可以看到如果没有注册中心,分布式环境中服务查找发现将会非常麻烦,一切需要手工配置,无法完成自动化。所以这里就需要一个第三者,协调服务提供者与消费者之间关系,这就是注册中心。
注册中心的选型:
- Nacos 一开始的默认选择,但发现依然太重 100多M
- Consul,也是一百多 M 的 Go 程序,一站式的,太重
- Redis,开始觉得它是轻量级的,但鲜见案例,放弃了
- etcd,十几 M 的 Go 程序,听说也占资源,但相对较好,就选它了
暂时还是不搞注册中心。参见《几个你不知道的dubbo注册中心细节》、dubbo-registry-etcd3。
小结
Dubbo 很轻,Jar 包只有 3m 多,铁定用它了~
参见:
- 《Dubbo 注解驱动》
- https://dubbo.apache.org/zh/blog/2018/08/07/%E5%9C%A8-dubbo-%E4%B8%AD%E4%BD%BF%E7%94%A8%E6%B3%A8%E8%A7%A3/
- 小白也能看懂的dubbo3应用级服务发现详解
以上是关于Dubbo 3 于 Spring MVC 下使用注解配置的主要内容,如果未能解决你的问题,请参考以下文章
spring+spring mvc+mybatis+mysql+dubbo整合开发任务流程后台管理系统
BAT:Java架构师必备的学习流程图(Spring/TCP/JVM/Spring MVC/JDBC/Spring Clound/Dubbo)