dubbo学习笔记-初步认识dubbo及dubbo的简单使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zlw420123/article/details/80344850

dubbo学习笔记

初步认识dubbo

dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的RPC实现服务的输出和输入功能,可以和Spring框架无缝集成。
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。
dubbo就是在这种背景中产生的。
下面有几大文档能极大帮助开发者熟悉dubbo这个框架的,建议大家有事没事去看看:
Dubbo用户手册(中文)
Dubbo开发手册(中文)
Dubbo管理手册(中文)

dubbo简单使用

下面将以一个简单的例子来说明dubbo的用法。
首先dubbo是面向接口开发的,所以第一步得先定义一个接口。
- api

package com.github.wenj91.kotlin.api

interface TestService {
    fun sayHello(name: String): String
}

接口的实现
- service.impl

package com.github.wenj91.kotlin.provider.service.impl

import com.github.wenj91.kotlin.api.TestService

class TestServiceImpl : TestService{
    override fun sayHello(name: String): String {
        return "hello, $name!"
    }
}

接口的export
- provider

package com.github.wenj91.kotlin.provider

import com.alibaba.dubbo.config.ApplicationConfig
import com.alibaba.dubbo.config.ProtocolConfig
import com.alibaba.dubbo.config.RegistryConfig
import com.alibaba.dubbo.config.ServiceConfig
import com.github.wenj91.kotlin.api.TestService
import com.github.wenj91.kotlin.provider.service.impl.TestServiceImpl

fun main(args: Array<String>){
    val applicationConfig = ApplicationConfig()
    applicationConfig.name = "dubbo-provider" //配置服务名

    val registryConfig = RegistryConfig()
    registryConfig.address = "N/A" //配置注册中心,这里不使用任何注册中心

    val protocolConfig = ProtocolConfig()
    protocolConfig.name = "dubbo" //配置协议及端口
    protocolConfig.port = 9090

    val serviceConfig = ServiceConfig<TestService>()
    serviceConfig.application = applicationConfig
    serviceConfig.registry = registryConfig
    serviceConfig.protocol = protocolConfig
    serviceConfig.setInterface(TestService::class.java)
    serviceConfig.ref = TestServiceImpl()
    serviceConfig.export() //export接口,暴露接口

    System.`in`.read()
}

服务已经暴露出来,下来就是接口的调用
- consumer

package com.github.wenj91.kotlin.consumer

import com.alibaba.dubbo.config.ApplicationConfig
import com.alibaba.dubbo.config.ReferenceConfig
import com.alibaba.dubbo.config.RegistryConfig
import com.github.wenj91.kotlin.api.TestService

fun main(args: Array<String>){
    val applicationConfig = ApplicationConfig()
    applicationConfig.name = "dubbo-consumer" //配置服务名

    val registryConfig = RegistryConfig()
    registryConfig.address = "N/A" //配置注册中心,这里不使用任何注册中心

    val referenceConfig = ReferenceConfig<TestService>()
    referenceConfig.application = applicationConfig
    referenceConfig.registry = registryConfig
    referenceConfig.url = "dubbo://127.0.0.1:9090" //配置直连链接,这样配置就把dubbo当作普通的RPC框架来使用,最简单最直接最本质
    referenceConfig.setInterface(TestService::class.java) //需要获取接口类型

    var testService = referenceConfig.get() //获取接口代理对象
    println(testService.sayHello("wenj91")) //远程调用接口并打印接口返回值

}

至此,dubbo已经能正常的工作了,当然这是最简单的配置使用,没有用到dubbo的一些高级特性,甚至没有用注册中心,这其实就是dubbo最本质的东西吧,抛开其它东西,它的本质就是一个rpc框架。

dubbo配置说明

dubbo功能丰富多样,这就说明dubbo的配置也是很丰富的,这样才能灵活的使用dubbo来实现我们的需求.当然这里只是简单的说明一下dubbo的配置使用,更多更全的说明请参考dubbo的schema 配置参考手册,里面有对dubbo的配置做详细的说明.

api配置

一开始我们使用的样例就是通过api简单配置来使用的,api配置跟其它配置是一对一的关系,完全通过api来进行配置使用,不依赖任何框架.
比如:applicationConfig.name = "dubbo-provider" 对应 <dubbo:application name="dubbo-provider" />

provider的api配置:

    //当前应用的配置
    val applicationConfig = ApplicationConfig()
    applicationConfig.name = "dubbo-provider" //配置服务名

    //注册中心的配置
    val registryConfig = RegistryConfig()
    registryConfig.address = "N/A" //配置注册中心,这里不使用任何注册中心

    //协议的配置
    val protocolConfig = ProtocolConfig()
    protocolConfig.name = "dubbo" //配置协议及端口
    protocolConfig.port = 9090

    //暴露服务配置
    val serviceConfig = ServiceConfig<TestService>()
    serviceConfig.application = applicationConfig
    serviceConfig.registry = registryConfig
    serviceConfig.protocol = protocolConfig
    serviceConfig.setInterface(TestService::class.java) //配置接口服务
    serviceConfig.ref = TestServiceImpl() //接口实现
    serviceConfig.export() //export接口,暴露接口

consumer的api配置:

    //当前应用的配置
    val applicationConfig = ApplicationConfig()
    applicationConfig.name = "dubbo-consumer" //配置服务名

    //注册中心的配置
    val registryConfig = RegistryConfig()
    registryConfig.address = "N/A" //配置注册中心,这里不使用任何注册中心

    // 引用远程服务
    val referenceConfig = ReferenceConfig<TestService>()
    referenceConfig.application = applicationConfig
    referenceConfig.registry = registryConfig
    referenceConfig.url = "dubbo://127.0.0.1:9090" //配置直连链接,这样配置就把dubbo当作普通的RPC框架来使用,最简单最直接最本质
    referenceConfig.setInterface(TestService::class.java) //需要获取接口类型

    var testService = referenceConfig.get() //获取接口代理对象
    println(testService.sayHello("wenj91")) //远程调用接口并打印接口返回值

xml配置

有关XML的详细配置项,请参见:dubbo的schema 配置参考手册

基于spring配置的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://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 这里的配置与api配置是一对一关系 -->
    <bean id="testService" class="com.github.wenj91.kotlin.provider.service.impl.TestServiceImpl" />
    <dubbo:application name="dubbo-provider" />
    <dubbo:registry address="N/A" />
    <dubbo:protocol name="dubbo" port="9090" />
    <dubbo:service interface="com.github.wenj91.kotlin.api.TestService" ref="testService" />
</beans>

provider加载配置并暴露服务:

package com.github.wenj91.kotlin.provider

import org.springframework.context.support.ClassPathXmlApplicationContext

fun main(args: Array<String>){
    val applicationContext = ClassPathXmlApplicationContext("classpath:provider.xml")
    applicationContext.start()
    System.`in`.read()
}

基于spring配置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://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
    <dubbo:application name="dubbo-consumer"  />

    <!-- 不使用注册中心 -->
    <dubbo:registry address="N/A" />

    <!-- 生成远程服务代理,可以和本地bean一样使用testService -->
    <dubbo:reference id="demoService" url="dubbo://127.0.0.1:9090" interface="com.github.wenj91.kotlin.api.TestService" />
</beans>

consumer加载配置并引用服务:

package com.github.wenj91.kotlin.consumer

import com.github.wenj91.kotlin.api.TestService
import org.springframework.context.support.ClassPathXmlApplicationContext

fun main(args: Array<String>){
    val applicationContext = ClassPathXmlApplicationContext("classpath:consumer.xml")
    applicationContext.start()

    val testService = applicationContext.getBean(TestService::class.java)
    println(testService.sayHello("wenj91"))
}

注解配置并与spring boot集成

注解配置将与Spring Boot集成使用,在这里,采用redis(redis://127.0.0.1:6379)做注册中心,服务配置版本号(version=”v1.0.0”)并分组(group=”wenj91”)

provider注解配置示例:

//dubbo provider config
package com.github.wenj91.kotlin.provider.config

import com.alibaba.dubbo.config.ApplicationConfig
import com.alibaba.dubbo.config.ProtocolConfig
import com.alibaba.dubbo.config.RegistryConfig
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class DubboProviderConfig{
    @Bean
    fun applicationConfig(): ApplicationConfig {
        var applicationConfig = ApplicationConfig()
        applicationConfig.name = "dubbo-provider" //应用配置

        return applicationConfig
    }

    @Bean
    fun registerConfig(): RegistryConfig {
        var registryConfig = RegistryConfig()
        registryConfig.address = "redis://127.0.0.1:6379" //注册中心配置,这里采用redis做注册中心

        return registryConfig
    }

    @Bean
    fun protocolConfig(): ProtocolConfig {
        var protocolConfig = ProtocolConfig()
        protocolConfig.port = 8888 //protocol配置

        return protocolConfig
    }
}

//service impl
package com.github.wenj91.kotlin.provider.service.impl

import com.alibaba.dubbo.config.annotation.Service
import com.github.wenj91.kotlin.api.TestService

//service config, 这里配置了版本号,以及给服务分组
@Service(version = "v1.0.0", group = "wenj91")
class TestServiceImpl: TestService {
    override fun sayHello(name: String): String {
        return "hello, $name!"
    }
}

//provider app
package com.github.wenj91.kotlin.provider

import com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
@DubboComponentScan
class ProviderApp

//启动应用并暴露服务
fun main(args: Array<String>){
    SpringApplication.run(ProviderApp::class.java, *args)
}

consumer注解配置示例:

//dubbo consumer config
package com.github.wenj91.kotlin.consumer.config

import com.alibaba.dubbo.config.ApplicationConfig
import com.alibaba.dubbo.config.ProtocolConfig
import com.alibaba.dubbo.config.RegistryConfig
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class DubboConsumerConfig{
    @Bean
    fun applicationConfig(): ApplicationConfig {
        var applicationConfig = ApplicationConfig()
        applicationConfig.name = "dubbo-consumer" //应用配置

        return applicationConfig
    }

    @Bean
    fun registerConfig(): RegistryConfig {
        var registryConfig = RegistryConfig()
        registryConfig.address = "redis://127.0.0.1:6379" //redis注册中心配置

        return registryConfig
    }

    @Bean
    fun protocolConfig(): ProtocolConfig {
        var protocolConfig = ProtocolConfig()
        protocolConfig.port = 8888 //protocol配置

        return protocolConfig
    }
}

//dubbo reference config
package com.github.wenj91.kotlin.consumer.config

import com.alibaba.dubbo.config.annotation.Reference
import com.github.wenj91.kotlin.api.TestService
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class DubboReferenceConfig{
    //引用服务配置, 这里需要与provider同样配置version与group
    @Reference(version = "v1.0.0", group = "wenj91")
    lateinit var testService: TestService

    @Bean
    fun testService(): TestService {
        return testService
    }
}

//consumer app
package com.github.wenj91.kotlin.consumer

import com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan
import com.github.wenj91.kotlin.api.TestService
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
@DubboComponentScan
class ConsumerApp

//启动应用并调用
fun main(args: Array<String>){
    var applicationContext = SpringApplication.run(ConsumerApp::class.java, *args)
    var testService = applicationContext.getBean(TestService::class.java)
    println(testService.sayHello("wenj91"))
}

dubbo注册中心

dubbo目前支持zookeeper,redis,multicast,simple作为注册中心,但是官方推荐使Zookeeper 注册中心

以下是这些注册中心成熟度说明,可以根据这个来选择合适的注册中心:

Feature Maturity Strength Problem Advise User
Zookeeper注册中心 Stable 支持基于网络的集群方式,有广泛周边开源产品,建议使用dubbo-2.3.3以上版本 (推荐使用) 依赖于Zookeeper的稳定性 可用于生产环境
Redis注册中心 Stable 支持基于客户端双写的集群方式,性能高 要求服务器时间同步,用于检查心跳过期脏数据 可用于生产环境
Multicast注册中心 Tested 去中心化,不需要安装注册中心 依赖于网络拓普和路由,跨机房有风险 小规模应用或开发测试环境
Simple注册中心 Tested Dogfooding,注册中心本身也是一个标准的RPC服务 没有集群支持,可能单点故障 试用

dubbo高级用法简介

JDK的SPI

SPI是英文Service Provider Interface的缩写。中文意思是服务提供商接口。满足某种服务标准的供应商提供的符合该标准的应用程序接口,SPI应该和该服务的API标准是兼容的,应用程序一般应该是基于API编写,除非是SPI中包含API中没有提供的功能而又必须使用。
SPI机制有点类似IOC的思想,就是将装配的控制权移到程序之外.

约定:
当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。 基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader

SPI具体实现示例:

//service
package com.github.wenj91.kotlin.spi.service

interface SpiService {
    fun spiMethod()
}

//service 1 impl
package com.github.wenj91.kotlin.spi.service.impl

import com.github.wenj91.kotlin.spi.service.SpiService

class Spi1ServiceImpl: SpiService{
    override fun spiMethod() {
        println("spi 1 method!")
    }
}

//service 2 impl
package com.github.wenj91.kotlin.spi.service.impl

import com.github.wenj91.kotlin.spi.service.SpiService

class Spi2ServiceImpl: SpiService{
    override fun spiMethod() {
        println("spi 2 method!")
    }
}

约定配置META-INF/services/com.github.wenj91.kotlin.spi.service.SpiService:

com.github.wenj91.kotlin.spi.service.impl.Spi1ServiceImpl
com.github.wenj91.kotlin.spi.service.impl.Spi2ServiceImpl

service加载调用:

//service loading
package com.github.wenj91.kotlin.spi

import com.github.wenj91.kotlin.spi.service.SpiService
import java.util.*

fun main(args: Array<String>){
    //加载服务
    var loader = ServiceLoader.load(SpiService::class.java)
    var it = loader.iterator()
    while (it.hasNext()){
        //获取服务并执行服务方法
        var s = it.next()
        println(s.spiMethod())
    }
}

dubbo扩展点加载与配置

Dubbo的扩展点加载从JDK标准的SPI (Service Provider Interface)扩展点发现机制加强而来。
Dubbo改进了JDK标准的SPI的以下问题:

JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。
增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。

约定:
在扩展类的jar包内,放置扩展点配置文件META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。

更多细节可以查阅dubbo官方手册Dubbo开发手册(中文)

dubbo之自定义filter

服务提供方和服务消费方调用过程拦截,Dubbo本身的大多功能均基于此扩展点实现,每次远程方法执行,该拦截都会被执行,请注意对性能的影响。

约定:

用户自定义 filter 默认在内置 filter 之后。
特殊值 default,表示缺省扩展点插入的位置。比如:filter=”xxx,default,yyy”,表示 xxx 在缺省 filter 之前,yyy 在缺省 filter 之后。
特殊符号 -,表示剔除。比如:filter=”-foo1”,剔除添加缺省扩展点 foo1。比如:filter=”-default”,剔除添加所有缺省扩展点。
provider 和 service 同时配置的 filter 时,累加所有 filter,而不是覆盖。比如: 和 ,则 xxx,yyy,aaa,bbb 均会生效。如果要覆盖,需配置:

扩展接口:
com.alibaba.dubbo.rpc.Filter

例子:
下面一个例子来说明filter的实现,例子实现的功能是在每次服务调用前打印hello world!

//TestFilter
package com.github.wenj91.kotlin.filter

import com.alibaba.dubbo.rpc.Filter
import com.alibaba.dubbo.rpc.Invocation
import com.alibaba.dubbo.rpc.Invoker
import com.alibaba.dubbo.rpc.Result

class TestFilter: Filter{
    override fun invoke(invoker: Invoker<*>?, invocation: Invocation?): Result {
        println("hello world!")
        return invoker!!.invoke(invocation)
    }
}

约定配置META-INF/dubbo/com.alibaba.dubbo.rpc.Filter:

testFilter=com.github.wenj91.kotlin.filter.TestFilter

consumer配置filter:

<!-- 新增filter属性 -->
<dubbo:reference id="demoService" filter="testFilter" url="dubbo://127.0.0.1:9090" interface="com.github.wenj91.kotlin.api.TestService" />

上述例子filter是通过手工配置指定激活的,如果想要自动激活filter,可以添加@Activate注解

对于可以被框架中自动激活加载扩展,此Annotation用于配置扩展被自动激活加载条件。
比如,过滤扩展,有多个实现,使用Activate Annotation的扩展可以根据条件被自动加载。{@link Activate#group()}生效的Group。具体的有哪些Group值由框架SPI给出。 {@link Activate#value()}在{@link com.alibaba.dubbo.common.URL}中Key集合中有,则生效。
底层框架SPI提供者通过{@link com.alibaba.dubbo.common.extension.ExtensionLoader}的{@link ExtensionLoader#getActivateExtension}方法获得条件的扩展。

如果需要自动激活可以将filter改造成下例子:

//TestFilter
package com.github.wenj91.kotlin.filter

import com.alibaba.dubbo.common.Constants
import com.alibaba.dubbo.common.extension.Activate
import com.alibaba.dubbo.rpc.Filter
import com.alibaba.dubbo.rpc.Invocation
import com.alibaba.dubbo.rpc.Invoker
import com.alibaba.dubbo.rpc.Result

//group配置默认消费者激活
@Activate(group = arrayOf(Constants.CONSUMER))
class TestFilter: Filter{
    override fun invoke(invoker: Invoker<*>?, invocation: Invocation?): Result {
        println("hello world!")
        return invoker!!.invoke(invocation)
    }
}

猜你喜欢

转载自blog.csdn.net/zlw420123/article/details/80344850