文章目录
- Maven_服务方
- Maven_服务消费方
- 测试通信
- 使用注册中心自动找服务
- 设置超时时间
- 重试次数
- 单独设置某个方法不可重试
- 处理多版本的问题
- 本地存根策略
- 负载均衡策略
- Dubbo高可用
- 服务降级
- 服务降级实现方式
Maven_服务方
-
pom文件,注意依赖的版本。
<properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <spring.version>5.0.6.RELEASE</spring.version> </properties> <packaging>war</packaging> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <!--dubbo --> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.5.7</version> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>com.github.sgroschupf</groupId> <artifactId>zkclient</artifactId> <version>0.1</version> </dependency> <dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>3.11.0.GA</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.tomcat.maven </groupId> <artifactId>tomcat7-maven-plugin</artifactId> <configuration> <port>8002</port> <path>/</path> </configuration> <executions> <execution> <!-- 打包完成后,运行服务 --> <phase>package</phase> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
-
服务方提供接口
public interface HelloService { String sayHello(String name); }
-
服务方接口实现类 :一定要特别注意这个Service注解,这是dubbo的Service注解
@com.alibaba.dubbo.config.annotation.Service public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello," + name + "!!!"; } }
-
服务方的配置文件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"> <!--1.服务提供方在zookeeper中的“别名”--> <dubbo:application name="dubbo-server"/> <!--2.注册中心的地址--> <dubbo:registry address="zookeeper://192.168.88.128:2181"/> <!--3.扫描类(将什么包下的类作为服务提供类)--> <dubbo:annotation package="service.impl"/> </beans>
-
服务方的web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring.xml</param-value> </context-param> </web-app>
Maven_服务消费方
-
消费方的pom文件和服务方的一致,但是需要修改tomcat的端口为8002
-
消费方的Controller
@RestController public class HelloAction { // 注意这个注解,这也是 dubbo提供的注解 @com.alibaba.dubbo.config.annotation.Reference private HelloService helloService; @RequestMapping("hello") @ResponseBody public String hello(String name) { return helloService.sayHello(name); } }
-
消费方的接口:controller中要依赖HelloService,所以我们创建一个接口,但是消费方不需要实现这个接口,因为让服务方会提供这个接口的服务!
public interface HelloService { String sayHello(String name); }
-
消费方的web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
测试通信
-
先启动服务方,再启动消费方,访问
http://localhost:8002/hello?name=james
,注意看服务方提供的接口:@com.alibaba.dubbo.config.annotation.Service public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello," + name + "!!!"; } }
使用注册中心自动找服务
-
分别修改dubbo-server和dubbo-consumer的spring.xml,
<!-- 让监控 去注册中心 自动找服务 --> <dubbo:monitor protocol="registry"/>
设置超时时间
-
由于网络或服务端不可靠,会导致调用过程中出现不确定的阻塞状态(超时)
-
为了避免超时导致客户端资源(线程)挂起耗尽,必须设置超时时间
-
在服务提供方的spring中添加如下配置:
<!--设置超时时间为2秒,默认为1秒--> <dubbo:provider timeout="2000"/>
-
模拟的网络延迟进行测试:
@com.alibaba.dubbo.config.annotation.Service public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { try { Thread.sleep(3000); }catch (Exception e){ e.printStackTrace(); } return "Hello," + name + "!!!"; } }
-
因为超时设置2秒,而模拟的网络延迟有3秒,超出时限,报错。
PS:配置原则:
- dubbo推荐在Provider上尽量多配置Consumer端属性:
- 作服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间,合理的重试次数,等等
- 在Provider配置后,Consumer不配置则会使用Provider的配置值,即Provider配置可以作消费者的缺省值。
重试次数
-
当出现失败,自动切换并重试其它服务器,dubbo重试的缺省值是2次,可以自行设置
-
在provider提供方配置:
<!-- 消费方连接第1次不算,再来重试3次,总共重试4次 --> <dubbo:provider timeout="2000" retries="3"/>
@com.alibaba.dubbo.config.annotation.Service public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { System.out.println("=============被调用 1 次==============="); try { Thread.sleep(3000); }catch (Exception e){ e.printStackTrace(); } return "Hello," + name + "!!!"; } }
-
不是所有的方法都适合设置重试次数
- 幂等方法:适合(当参数一样,无论执行多少次,结果是一样的,例如:查询,修改)
- 非幂等方法:不适合(当参数一样,执行结果不一样,例如:删除,添加)
单独设置某个方法不可重试
-
提供方接口添加sayNo()方法并实现
public interface HelloService { String sayHello( String name ); String sayNo(); }
@com.alibaba.dubbo.config.annotation.Service public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { System.out.println("=============被调用 1 次==============="); try { Thread.sleep(3000); }catch (Exception e){ e.printStackTrace(); } return "Hello," + name + "!!!"; } public String sayNo() { System.out.println("-------no被调用一次-------"); return "no!"; } }
-
消费方接口添加sayNo()方法声明
public interface HelloService { String sayHello( String name ); String sayNo(); }
-
消费方controller
@RestController public class HelloAction { @com.alibaba.dubbo.config.annotation.Reference private HelloService helloService; @RequestMapping("hello") @ResponseBody public String hello(String name) { return helloService.sayHello(name); } @GetMapping("no") @ResponseBody public String no() { return helloService.sayNo(); } }
-
消费方配置方法重试次数
<dubbo:reference interface="service.HelloService" id="helloService"> <dubbo:method name="sayHello" retries="3"/> <dubbo:method name="sayNo" retries="0"/> <!-- 不重试 --> </dubbo:reference>
处理多版本的问题
-
一个接口,多个(版本的)实现类,可以使用定义版本的方式引入
-
为HelloService接口定义两个实现类,提供者修改配置:
<dubbo:service interface="service.HelloService" class="service.impl.HelloServiceImpl01" version="1.0.0"/> <dubbo:service interface="service.HelloService" class="service.impl.HelloServiceImpl02" version="2.0.0"/>
-
消费者就可以根据version的版本,选择具体的服务版本
<dubbo:reference loadbalance="roundrobin" interface="service.HelloService" id="helloService" version="2.0.0">
<dubbo:method name="sayHello" retries="3"/>
<dubbo:method name="sayNo" retries="0"/> <!-- 不重试 -->
</dubbo:reference>
- 当消费者的版本修改为
version="*"
,那么就会随机调用服务提供者的版本
本地存根策略
-
目前的通信案例所有的操作全都是 消费者发起,由服务提供者执行,这样会让提供者承担所有压力,一些功能消费者完全能够胜,把合法的参数再发送给提供者执行,效率高了,提供者也没那么累了,这就是本地存根
-
简单来讲,先在消费者处理一些业务逻辑,再调用提供者的过程,就是“本地存根”
-
本地存根”代码实现肯定在 消费者,创建一个HelloServiceStub类并且实现HelloService接口,并且必须使用构造方法的方式注入
public class HelloServiceStub implements HelloService { private HelloService helloService; // 注入HelloService public HelloServiceStub(HelloService helloService) { this.helloService = helloService; } public String sayHello(String name) { System.out.println("本地存根数据验证。。。"); if(!StringUtils.isEmpty(name)){ return helloService.sayHello(name); } return "i am sorry!"; } public String sayNo() { return helloService.sayNo(); } }
-
修改消费者配置:
<dubbo:reference interface="service.HelloService" id="helloService" version="1.0.0" stub="service.impl.HelloServiceStub"> <dubbo:method name="sayHello" retries="3"/> <dubbo:method name="sayNo" retries="0"/> </dubbo:reference>
负载均衡策略
- 负载均衡(Load Balance), 其实就是将请求分摊到多个操作单元上进行执行,从而共同完成工作任务。
- 简单的说,好多台服务器,不能总是让一台服务器干活,应该“雨露均沾”
- dubbo一共提供4种策略,缺省为 random 随机分配调用
可以使用管理端修改权重
Dubbo高可用
- zookeeper注册中心宕机,还可以消费dubbo暴露的服务
- 监控中心宕掉不影响使用,只是丢失部分采样数据
- 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
- 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
- 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
- 服务提供者无状态,任意一台宕掉后,不影响使用
- 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
服务降级
- 服务降级,就是根据实际的情况和流量,对一些服务有策略的停止或换种简单的方式处理,从而释放服务器的资源来保证核心业务的正常运行
为什么要服务降级?
- 为什么要使用服务降级,这是防止分布式服务发生雪崩效应。
- 雪崩效应就是蝴蝶效应,当一个请求发生超时,一直等待着服务响应,那么在高并发情况下,很多请求都是因为这样一直等着响应,直到服务资源耗尽产生宕机,而宕机之后会导致分布式其他服务调用该宕机的服务也会出现资源耗尽宕机,这样下去将导致整个分布式服务都瘫痪,这就是雪崩。
服务降级实现方式
- 在 管理控制台配置服务降级:屏蔽和容错
- 屏蔽:mock=force:return+null 表示消费方对该服务的方法调用都 直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
- 容错:mock=fail:return+null 表示消费方对该服务的方法调用在 失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。