Dubbo以及Zookeeper(RPC远程调用)

OSI七层模型

  1. 应用层:为应用程序提供服务【HTTP、FTP、DNS】

  2. 表示层:数据格式转换、数据加密【Telnet】

  3. 会话层:建立、管理和维会话【SMTP】

  4. 传输层:建立、管理和维护端到端的连接(服务端到客户端)【UDP、TCP】

  5. 网络层:IP地址及路由选择【IP、RARP】

  6. 数据链路层:提供介质访问和链路管理

  7. 物理层:物理层(wife,网络)

RPC协议

Dubbo以及Zookeeper(RPC远程调用)_第1张图片

  • 在微服务的设计中,一个服务A如果访问另一个Module下的服务B,可以采用HTTP REST传输数据,并在两个服务之间进行序列化和反序列化操作,服务B把执行结果返回过来。

Dubbo以及Zookeeper(RPC远程调用)_第2张图片

  • 由于HTTP在应用层中完成,整个通信的代价较高,远程过程调用中直接基于TCP进行远程调用,数据传输在传输层TCP层完成,更适合对效率要求比较高的场景,RPC主要依赖于客户端和服务端之间建立Socket连接进行,底层实现比REST更复杂。

Dubbo以及Zookeeper(RPC远程调用)_第3张图片
Dubbo以及Zookeeper(RPC远程调用)_第4张图片

rpc完整调用过程:

  1. **服务消费方(client)调用以本地调用方式调用服务; **

  2. clientstub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;

  3. clientstub找到服务地址,并将消息发送到服务端;

  4. serverstub收到消息后进行解码;

  5. serverstub根据解码结果调用本地的服务;

  6. 本地服务执行并将结果返回给server stub;

  7. serverstub将返回结果打包成消息并发送至消费方;

  8. clientstub接收到消息,并进行解码;

  9. 服务消费方得到最终结果。

RPC框架的目标就是要2~8这些步骤都封装起来,这些细节对用户来说是透明的,不可见的。

Dubbo的使用(接口类路径相同就好了,以反射的思维来思考)

Dubbo架构

使用dubbo来作为服务化中间件,dubbo作为一个RPC框架,大致的原理如下图:

Dubbo以及Zookeeper(RPC远程调用)_第5张图片

  • Registry:注册中心;和服务的消费者,和服务提供者都建立长连接。服务提供者注册服务到注册中心;服务消费者从注册中心获取服务提供者列表;(推荐使用zookeeper)

  • Consumer: 服务消费端;服务消费者从注册中心获取到服务提供者列表后,根据负载均衡算法,选择一个服务提供者,和服务提供者直接建立连接,开始调用服务;

  • Provider:服务提供者;服务提供者提供RPC服务;

  • Monitor: 监控服务的调用情况统计;

  • 注册中心为N+1对等集群,一台挂掉后,会自动切换到另外一台注册中心

  • 注册中心全部挂掉后,消息消费者本地会缓存服务提供者列表,所以不影响当时的服务调用。

  • 服务提供者为集群,一台挂掉后,通过心跳过程,注册中心会立即刷到服务消费者告知;

Zookeeper作用(注册中心):

Zookeeper用来注册服务和进行负载均衡,哪一个服务由那一台机器来提供必需让调用者知道,简单来说就是IP地址和服务名称的对应关系。当然也可以通过硬编码的方式把这种对应关系在调用方业务代码中实现,但是如果提供服务的机器挂掉,调用者无法知晓,如果不更改代码会继续请求挂掉的机器提供服务。Zookeeper通过心跳机制可以检测挂掉的机器的IP和服务对应关系从列表关系中删除。至于支持高并发,简单来说就是横向扩展,在不更改代码的情况通过增加机器数量提高运算能力。通过添加新的机器向Zookeeper注册服务,服务的提供者多了能服务的客户就多了。

从以上架构可知,运维dubbo服务主要包含 DUBBO应用(APP) 监控中心(Monitor) 服务中心(Admin)管理三个方面,因此一下从三个方面分析对比选型。

导入依赖

首先是maven引入的依赖

<dependency>
    
    <groupId>org.apache.dubbogroupId>
    <artifactId>dubbo-spring-boot-starterartifactId>
    <version>${dubbo.version}version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-log4j12artifactId>
        exclusion>
    exclusions>
dependency>
<dependency>
    <groupId>org.apache.dubbogroupId>
    <artifactId>dubboartifactId>
    <version>${dubbo.version}version>
dependency>

<dependency>
    <groupId>org.apache.curatorgroupId>
    <artifactId>curator-frameworkartifactId>
    <version>4.0.1version>
dependency>
<dependency>
    <groupId>org.apache.curatorgroupId>
    <artifactId>curator-recipesartifactId>
    <version>2.8.0version>
dependency>

<dependency>
    <groupId>org.apache.zookeepergroupId>
    <artifactId>zookeeperartifactId>
    <version>3.4.13version>
    <type>pomtype>
    <exclusions>
        <exclusion>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-log4j12artifactId>
        exclusion>
    exclusions>
dependency>
<dependency>
    <groupId>com.101tecgroupId>
    <artifactId>zkclientartifactId>
    <version>0.10version>
dependency>

注意:如果在项目中不想使用继承springboot的父类,但是又想使用springboot来构建项目,你需要在自己的root-model的pom下添加如下配置:


<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-dependenciesartifactId>
            <version>2.3.4.RELEASEversion>
            <type>pomtype>
            <scope>importscope>
        dependency>
    dependencies>
dependencyManagement>

xml配置使用dubbo

Dubbo-Server(服务端)


<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="demo-provider"/>
    
    <dubbo:registry address="zookeeper://8.129.219.11:2181"/>
    <dubbo:protocol name="dubbo" port="20890"/>
    
    <dubbo:service interface="com.edu.service.StudentService" ref="studentService"/>

    
    <bean id="studentService" class="com.edu.service.impl.StudentServiceImpl"/>
    
	

beans>

dubbo-customer(消费端)


<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: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://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <context:component-scan base-package="com.edu.provider,com.edu.api,com.*,com.edu.config"/>
    <dubbo:application name="demo-consumer"/>
    <dubbo:registry address="zookeeper://8.129.219.11:2181"/>
    <dubbo:reference id="demoService" check="false" interface="com.edu.service.StudentService"/>
    
	

beans>

标签说明(详情):

标签 用途 解释
dubbo:service/ 服务配置 用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心
dubbo:reference/ 引用配置 用于创建一个远程服务代理,一个引用可以指向多个注册中心
dubbo:protocol/ 协议配置 用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受
dubbo:application/ 应用配置 用于配置当前应用信息,不管该应用是提供者还是消费者
dubbo:module/ 模块配置 用于配置当前模块信息,可选
dubbo:registry/ 注册中心配置 用于配置连接注册中心相关信息
dubbo:monitor/ 监控中心配置 用于配置连接监控中心相关信息,可选
dubbo:provider/ 提供方配置 当 ProtocolConfig 和 ServiceConfig 某属性没有配置时,采用此缺省值,可选
dubbo:consumer/ 消费方配置 当 ReferenceConfig 某属性没有配置时,采用此缺省值,可选
dubbo:method/ 方法配置 用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息
dubbo:argument/ 参数配置 用于指定方法参数配置

关于参数设置的覆盖关系,官网也有说明:

  • 方法级优先,接口级次之,全局配置再次之。

  • 如果级别一样,则消费方优先,提供方次之。

其中,服务提供方配置,通过 URL 经由注册中心传递给消费方。

注意:每个标签都有自己特定的属性配置框架会自动使用ConsumerConfigServiceConfig,ProviderConfig等提供的缺省配置。

引用缺省是延迟初始化的,只有引用被注入到其它 Bean,或被 getBean() 获取,才会初始化。如果需要饥饿加载,即没有人引用也立即生成动态代理,可以配置:

注解使用dubbo

springboot启动类设置

@EnableDubbo(scanBasePackages="com.edu.service")
//@DubboComponentScan("com.edu.service")效果一致
@SpringBootApplication
public class BootUserServiceProviderApplication {
     

	public static void main(String[] args) {
     
		SpringApplication.run(BootUserServiceProviderApplication.class, args);
	}
}

dubbo配置类设置:

@Configuration
public class MyDubboConfig {
     
	
	@Bean
	public ApplicationConfig applicationConfig() {
     
		ApplicationConfig applicationConfig = new ApplicationConfig();
		applicationConfig.setName("boot-user-service-provider");
		return applicationConfig;
	}
	
	//
	@Bean
	public RegistryConfig registryConfig() {
     
		RegistryConfig registryConfig = new RegistryConfig();
		registryConfig.setProtocol("zookeeper");
		registryConfig.setAddress("8.129.219.11:2181");
		return registryConfig;
	}
	
	//
	@Bean
	public ProtocolConfig protocolConfig() {
     
		ProtocolConfig protocolConfig = new ProtocolConfig();
		protocolConfig.setName("dubbo");
		//通信端口
		protocolConfig.setPort(20882);
		return protocolConfig;
	}
	
	/**
	 *
		
	
	 */
	@Bean
	public ServiceConfig<UserService> userServiceConfig(UserService userService){
     
		ServiceConfig<UserService> serviceConfig = new ServiceConfig<>();
		serviceConfig.setInterface(UserService.class);
		serviceConfig.setRef(userService);
		serviceConfig.setVersion("1.0.0");
		
		//配置每一个method的信息
		MethodConfig methodConfig = new MethodConfig();
		methodConfig.setName("getUserAddressList");
		methodConfig.setTimeout(1000);
		
		//将method的设置关联到service配置中
		List<MethodConfig> methods = new ArrayList<>();
		methods.add(methodConfig);
		serviceConfig.setMethods(methods);
		
		//ProviderConfig
		//MonitorConfig
		
		return serviceConfig;
	}

}


要注入的话使用注解@Reference

@Reference
private StudentService studentService

Dubbo拓展

dubbo本地存根

在 Dubbo 中利用本地存根在客户端执行部分逻辑

远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub 1,然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。

Dubbo以及Zookeeper(RPC远程调用)_第6张图片

  • 在 spring 配置文件中按以下方式配置:
<dubbo:service interface="com.foo.BarService" stub="true" />

<dubbo:service interface="com.foo.BarService" stub="com.foo.BarServiceStub" />

  • 提供 Stub 的实现 :
package com.foo;
public class BarServiceStub implements BarService {
     
	//暴露的接口
    private final BarService barService;
    
    // 构造函数传入真正的远程代理对象
    public BarServiceStub(BarService barService){
     
        this.barService = barService;
    }
 
    public String sayHello(String name) {
     
        // 此代码在客户端执行, 你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等
		//执行自己的逻辑
        try {
     
			//开始执行暴露的接口的方法
            return barService.sayHello(name);
        } catch (Exception e) {
     
            // 你可以容错,可以做任何AOP拦截事项
            return "容错数据";
        }
    }
}


  1. Stub 必须有可传入 Proxy(暴露接口) 的构造函数。 ↩︎

  2. 在 interface 旁边放一个 Stub 实现,它实现 BarService 接口,并有一个传入远程 BarService 实例的构造函数 ↩︎

zookeeper宕机与dubbo直连

就是zookeeper(注册中心)挂了,怎么搞。

目的:我们要实现一个zookeeper注册中心宕机,还可以消费dubbo暴露的服务(重点:本地缓存)

  1. 监控中心宕掉不影响使用,只是丢失部分采样数据

  2. 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务

  3. 注册中心对等集群,任意一台宕掉后,将自动切换到另一台

  4. 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯

  5. 服务提供者无状态,任意一台宕掉后,不影响使用

  6. 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复

使用dubbo直连

@Reference(url="127.0.0.1:20882")//使用dubbo直连的方式,跳过了注册中心
private StudentService studentService

集群下dubbo负载均衡配置(类似Nginx)

配置:

  • 服务端服务级别:
<dubbo:service interface="..." loadbalance="roundrobin" weight="权重"/>

  • 客户端服务级别:
<dubbo:reference interface="..." loadbalance="roundrobin" />

  • 服务端方法级别:
<dubbo:service interface="...">
<dubbo:method name="..." loadbalance="roundrobin"/>
dubbo:service>

  • 客户端方法级别:
<dubbo:reference interface="...">
<dubbo:method name="..." loadbalance="roundrobin"/>
dubbo:reference> 

在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random 随机调用。

  • RandomLoadBalance(按权重随机)

    • 随机,按权重设置随机概率。

    • 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。

  • RoundRobinLoadBalance(轮询)

    • 轮循,按公约后的权重设置轮循比率。

    • 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

  • LeastActiveLoadBalance(最少活跃数)

    • 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。

    • 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

  • ConsistentHashLoadBalance(一致性hash)

    • 一致性 Hash,相同参数的请求总是发到同一提供者。

    • 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。算法参见

    • 缺省只对第一个参数 Hash,如果要修改,请配置

    • 缺省用 160 份虚拟节点,如果要修改,请配置

服务降级/服务容错以及Hystrix

服务降级

压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作。

RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.c
lass).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:
2181"));
registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configur
ators&dynamic=false&application=foo&mock=force:return+null"));

mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值, 不发起远程调用。 用来屏蔽不重要服务不可用时对调用方的影响。

还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后, 再返回null 值, 不抛异常。 用来容忍不重要服务不稳定时对调用方的影响。

集群容错

在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。

集群容错模式

FailoverCluster

失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过retries=“2” 来设置重试次数(不含第一次)。

重试次数配置如下:

<dubbo:service retries="2" />

<dubbo:reference retries="2" />

<dubbo:reference>
    
dubbo:reference>

FailfastCluster

快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

FailsafeCluster

失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

FailbackCluster

失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

ForkingCluster

并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过forks=“2” 来设置最大并行数。

BroadcastCluster

广播调用所有提供者,逐个调用,任意一台报错则报错 [2]。通常用于通知所有提供者更新缓存或日志等本地资源信息。

集群模式配置

按照以下示例在服务提供方和消费方配置集群模式

<dubbo:service cluster="failsafe" />

<dubbo:reference cluster="failsafe" />

整合hystrix(熔断器)

Hystrix 旨在通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能

1、配置spring-cloud-starter-netflix-hystrix

spring boot官方提供了对hystrix的集成,直接在pom.xml里加入依赖:

        <dependency>
             <groupId>org.springframework.cloudgroupId>
             <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
             <version>1.4.4.RELEASEversion>
        dependency>
  

然后在Application类上增加@EnableHystrix来启用hystrix starter:

@SpringBootApplication
@EnableHystrix 
@EnableDubbo
public class  ProviderApplication {
     
}

2、配置Provider端

在Dubbo的Provider上增加@HystrixCommand配置,这样子调用就会经过Hystrix代理。


@Service(version = "1.0.0")
public class  HelloServiceImpl implements HelloService {
     
    @HystrixCommand(commandProperties = {
     
     @HystrixProperty(name =  "circuitBreaker.requestVolumeThreshold", value = "10"),
     @HystrixProperty(name =  "execution.isolation.thread.timeoutInMilliseconds", value =  "2000") })
    @Override
    public String sayHello(String name) {
     
        // System.out.println("async  provider received: " + name);
        // return "annotation: hello,  " + name;
        throw new  RuntimeException("Exception to show hystrix enabled.");
    }
}

3、配置Consumer端

对于Consumer端,则可以增加一层method调用,并在method上配置@HystrixCommand。当调用出错时,会走到fallbackMethod = "reliable"的调用里。

@Reference(version =  "1.0.0")
private HelloService demoService;
@HystrixCommand(fallbackMethod =  "reliable")//出错调用reliable方法
public String doSayHello(String name) {
     
	return demoService.sayHello(name);
}
public String reliable(String name) {
     
	return "hystrix fallback  value";
}

Dubbo原理

框架设计

你可能感兴趣的:(rpc,网络协议,ssm框架,java,spring,zookeeper,rpc,分布式)