最近无意中了解了泛化调用,起初云里雾里的,后面跟着官方的示例走了一遍,通过思考知道它是什么了。
什么是泛化调用?
它主要应对一种场景的,我要调用某些服务的某些接口,但是我并不知道有哪些系统,所以也就没有办法通过Jar包方式依赖,这种解决方案呢,除了HTTP呢,也可以RPC,但是前提呢就需要被调用的某些服务通过某种方式主动注册到注册中心,也就是服务提供者,而这边呢从注册中心拿到注册的信息以及接口信息(相当于消费端),拿到以后可以使用Dubbo泛化调用方式(也可以用其他方式)来调用服务端的接口。
通俗来讲就是你需要启动你的服务注册中心(本节用zookeeper),然后你需要将服务放入到注册中心中,消费端(网关、测试平台等情形)使用时则去注册中心找到信息,通过Dubbo泛化调用GenericService.$invoke()调用到对应的服务接口中。
我们会讲下代码,不过首先要安装zookeper,本地版本有可以跳过,注意代码里的版本和你安装的版本,如果差太多,启动服务会连不上zookeper。
2.安装zookerper
Windows安装Zookeeper,链接如下,我下载安装的是3.6.4的 apache-zookeeper-3.6.4-bin.tar.gz 包
下载后解压压缩包,然后添加data文件
接着进入conf文件, 讲zoo_sample.cfg复制一份,然后将复制的那份更改为zoo.cfg文件
然后编辑 zoo.cfg文件,将dataDir属性更改为我们的data目录地址
然后配置环境变量,点击环境变量,点击新建,名字为:ZOOKEEPER_HOME,然后填写我们的目的路径
然后点击path进去编辑 ,把ZOOKEEPER_HOME写上,按如下的就可以啦,退出时全部选确定。
然后打开cmd,输入zkserver,就启动成功啦,端口号是2181
3.代码
大家可以借鉴dubbo官网的搭建方式,链接如下,还是很详细的。
https://cn.dubbo.apache.org/zh-cn/overview/quickstart/java/spring-boot/
你按官方文档安装你就会有如下的代码结构。
dubbo-springboot-demo-provider:为服务提供者
dubbo-springboot-demo-interface:服务接口,共享的接口
dubbo-springboot-demo-consumer:服务消费者
3.1 dubbo-springboot-demo-interface
咱们先来说下服务共享者,很简单,我们只定义了一个接口叫DemoService,并定义了方法sayHello
/**
* @Author df
* @Description: TODO
* @Date 2024/3/5 10:47
*/
public interface DemoService {
String sayHello(String name);
}
它的pom.xml
<?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">
<parent>
<artifactId>dubbo-spring-boot-demo</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo-spring-boot-demo-interface</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
3.2 dubbo-springboot-demo-provider
然后服务提供者,类DemoServiceImpl,实现
DemoService,也就是说它依赖dubbo-springboot-demo-interface的jar。
/**
* @Author df
* @Description: TODO
* @Date 2024/3/5 10:56
*/
@DubboService
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
return "Hello " + name;
}
}
服务提供者还要提供启动服务的入口,所以需要下面这个代码,因为只有启动代码,才能把自己的接口注册到服务中心里。
/**
* @Author df
* @Description: TODO
* @Date 2024/3/5 11:26
*/
@SpringBootApplication
@EnableDubbo
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
它的application.yml文件
dubbo:
application:
name: dubbo-springboot-demo-provider
protocol:
name: dubbo
port: -1
registry:
address: zookeeper://169.254.165.11:2181
它的pom.xml文件
<?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">
<parent>
<artifactId>dubbo-spring-boot-demo</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo-spring-boot-demo-provider</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>dubbo-spring-boot-demo-interface</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<type>pom</type>
<exclusions>
<exclusion>
<artifactId>slf4j-reload4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- spring boot starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.example</groupId>
<artifactId>dubbo-spring-boot-interface</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
3.3 dubbo-springboot-demo-consume
在消费者中我们定义了Task类,实现CommandLineRunner,代表项目启动执行一次run方法,然后我们取DemoService,当然这里要用 @DubboReference,拿到以后直接调用方法就ok了。
在定义一个循环线程一直执行方法拿到结果打印来做测试。
/**
* @Author df
* @Description: TODO
* @Date 2024/3/5 11:44
*/
@Component
public class Task implements CommandLineRunner {
@DubboReference
private DemoService demoService;
// 消费者调用RPC生产者消息
// CommandLineRunner在程序启动后执行run方法,
@Override
public void run(String... args) throws Exception {
String result = demoService.sayHello("world");
System.out.println("two-Receive result ======> " + result);
new Thread(()-> {
while (true) {
try {
Thread.sleep(1000);
System.out.println(new Date() + " Receive result ======> " + demoService.sayHello("world"));
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}).start();
}
}
消费者也需要启动程序,所以我们也加一个程序的启动入口。
/**
* @Author df
* @Description: TODO
* @Date 2024/3/5 11:27
*/
@SpringBootApplication
@EnableDubbo
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
消费者的 application.yml,也是需要将自己本身服务注册到服务中心。
dubbo:
application:
name: dubbo-springboot-demo-consumer
protocol:
name: dubbo
port: -1
registry:
address: zookeeper://${zookeeper.address:127.0.0.1}:2181
pom.xml
<?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">
<parent>
<artifactId>dubbo-spring-boot-demo</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo-spring-boot-demo-consumer</artifactId>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>dubbo-spring-boot-demo-interface</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<type>pom</type>
<exclusions>
<exclusion>
<artifactId>slf4j-reload4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- spring boot starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
他们的父类pom.xml
<?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>
<groupId>org.example</groupId>
<artifactId>dubbo-spring-boot-demo</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>dubbo-spring-boot-demo-interface</module>
<module>dubbo-spring-boot-demo-provider</module>
<module>dubbo-spring-boot-demo-consumer</module>
</modules>
<properties>
<dubbo.version>3.0.8</dubbo.version>
<spring-boot.version>2.7.7</spring-boot.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
先启动服务提供接口,打印如下控制台信息,代表我们已经连上zookeper了,等待消费者消费呢。
我们在启动消费者的服务,消费者就调用到我们对应的方法啦。
我们现在之所以能调用到对应的服务是因为我们消费方依赖了共用接口,如果我们现在不依赖了,就没有办法用上面的方式了,就需要用到GenericService,我们来实现下。
我们在创建一个FHTask类,代码如下, 通过@DubboReference将接口信息传入,再通过genericService.$invoke将方法和参数类型和参数传入,最后会调用到最终的实现类,拿到结果打印下就OK。
/**
* @Author df
* @Description: TODO
* @Date 2024/3/5 11:44
*/
@Order(1)
@Component
public class FHTask implements CommandLineRunner {
// 泛化调用,不主动获取没有依赖情况下
@DubboReference(interfaceName = "org.apache.dubbo.springboot.demo.DemoService")
GenericService genericService;
// 消费者调用RPC生产者消息
// CommandLineRunner在程序启动后执行run方法,
@Override
public void run(String... args) throws Exception {
// 拿到接口关键信息,重新构建个请求
Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"fh-Invoke"});
System.out.println("Receive result ======> " + result);
}
}
由于没有了interface的依赖,所以需要将Task注释掉,代码如下,
@Order(2)
@Component
public class Task implements CommandLineRunner {
//@DubboReference
//private DemoService demoService;
// 消费者调用RPC生产者消息
// CommandLineRunner在程序启动后执行run方法,
@Override
public void run(String... args) throws Exception {
// String result = demoService.sayHello("world");
//System.out.println("two-Receive result ======> " + result);
// new Thread(()-> {
// while (true) {
// try {
// Thread.sleep(1000);
// System.out.println(new Date() + " Receive result ======> " + demoService.sayHello("world"));
// } catch (InterruptedException e) {
// e.printStackTrace();
// Thread.currentThread().interrupt();
// }
// }
// }).start();
}
}
启动服务者和消费者,也还是调用到具体的方法啦。
它主要是根据注册的接口信息,以及方法,参数等信息去注册中心找到对应匹配的实现类,进行调用方法,这样我们自己就不用写复杂的业务代码就能实现了没有依赖情况代码功能的调用使用。