为什么要用Dubbo?
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构越来越流行。和传统的单体架构相比,分布式架构多了一个远程服务之间的通信。无论SOA还是微服务,本质上都是对业务服务的提炼和复用,因此远程服务之间的调用是实现分布式的关键因素。
目前在远程通信这个领域,有很多非常成熟的RPC框架,比如 RMI,WebService,Hessian,Dubbo,Thrift等,即使我们不使用这些RPC框架,我们也可以通过socket或者NIO也可以实现远程通信,那么我们为什么要使用这些现成的RPC框架呢?
原因是因为如果我们自己去开发一个网络通信,需要考虑到下面几点:
底层网络通信协议的处理
序列化和反序列化的工作
这些工作本身就是通用的,不应该由业务人员自己来实现,所以才有了RPC框架,使得开发人员不需要关心底层的通信逻辑。
同时,当企业开始大规模的服务化之后,我们还需要考虑以下几点:
如何实现对服务链路的跟踪和监控
当服务越来越多时,服务 URL 配置管理变得非常困难,需要服务注册中心来解决服务的发现与感知问题
要有容错机制防止一个节点故障引发大规模 的系统故障
负载均衡进行分发请求
对于这些要求,传统的RPC技术就有些力不从心了,所以很多公司就根据需求研发自己的RPC框架,Dubbo就是其中的一个。
dubbo 主要是一个分布式服务治理解决方案,那么什么是服务治理?服务治理主要是针对大规模服务化以后,服务之间的路由、负载均衡、容错机制、 服务降级这些问题的解决方案,而 Dubbo 实现的不仅仅是远程服务通信, 并且还解决了服务路由、负载、降级、容错等功能。
Dubbo的基本使用
我们先通过一个简单的demo来初步了解一下Dubbo的基本用法
创建两个项目dubbo-pratice-service 和dubbo-pratice-client。dubbo-pratice-service这个工程中包含两个子工程,practice-service-api和practice-service-provider。
dubbo-pratice-service这个工程中包含两个子工程,practice-service-api和practice-service-provider,dubbo-practice-service工程作为父工程来管理版本信息,pom.xml文件如下:
dubbo-practice-service的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>
<groupId>com.yrk.dubbo</groupId>
<artifactId>dubbo-practice-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>practice-service-api</module>
<module>practice-service-provider</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
通过dependencyManagement来管理项目中依赖的lib的版本。
3. practice-service-api这个工程用来维护所提供服务的api,目录结构如下:
4. practice-service-api的pom文件如下:
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.yrk.dubbo</groupId>
<artifactId>dubbo-practice-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>practice-service-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>practice-service-api</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
同时在practice-service-api这个工程中定义LoginService接口:
public interface LoginService {
String login(String username, String password);
}
practice-service-provider工程用来提供服务的实现,项目结构如下:
practice-service-provider的pom文件如下:
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.yrk.dubbo</groupId>
<artifactId>dubbo-practice-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>practice-service-provider</artifactId>
<name>practice-service-provider</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>com.yrk.dubbo</groupId>
<artifactId>practice-service-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
为LoginService接口提供实现类:
package org.practice.service.provider;
import org.practice.service.api.LoginService;
public class LoginServiceImpl implements LoginService{
public String login(String username, String password) {
if ("admin".equalsIgnoreCase(username) && "admin".equalsIgnoreCase(password)) {
return "Success";
}
return "Failed";
}
}
接下来我们使用xml的形式通过dubbo来发布服务:
我们需要在resources目录下面新建META-INF/spring目录,同时在这个目录下面新建application.xml文件,我们需要在这个文件中定义dubbo相关的信息:
application.xml:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
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="practice-service" />
<!-- 使用multicast 广播注册中心暴露服务地址 -->
<dubbo:registry address="N/A"/>
<!-- 使用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="org.practice.service.api.LoginService" ref="loginService"/>
<bean id="loginService" class="org.practice.service.provider.LoginServiceImpl"/>
</beans>
然后通过Main.main(args)来启动服务:
package org.practice.service.provider;
import org.apache.dubbo.container.Main;
public class App
{
public static void main( String[] args )
{
Main.main(args);
}
}
启动服务的时候回在console中看到如下信息
2019-08-15 08:41:38,720 INFO [main] o.a.dubbo.config.AbstractConfig - [DUBBO] Export dubbo service org.practice.service.api.LoginService to local registry url : injvm:
2019-08-15 08:41:38,723 INFO [main] o.a.dubbo.config.AbstractConfig - [DUBBO] Export dubbo service org.practice.service.api.LoginService to url dubbo:
其中dubbo://xxxx:20880/org.practice.service.api.LoginService就是我们我们服务的url。
dubbo-practice-client工程是服务调用的客户端,pom文件如下:
<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>com.yrk.dubbo</groupId>
<artifactId>dubbo-practice-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>dubbo-practice-client</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>com.yrk.dubbo</groupId>
<artifactId>practice-service-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
在resources目录下创建application.xml文件,内容如下:
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
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="practice-client" />
<dubbo:registry address="N/A"/>
<!-- 通过reference标签获取服务 -->
<dubbo:reference id="loginService" interface="org.practice.service.api.LoginService"
url="dubbo://172.17.23.32:20880/org.practice.service.api.LoginService"/>
</beans>
在Main方法中获取loginService并调用login方法:
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"application.xml"});
LoginService loginService = (LoginService) context.getBean("loginService");
System.out.println(loginService.login("admin", "admin"));
}
}
运行之后可以在控制台中看到结果:
2019-08-15 10:38:26,811 DEBUG [NettyClientWorker-1-1] i.n.u.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@6bf5b77a
2019-08-15 10:38:27,251 DEBUG [DubboClientHandler-172.17.23.32:20880-thread-1] o.a.d.r.transport.DecodeHandler - [DUBBO] Decode decodeable message org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcResult, dubbo version: 2.7.2, current host: 172.17.23.32
Success
表明远程服务调用成功。
关于 Dubbo 启动的真相
通过上面的例子,我们用Dubbo实现了一个非常简单的远程服务调用,同时在上面的例子中,我们并没有使用到tomcat、jetty这类容器,那么Dubbo是如何实现的呢?
其实Dubbo内部提供了几种容器供我们使用去启动和发布服务:
Spring Container: 自动加载META-INF/spring目录下的所有spring配置
logback Container: 自动装配logback日志
Log4j Container: 自动配置log4j的配置
同时Dubbo提供了一个Main.main方法可以快速启动相应的容器,默认情况下只会启动spring容器,Spring容器的类是SpringContainer.java,启动的代码如下:
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;
}
context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+"), false);
context.refresh();
context.start();
}
评论