现在dubbo已经普遍的应用到了各种互联网公司的开发中,同时也称为了Java工程师面试过程中必备可少的问题,本篇开始我们将简单介绍dubbo中的各种应用于配置,希望对于各位即将面临找工作的学子有所帮助。
为什么需要dubbo
首先,我们需要知道为什么需要dubbo。在我们学习Java Web的过程中,我们将站点、数据库等文件同时放在我们开发的电脑上,我们也可以进行运行及展示;等到开发完成,我们可以在阿里云、腾讯云等云上搭建服务器,进行服务的部署,也可以对外提供服务,那我们为什么需要使用dubbo呢?
下面我们通过系统架构的发展历程来了解一下,我们为什么需要使用dubbo。在我们上面提到的在开发一个Java Web项目后,在阿里云等服务器上进行服务的部署,其实站点的流量与用户都很小,只有一个应用也不会有延迟、并发等问题的出现。此时将所有的应用集中在一台服务器上可以降低部署的成本与节点。例如,一个电商项目,当用户量以及流量很小时,我们就可以将用户模块、订单模块、支付模块等都做在一个工程中,以一个应用的形式部署在一台服务器上。
当我们的系统流量增加时,可能一台服务器的硬件不足以支撑此时的访问量,我们就水平扩展器,增加服务器为一个集群,来提升系统的性能。
当服务的流量继续增大时,通过增加服务器的方式对系统性能的提升到达瓶颈,可以通过垂直扩展将服务拆分成各个模块形成子工程来提升效率,如将上面提到的电商项目中的用户模块、订单模块、支付模块,单独拆分成子工程,每个子工程再形成一个集群。
随着子工程越来越多,各个子工程中会存在功能相同或相似的代码。将整个项目冗余的功能模块抽取出来作为独立工程,对外提供特定功能,这些抽取出的功能就称为微服务。微服务应用称为服务提供者,而调用这些微服务的应用就称为服务消费者。
随着系统功能的完善,微服务模块数量也会变得越来越多,同样随着系统流量(页面访问量)的增长,消费者与提供者工程服务器数量需要随之增加。集群之间无法再使用直连方式,需要通过注册中心的方式进行连接。提供者将服务注册到注册中心,消费者从注册中心订阅服务进行消费,此时消费者无需与提供者进行绑定。
消费者订阅提供者提供的服务,因为此时不再是直连方式,则需要通过远程调用的方式调用对应的方法。那么此时就需要一个远程调用框架,dubbo就是一款高性能的RPC框架。
dubbo
这里借用dubbo官网介绍:Apache Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均能,以及服务自动注册和发现。
RPC(Remote Procedure Call Protocol)–远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络服务的协议。RPC假定某些传输协议的存在,如TCP或UDP。RPC采用C/S模式(客户机/服务器模式)。请求程序作为客户机,提供服务程序作为服务器,客户机与服务器的概念是相对的。客户机调用进程发送一个有进程参数的调用信息到服务进程,然 后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息 到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后, 客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
dubbo的四大组件
在dubbo中存在四大组件
- provider:服务提供者
- consumer:服务消费者
- registry:服务注册与发现的中心,提供目录服务,亦称为服务注册中心
- monitor:统计服务的调用次数、调用时间等信息的日志服务,并可以对服务设置权限、降级处理等,称为服务管控中心
上图是dubbo官网给出的四大组件关系图,从图中我们可以简单看出dubbo的从启动到注册、订阅的过程,其中包括了初始化、同步与异步的操作。启动过程、服务的注册与订阅过程在初始化时就完成;注册中心通知消费消费者以及monitor统计的过程则是异步过程;消费者远程调用提供者方法过程则为同步过程。
registry的主要作用:
保存服务的名字以及对应的地址列表
如果服务的地址有变化,需要notify服务消费者(即图中的notify过程)
provider的主要作用:
提供服务的接口
需要实现服务(实现类)
需要主动注册服务(remote与local注册)
暴露服务
consumer的主要作用:
需要根据服务名称从注册中心中获取提供者地址列表
进行负载均衡选出一个地址进行调用
dubbo系统框架搭建
在了解了为什么需要dubbo与dubbo的四大组件后,我们可以简单搭建dubbo的入门示例。在dubbo中,可以使用直连方式与注册中心方式调用提供者提供的服务,由于直连方式可能会因为服务器的宕机而无法提供服务,所以并不适合在生产环境中使用,仅简单知道即可。在生产环境中我们通常使用注册中心方式调用提供者提供的服务。下面简单演示两种方式的demo示例。本示例中dubbo使用2.7版本,spring使用4.3.16版本。
第一个dubbo程序-采用直连方式
在dubbo中,服务的名称一般都是业务接口的名称,无论是服务提供者向服务注册中心注册服务,还是服务消费者从注册中心索取服务,都是通过接口名称进行注册与查找的。即提供者与消费者都依赖于业务接口。创建业务接口如下,后面的提供者都是该接口的实现类。
/**
* 业务接口
*/
public interface SomeService {
String hello(String name);
}
创建其实现类SomeServiceImpl如下所示。
public class SomeServiceImpl implements SomeService {
@Override
public String hello(String name) {
System.out.println(name + ",我是提供者");
return "Hello Dubbo World! " + name;
}
}
上面实现类非常简单,不做过多解释,下面看其配置文件,如下所示。在<dubbo:registry>标签中令address=“N/A”,则表示不指定注册中心,使用<dubbo:service>标签进行服务的暴露,interface指定要暴露的接口,ref则指定其实现类。
<?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.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!--指定当前工程在Monitor中显示的名称,一般与工程名相同-->
<dubbo:application name="01-provider"/>
<!--指定服务注册中心:不指定注册中心-->
<dubbo:registry address="N/A"/>
<!--注册服务执行对象-->
<bean id="someService" class="com.abc.provider.SomeServiceImpl"/>
<!--服务暴露-->
<dubbo:service interface="com.abc.service.SomeService"
ref="someService"/>
</beans>
下面我们看直连方式下消费者的实现,代码实现如下所示。
public class ConsumerRun {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("spring-consumer.xml");
SomeService service = (SomeService) ac.getBean("someService"); // bean的名字与<dubbo:reference>中id的名字相同
String hello = service.hello("China");
System.out.println(hello);
}
}
上述实现代码很简单,从spring容器中获取bean,并调用其方法。下面我们看一下直连方式下consumer对应的xml配置文件。
<?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.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!--指定当前工程在Monitor中显示的名称,一般与工程名相同-->
<dubbo:application name="01-consumer"/>
<!--指定服务注册中心:不指定注册中心-->
<dubbo:registry address="N/A"/>
<!--订阅服务:采用直连式连接消费者-->
<dubbo:reference id="someService"
interface="com.abc.service.SomeService"
url="dubbo://localhost:20880"/>
</beans>
从配置文件中,可以看到当采取直连方式连接时,在<dubbo:registry>中address属性设置为"N/A",在<dubbo:reference>中使用url指定连接的地址,示例中使用的为dubbo协议。
使用zookeeper注册中心
由于直连方式由很大的局限性,在生产环境中一般使用注册中心方式进行服务的发布与订阅。zookeeper目前广泛作为注册中心使用,我们的示例中也使用zookeeper作为注册中心。
Java代码与直连方式大体相同,我们不在赘述,关注提供者与消费者的xml配置文件。首先我们来看提供者的配置文件。
<?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.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="02-provider-zk"/>
<!--指定服务注册中心:zk单机-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!--<dubbo:registry protocol="zookeeper" address="zkOS:2181"/>-->
<!--指定服务注册中心:zk集群-->
<!--<dubbo:registry address="zookeeper://zkOS1:2181?backup=zkOS2:2181,zkOS3:2181,zkOS4:2181"/>-->
<!--<dubbo:registry protocol="zookeeper" address="zkOS1:2181,zkOS2:2181,zkOS3:2181,zkOS4:2181"/>-->
<bean id="someService" class="com.abc.provider.SomeServiceImpl"/>
<dubbo:service interface="com.abc.service.SomeService"
ref="someService"/>
</beans>
与直连方式最大的不同在于在<dubbo:registry>中的address不在为"N/A",更改为注册中心的地址,我们演示使用的为本地注册中心,地址为"zookeeper://localhost:2181",更改配置文件后,我们启动provider后,使用zookeeper客户端查看注册的服务,如下图所示,"com.abc.service.SomeService"有一个children,即一个提供者,服务正常注册到注册中心。
下面我们来看zookeeper注册中心下的消费者配置文件,如下所示。
<?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.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<dubbo:application name="02-consumer-zk"/>
<!--指定服务注册中心:zk单机-->
<dubbo:registry address="zookeeper://localhost:2181" />
<!--<dubbo:registry protocol="zookeeper" address="zkOS:2181"/>-->
<!--指定服务注册中心:zk集群-->
<!--<dubbo:registry address="zookeeper://zkOS1:2181?backup=zkOS2:2181,zkOS3:2181,zkOS4:2181"/>-->
<!--<dubbo:registry protocol="zookeeper" address="zkOS1:2181,zkOS2:2181,zkOS3:2181,zkOS4:2181"/>-->
<dubbo:reference id="someService" interface="com.abc.service.SomeService"/>
</beans>
同样,consumer的xml也改变了<dubbo:registry>中的address值。启动consumer服务,可以看到provider方面正常输出结果,说明通过消费者通过订阅zookeeper中服务调用到了真正的服务。
dubbo-admin管控平台
在dubbo中其实提供了dubbo管控平台用于dubbo的服务治理与服务监控,提供了可视化界面,可用于查询服务是否注册到注册中心。dubbo管控平台github地址:https://github.com/apache/dubbo-admin,大家可以通过git clone或下载zip包的方式进行下载。
它是一个标准的spring boot项目,在本地可以使用idea进行启动。具体操作可参考github中文说明进行操作。成功运行界面如下所示。
关闭服务检查
在dubbo中,默认情况下如果服务消费者先于服务提供者启动,服务消费者就会报错(No provider available for the service com.abc.service.SomeService from the url zookeeper://localhost:2181),如下图所示。因为默认情况下,消费者启动时会检查其要消费的服务是否启动,如果没有启动则会抛出异常。
可以在消费者端<dubbo:reference>中使用check="fasle"属性关闭服务检查功能,修改后再次启动consumer,不在报错。
<dubbo:reference id="someService" interface="com.abc.service.SomeService" check="false"/>
check="false"的存在还是必要的。在平时,只要注意服务启动顺序,该属性看似可以不使用。但在循环消费场景下是必须要使用的。即 A 消费 B 服务,B 消费 C 服务,而 C 消费 A 服务。这是典型的循环消费。在该场景下必须至少有一方要关闭服务检查功能,否则将无法启动任何一方。
总结
本篇介绍了dubbo的使用场景,介绍了dubbo中的四大组件,并搭建直连方式和注册中心方式的的demo示例。最后介绍了需要注意的关闭服务检查功能。在下一篇中,我们将介绍一些dubbo的高级配置。