在文章开始的时候先引入官网的架构图,
节点角色说明
节点 | 角色说明 |
---|---|
Provider |
暴露服务的服务提供方 |
Consumer |
调用远程服务的服务消费方 |
Registry |
服务注册与发现的注册中心 |
Monitor |
统计服务的调用次数和调用时间的监控中心 |
Container |
服务运行容器 |
以上取自dubbo官网文档 http://dubbo.apache.org/zh-cn/docs/user/preface/architecture.html
dubbo干嘛的?一个分布式的RPC远程调用框架,可以使得调用远程服务如同调用本地服务一般的框架.具体介绍可以前往官网文档查看,这里不多加赘述,官网文档地址已在上文提及.
接下来看一个dubbo的样例.
先看一下整个样例的层级结构
api为公共组件,包括服务的接口规范
consumer为服务消费方,provider为服务提供方.
代码如下:
public interface DoSomeDemo {
String doSome(String name);
}
pom文件配置如下
dubboDemo
dubboDemo
1.0-SNAPSHOT
4.0.0
api
dubboDemo
api
1.0-SNAPSHOT
com.alibaba
dubbo
2.6.2
org.apache.zookeeper
zookeeper
3.4.9
org.apache.curator
curator-recipes
4.0.0
pom文件中引入了dubbo,zookeeper(注册中心)
接下来是consumer模块的代码
代码如下
public class Consumer {
public Object getRemoteCall(String url){
//创建一个远程服务代理
ReferenceConfig referenceConfig=new ReferenceConfig();
//配置当前应用信息
ApplicationConfig applicationConfig=new ApplicationConfig("consumer");
referenceConfig.setApplication(applicationConfig);
//选择远程服务
referenceConfig.setInterface(DoSomeDemo.class);
//直连的方式 无需注册中心,将url传入即可
//referenceConfig.setUrl(url);
//Multicast注册中心 不需要启动任何中心节点,只要广播地址一样,就可以互相发现
// RegistryConfig registryConfig=new RegistryConfig("multicast://224.1.1.1:111");
//zookeeper注册中心
RegistryConfig registryConfig=new RegistryConfig("zookeeper://127.0.0.1:2181");
referenceConfig.setRegistry(registryConfig);
//和本地bean一样使用xxxService,返回的就是拿到的远程服务对象(实际为一个代理对象)
return referenceConfig.get();
}
public static void main(String[] args) throws IOException {
Consumer consumer=new Consumer();
//如果使用直连方式则需要将整个url传入,本机的ip为10.111.23.237
//DoSomeDemo doSomeDemo = (DoSomeDemo) consumer.getRemoteCall("dubbo://10.111.23.237:20880/dubbo.service.DoSomeDemo");
DoSomeDemo doSomeDemo = (DoSomeDemo) consumer.getRemoteCall(null);
while (!"Q".equals(read())){
//调用远程服务
System.out.println(doSomeDemo.doSome("ok"));
}
}
//读取后台输入
private static String read(){
LineNumberReader lineNumberReader = new LineNumberReader(
new InputStreamReader(System.in));
try {
return lineNumberReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}
代码中列出了三种注册中心的方式,直连(无注册中心),Multicast注册中心(不需要启动任何中心节点),zookeeper注册中心,这里采取zookeeper举例.
这里的配置只满足最简单的举例,详细的配置依赖关系如下:图片取自dubbo官网api
说回代码,consumer已经有了,接下来看看provider
dubbo.service目录底下为实现api接口的实现
public class DoSomeDemoImpl implements DoSomeDemo {
@Override
public String doSome(String name) {
return ("provider --- dubbo.service.DoSomeDemoImpl"+"------"+name+"---"+ RpcContext.getContext().getLocalAddressString());
}
}
打印出Rpc调用的端口及方法入参,便于之后查看对比结果
pom文件中引入api模块即可
dubboDemo
api
1.0-SNAPSHOT
最为关键的Provider的实现代码如下
public class Provider {
public static void start(int port){
//服务提供者暴露服务配置
ServiceConfig serviceConfig=new ServiceConfig();
//服务config 相当于 配置
ApplicationConfig applicationConfig=new ApplicationConfig("provider");
serviceConfig.setApplication(applicationConfig);
//服务提供者协议配置 协议config 相当于 配置
ProtocolConfig protocolConfig=new ProtocolConfig("dubbo",port);
serviceConfig.setProtocol(protocolConfig);
//直连 协议config
//RegistryConfig registryConfig=new RegistryConfig(RegistryConfig.NO_AVAILABLE);
//Multicast注册中心
//RegistryConfig registryConfig=new RegistryConfig("multicast://224.1.1.1:111");
RegistryConfig registryConfig=new RegistryConfig("zookeeper://127.0.0.1:2181");
serviceConfig.setRegistry(registryConfig);
//将服务的具体方法设置进去
serviceConfig.setInterface(DoSomeDemo.class);
serviceConfig.setRef(new DoSomeDemoImpl());
// 暴露及注册服务
serviceConfig.export();
//拿到当前服务的端口
port=((URL)serviceConfig.getExportedUrls().get(0)).getPort();
System.out.println("服务启动:"+port);
}
public static void main(String[] args) throws IOException {
Provider.start(-1);
System.in.read();
}
}
简言之,设置服务,选择协议,选择注册中心,将服务方法设入,全部放进服务配置中,然后暴露此服务
到这里,其实已经能够将Consumer与Provider启动,并且能够在Consumer中调用"DoSomeDemo"接口的实现方法了.
接下来看看Xml配置方式该如何设置
consumer.xml配置文件如下
没错,和属性配置没什么区别,定义服务名称(consumerSpringXml),定义注册中心(zookeeper://127.0.0.1:2181),创建远程服务代理(dubbo.service.DoSomeDemo)
启动类代码如下
public class SpringConsumerXml {
public static void main(String[] args) throws IOException {
ApplicationContext context=new ClassPathXmlApplicationContext("consumer.xml");
((ClassPathXmlApplicationContext)context).start();
System.in.read();
DoSomeDemo doSomeDemo= (DoSomeDemo) context.getBean("doSomeDemo");
while (!"Q".equals(read())){
System.out.println(doSomeDemo.doSome("Xml"));
}
}
private static String read(){
LineNumberReader lineNumberReader = new LineNumberReader(
new InputStreamReader(System.in));
try {
return lineNumberReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}
加载xml文件启动,拿到doSomeDemo对象,直接调用即可,与调用本地对象没有区别
接下来看看provider用xml配置的模块
provider.xml代码如下
与consumer.xml不同的在于需要将提供的服务对象交给spring容器管理
启动类代码如下:
public class SpringProviderXml {
public static void main(String[] args) throws IOException {
ApplicationContext context=new ClassPathXmlApplicationContext("provider.xml");
((ClassPathXmlApplicationContext)context).start();
System.in.read();
}
}
单纯的加载xml文件,启动,System.in.read();是为了让服务不停止
最后看看使用注解的方式
注意,这里的DoService类
import com.alibaba.dubbo.config.annotation.Reference;
import dubbo.service.DoSomeDemo;
import org.springframework.stereotype.Service;
@Service
public class DoService {
@Reference
DoSomeDemo doSomeDemo;
public String doSome(String name){
return doSomeDemo.doSome(name);
}
}
@Reference为dubbo的注解,而@Service为spring的注解,为了将该对象交由spring容器管理
properties文件配置则更加简单
dubbo.application.name:consumerSpringProperties
dubbo.registry.address:zookeeper://127.0.0.1:2181
仅此而已
启动类则配置稍微多一些
@Configuration
@EnableDubbo(scanBasePackages = "service")
@PropertySource("consumer.properties")
@ComponentScan("service")
public class SpringConsumerProperties {
public static void main(String[] args) {
ApplicationContext context=new AnnotationConfigApplicationContext(SpringConsumerProperties.class);
((AnnotationConfigApplicationContext)context).start();
DoService doService= (DoService) context.getBean("doService");
while (!"Q".equals(read())){
System.out.println(doService.doSome("Properties"));
}
}
private static String read(){
LineNumberReader lineNumberReader = new LineNumberReader(
new InputStreamReader(System.in));
try {
return lineNumberReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}
注解从上往下依次介绍,@Configuration为spring配置类注解,这里不再赘述,
@EnableDubbo(scanBasePackages = "service") @ComponentScan("service") 这两个注解为开启dubbo 并设置注解的扫描范围,由于这里的样例将包名取为service且只有一层,所以容易引起误解,实际情况则需要将包名路径全部写入
@PropertySource("consumer.properties") 读取配置文件的注解,这里也不多介绍
最后再来看看Provider用注解的配置
properties文件
dubbo.application.name:providerSpringProvider
dubbo.protocol.name:dubbo
dubbo.protocol.port:-1
dubbo.registry.address:zookeeper://127.0.0.1:2181
暴露的服务方法
import com.alibaba.dubbo.config.annotation.Service;
@Service
public class DoSomeDemoPropertiesImpl implements DoSomeDemo {
@Override
public String doSome(String name) {
return ("provider --- dubbo.service.DoSomeDemoPropertiesImpl"+"------"+name+"---"+ RpcContext.getContext().getLocalAddressString());
}
}
而这里要注意的则是@Service注解为dubbo注解,不是spring的
启动类
@Configuration
@PropertySource("provider.properties")
@EnableDubbo(scanBasePackages = "dubbo.service")
public class SpringProviderProperties {
public static void main(String[] args) throws IOException {
ApplicationContext context=new AnnotationConfigApplicationContext(SpringProviderProperties.class);
((AnnotationConfigApplicationContext)context).start();
System.in.read();
}
}
大致与Consumer相似,这里不再赘述