龙空技术网

Dubbo - 初识Apache Dubbo

无法无天过路客 92

前言:

此刻大家对“apacheencoding”可能比较注意,姐妹们都想要学习一些“apacheencoding”的相关内容。那么小编在网摘上搜集了一些对于“apacheencoding””的相关资讯,希望看官们能喜欢,大家快快来学习一下吧!

为什么要用Dubbo?

随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构越来越流行。和传统的单体架构相比,分布式架构多了一个远程服务之间的通信。无论SOA还是微服务,本质上都是对业务服务的提炼和复用,因此远程服务之间的调用是实现分布式的关键因素。

目前在远程通信这个领域,有很多非常成熟的RPC框架,比如 RMI,WebService,Hessian,Dubbo,Thrift等,即使我们不使用这些RPC框架,我们也可以通过socket或者NIO也可以实现远程通信,那么我们为什么要使用这些现成的RPC框架呢?

原因是因为如果我们自己去开发一个网络通信,需要考虑到下面几点:

底层网络通信协议的处理序列化和反序列化的工作

这些工作本身就是通用的,不应该由业务人员自己来实现,所以才有了RPC框架,使得开发人员不需要关心底层的通信逻辑。

同时,当企业开始大规模的服务化之后,我们还需要考虑以下几点:

如何实现对服务链路的跟踪和监控当服务越来越多时,服务 URL 配置管理变得非常困难,需要服务注册中心来解决服务的发现与感知问题要有容错机制防止一个节点故障引发大规模 的系统故障负载均衡进行分发请求

对于这些要求,传统的RPC技术就有些力不从心了,所以很多公司就根据需求研发自己的RPC框架,Dubbo就是其中的一个。

dubbo 主要是一个分布式服务治理解决方案,那么什么是服务治理?服务治理主要是针对大规模服务化以后,服务之间的路由、负载均衡、容错机制、 服务降级这些问题的解决方案,而 Dubbo 实现的不仅仅是远程服务通信, 并且还解决了服务路由、负载、降级、容错等功能。

Dubbo的基本使用

我们先通过一个简单的demo来初步了解一下Dubbo的基本用法

1. 创建两个项目dubbo-pratice-service 和dubbo-pratice-client。dubbo-pratice-service这个工程中包含两个子工程,practice-service-api和practice-service-provider。

2. 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="; xmlns:xsi="; xsi:schemaLocation=" ;>  <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=" ; xmlns=";    xmlns:xsi=";>  <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>;/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);}

5. practice-service-provider工程用来提供服务的实现,项目结构如下:

6. practice-service-provider的pom文件如下:

<?xml version="1.0"?><project xsi:schemaLocation=" ; xmlns=";    xmlns:xsi=";>  <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>;/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>

7. 为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=";       xmlns:dubbo=";       xmlns=";       xsi:schemaLocation="         ;>              <!-- 服务提供方应用信息 -->       <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://127.0.0.1/org.practice.service.api.LoginService?anyhost=true&application=practice-service&bean.name=org.practice.service.api.LoginService&bind.ip=xxxx&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.practice.service.api.LoginService&methods=login&pid=11664®ister=true&release=2.7.2&side=provider×tamp=1565829698502, dubbo version: 2.7.2, current host: xxxx2019-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://xxxx:20880/org.practice.service.api.LoginService?anyhost=true&application=practice-service&bean.name=org.practice.service.api.LoginService&bind.ip=xxxx&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.practice.service.api.LoginService&methods=login&pid=11664®ister=true&release=2.7.2&side=provider×tamp=1565829698502, dubbo version: 2.7.2, current host: xxxx

其中dubbo://xxxx:20880/org.practice.service.api.LoginService就是我们我们服务的url。

8. dubbo-practice-client工程是服务调用的客户端,pom文件如下:

<project xmlns="; xmlns:xsi=";  xsi:schemaLocation=" ;>  <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>;/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=";       xmlns:dubbo=";       xmlns=";       xsi:schemaLocation="         ;>            <!-- 服务提供方应用信息 -->       <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@6bf5b77a2019-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.32Success

表明远程服务调用成功。

关于 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();    }

标签: #apacheencoding