初始Dubbo
文章目录
一、前言
1、什么是分布式框架
分布式系统是若干独立系统的集合,但是用户使用起来像是在使用一套系统
2、为什么需要分布式系统?
规模的逐步扩大和业务的复杂,单台计算机扛不住双十一那样的流量,俗话说:三个臭皮匠抵一个诸葛亮
3、应用架构的发展演变
(1)单一架构
当网站流量很小的时候,我们将所有的应用(业务)放到一台服务器上,打包运行公司管理系统/超市收银系统
- 优点:开发简单,部署简单
- 缺点:扩展不容易(怎么处理日益增长的流量),谁都改一个,维护不容易,性能提升难
(2)垂直应用架构
将大应用拆分成为小应用(一般按照业务拆分),根据不同的访问频率决定各自业务部署的服务器数量
- 优点:扩展容易
- 缺点:页面一改,可能造成整个项目重新部署,业务和界面没有分离开,随着业务种类增加,怎么解决业务之间的互相调用问题,订单服务器和用户服务器交互效率的问题
(3)分布式架构(基于RPC:远程过程调用)
将业务拆分后,用某种方式实现各个业务模块的远程调用和复用,这时一个好的 RPC 框架就决定了你的分布式架构的性能,怎么调用,何时调用,服务器挂了怎么办… 我们需要一个框架来帮我们解决这个问题(当然大大神可以自己写一个,但是应对大流量的成功者莫过于中国的阿里巴巴公司,顶住了淘宝双十一的流量,反观一些学校内部的选课系统,对于大流量时只有两个字——宕机)。
(4)Dubbo
Dubbo是一个高性能的 RPC 框架解决了分布式中的调用问题
- 优点:解决了分布式系统中互相调用的问题
- 缺点:假设有100台服务器,50台用户业务服务器,50台订单业务服务器,但是在上线后发现,用户服务器使用率很小,但是订单服务器压力很大,最佳配比应该是1:4,这时候就要求我们还有一个统一管理的调度中心
二、初识Dubbo
1、为什么Dubbo说自己性能高
高性能要从底层的原理说起,既然是一个 RPC 框架,主要干的就是远程过程(方法)调用,那么提升性能就要从最关键、最耗时的两个方面入手:序列化和网络通信。
序列化:我们学习 Java 网络开发的时候知道,本地的对象要在网络上传输,必须要实现 Serializable 接口,也就是必须序列化。我们序列化的方案很多:xml、json、二进制流… 其中效率最高的就是二进制流(因为计算机就是二进制的)。然而 Dubbo 采用的就是效率最高的二进制。
网络通信:不同于 HTTP 需要进行7步走(三次握手和四次挥手),Dubbo 采用 Socket 通信机制,一步到位,提升了通信效率并且可以建立长连接,不用反复连接,直接传输数据
2、dubbo的发展过程
dubbo之前一直都作为 Alibaba 公司内部使用的框架。
- 2011年,dubbo 被托管到了 GitHub上(开源)
- 2014年11月发布 2.4.11 版本后宣布停止更新。此后一段时间很多公司开源了自己基于 Dubbo 的变种版本(例如当当网的 Dubbo X,网易考拉的 Dubbo K)
- 2017年 SpringCloud 横空出世,Dubbo 感觉到压力后连续更新了几个版本
- 2018年1月,阿里公司联合当当网将 Dubbo 和 Dubbo X 合并,发布了 2.6 版本
- 2018年除夕夜阿里将 Dubbo 贡献给了 Apache 基金会
- 2018除夕夜至今,Apache 维护和更新 Dubbo
三、dubbo框架
1、dubbo概述
Apache Dubbo(incubating)是一款高性能、轻量级的开源 Java RPC 框架,它提供了三大核心能力:面向接口的远程方法调用,
智能容错和负载均衡,以及服务自动注册和发现。
Dubbo是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案、服务治理方案。
官网:http://dubbo.apache.org/en-us/
2、基本结构
(1)结构说明
- 服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务
- 服务消费者(Consumer):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选台提供者进行调用,如果调用失败,再选另一台调用
- 注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
- 监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
(2)调用关系说明
-
服务容器负责启动,加载,运行服务提供者
-
服务提供者在启动时,向注册中心注册自己提供的服务
-
服务消费者在启动时,向注册中心订阅自己所需的服务
-
注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
-
服务消费者,从提供者地址列表中,选台提供者进行调用,如果调用失败,再选另一台调用
-
服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心
3、dubbo支持的协议
支持多种协议:dubbo、hessian、rmi、http、webservice、thrift、memcached、redis。
dubbo官方推荐使用 dubbo 协议。dubbo 协议默认端口 20880。
使用 dubbo 协议,spring 配置文件加入:
<dubbo:protocol name="dubbo" port="20880" />
4、直连方式
通过调用远程方法获取用户信息
(1)创建服务提供者
A、配置POM文件
<!--Spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--springmvc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--dubbo依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
B、创建实体
package cn.edu.huat.domain;
public class User implements Serializable {
private Integer id;
private String username;
private Integer age;
//setter and getter
}
C、提供服务接口
package cn.edu.huat.service;
import cn.edu.huat.domain.User;
public interface UserService {
//根据用户标识获取用户信息
User queryUserById(Integer id);
}
D、实现服务接口
package cn.edu.huat.service.impl;
import cn.edu.huat.domain.User;
import cn.edu.huat.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public User queryUserById(Integer id) {
User user = new User();
user.setId(1001);
user.setUsername("zs");
user.setAge(22);
return user;
}
}
E、配置dubbo服务提供者的核心配置文件(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.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!--服务提供者声明名称:必须保证服务名称的唯一性,它的名称是dubbo内部使用的唯一标识-->
<dubbo:application name="dubbo-ch01-link" />
<!--访问服务协议的名称及端口号,Dubbo官方推荐使用的是dubbo协议,默认端口号是20880
name:指定协议的名称
port:指定协议的端口号(默认为20880)-->
<dubbo:protocol name="dubbo" port="20880"/>
<!--暴露服务接口:dubbo:service
interface:暴露服务接口的全限定类名
ref:接口引用的实现类在spring容器中的标识
registry:如果不使用注册中心,则值为N/A-->
<dubbo:service interface="cn.edu.huat.service.UserService" ref="userService" registry="N/A"/>
<!--将接口的实现类加载到spring容器中-->
<bean id="userService" class="cn.edu.huat.service.impl.UserServiceImpl" />
</beans>
F、添加监听器(web.xml)
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dubbo-userservice-provider.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
(2)创建服务消费者
A、配置POM文件
<!--Spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--springmvc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--dubbo依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<!--依赖服务提供者,需要将服务提供者打成jar包-->
<dependency>
<groupId>cn.edu.huat</groupId>
<artifactId>dubbo-ch01-link</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
B、创建控制层
package cn.edu.huat.controller;
import cn.edu.huat.domain.User;
import cn.edu.huat.service.UserService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
@Controller
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/user")
public String userDetail(Model model, Integer id){
User user = this.userService.queryUserById(id);
model.addAttribute("user",user);
return "userDetail";
}
}
C、配置dubbo服务提供者的核心配置文件(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.xsd
http://dubbo.apache.org/schema/dubbo
http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!--声明服务消费者的名称:保证唯一性-->
<dubbo:application name="dubbo-ch02-link-consumer" />
<!--引用远程服务接口:
id:远程服务接口对象名称
interface:调用远程接口的全限定类名
url:访问服务接口的地址
registry:不使用注册中心,值为:N/A
-->
<dubbo:reference id="userService" interface="cn.edu.huat.service.UserService" url="dubbo://localhost:20880" registry="N/A" />
</beans>
D、配置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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描组件-->
<context:component-scan base-package="cn.edu.huat.controller" />
<!--配置注解驱动-->
<mvc:annotation-driven />
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
E、添加中央调度器(web.xml)
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml,classpath:dubbo-consumer.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
F、显示页面
<h2>用户详情</h2>
用户表示:${user.id}<br>
用户名称:${user.username}<br>
用户年龄:${user.age}<br>
注意:需要创建2个 tomcat,分别对应服务提供者以及服务消费者,然后通过服务消费者的 tomcat 进行访问。
5、dubbo服务化最佳实践
(1)分包
建议将服务接口、服务模型等均放在公共包中。
(2)粒度
- 服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤
- 服务接口建议以业务场景为单位划分,并对相近业务做抽象,防止接口数量爆炸
- 不建议使用过于抽象的通用接口,如:Map query(Map),这样的接口没有明确语义,会给后期维护带来不便
(3)版本
每个接口都应定义版本号,区分同一接口的不同实现,如
<dubbo:service interface="com.xxx.XxxService" version="1.0" />
6、直边方式(改造案例)
抽象分散在多个项目中的公共接口,实体类到一个项目中,在其他项目如服务提供者,服务消费者依赖公共的资源。
(1)创建公共资源项目
A、添加User实体类
B、提供服务接口
package cn.edu.huat.service;
import cn.edu.huat.domain.User;
public interface UserService {
User queryUserById(Integer id);
}
(2)创建服务提供者
A、配置POM文件
<!--Spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--springmvc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--dubbo依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<!--接口依赖-->
<dependency>
<groupId>cn.edu.huat</groupId>
<artifactId>dubbo-ch03-link-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
B、实现服务接口
package cn.edu.huat.service.impl;
import cn.edu.huat.domain.User;
import cn.edu.huat.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public User queryUserById(Integer id) {
User user = new User();
user.setId(id);
user.setUsername("dubbo");
return user;
}
}
C、配置dubbo服务提供者的核心配置文件(spring配置文件),同上案例
D、添加监听器(web.xml),同上案例
(3)创建服务消费者
A、配置POM文件
<!--Spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--springmvc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--dubbo依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<!--接口依赖-->
<dependency>
<groupId>cn.edu.huat</groupId>
<artifactId>dubbo-ch03-link-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
B、创建控制层
package cn.edu.huat.controller;
import cn.edu.huat.domain.User;
import cn.edu.huat.service.UserService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
@Controller
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/userDetail")
public String userDetail(Integer id, Model model){
User user = userService.queryUserById(id);
model.addAttribute("user",user);
return "userDetail";
}
}
C、配置dubbo服务提供者的核心配置文件(spring配置文件),同上案例
D、配置spring配置文件,同上案例
F、添加中央调度器(web.xml),同上案例
E、显示页面,同上案例
7、dubbo常用标签
Dubbo中常用标签。分为三个类别:公用标签、服务提供者标签、服务消费者标签
(1)公用标签
<dubbo:application/>以及<dubbo:registry/>
A、配置应用信息
<dubbo:application name ="服务的名称" />
B、配置注册中心
<dubbo:registry address="ip:port" protocol="协议" />
(1)服务提供者标签
配置暴露的服务
<dubbo:service interface="服务接口名” ref="服务实现对象bean" />
(3)服务消费者
配置服务消费者引用远程服务
<dubbo:reference id="服务引用bean的id" interface="服务接口名" />