Dubbo进阶

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

Dubbo进阶

CoderJerry   2020-03-10 我要评论
# 注册中心zookeeper 什么是注册中心: 注册中心就是用来存储服务信息的地方,就像房屋中介一样; 为什么需要注册中心: 在前面的例子中我们使用了客户端与服务器直连的方式完成了服务的调用,在实际开发中这回带来一些问题,例如服务器地址变更了,或服务搭建了集群,客户端不知道服务的地址,此时注册中心就派上用场了,服务提供方发布服务后将服务信息放在zookeeper中,然后消费方从zookeeper获取服务器信息,进行调用,这样就使提供方和消费方解开了耦合,也让服务提供方可以更方便的搭建集群; 使用: 1.启动zookeeper,单机和集群都可以 2.在双方配置文件中指定注册中心的信息(内容相同) ```xml ``` 需要说明的是,注册中心不是必须使用zookeeper,dubbo还支持其他三种:Simple,Redis,Multicast,因其优秀的可用性,官方推荐使用zookeeper; # Dubbo的其他配置方式 ## API配置 简单的说就是不使用配置文件而是使用使用代码来完成配置,该方式主要用于测试环境或集成其他框架,不推荐用于生产环境; 服务提供者: ```java public class ProviderApplication { public static void main(String[] args) throws IOException { // xmlConfig(); apiConfig(); System.in.read();//阻塞主线程保持运行 } private static void apiConfig() { //应用配置 ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("my-service"); applicationConfig.setQosEnable(true); //注册中心 RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setProtocol("zookeeper"); registryConfig.setAddress("10.211.55.6:2181,10.211.55.7:2181"); //rpc协议 ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setName("dubbo"); protocolConfig.setPort(20880); //发布服务 ServiceConfig serviceConfig = new ServiceConfig(); serviceConfig.setApplication(applicationConfig); serviceConfig.setProtocol(protocolConfig); serviceConfig.setRegistry(registryConfig); serviceConfig.setInterface(HelloService.class); serviceConfig.setRef(new HelloServiceImpl()); serviceConfig.export(); } ``` 消费者: ```java public class ConsumerApplication { public static void main(String[] args) { // xmlConfig(); apiConfig(); } private static void apiConfig() { //应用配置 ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("my-consumer"); applicationConfig.setQosEnable(false); //注册中心 RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setProtocol("zookeeper"); registryConfig.setAddress("10.211.55.6:2181,10.211.55.7:2181"); //调用服务 ReferenceConfig serviceReferenceConfig = new ReferenceConfig(); serviceReferenceConfig.setApplication(applicationConfig); serviceReferenceConfig.setRegistry(registryConfig); serviceReferenceConfig.setInterface(HelloService.class); HelloService service = serviceReferenceConfig.get(); String tom = service.sayHello("tom"); System.out.println(tom); } ``` ## 注解配置 注解配置是使用较多的一种方式,可加快开发速度,让我们从繁琐的配置文件中解脱出来; Dubbo使用了Spring容器来管理bean,所以配置方式也大同小异,可使用Configuration将一个类作为配置类;在该类中提供必要的几个bean ### 服务提供者 配置类: ```java @Configuration @EnableDubbo(scanBasePackages = "com.yyh.service") public class ProviderConfiguration { //无论如何配置我们最终需要的还是那几个bean @Bean public ApplicationConfig applicationConfig(){ //应用配置 ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("my-service"); applicationConfig.setQosEnable(true); return applicationConfig; } @Bean public RegistryConfig registryConfig(){ RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setProtocol("zookeeper"); registryConfig.setAddress("10.211.55.6:2181,10.211.55.7:2181"); return registryConfig; } @Bean public ProtocolConfig protocolConfig(){ //rpc协议 ProtocolConfig protocolConfig = new ProtocolConfig(); protocolConfig.setName("dubbo"); protocolConfig.setPort(20880); return protocolConfig; } } ``` 服务实现类: ```java //注意该注解是Dubbo提供的 不要用错 @Service public class HelloServiceImpl implements HelloService { public String sayHello(String name) { return "hello: "+name; } } ``` 发布服务: ```java public static void main(String[] args) throws IOException { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class); context.start(); System.in.read();//阻塞主线程保持运行 } ``` ### 消费者 配置类: ```java @Configuration @EnableDubbo @ComponentScan("com.yyh.consumer") public class ConsumerConfiguration { @Bean public ApplicationConfig applicationConfig(){ //应用配置 ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName("my-consumer"); applicationConfig.setQosEnable(true); return applicationConfig; } @Bean public RegistryConfig registryConfig(){ RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setProtocol("zookeeper"); registryConfig.setAddress("10.211.55.6:2181,10.211.55.7:2181"); return registryConfig; } } ``` 消费者类: ```java @Component public class SayHelloConsumer { @Reference private HelloService helloService; public void sayHello(){ String jack = helloService.sayHello("jack"); System.out.println(jack); } } ``` 执行测试: ```java public class ConsumerApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class); SayHelloConsumer consumer = context.getBean(SayHelloConsumer.class); consumer.sayHello(); } ``` 可以发现消费者不需要指定ProtocolConfig,主要服务端固定端口即可; ## 使用properties配置 相比xml和api的方式,properties是体量是最轻的,在面对一些简单配置时可以采用properties ### 服务提供者 在resource下提供名为`dubbo.properties`的文件,内容如下: ```properties dubbo.application.name=my-service dubbo.application.owner=jerry dubbo.protocol.dubbo.port=1099 dubbo.registry.address=zookeeper://10.211.55.6:2181 ``` 配置类: ```java @Configuration @EnableDubbo(scanBasePackages = "com.yyh.service") @PropertySource("classpath:https://img.qb5200.com/download-x/dubbo.properties") public class AnnotationAndPropperties { } ``` 测试代码: ```java public class ProviderApplication { public static void main(String[] args) throws IOException { annotationAndPropConfig(); System.out.println("服务已启动 按任意键退出"); System.in.read();//阻塞 主线程 保持运行 } private static void annotationAndPropConfig() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationAndPropperties.class); context.start(); } } ``` ### 消费者 同样提供properties文件,但不需要指定protocol ```properties dubbo.application.name=my-service dubbo.application.qos.enable=false dubbo.application.owner=jerry dubbo.registry.address=zookeeper://10.211.55.6:2181 ``` 配置类: ```java @EnableDubbo @Configuration @ComponentScan("com.yyh.consumer") @PropertySource("classpath:https://img.qb5200.com/download-x/dubbo.properties") public class AnnotationAndPropConfiguration { } ``` 消费者类: ```java @Component public class SayHelloConsumer { @Reference private HelloService helloService; public void sayHello(){ String jack = helloService.sayHello("jack"); System.out.println(jack); } } ``` 测试类: ```java public class ConsumerApplication { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class); SayHelloConsumer consumer = context.getBean(SayHelloConsumer.class); consumer.sayHello(); } ``` 强调:注解使用时,扫描服务的实现类使用dubbo提供的EnableDubbo注解,而扫描其他bean用的是spring的ComponentScan注解; # 常用配置项 ## 1.启动时检查 默认情况下,dubbo在启动时会自动检查依赖(作为消费者)的服务是否可用,若服务不可用则直接抛出异常并阻止容器正常初始化,但在一些情况下我们会希望先启动程序,因为服务可能会在之后的时间里变为可用的; ### 启动检查注册中心 ```xml ``` ### 启动检查服务提供方(对所有提供者) ```xml ``` ### 启动检查服务提供方(对某个提供者) ```xml ``` properties文件写法: ```properties java -Ddubbo.reference.com.foo.BarService.check=false #强制修改所有reference的check java -Ddubbo.reference.check=false #当reference的check为空时有效 java -Ddubbo.consumer.check=false java -Ddubbo.registry.check=false ``` ## 2.集群容错 在后续的使用中我们可能会对某一个服务部署多个示例形成集群,随着项目的运行时间越来越常,一些服务节点可能会宕机或是由于网络原因暂时不可用,集群容错可指定在调用服务失败时dubbo要采取的行为; dubbo提供以下6种容错机制: | 策略名称 | 优点 | 缺点 | 主要应用场景 | | -------------- | -------------------------------- | -------------------------------------- | ---------------------------------------------------- | | failover(默认) | 对调用者屏蔽调用失败的信息 | 额外资源开销,资源浪费 | 通讯环境良好,并发不高的场景 | | failfast | 业务快速感知失败状态进行自主决策 | 产生较多报错的信息 | 非幂等性操作,需要快速感知失败的场景 | | failsafe | 即使失败了也不会影响核心流程 | 对于失败的信息不敏感,需要额外的监控 | 旁路系统,失败不影响核心流程正确性的场景 | | failback | 失败自动异步重试 | 重试任务可能堆积 | 对于实时性要求不高,且不需要返回值的一些异步操作 | | forking | 并行发起多个调用,降低失败概率 | 消耗额外的机器资源,需要确保操作幂等性 | 资源充足,且对于失败的容忍度较低,实时性要求高的场景 | | broadcast | 支持对所有的服务提供者进行操作 | 资源消耗很大 | 通知所有提供者更新缓存或日志等本地资源信息 | **幂等性:指的是每次调用都会产生相同的结果,即不会对数据进行写操作(增删改)** ### 配置方式: 容错配置分为两个粒度:接口级别,方法级别 #### 服务方配置: 服务方配置即将容错配置放在服务提供方,这样一来所有消费方就可以使用统一的容错机制,而不用每个消费方都配一遍; ```xml ``` #### 消费方配置: ```xml ``` ## 3.负载均衡 为了提高系统的可用性,能够承受更大的并发量,我们会将压力的服务部署为集群,但是如果每次请求都交给集群中的同一个节点,那这个几点很可能直接就宕了,所以合理的分配任务给集群中的每一台机器也是我们必须考虑的事情,好在dubbo已经提供相应的功能,我们只需简单的配置即可完成负载均衡; dubbo支持的任务分配方式: ### 随机random 顾名思义,从Provider列表中选择随机选择一个,但是我们可以为Provider指定权重,权重越大的被选中的几率越高,因此对于性能更好的机器应设置更大的权重,反之则反,如果不指定负载均衡,默认使用随机负载均衡; ### 轮询roundrobin 即依次调用所有Provider,每个Provider轮流处理请求,当然我们也可以指定权重,Provider收到的请求数量比约等于权重比; 性能差的机器可能会累积一堆请求,最终拖慢整个系统; ### 基于活跃数leastactive 每个Provider收到一个请求则将活跃数+1,每处理完成一个请求则活跃数-1,新的请求将会交给活跃数最少的Provider; 简单的说性能越好的机器将收到更多的请求,反之则反; ### 基于hash一致consistenthash 将根据Provider的 ip 或者其他的信息为Provider生成一个 hash,并将这个 hash 投射到 [0, 232 - 1] 的圆环上。当有请求时,则使用请求参数计算得出一个hash值。然后查找第一个大于或等于该 hash 值的缓存Provider,并将请求交予该Provider处理。如果当前Provider挂了,则在下一次请求时,为缓存项查找另一个大于其 hash 值的Provider即可。 具体算法参考:[去官网看看](http:/https://img.qb5200.com/download-x/dubbo.apache.org/zh-cnhttps://img.qb5200.com/download-x/docs/source_code_guide/loadbalance.html) ### 配置方法: 与容错配置一样,我们可以选择在服务方或是消费方进行设置; 服务方: ```xml ``` 消费方: ```xml ``` ## 直连 即跳过注册中新直接找服务提供方,必须提前明确服务提供方的地址,所以该方式一般仅用于开发调试; ```xml ``` ## 仅订阅 仅订阅指的是,不发布服务到注册中心,只从注册中心订阅依赖的服务; 使用场景:当我们要开发一个新的Provider,而这个Provider需要依赖其他Provider时,使用,其目的是避免正在开发的服务发布后被消费方调用,因为开发还未完成,可能造成意想不到的结果; 这就用到了仅订阅,再搭配直连即可完成开发调试; ```xml ``` ## 仅注册 仅注册指的是,发布自身服务到注册中心,但不从注册中心订阅依赖的服务; 使用场景: 自身需要对外提供服务,但是依赖的某个服务还在开发调试总,不能正常提供访问; ```xml ``` ## 多注册中心 对于一些大型系统,为了加快响应速度,可能会在不同地区进行部署,例如阿里云分布在7个不同城市,有的时候可能因为当地系统还未部署完成,但是仍然需要提供访问,这是就需要我们将相同的服务注册到多个不同的注册中心; 反过来,一些时候当前系统依赖的服务可能部署在不同的注册中心中,这就需要同时向多个不同的注册中心订阅服务; 配置方式也非常简单,添加额外registry即可; ### 发布到多个注册中心 ```xml ``` ### 从不同注册中心订阅 ```xml ```

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们