第一章,RPC基础知识
RPC?
RPC是指远程过程调用,是一种进程间通讯方式,是一种技术思想,而不是规范。它允许程序调用另一个地址空间(网络的另一台机器上)的过程或函数,而不用开发人员显示编码这个调用的细节。调用本地方法何调用远程方法一样。
RPC的实现方式可以不同。例如java中的rmi,spring远程调用等。
RPC特点:简单-使用简单,建立分布式应用更容易。高效-调用过程看起来十分清晰,效率高。通用-进程间通讯的方式,有通用的规则。
RPC基本原理
RPC调用过程
1.调用方法client要使用右侧server的功能(方法),发起对方法的调用
2.client stub是RPC中定义的存根,看作是client的助手。stub把要调用的方法参数进行序列化,方法名称和其它数据包装起来
3.通过网络socket,把方法调用的细节内容发送给右侧的server
4.server端通过socket接受请求的方法名称,参数等数据,传给stub
5.server端接到的数据由server stub处理,调用server的真正方法,处理业务
6.server方法处理完业务,把处理的结果对象交给了助手,助手把Object进行序列化,对象转为二进制数据
7.server助手二进制数据交给网络处理程序
8.通过网络将二进制数据,放松给client
9.client接数据,交给client助手
10.client助手,就收数据通过反序列化为java对象,作为远程方法调用结果
其他:RPC通讯是基于tcp或udp协议,反序列化方式(sml/json/二进制)
第二章,dubbo框架
dubbo概述
Apache Dubbo是一款高性能,轻量级的开源java RPC框架,它提供了三大核心能力,面向接口的远程方法调用,只能容错和负载均衡,以及服务自动注册和发现。Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,服务治理方案。
服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,想注册中心注册自己提供的服务。
服务消费者(Consumer):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
注册中心(Registy):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计 数据到监控中心。
第三章,服务提供者(provider)
1.新建一个空maven工程
2.在pom文件中导入相关依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.3</version>
</dependency>
3.写好实体类,业务层和实现类
4.新建spring的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://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的服务名称
name:dubbo的服务名称,自定义的字符串,可以使用项目的名称,
服务的名称最好是唯一值,dubbo框架内部用来区分服务的
-->
<dubbo:application name="orderserviceprovider"/>
<!--声名访问dubbo服务的协议
name:协议名称
port:端口号
-->
<dubbo:protocol name="dubbo" port="20880"/>
<!--生命服务的接口,暴露服务
interface:服务接口的权限名称
ref:接口的实现类对象的id
registry:是都是用注册中心,第一个项目是直连方式,不使用注册中心,所以赋值为“N/A”
-->
<dubbo:service interface="com.rlw.service.OrderService" ref="orderService" registry="N/A"/>
<!--生命接口的实现类对象-->
<bean id="orderService" class="com.rlw.service.impl.OrderServiceImpl"/>
</beans>
5.调试运行
package com;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class OrderProvicerApplication {
public static void main(String[] args) {
String config = "orderservice-provider.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//容器启动方法
((ClassPathXmlApplicationContext) ctx).start();
//使之一直运行
System.in.read();
}
}
第四章,服务消费者(Consumer)
1.新建一个maven项目
2.在pom文件中导入相关依赖,而且多导入一个服务提供者的jar包依赖,为了使用服务提供者中的实体类以及部分方法。
<?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,rlw</groupId>
<artifactId>link-main-web</artifactId>
<version>1.0-SNAPSHOT</version>
<name>link-main-web</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.3</version>
</dependency>
<!--提供者的依赖项-->
<dependency>
<groupId>com.rlw</groupId>
<artifactId>link-orderservice-provider</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.写好实体类,业务层和实现类
4.书写sprig配置dubbo的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://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="link-main-web"/>
<!--声名要使用的远程接口
id:dubbo创建的接口的实现类对象的名称(动态代理)
interface:远程的接口全限定名称(服务提供者的接口)
registry:表示是否使用注册中心,不适用赋值为“N/A”
url:访问服务提供者的地址,直连方式中,地址是固定的
-->
<dubbo:reference id="remoteOrderService"
interface="com.rlw.service.OrderService"
registry="N/A"
url="dubbo://localhost:20880"/>
<!--声名自定义的业务对象-->
<bean id="shopService" class="com.rlw.service.impl.ShopServiceImpl">
<property name="orderService" ref="remoteOrderService"/>
</bean>
</beans>
5.调式调用服务提供者中的方法,以及查看返回结果
public class App
{
public static void main( String[] args ) {
String config = "main-consume.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//从容其中获取业务对象
ShopService shopService = (ShopService)ctx.getBean("shopService");
Order order = shopService.buyGoods(1, "thinkpad", 2000, 1);
System.out.println(order);
}
}
第五章,dubbo服务化最佳实践
分包:
建议将服务接口,服务模型,服务异常等均放在公共包中。
粒度:
服务接口尽可能最大粒度,每个服务方法应代表一个功能,而不是某个功能的一个步骤,否则将面临分布式事务的问题,Dubbo暂未提供分布式事务支持。
服务接口建议以业务场景为单位划分,并对相近业务成抽象,防止接口数量爆炸(就是多个功能放在一个方法,而不是多个功能多个方法,否则事务回滚不方便)。
不建议使用过于抽象的通用接口,如:Map query(Map),这样的接口没有明确语义,后期维护困难。
版本:
每个接口都应该定义版本号,为后续不兼容升级提供问题,如:<dubbo:service interface=“com.xxx.xxxService” version=“1.0”>。建议使用两位版本号,要变更服务版本,先升级一半提供者为新版本,再将消费者全部升为新版本,然后将剩下的一半提供者升为新版本。
创建公共资源项目:
服务提供者,消费者,网站等多个服务中共用,重复使用的类单独定义在一个项目。
1.创建公共的maven java project
项目名称:node-shop-interface
2.将之打成jar包,方便后续使用
3.创建node-shop-orderservice和node-shop-userservice两个提供者web项目,这里以其中一个为例,另一个是一样的。
4.引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
//node-shop-interface,jar包
<dependency>
<groupId>com.rlw</groupId>
<artifactId>node-shop-interface</artifactId>
<version>1.0.0</version>
</dependency>
5.书写服务类impl,注意这里引用的是node-shop-interface项目中的通用接口
package com.rlw.service.impl;
import com.rlw.domain.Order;
import com.rlw.service.OrderService;
import java.util.UUID;
public class OrderServiceImpl implements OrderService {
@Override
public Order createOrder(Integer userId, String goodName, float price, int amount) {
Order order = new Order();
order.setId(UUID.randomUUID().toString().replaceAll("-",""));
order.setGoodName("ceshi");
order.setPrice(111);
order.setAmount(111);
return order;
}
}
6.创建spring容器,配置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:application name="node-shop-orderservice"/>
<!--定义服务端口号-->
<dubbo:protocol name="dubbo" port="20882"/>
<!--暴露服务-->
<dubbo:service interface="com.rlw.service.OrderService"
registry="N/A" ref="orderService"/>
<!--声名服务接口的实现类对象-->
<bean id="orderService" class="com.rlw.service.impl.OrderServiceImpl"/>
</beans>
7.配置web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:orderservice-provider.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
8.创建消费者web项目node-show-web
9.引入maven依赖
<?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.rlw</groupId>
<artifactId>node-show-web</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>node-show-web Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>com.rlw</groupId>
<artifactId>node-shop-interface</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--标签库-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
10书写controller,service,servieImpl,
在serviceImpl调用提供者的服务orderService.buyOrder();或者其他服务者的方法;
11.springmvc配置写好
<?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.rlw.controller"/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--注解驱动-->
<mvc:annotation-driven />
</beans>
12.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:application name="node-show-web"/>
<!--声名要使用的远程接口-->
<dubbo:reference
interface="com.rlw.service.UserInfoService"
id="userService"
registry="N/A"
url="dubbo://localhost:20881"
check="false"/>
<dubbo:reference
interface="com.rlw.service.OrderService"
id="orderService"
registry="N/A"
url="dubbo://localhost:20882"
check="false"/>
<!--生命自定义的service-->
<bean id="shopService" class="com.rlw.service.impl.ShopServiceImpl">
<property name="orderService" ref="orderService"/>
<property name="userInfoService" ref="userService"/>
</bean>
</beans>
13.测试消费者调用服务者是否成功
成功调用到了服务者中set的id和名字
第六章,注册中心-Zookerper
注册中心描述:
对于服务的提供方,他需要发布服务,而且由于系统应用的复杂性,服务的数量,类型也不断膨胀,对于服务消费方,她最关心如何获取到他所需要的服务,而对复杂的应用系统,需要管理大量的服务调用。
而且,对于服务提供方和服务消费方来说,他们还有可能兼具这两种角色,急需要提供服务,有需要消费服务。通过将服务统一管理起来,可以有效地优化内部应用对服务发布/使用的流程和管理。服务注册中心可以通过特定协议来完成服务对外的统一。Dubbo提供的注册中心有如下几种类型可供选择:
Multicast注册中心:组播方式
Redis注册中心:使用Redis作为注册中心
Simple注册中心:就是一个Dubbo服务。作为注册中心。提供查找服务的功能
Zookeeper注册中心:使用Zookeeper作为注册中心
推荐Zookeeper
注册中心工作方式:
windows安装zookeeper:
官网下载,解压,bin目录和config目录为主,bin目录下zkServer.cmd启动zookeeper
Linux中安装zooleeper:
zookeeper上传到home目录下,解压到/usr/local目录下,向windows一样修改配置文件名称为zoo.cfg,其中修改配置与windows一样,别忘了zookeeper需要在jdk的基础之上运行,zkServer.sh启动zookeeper
成功运行
第七章,使用zookeeper,改造上节dubbo直连方式的项目
两个服务者:举其中一个为例
1.添加zookeeper的maven依赖:
<!--zookeeper依赖-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.1.0</version>
</dependency>
2.改变之前的容器中dubbo的配置文件
(1)之前的直连方式改为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="node-shop-orderservice"/>
<!--定义服务端口号-->
<dubbo:protocol name="dubbo" port="20882"/>
<!--zookeeper的注册中心-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!--暴露服务-->
<dubbo:service interface="com.rlw.service.OrderService"
ref="orderService"/>
<!--声名服务接口的实现类对象-->
<bean id="orderService" class="com.rlw.service.impl.OrderServiceImpl"/>
</beans>
(2)并且有相应的小改动
一个消费者:
<?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="node-show-web"/>
<!--声名要使用的远程接口-->
<dubbo:reference
interface="com.rlw.service.UserInfoService"
id="userService"
check="false"/>
<dubbo:reference
interface="com.rlw.service.OrderService"
id="orderService"
check="false"/>
<!--声名zookeeper-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!--生命自定义的service-->
<bean id="shopService" class="com.rlw.service.impl.ShopServiceImpl">
<property name="orderService" ref="orderService"/>
<property name="userInfoService" ref="userService"/>
</bean>
</beans>
启动zookeeper:
一个问题,在启动zookeeper之后,访问服务者后,关掉zookeeper服务后还可以访问到服务者嘛?
答案是可以的,zookeeper在消费者访问服务者的同时就已经将服务者的路径地址缓存起来,之后也是可以访问服务者的,就算zookeeper宕机了也不影响程序的运行,zookeeper接收所有的服务者以供消费者使用。
第八章,dubbo的配置
配置原则:
在服务提供者配置访问参数,因为服务提供者更了解服务的各种参数。
关闭检查:
dubbo缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,组织Spring初始化完成,一边上线,能及早发现问题,默认check=true。通过check=“false”关闭检查,比如在测试阶段,有些服务不关系,或者出现了循环依赖,必须有一方先起动。
例一:关闭某个服务的启动检查
<dubbo:reference
interface="com.rlw.service.OrderService"
id="orderService"
check="false"/>
例二:关闭注册中心启动时检查
<dubbo:registry check="false"/>
默认启动服务时检查注册中心存在并已运行,注册中心不会报错。
重试次数:
消费者访问提供者,如果访问失败,则切换重试访问其他服务器,但重试会带来更长延迟。访问时间变长,用户体验较差,多次重新访问服务器有可能访问成功。可通过retries=“2”来设置重试次数。
<dubbo:service retries="2"/>
//或者
<dubbbo:reference retries="2"/>
超时时间:
由于网络或服务端不可靠,会导致调用出现一种不确定的中间状态(超时),为了避免超时导致客户端资源挂起耗尽,必须设置超时时间。
timeout:调用远程服务超时时间(毫秒)
消费端设置
<dubbo:reference interface="com.rlw.xxx" timeout="2000"/>
服务端设置
<dubbo:server interface="com.rlw.xxx" timeout="2000"/>
版本号:
消费者指定使用哪个版本
服务者定义版本
第九章,监控中心
什么是监控中心:
dubbo的使用,其实只需要有注册中心,消费者,提供者这三个就可以使用了,但是并不能看到有哪些消费者和提供者,为了更好的调试,发现问题,解决问题,因此引入了dubbo-admin。通过dubbo-admin可以对消费者和提供者进行管理,可以在dubbo应用部署做动态的调整,服务的管理。
dubbo-admin:
图形化服务管理页面:安装时需要指定注册中心地址,既可以从注册中心中获取到所有的提供者/消费者进行配置管理
dubbo-monitor-simple:
简单的监控中心