1.远程调用涉及的概念
1.1协议
协议是指多方共同遵循的规范,在网络中的计算机进行数据交换依靠各种协议:http、ftp等。
一台计算机按照规定好的格式发送数据,另一台计算机的程序按照指定的格式接收数据,两台计算机用互相理解的格式读写数据,达到数据交换的目的。
1.2RPC-远程过程调用协议
RPC是什么?
RPC是远程过程调用(Remote Procedure Call)的缩写形式。是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。该协议允许运行于一台计算机的程序调用另一台计算机的程序,程序员无需为网络交互功能编码。
RPC的作用?
主要功能是让构建分布式应用更容易,在提供强大的远程调用能力时不损失本地调用的语义简洁性(远程调用的语法跟本地调用的语法一样简洁)。在另一台计算机上的程序调用另外的计算机上的功能时,就像是使用自己的功能一样。RPC技术提供了透明的访问其他服务的底层实现细节,使用分布式系统的服务更加方便。
分布式是什么?
分布式是指多台计算机位于网络系统中,多台计算机形成一个整体对外界提供服务。用户使用系统不知道是多台计算机,使用不同的操作系统、不同的应用程序提供服务。
2.Dubbo初识
Apache Dubbo是一款高性能、轻量级的Java RPC框架,它有三个核心功能:面向接口的远程方法调用、智能容错和负载均衡、服务自动注册和发现。
Dubbo官网
高性能要从底层的原理说起,既然是一个RPC框架,要做的就是远程过程方法调用,那么提升性能就要从最关键、最耗时的两个方面入手:序列化和网络通信。
序列化:本地的对象要在网络上传输,必须要实现Serializable接口,也就是必须序列化。我们序列化的方式有很多:xml、json、二进制流…其中效率最高的就是二进制流,因为计算机就是二进制的,Dubbo采用的就是效率最高的二进制。
网络通信:不同于HTTP要进行七步走(三次握手四次挥手),Dubbo采用Socket通信机制,一步到位,提升了通信的效率,并且建立了长连接,不必反复连接。
废话:2017年SpringCloud横空出世后,已经停更好几年的Dubbo开始更新,阿里巴巴整合了当当网的Dubbo X,然后把Dubbo贡献给了Apache基金会,所以现在Dubbo是由Apache在维护和更新。
2.1Dubbo能做什么
- 实现透明的远程方法调用,就像调用本地方法一样,可以忽略远程调用的实现细节,简单配置即可使用。
- 服务的自动注册和服务发现。通过注册中心,服务实现动态管理(增减服务方)。调用服务的消费者无需写死调用地址。
- 软件的负载均衡实现和容错机制,无需使用硬件。降低成本。
2.2Dubbo的底层实现原理
Dubbo的底层实现是动态代理,由Dubbo框架创建远程服务(接口)对象的的代理对象,通过代理对象调用远程方法。
2.3Dubbo支持的协议
支持8种协议:dubbo、hessian、rmi、http、webservice、thrift、memcached、redis。
Dubbo官方推荐使用dubbo协议,默认端口号为20880。
2.4Dubbo协议
dubbo协议的特点:
dubbo协议采用单一长连接和异步通信,适用于小数据量、大并发的服务调用,以及服务消费者(Consumer)机器数远大于服务提供者(Provider)机器数的情况。异步通信可以提高处理效率
网络通信:
Dubbo协议底层网络通信默认的是netty,性能非常优秀,官方推荐使用。
dubbo协议不适用的场景:
dubbo协议不适合传送大数据量的服务,比如传文件、传视频等,除非请求量很低。
2.5长连接和短连接
长连接的网络通道一旦建立就可以一直使用,除非断线宕机,省去了建立网络通道的时间。传输小数据量速度快,省去排队时间,所以效率高。一般服务器与服务器之间使用长连接。
短连接每次传输数据都要建立新的连接,耗时,但是使用完毕后连接断,不会占用服务器资源。一般服务器和客户端之间使用短链接
2.6Dubbo架构
此架构图来自于动力节点
此架构图来自于Dubbo官网
实际开发中需要的provider、consumer、register
可以将Container看成美团,里面有很多商家(Provider),商家每天把要卖的饭放到菜单(Registry),顾客(Cunsumer)查看菜单,然后选择自己想要的(notify),最后去商家取货(invoke),平台拥有者(Monitor)可以监控商家和顾客的操作。
官方解释:
- Provider:暴露服务的服务提供方
- Consumer:调用远程服务的服务消费方
- Registry:服务注册与发现的注册中心
- Monitor:统计服务的调用次数和调用时间的监控中心
- Container:服务运行容器
调用关系说明:
- 0:start 服务容器负责启动,加载,运行服务提供者。
- 1:register 服务提供者在启动时,向注册中心注册自己提供的服务。
- 2:subscribe 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 3:notify 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 4:invoke 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 5:count 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
3.Dubbo直连
点对点直连:消费者直接访问服务提供者,没有注册中心。消费者必须指定服务提供者的访问地址(url)。
服务提供者步骤:
- 新建一个maven web工程,作为服务的提供者
- 导入相关依赖
- 定义实体类,实现序列化接口
- 定义服务的接口和实现类
- 定义spring的配置文件
a:声明Dubbo服务提供者的名称:保证唯一
b:声明Dubbo使用的协议和端口号
c:暴露服务,使用直连方式 - 修改web.xml,注册spring监听器
创建一个maven项目,由于Dubbo与Spring是无缝对接的,所以在pom.xml中添加Spring的相关依赖。
<!--Spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<!--dubbo依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
注意: Dubbo的版本为2.6.2时,启动会报错(无法将register的“N/A”从String类型转换为Boolen类型)。解决方法,对Dubbo版本进行升级(本文升到2.6.4)。
本案例是Provider提供一个根据用户id查询用户信息的服务。首先创建实体类User:
public class User {
private Integer id;
private String name;
private Integer age;
}
在创建一个UserService接口,提供一个根据id查询用户的方法,消费者远程调用此接口:
public interface UserService {
/**
* provider提供的服务 消费者远程调用此接口
* @param id
* @return
*/
User getUserById(Integer id);
}
实现此接口,重写方法,模拟数据:
public class UserServiceImpl implements UserService {
@Override
public User getUserById(Integer id) {
User user=new User();
user.setId(1);
user.setName("张三");
user.setAge(18);
return user;
}
}
以上的步骤都是被写烂了的,关键在一在Spring的配置文件中配置Dubbo:dubbo-provider.xml
注意: dubbo的包有两个,引入Apache的:dubbo.apache.org
步骤:
- 声明Provider:dubbo:application
- 协议Protocol:dubbo:protocol
- 暴露服务:dubbo:service
<?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="provider"/>
<!--访问服务协议的名称和端口号,dubbo官方推荐使用的是dubbo协议,端口号默认为20880-->
<!--
name:指定协议的名称
port:指定协议的端口号,默认20880
-->
<dubbo:protocol name="dubbo" port="20880"/>
<!--暴露服务的接口 dubbo:service
interface:暴露服务接口的全限定类名
ref:是这个接口的实现类在Spring容器中的标识,即id
register:由于直连方式不使用注册中心,所以要设置register的值为 N/A
-->
<dubbo:service interface="com.zhouqun.service.UserService" ref="userService" registry="N/A"/>
<!--将服务接口的实现类注入到Spring容器中-->
<bean id="userService" class="com.zhouqun.service.impl.UserServiceImpl"/>
</beans>
最后在web.xml中配置监听器:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" metadata-complete="true">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dubbo-provider.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
Consumer需要知道Provider提供了哪些接口,以及接口中有哪些方法,所以需要把Provider的Model打成jar包放到本地仓库。(打包快捷键:双击ctrl)去Provider模块的pom.xml中将
<packaging>war</packaging>
注释掉。打包完成后去掉注释。
注意: 打包时若双击ctrl无效,则用maven的Lifecycle的install工具打包,打成的jar包会在设置的本地仓库中。
服务消费者步骤:
- 创建一个maven web工程,服务的消费者Consumer
- 配置pom.xml,引入依赖(Spring,Dubbo,Provider)
- 设置Dubbo的核心配置文件
- 编写Controller
- 配置中央调度器(DispatcherServlet)
在Consumer的pom.xml中引入依赖:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--Spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<!--dubbo依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<!--provider依赖-->
<dependency>
<groupId>com.zhouqun</groupId>
<artifactId>Provider</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
此处的Dubbo版本要与Provider一致,报错之后Provider的版本升级至2.6.4,这里也要升级。
配置核心文件:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd ">
<!--声明消费者名称,保证唯一性-->
<dubbo:application name="consumer"/>
<!--引用远程服务接口-->
<!--
id:Provider实现类注入时的id
interface:调用远程接口的全限定类名
url:访问服务器接口的地址(provider中设置的protocol的协议名和端口号)
register:不使用注册中心,值为N/A
-->
<dubbo:reference
id="userService"
interface="com.zhouqun.service.UserService"
url="dubbo://localhost:20880"
registry="N/A"/>
<!--<dubbo:registry address="N/A"/>-->
</beans>
因为要编写Controller,所以要再配置一个Spring的配置文件(spring-context)来扫组件,开启注解驱动,如果要走视图的话还要配置视图解析器:
<?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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描组件-->
<context:component-scan base-package="com.zhouqun.controller"/>
<!--开启注解驱动-->
<mvc:annotation-driven/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
再com.zhouqun.controller包下创建一个UserController,调用Provider中的UserService接口进行用户查询:
import com.zhouqun.pojo.User;
import com.zhouqun.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author 周区区
* @version 1.0
* @date 2021/7/10 14:13
*/
@Controller
public class UserController {
@Autowired
UserService userService;
@GetMapping(value = "/user/{id}")
public String UserDetail(@PathVariable("id") Integer id, Model model){
User user = userService.getUserById(id);
model.addAttribute("user",user);
return "userdetail";
}
}
然后在web.xml中配置中央调度器DispatcherServlet:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" metadata-complete="true">
<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:dubbo-consumer.xml,classpath:spring-context.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
因为UserController返回了一个视图:userdetail,所以要创建一个userdetail.jsp,用来呈现用户信息。
<%--
Created by IntelliJ IDEA.
User: 周群
Date: 2021/7/10
Time: 14:49
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户详情</title>
</head>
<body>
<h1>用户详情</h1>
<div>用户名称:${user.name}</div>
<div>用户年纪:${user.age}</div>
</body>
</html>
然后配置Tomcat,注意,Provider的端口号和Consumer的端口号不能一样,否则会端口号被占用。
注意,这里要先启动provider,在启动consumer。
启动之后会报错:
java.lang.IllegalStateException: Serialized class com.zhouqun.pojo.User must implement java.io.Serializable
因为数据在网络中传输需要进行序列化,所以要使User实体类实现Serializable接口。再次运行:
4.Dubbo常用标签
可以分为三大类:
- 公共标签
- 服务提供者标签
- 服务消费者标签
4.1公共标签
配置应用信息:名称最好是唯一值,用来给Dubbo框架内部使用
<dubbo:application name="服务的名称"/>
配置注册中心
<dubbo:registry address="ip:port" protocol="协议名"/>
4.2服务提供者标签
配置访问服务提供者的协议信息:协议名和端口号
<dubbo:protocol name="dubbo" port="20880"/>
暴露配置的服务:
<dubbo:service interface="服务接口的全限定类名" ref="接口的实现类在Spring容器中的标识,即id"/>
4.3服务消费者标签
配置服务消费者引用的远程服务:
<dubbo:reference id="服务实现类注入时的id,与上面的ref一致" interface="服务的全限定类名"/>
这么一看,dubbo:service和dubbo:reference的内容其实差不多。
5.常用项目配置
5.1关闭检查check
dubbo默认会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止spring初始化完成,以便上线时能及早发现问题。默认“check=true”,设置值为false关闭检查。
例如:测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。
例1:关闭某个服务的启动时检查:
<dubbo:reference interface="com.zhouqun.UserService" check="false"/>
例2:关闭注册中心启动时检查:
默认启动时注册中心存在并运行,不启动报错
<dubbo:registry check="false"/>
5.2请求重试retries
远程服务调用重试次数,不包括第一次调用,默认是2次,加上第一次一共是3次。
<dubbo:reference retries="5"/>
6.注册中心Zookeeper
6.1为什么使用注册中心
对于服务提供方,它需要发布服务,而且由于系统的复杂性,服务的数量、类型页不断膨胀。对于服务消费方,它最关心如何获取到它需要的服务,而面对复杂的系统,需要管理大量的服务调用。
而且,服务的提供方也有可能是服务的消费方,充当两种角色。通过将服务统一管理起来。可以有效的优化内部应用对服务发布/使用的流程和管理。服务注册中心可以通过特定的协议来完成服务对外的统一。Dubbo提供的注册中心由以下几种类型:
- Multicast注册中心:组播方式
- Redis注册中心:使用Redis作为注册中心
- Simple注册中心:就是一个dubbo服务。作为注册中心,提供查找服务的功能。
- Zookeeper注册中心:使用Zookeeper作为注册中心。
推荐使用Zookeeper,这是Apache Hadoop的子项目,是一个树形的目录服务,支持变更推送,适合作为dubbo服务的注册中心,工业强度较高,可用于生产环境。
Zookeeper就像windows的资源管理器,包含了所有的文件,服务提供者和服务消费者都在注册中心登记,服务消费者使用的访问地址不用写死。而且注册中心使用心跳机制更新服务提供者的状态。不能使用的服务提供者会自动从注册中心删除,服务提供者不会调用不能使用的服务。
6.2注册中心是什么
Zookeeper是一个高性能、分布式、开源的分布式应用程序协调服务。简称zk,翻译为动物管理员。可以理解为windows中的资源管理器或者注册表。Zookeeper是一个树形结构,这种树形结构和标准文件系统类似。Zookeeper树中的每个节点被称为Znode,和文件系统的树目录一样,Zookeeper中的每个节点可以拥有子节点,每个节点表示一个唯一服务资源。Zookeeper运行需要java环境。
6.3Zookeeper在windows上的安装
Windows安装Zookeeper
Zookeeper安装包下载地址:Zookeeper3.6.3
将下载的压缩包解压,bin文件夹里面存放了可执行文件:zkServer.cmd,用来启动Zookeeper。
然后打开conf文件夹,里面有一个Zookeeper的配置文件:zoo_sample.cfg,这是Zookeeper配置文件的样例文件,不是真实的配置文件,我们需要把这个文件拷贝一份并改名zoo.cfg。
在zoo.cfg中修改文件保存位置,一般在Zookeeper的安装目录下新建一个data文件夹,拷贝这个路径,把zoo.cfg的文件路径修改成这个。
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes. 存放Zookeeper接收的文件 不要使用临时目录
dataDir=D:/Apache/Zookeeper/apache-zookeeper-3.6.3/data
接下来启动Zookeeper,去bin文件夹下双击zkServer.cmd。
出现DOS界面闪退,由于看不到报错原因,先去修改zkServer.cmd,将zkServer.cmd 末尾的代码中 :
call %JAVA% --> java
想要查看报错异常,添加 pause 即可
保存文件再次双击zkServer.cmd,可以看到报错信息:
包下错了,gg。
zookeeper 从 3.5 版本以后,命名就发生了改变,如果是 apache-zookeeper-3.5.5.tar.gz 这般命名的,都是未编译的,而 apache-zookeeper-3.5.5-bin.tar.gz 这般命名的,才是已编译的包。所以,重新下包,再来一遍。
新的下载地址:Zookeeper3.6.3
重新配置zoo.cfg,双击zkServer.cmd,成功运行。
6.4Linux环境下安装Zookeeper
linux环境下安装Zookeeper:安装视频
7.Dubbo项目使用Zookeeper
7.1新建工程
新建一个普通maven项目:zk-Interface,用来存放实体bean和业务接口。
新建一个maven web项目:zk-Provider,作为服务提供者。
新建一个maven web项目:zk-Consumer,作为服务消费者。
7.2zk-Interface
在这个Model下面创建com.dubbo.pojo包放实体类,实体类一定要序列化。com.dubbo.service包放业务接口。
public class User implements Serializable {
private Integer id;
private String name;
private String sex;
}
public interface UserService {
/**
* 根据用户id查询用户
* @param id
* @return
*/
User getUserById(Integer id);
}
业务接口需要在Provider中实现。
7.3zk-Provider
首先导入依赖,spring、dubbo,zookeeper,虽然Interface已经暴露的接口,但是provider也需要暴露了什么接口,所以还需要导入zk-Interface的依赖。
<!--Spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<!--dubbo依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.4</version>
</dependency>
<!--zookeeper依赖-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.1.0</version>
</dependency>
<!--zk-Interface依赖-->
<dependency>
<groupId>com.zhouqun</groupId>
<artifactId>zk-Interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
然后新建一个com.dubbo.servide.impl包,实现Provider的接口。
public class UserServiceImpl implements UserService{
@Override
public User getUserById(Integer id) {
User user=new User();
user.setId(id);
user.setName("张三");
user.setSex("男");
return user;
}
}
然后开始配置Dubbo的核心配置文件:provider.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服务提供者的名称,保证唯一性-->
<dubbo:application name="zk-Provider"/>
<!--声明协议和端口号,不管是不是直连都需要去声明-->
<dubbo:protocol name="dubbo" port="20880"/>
<!--指定注册中心地址和端口号-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!--暴露服务接口-->
<dubbo:service interface="com.dubbo.service.UserService" ref="userService"/>
<!--加载接口实现类-->
<bean id="userService" class="com.dubbo.service.impl.UserServiceImpl"/>
</beans>
可见配置文件比点对点直连多了个 dubbo:registry标签声明注册中心。
接下来去web.xml中配置监听器。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" metadata-complete="true">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:provider.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
然后配置tomcat。端口号(8081,1098)
7.4zk-Consumer
首先添加依赖,与zk-Provider一致。这里就不贴了。
编写dubbo配置文件: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">
<!--声明dubbo消费者名称,保证唯一性-->
<dubbo:application name="zk-Consumer"/>
<!--指定注册中心-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!--引用远程服务接口-->
<dubbo:reference interface="com.dubbo.service.UserService" id="userService"/>
</beans>
因为要编写Controller,所以要再配置一个Spring的配置文件(springContext)来扫组件,开启注解驱动,如果要走视图的话还要配置视图解析器:
<?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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描组件-->
<context:component-scan base-package="com.dubbo.controller"/>
<!--开启注解驱动-->
<mvc:annotation-driven/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
然后在web.xml中配置DispatcherServlet:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" metadata-complete="true">
<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:consumer.xml,classpath:springContext.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
然后编写UserController:
@Controller
public class UserController {
@Autowired
UserService userService;
@RequestMapping(value = "/user/{id}")
public String userDetail(@PathVariable("id") Integer id, Model model){
User user = userService.getUserById(id);
model.addAttribute("user",user);
return "userdetail";
}
}
最后编写一个userdetail.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户详情</title>
</head>
<body>
<div>用户名称:${user.name}</div>
<div>用户年纪:${user.age}</div>
</body>
</html>
然后配置tomcat:(8080,1099)
7.5启动项目
首先启动注册中心,双击zkService.cmd
然后启动Provider
最后启动Consumer
8.版本号version的使用
当服务进行扩展时要更新版本号,再次发布,但不能干掉以前的老版本(等同软件更新,有人不愿意更新,就会继续使用老版本,不能强制所有人使用新版本),此时就涉及到版本号version的使用。
服务提供者创建:
依旧沿用上一节的zk-Interface,然后新建服务提供者和服务消费者。Provider的依赖与上一节zk-Provider的依赖一样。
然后新建com.dubbo.service.impl包,新建服务接口UserService的两个实现类:UserServiceImpl1、UserServiceImpl2。
public class UserServiceImpl1 implements UserService {
@Override
public User getUserById(Integer id) {
User user = new User();
user.setName("老藏"+"01版本");
user.setSex("男");
return user;
}
}
public class UserServiceImpl2 implements UserService {
@Override
public User getUserById(Integer id) {
User user = new User();
user.setName("老李"+"版本02");
user.setSex("女");
return user;
}
}
编写dubbo配置文件:
不管一个接口有多少个实现类,只要服务提供者指定了版本号,那么消费者引用远程接口服务的时候就必须指定版本号。
<?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="multi-Provider"/>
<!--声明协议和端口号-->
<dubbo:protocol name="dubbo" port="20880"/>
<!--使用注册中心-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!--暴露服务 通过版本号来识别调用哪个服务-->
<dubbo:service interface="com.dubbo.service.UserService" ref="UserService1" version="1.0.0"/>
<dubbo:service interface="com.dubbo.service.UserService" ref="UserService2" version="2.0.0"/>
<!--声明实现类-->
<bean id="userService1" class="com.dubbo.service.impl.UserServiceImpl1"/>
<bean id="userService2" class="com.dubbo.service.impl.UserServiceImpl2"/>
</beans>
然后在web.xml中配置监听器:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" metadata-complete="true">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:multi-provider.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
服务消费者创建:
消费者的依赖跟multi-Provider一样,然后创建dubbo项目的配置文件,multi-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">
<!--声明服务消费者的名称:保证唯一性-->
<dubbo:application name="multi-Consumer"/>
<!--使用注册中心-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!--声明引用的远程服务 一定要指定版本号-->
<dubbo:reference id="userService1" interface="com.dubbo.service.UserService" version="1.0.0"/>
<dubbo:reference id="userService2" interface="com.dubbo.service.UserService" version="2.0.0"/>
</beans>
然后创建一个spring配置文件:springContext.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: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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描组件-->
<context:component-scan base-package="com.dubbo.controller"/>
<!--开启注解驱动-->
<mvc:annotation-driven/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
然后在web.xml中配置中央调度器:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" metadata-complete="true">
<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:multi-Consumer.xml,classpath:springContext.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
然后编写Controller:
@Controller
public class UserController {
//类型一致时通过byName扫描,所以这里变量名要和配置文件中的id一样。
@Autowired
UserService userService1;
@Autowired
UserService userService2;
@RequestMapping(value = "/user/{id}")
public String getUserById(@PathVariable("id") Integer id,Model model){
User user1 = userService1.getUserById(id);
User user2 = userService2.getUserById(id);
model.addAttribute("user1",user1);
model.addAttribute("user2",user2);
return "userdetail";
}
}
配tomcat,启动注册中心,启动提供者,启动消费者。
9.监控中心
9.1什么是监控中心
Dubbo的使用,其实只需要注册中心、服务提供者、服务消费者。但是并不能看到有哪些消费者和提供者,为了更好的调试、发现问题、解决问题,因此引入了dubbo-admin。通过dubbo-admin可以对消费者和提供者进行管理。可以在dubbo应用部署做动态的调整,服务的管理。
dubbo-admin下载地址
git失败,上链接dubbo监控中心配置
10.负载均衡
集群: 集群是一种计算机系统,是一种服务结构。把一组多个计算机,包括硬件和软件组织在一个网络中。互相连接起来共同完成某个工作。对用户来说使用的是一个计算机,集群对用户是透明的。
负载均衡: 负载均衡是以集群为前提的,意思是将负载进行平衡、分摊到多个操作单元上进行执行。
对于网络而言,并不是一开始就需要负载均衡,当网络访问量不断增长,单个处理单元无法满足负载需求时,网络应用流量将要出现瓶颈时,负载均衡才会起作用。一般通过一个或多个前端负载均衡器,将工作负载分发到后端的一组服务器上,从而达到整个系统的高性能和高可用性。
负载均衡有两方面的含义:首先,单个重负载的工作分配到多个服务器做并行处理,每个服务器处理结束后,将结果汇总,返回给客户,系统处理能力得到大幅度提高,这是集群技术带来的优势。其次,大量的并发访问或数据流量分担到多台服务器分别处理,减少用户等待响应的时间。每个访问分配给不同的服务器处理。
10.1负载策略
Random LoadBalance:
随机,按照权重设置随机概率。在一个截面上碰撞的概率高,但是调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
RoundRobin LoadBalance:
轮询,按照公约后的权重设置轮询比例。存在慢的提供者累积请求问题:加入第二台服务器很慢,但没挂,当请求调到第二台服务器时就卡在那里,久而久之,所有的请求都卡在调第二台服务器上。(适用于服务器性能差不多的情况)
Least Active LoadBalance:
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少的请求,因为慢的提供者调用前后计数差会越大。(适用于服务器性能差异较大的情况)
ConsistentHash LoadBalance:
一致性Hash,相同参数的请求总是发到同一提供者。当某一台提供者挂掉了,原本发往该提供者的请求,基于虚拟节点,平摊到其他提供者,不会引起剧烈变动。(优势,发生故障可平稳过渡)缺省只对第一个参数hash,如果要修改,请配置:
<dubbo:parameter key="hash.arguments" value="0,1"/>
10.2配置方式
<dubbo:service interface="..." loadbalance="roundrobin"/>
或
<dubbo:reference interface="..." loadbalance="roundrobin"/>
随机:loadbalance=“random”
轮询:loadbalance=“roundrobin”
最少活跃:loadbalance=“leastactive”
一致性Hash:loadbalance="consistenthash "
10.3代码实现
要用到linux,上链接吧负载均衡代码实现
彩蛋:关于翻译的插件(IDEA2017)
点击下载,然后重启IDEA,选中单词或语句,按ctrl+shift+y即可翻译。
关于快速切换的快捷键:下图标红的地方首字母都有个下划线,想快速切换时,按alt+【下标】即可切换,例如,想打开Project,只需要按【alt+1】