源码地址
springboot2教程系列
Dubbo官网资料
1)导入dubbo-starter,在application.properties配置属性,使用@Service【暴露服务】使用@Reference【引用服务】,使用@EnableDubbo注解。但是没有提供dubbo:method标签的对应注解。
2)保留dubbo xml配置文件,导入dubbo-starter,使用@ImportResource导入dubbo的配置文件即可。不再使用@Service【暴露服务】使用@Reference【引用服务】,不使用@EnableDubbo注解。
3)使用注解API的方式,将每一个组件手动创建到容器中,让dubbo来扫描其他的组件,使用@Service【暴露服务】使用@Reference【引用服务】,使用@EnableDubbo注解。
com.alibaba.spring.boot
dubbo-spring-boot-starter
2.0.0
org.apache.zookeeper
zookeeper
3.4.8
org.slf4j
slf4j-log4j12
org.slf4j
slf4j-api
com.github.sgroschupf
zkclient
0.1
定义接口
public interface RemoteUserService {
String sayHello(String name);
}
接口实现
@Component
@Service(version = "2.6.0",timeout = 10000,interfaceClass = RemoteUserService.class)
public class RemoteUserServiceImpl implements RemoteUserService {
@Value("${server.port}")
private Integer port;
@Override
public String sayHello(String name) {
System.out.println("provider");
return "hi!server port:"+port+";name="+name;
}
}
启动类添加@EnableDubboConfiguration
@RestController
public class RemoteUserController {
@Reference(version = "2.7.0",async=true,sent=false)
private RemoteUserService remoteUser;
}
启动类添加@EnableDubboConfiguration
配置文件
# Tomcat
server:
tomcat:
max-threads: 1000
min-spare-threads: 10
port: 8091
spring:
dubbo:
appname: consumer
registry: zookeeper://10.10.2.138:2181?backup=10.10.2.139:2181,10.10.2.137:2181
protocol:
name: dubbo
port: 20883
#启动时检查(check),关闭所有服务的启动时检查 @Reference(check = false)
spring.dubbo.consumer.check: false
#超时(timeout,默认为1000), @Reference(timeout=XXX)
spring.dubbo.consumer.timeout: 10000
#超时后,重试次数(retries) @Reference(retries=XXX)
spring.dubbo.consumer.retries: 1
#是否注册服务
spring.dubbo.server: false
spring.application.name: dubbo-consumer
启动类
@SpringBootApplication
@ImportResource(value = {"classpath:dubbo.xml"})
@EnableDubbo
public class DubboProviderXMLApplication {
public static void main(String[] args){
SpringApplication app = new SpringApplication(DubboProviderXMLApplication.class);
// app.setWebEnvironment(false);
app.run(args);
}
}
dubbo.xml放到resources目录下
<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="dubbo-provider" owner="dubbo-provider" />
<dubbo:registry protocol="zookeeper" address="zookeeper://10.10.2.138:2181?backup=10.10.2.139:2181,10.10.2.137:2181" client="zkclient" />
<dubbo:protocol name="dubbo" port="20889" />
<dubbo:service interface="cn.myframe.service.RemoteUserService"
ref="user2Service" timeout="10000" />
beans>
@Component
public class ProviderDubboConfig {
@Autowired(required = false)
RemoteUserService userService;
@Autowired(required = false)
DubboProperties dubboProperties;
@PostConstruct
public void userServiceConfig(){
ServiceConfig<RemoteUserService> serviceConfig = new ServiceConfig<>();
serviceConfig.setInterface(RemoteUserService.class);
serviceConfig.setRef(userService);
serviceConfig.setVersion("2.6.0");
serviceConfig.setApplication(dubboProperties.getApplication());
serviceConfig.setRegistry(dubboProperties.getRegistry());
serviceConfig.setProtocol(dubboProperties.getProtocol());
// serviceConfig.setProtocols(dubboProperties.getProtocols());
//配置每一个method的信息
MethodConfig methodConfig = new MethodConfig();
methodConfig.setName("sayHello");
methodConfig.setTimeout(1000);
//将method的设置关联到service配置中
List<MethodConfig> methods = new ArrayList<>();
methods.add(methodConfig);
serviceConfig.setMethods(methods);
serviceConfig.export();
// return serviceConfig;
}
}
@Autowired
DubboProperties dubboProperties;
@Bean
public RemoteUserService remoteUserService(){
ReferenceConfig<RemoteUserService> referenceConfig = new ReferenceConfig<>();
referenceConfig.setInterface(RemoteUserService.class);
referenceConfig.setTimeout(5000);
referenceConfig.setVersion("2.6.0");
referenceConfig.setRetries(3);
referenceConfig.setCheck(false);
referenceConfig.setRegistry(dubboProperties.getRegistry());
referenceConfig.setApplication(dubboProperties.getApplication());
return referenceConfig.get();
}
spring.dubbo.consumer.check:设置为false时,启动时检查(check),关闭所有服务的启动时检查。设置true时,如果服务没启动,启动会报错。也可以单独给某个接口设置@Reference(check = false)
,而且覆盖application.yml中的spring.dubbo.consumer.check
配置。
spring.dubbo.consumer.Cluster: 可以自行扩展集群容错策略,@Reference(cluster = "failover ")
。
failover
失败自动切换 ,默认策略。当出现失败,重试其它服务器 ,spring.dubbo.consumer.retries
设置重试次数(不含第一次) 。
failfast
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录
failsafe
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
failback
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
forking
并行调用多个服务器,只要一个成功即返回。
broadcast
广播调用所有提供者,逐个调用,任意一台报错则报错 。
spring.dubbo.consumer.loadbalance: 均衡策略 。@Reference(loadbalance = "roundrobin")
random
随机,按权重设置随机概率 ,缺省
roundrobin
轮询,按公约后的权重设置轮询比率。存在慢的提供者累积请求的问题
leastactive
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差 。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大
consistenthash
一致性 Hash,相同参数的请求总是发到同一提供者 ,缺省只对第一个参数 Hash
在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者 。
spring.dubbo.consumer.url: dubbo://localhost:20881
,或者@Reference(url = "dubbo://localhost:20881")
application.yml配置,可能配置协议有rmi
http
spring:
dubbo:
appname: provider
registry: zookeeper://10.10.2.138:2181?backup=10.10.2.139:2181,10.10.2.137:2181
protocols:
- name: rmi
port: 7885
hose: 127.0.0.1
- name: dubbo
port: 28802
hose: 127.0.0.1
- name: http
port: 8500
server: servlet #默认是jetty
同时协议有使用才会有效
@Service(version = "2.7.0",interfaceClass = RemoteUserService.class,protocol={"http","rmi"})
当一个接口有多种实现时,可以用 group 区分。
生产者
@Service(version = "2.7.0",group = "service2",timeout = 10000,interfaceClass = RemoteUserService.class)
public class RemoteUserService2Impl implements RemoteUserService {...}
@Service(version = "2.7.0",group = "service1",timeout = 10000,interfaceClass = RemoteUserService.class)
public class RemoteUserServiceImpl implements RemoteUserService {...}
消费者
@Reference(version = "2.7.0",group = "*") #任意组
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
生产者
@Service(version = "2.6.0",timeout = 10000,interfaceClass = RemoteUserService.class)
public class RemoteUserService2Impl implements RemoteUserService {...}
@Service(version = "2.7.0",timeout = 10000,interfaceClass = RemoteUserService.class)
public class RemoteUserServiceImpl implements RemoteUserService {...}
消费者
@Reference(version = "2.7.0") #version="*" 表示任意版本
lru
基于最近最少使用原则删除多余缓存,保持最热的数据被缓存。threadlocal
当前线程缓存,比如一个页面渲染,用到很多 portal,每个 portal 都要去查用户信息,通过线程缓存,可以减少这种多余访问。jcache
与 JSR107 集成,可以桥接各种缓存实现。lru
的缺省cache.size为1000
@Reference(version = "2.7.0",cache = "lru")
@Reference(version = "2.7.0",async = true)
sent="true"
等待消息发出,消息发送失败将抛出异常。sent="false"
不等待消息发出,将消息放入 IO 队列,即刻返回。@Reference(version = "2.7.0",async = true,sent = true)
消费者
@RequestMapping(value="/dubbo/attachment/{name}")
public String attachment(@PathVariable("name") String name){
RpcContext.getContext().setAttachment("name", "1");
String result=remoteUser.attachment();
return result;
}
注意:path, group, version, dubbo, token, timeout 几个 key 是保留字段,请使用其它值
参数回调方式与调用本地 callback 或 listener 相同,只需要在 Spring 的配置文件中声明哪个参数是 callback 类型即可。Dubbo 将基于长连接生成反向代理,这样就可以从服务器端调用客户端逻辑 。
public interface CallbackService {
void addListener(String key, CallbackListener listener);
}
public interface CallbackListener {
void changed(String msg);
}
@Component("callbackService")
public class CallbackServiceImpl implements CallbackService{
public final static Map<String, CallbackListener> listeners =
new ConcurrentHashMap<String, CallbackListener>();
public void addListener(String key, CallbackListener listener) {
listeners.put(key, listener);
listener.changed("key:"+key+",调用回调方法"); // 发送变更通知
}
}
服务提供者配置
@PostConstruct
public void callBackrServiceConfig(){
ServiceConfig<CallbackService> serviceConfig = new ServiceConfig<>();
serviceConfig.setInterface(CallbackService.class);
serviceConfig.setRef(callbackService);
serviceConfig.setVersion("2.6.0");
serviceConfig.setApplication(dubboProperties.getApplication());
serviceConfig.setRegistry(dubboProperties.getRegistry());
serviceConfig.setProtocol(dubboProperties.getProtocol());
serviceConfig.setCallbacks(100); //设置回调参数个数
//配置每一个method的信息
MethodConfig methodConfig = new MethodConfig();
methodConfig.setName("addListener");
ArgumentConfig argumentConfig = new ArgumentConfig();
argumentConfig.setIndex(1);
argumentConfig.setCallback(true);
methodConfig.setArguments(Arrays.asList(argumentConfig));
//将method的设置关联到service配置中
List<MethodConfig> methods = new ArrayList<>();
methods.add(methodConfig);
serviceConfig.setMethods(methods);
serviceConfig.export();
}
触发回调
@GetMapping("/listener")
public String listener(){
for(Map.Entry<String, CallbackListener> entry : CallbackServiceImpl.listeners.entrySet()) {
entry.getValue().changed("key:" + entry.getKey() + ",调用回调方法");
}
return "success";
}
@Reference(version = "2.6.0")
private CallbackService callbackService;
服务消费者调用
@RequestMapping(value="/dubbo/listener/{name}")
public void listener(@PathVariable("name") String name){
callbackService.addListener(name,new CallbackListener(){
public void changed(String msg) {
System.out.println("callback1:" + msg);
}
});
}
在调用之前、调用之后、出现异常时,会触发 oninvoke
、onreturn
、onthrow
三个事件,可以配置当事件发生时
public interface IDemoService {
public Project get(String projectName);
}
@Component
@Service(version = "2.7.0",timeout = 10000,interfaceClass = IDemoService.class)
public class NormalDemoService implements IDemoService{
@Override
public Project get(String projectName) {
return new Project(projectName);
}
}
public interface Notify {
public void onreturn(Project project, String name);
public void onthrow(Throwable ex, String name);
}
@Component
public class NotifyImpl implements Notify {
public Map<String, Project> ret = new HashMap<String, Project>();
public Map<String, Throwable> errors = new HashMap<String, Throwable>();
public void onreturn(Project project, String name) {
System.out.println("onreturn:" + project+",name:"+name);
ret.put(name, project);
}
public void onthrow(Throwable ex, String name) {
errors.put(name, ex);
}
}
@Configuration
public class DubboConfig {
@Autowired
NotifyImpl notify;
@Autowired
DubboProperties dubboProperties;
@Bean
public IDemoService demoService(){
ReferenceConfig<IDemoService> referenceConfig = new ReferenceConfig<>();
referenceConfig.setInterface(IDemoService.class);
referenceConfig.setTimeout(5000);
referenceConfig.setVersion("2.7.0");
referenceConfig.setRetries(3);
referenceConfig.setRegistry(dubboProperties.getRegistry());
referenceConfig.setApplication(dubboProperties.getApplication());
//配置每一个method的信息
MethodConfig methodConfig = new MethodConfig();
methodConfig.setName("get");
methodConfig.setTimeout(1000);
methodConfig.setAsync(true);
methodConfig.setOnreturn(notify);
methodConfig.setOnreturnMethod("onreturn");
//将method的设置关联到service配置中
List<MethodConfig> methods = new ArrayList<>();
methods.add(methodConfig);
referenceConfig.setMethods(methods);
return referenceConfig.get();
}
}
callback
与 async
功能正交分解,async=true
表示结果是否马上返回,onreturn
表示是否需要回调。
两者叠加存在以下几种组合情况 [2]:
async=true onreturn="xxx"
async=false onreturn="xxx"
async=true
async=false
@Autowired
private IDemoService demoService;
@RequestMapping(value="/dubbo/notity/{name}")
public String notity(@PathVariable("name") String name){
demoService.get(name);
return "success";
}
通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。
@Reference(version = "2.7.0",mock = "cn.myframe.service.MockUserServicImpl")
提供 Mock 实现
public class MockUserServicImpl implements RemoteUserService {
@Override
public String sayHello(String name) {
return "error";
}
}
调用
@RequestMapping(value="/dubbo/sayhello/{name}")
public String sayHello(@PathVariable("name") String name){
String result=remoteUser.sayHello(name);
return result;
}
当提供者服务不可用时,返回error
@Service(version = "2.7.0",interfaceClass = RemoteUserService.class,executes=10)
服务器端并发执行(或占用线程池线程数)不能超过 10 个
@Service(version = "2.7.0",interfaceClass = RemoteUserService.class,actives = 10)
每客户端并发执行(或占用连接的请求数)不能超过 10 个
粘滞连接用于有状态服务,尽可能让客户端总是向同一提供者发起调用,除非该提供者挂了,再连另一台。
@Reference(sticky = true)