Dubbo-api中定义的UserQueryFacade接口可以发布在私服上,这样子dubbo-consumer和dubbo-provider就可以以maven依赖的形式导入使用。dubbo-provider需要提供接口的实现类,dubbo-consumer需要订阅该实现类,他们的元数据都通过zk进行记录。
许多教程都通过Spring XML的形式进行dubbo服务注册的,其实还有很多种方式,参考配置加载流程 | Apache Dubbo:
- Spring XML
参见示例
<!-- dubbo-provier.xml -->
<dubbo:application name="demo-provider"/>
<dubbo:config-center address="zookeeper://127.0.0.1:2181"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181" simplified="true"/>
<dubbo:metadata-report address="redis://127.0.0.1:6379"/>
<dubbo:protocol name="dubbo" port="20880"/>
<bean id="demoService" class="org.apache.dubbo.samples.basic.impl.DemoServiceImpl"/>
<dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService" ref="demoService"/>
- Spring Annotation
参见示例
// AnnotationService服务实现
@Service
public class AnnotationServiceImpl implements AnnotationService {
@Override
public String sayHello(String name) {
System.out.println("async provider received: " + name);
return "annotation: hello, " + name;
}
}
## dubbo.properties
dubbo.application.name=annotation-provider
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
- Spring Boot
参见示例
## application.properties
# Spring boot application
spring.application.name=dubbo-externalized-configuration-provider-sample
# Base packages to scan Dubbo Component: @com.alibaba.dubbo.config.annotation.Service
dubbo.scan.base-packages=com.alibaba.boot.dubbo.demo.provider.service
# Dubbo Application
## The default value of dubbo.application.name is ${spring.application.name}
## dubbo.application.name=${spring.application.name}
# Dubbo Protocol
dubbo.protocol.name=dubbo
dubbo.protocol.port=12345
## Dubbo Registry
dubbo.registry.address=N/A
## DemoService version
demo.service.version=1.0.0
- Java API
参考示例
public static void main(String[] args) throws IOException {
ServiceConfig<GreetingsService> service = new ServiceConfig<>();
service.setApplication(new ApplicationConfig("first-dubbo-provider"));
service.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
service.setInterface(GreetingsService.class);
service.setRef(new GreetingsServiceImpl());
service.export();
System.out.println("first-dubbo-provider is running.");
System.in.read();
}
Spring XML配置发布与服务调用
创建了三个模块,整体结构如下:
-
在dubbo-api模块中定义接口,并生成别的模块可以依赖的maven jar包,那我这里其实是provider,consumer,api都在localhost上,因此直接本地install生成maven jar.
package com.jxz.dubbo.api; /** * @Author jiangxuzhao * @Description * @Date 2023/6/4 */ public interface UserQueryFacade { String query(String name); }
-
在dubbo-provider模块中定义服务提供者,实现前面发布的接口
2.1 先提供pom依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.jxz.dubbo</groupId> <artifactId>DubboLearning</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>dubbo-provider</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- 引用自定义接口maven jar依赖--> <dependency> <groupId>com.jxz.dubbo</groupId> <artifactId>dubbo-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- 引用dubbo依赖--> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>2.7.1</version> </dependency> </dependencies> </project>
2.2 服务提供者提供实现接口的服务提供类
package com.jxz.dubbo.provider.impl; import com.jxz.dubbo.api.UserQueryFacade; /** * @Author jiangxuzhao * @Description * @Date 2023/6/4 */ public class UserQueryFacadeImpl implements UserQueryFacade { @Override public String query(String name) { return "query name is "+name; } }
2.3 dubbo provider配置文件放在resources/META-INF/spring
参考快速开始 | Apache Dubbo,其中各个配置含义参考XML 配置 | Apache 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://dubbo.apache.org/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <!-- 提供方的应用服务名,最好是当前应用归属的系统名称,用于计算依赖关系 --> <dubbo:application name="dubbo-provider" /> <!-- 注册中心的地址名,这里可以先注册到本地,到时用url注册到ZK中 --> <dubbo:registry address="N/A" /> <!-- 服务提供方需要暴露的服务接口,采用dubbo协议,端口号为20880 --> <dubbo:protocol name="dubbo" port="20880" /> <!-- 服务提供方需要暴露的服务功能,也就是对应接口和实现类 --> <dubbo:service interface="com.jxz.dubbo.api.UserQueryFacade" ref="UserQueryFacadeImpl" /> <!-- 和本地bean一样注册服务实现类,id对应上面ref --> <bean id="UserQueryFacadeImpl" class="com.jxz.dubbo.provider.impl.UserQueryFacadeImpl" /> </beans>
其中详细的含义可以看参考手册,比如各种XML配置的详细含义Schema 配置参考手册 | Apache Dubbo,以及<dubbo:protocol指定的服务提供协议,缺省为dubbo,这我理解起来每个协议就是对rpc传输中各种配置的规范,看协议参考手册 | Apache Dubbo,其中Dubbo协议为默认的单一长链接。
2.4 Dubbo-provider服务提供者启动类中,启动spring容器
package com.jxz.dubbo.provider; import org.apache.dubbo.container.Main; /** * @Author jiangxuzhao * @Description 服务提供者启动类中,启动spring容器 * @Date 2023/6/4 */ public class ProviderApplication { public static void main(String[] args) { Main.main(new String[]{"spring"}); } }
其中SpringContainer会去读取相关的配置文件,参考源码:默认读取DEFAULT_SPRING_CONFIG,即 “classpath*:META-INF/spring/*.xml”,因此我们的dubbo provider配置文件写在这个目录下
public class SpringContainer implements Container { public static final String SPRING_CONFIG = "dubbo.spring.config"; public static final String DEFAULT_SPRING_CONFIG = "classpath*:META-INF/spring/*.xml"; private static final Logger logger = LoggerFactory.getLogger(SpringContainer.class); static ClassPathXmlApplicationContext context; public static ClassPathXmlApplicationContext getContext() { return context; } @Override public void start() { String configPath = ConfigUtils.getProperty(SPRING_CONFIG); if (StringUtils.isEmpty(configPath)) { configPath = DEFAULT_SPRING_CONFIG; } // 内部也是通过ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+"), false); context.refresh(); context.start(); }
2.5 provider服务提供者注册测试
-
在dubbo-consumer模块中定义服务消费者
3.1 先提供pom依赖:
<dependencies>
<!-- 引用自定义接口maven jar依赖-->
<dependency>
<groupId>com.jxz.dubbo</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 引用dubbo依赖-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.1</version>
</dependency>
</dependencies>
3.2 dubbo consumer配置文件放在resources/META-INF/spring
<?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://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="dubbo-consumer" />
<!-- 注册中心的地址名,这里可以先注册到本地,到时用url注册到ZK中 -->
<dubbo:registry address="N/A" />
<!-- 引用服务提供者的接口,生成远程服务代理,可以和本地bean一样使UserQueryFacade,其中url为服务提供者export的url -->
<dubbo:reference id="UserQueryFacade" interface="com.jxz.dubbo.api.UserQueryFacade" url="dubbo://127.0.0.1:20880"/>
</beans>
其中指定了想调用的接口名以及对应的服务提供端口,id就是本地调用的对象
3.3 dubbo-consumer服务消费者启动类
加载Spring配置,并调用远程服务
package com.jxz.dubbo.consumer;
import com.jxz.dubbo.api.UserQueryFacade;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/6/5
*/
public class ConsumerApplication {
public static void main(String[] args) {
String configPath = "classpath*:META-INF/spring/*.xml";
// 加载Spring配置
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(configPath);
context.start();
// 获取远程代理对象
UserQueryFacade userQueryFacade = (UserQueryFacade)context.getBean(UserQueryFacade.class);
String str = userQueryFacade.query("jiangxuzhao"); // 执行远程方法
System.out.println( str ); // 显示调用结果
}
}
3.4 consumer服务消费者测试
碰到bug: No qualifying bean of type ‘com.jxz.dubbo.api.UserQueryFacade’ available
那是因为我在创建META-INF/spring目录的时候,直接META-INF.spring创建了一个层级目录,导致xml配置不上,因此没法找到相关服务。
-
集成ZK
上面的服务调用都是在本地直接写死的,现在想从ZK中获取服务调用提供方的地址,首先去Index of /zookeeper (apache.org)下载zk安装包
4.1 修改dubbo provider配置文件放在resources/META-INF/spring
<?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://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 提供方的应用服务名,最好是当前应用归属的系统名称,用于计算依赖关系 -->
<dubbo:application name="dubbo-provider" />
<!-- 注册中心的地址名,用url注册到ZK中 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<!-- 服务提供方需要暴露的服务接口,采用dubbo协议,端口号为20880 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 服务提供方需要暴露的服务功能,也就是对应接口和实现类 -->
<dubbo:service interface="com.jxz.dubbo.api.UserQueryFacade" ref="UserQueryFacadeImpl" />
<!-- 和本地bean一样注册服务实现类,id对应上面ref -->
<bean id="UserQueryFacadeImpl" class="com.jxz.dubbo.provider.impl.UserQueryFacadeImpl" />
</beans>
4.2 修改dubbo consumer配置文件放在resources/META-INF/spring
<?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://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="dubbo-consumer" />
<!-- 注册中心的地址名,用url注册到ZK中 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<!-- 引用服务提供者的接口,生成远程服务代理,可以和本地bean一样使UserQueryFacade,其中不需要指定url了 -->
<dubbo:reference id="UserQueryFacade666666" interface="com.jxz.dubbo.api.UserQueryFacade"/>
</beans>
服务方和消费方都修改pom依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.jxz.dubbo</groupId>
<artifactId>DubboLearning</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>dubbo-provider</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 引用自定义接口maven jar依赖-->
<dependency>
<groupId>com.jxz.dubbo</groupId>
<artifactId>dubbo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 引用dubbo依赖-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.1</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.3.0</version>
</dependency>
</dependencies>
</project>
其中控制台一直报log4j和slf相关的错误,继续添加依赖:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
同时配置log4j.properties
log4j.rootLogger=debug, stdout, R
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log
log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=5
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
本来以为配置日志没什么用的,同时这时候provider我在idea控制台只是能运行,但是每次都是过一会就exit with code 1,却没有一点查错信息。配完日志以后,发现控制台打印的东西就多了,同时在provider端打印出了20880端口被占用,这不就是我为服务提供者的UserQueryFacadeImpl指定的端口嘛,之前并没有将其关闭,遂kill -9然后重新启动。Dubbo service server started!成功启动。
依次启动provider和consumer,发现consumer输出“query name is jiangxuzhao”,同时zk中注册上了两者的信息:
小小的修改了一下consumer,看它获取bean是根据class还是id获取
package com.jxz.dubbo.consumer;
import com.jxz.dubbo.api.UserQueryFacade;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/6/5
*/
public class ConsumerApplication {
public static void main(String[] args) throws IOException {
String configPath = "classpath*:META-INF/spring/*.xml";
// 加载Spring配置
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(configPath);
context.start();
// 获取远程代理对象,这是通过<dubbo:reference>中定义的class获取
// UserQueryFacade userQueryFacade = (UserQueryFacade)context.getBean(UserQueryFacade.class);
// 也支持通过bean id获取
UserQueryFacade userQueryFacade = (UserQueryFacade)context.getBean("UserQueryFacade666666");
String str = userQueryFacade.query("jiangxuzhao"); // 执行远程方法
System.out.println( str ); // 显示调用结果
System.in.read(); // 阻塞等待
}
}
又尝试着加上个dubbo-provider2,直接就复制模块改下provider.xml中的port
<?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://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 提供方的应用服务名,最好是当前应用归属的系统名称,用于计算依赖关系 -->
<dubbo:application name="dubbo-provider" />
<!-- 注册中心的地址名,用url注册到ZK中 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181" />
<!-- 服务提供方需要暴露的服务接口,采用dubbo协议,端口号为20880 -->
<dubbo:protocol name="dubbo" port="20881" />
<!-- 服务提供方需要暴露的服务功能,也就是对应接口和实现类 -->
<dubbo:service interface="com.jxz.dubbo.api.UserQueryFacade" ref="UserQueryFacadeImpl" />
<!-- 和本地bean一样注册服务实现类,id对应上面ref -->
<bean id="UserQueryFacadeImpl" class="com.jxz.dubbo.provider.impl.UserQueryFacadeImpl" />
</beans>
Java API配置发布与服务调用
- Dubbo-api中定义新的接口
package com.jxz.dubbo.api;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/6/17
*/
public interface UserUpdateFacade {
String updateUser(String name);
}
- Dubbo-provider中实现接口并注册
package com.jxz.dubbo.provider.impl;
import com.jxz.dubbo.api.UserUpdateFacade;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/6/17
*/
public class UserUpdateFacadeImpl implements UserUpdateFacade {
@Override
public String updateUser(String name) {
return "Update your name is "+name;
}
}
服务暴露
package com.jxz.dubbo.provider;
import com.jxz.dubbo.api.UserUpdateFacade;
import com.jxz.dubbo.provider.impl.UserUpdateFacadeImpl;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;
import java.io.IOException;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/6/17
*/
public class JavaApiProviderApplication {
public static void main(String[] args) throws IOException {
// 创建暴露UserUpdateFacade接口的对象
ServiceConfig<UserUpdateFacade> service = new ServiceConfig<>();
service.setApplication(new ApplicationConfig("dubbo-provider-javaapi"));
service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
service.setInterface(UserUpdateFacade.class);
// 设置实现类并暴露服务
service.setRef(new UserUpdateFacadeImpl());
service.export();
System.out.println("UserUpdateFacade-dubbo-provider is running...");
// 阻塞等待
System.in.read();
}
}
- Dubbo-consumer中调用服务
package com.jxz.dubbo.consumer;
import com.jxz.dubbo.api.UserUpdateFacade;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import java.io.IOException;
/**
* @Author jiangxuzhao
* @Description
* @Date 2023/6/17
*/
public class JavaApiConsumerApplication {
public static void main(String[] args) throws IOException {
// 创建UserUpdateFacade接口的引用对象
ReferenceConfig<UserUpdateFacade> reference = new ReferenceConfig<>();
reference.setApplication(new ApplicationConfig("dubbo-consumer-javaapi"));
reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
reference.setInterface(UserUpdateFacade.class);
// 直接拿到代理对象并进行远程调用
UserUpdateFacade userUpdateFacade = reference.get();
System.out.println(userUpdateFacade.updateUser("jiangxuzhao"));
// 阻塞等待
System.in.read();
}
}
消费者输出:
Update your name is jiangxuzhao