赞
踩
linux下安装,linux下安装zookeeper;
windows下安装Dubbo:
下载zookeeper-3.4.11.tar.gz
,解压后进入bin
目录,命令行运行zkServer.cmd
,会报错,提示找不到zoo.cfg配置文件,如下:
ERROR [main:QuorumPeerMain@88] - Invalid config, exiting abnormally
org.apache.zookeeper.server.quorum.QuorumPeerConfig$ConfigException: Error processing C:\Users\lenovo\Desktop\dubbo\zookeeper-3.4.11\bin\..\conf\zoo.cfg
at org.apache.zookeeper.server.quorum.QuorumPeerConfig.parse(QuorumPeerConfig.java:156)
at org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:104)
at org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:81)
Caused by: java.lang.IllegalArgumentException: C:\Users\lenovo\Desktop\dubbo\zookeeper-3.4.11\bin\..\conf\zoo.cfg file is missing
解决: 进入zookeeper的conf目录下,找到zoo_xample.cfg
文件,复制一份,重命名为zookeeper
即可。可以进入到这个配置文件瞄一眼,看到快照存储位置/tmp/zookeeper
,监听的端口2181(可用于客户端连接)
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/tmp/zookeeper
# the port at which the clients will connect
clientPort=2181
既然让不要用/tmp/zookeeper
来存储,那么我们就修改以下,比如修改到conf同级data文件
dataDir=../data
现在再来启动,看到下面的日志说明成功了
也可以使用客户端来访问一下,同样还是进入到bin目录,命令行执行./zkCli.cmd
,并且查看根节点有哪些目录
更多关于zookeeper的命令行操作,查看zookeeper数据结构和监听功能
tips:至于Dubbo则是不需要安装的,因为dubbo本身并不是一个服务软件。它其实就是一个jar包能够帮你的java程序连接到zookeeper,并利用zookeeper消费、提供服务。但是为了让用户更好的管理监控众多的dubbo服务,官方提供了一个可视化的监控程序,不过这个监控即使不装也不影响使用(下面装的是管理控制台)。
下载dubbo-admin,进入目录,打jar包mvn clean pagkage
,接下来在target目录下就能看到dubbo-admin-0.0.1-SNAPSHOT.jar
,该目录下命令行运行java -jar dubbo-admin-0.0.1-SNAPSHOT.jar
程序。启动起来后浏览器访问localhost:7001
,要求输入用户名和密码,均为root
。
这个时候服务数,应用数都为0。接下来编写服务提供者和服务消费者程序,之后再来看这个管理控制台界面。
需要注意的是在application.properties文件里面是配置了zookeeper的地址和监听的端口号。
服务提供方:提供商品查询的服务;
服务消费方:传入参数,调用服务提供方的服务。
通用服务:存放服务接口,服务模型,服务异常等均放在 API。
所以需要创建三个工程,使用idea的话,那么就先创建一个空的project,再创建三个moduel。
通用服务:service-interface
实体类:
public class Goods implements Serializable {
private Integer id;
private String name;
private Double price;
private String desc;
toString,全参和无参构造器,getter和setter方法
}
服务接口:
public interface GoodsService {
Goods getGoods(int id);
}
最后将该模块安装到本地仓库,mvn install
服务提供方: goods-service-provider
pom.xml引入依赖 分别为dubbo,zk, 通用服务
<?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.scu</groupId> <artifactId>goods-service-provider</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>com.scu</groupId> <artifactId>service-interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- 引入dubbo --> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.2</version> </dependency> <!-- 由于我们使用zookeeper作为注册中心,所以需要操作zookeeper dubbo 2.6以前的版本引入zkclient操作zookeeper dubbo 2.6及以后的版本引入curator操作zookeeper 下面两个zk客户端根据dubbo版本2选1即可 --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.12.0</version> </dependency> </dependencies> </project>
服务配置文件(Spring配置文件)applicatonContext
.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" 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="goods-service-provider"/> <!--使用zookeeper广播注册中心暴露服务地址--> <dubbo:registry address="zookeeper://localhost:2181"/> <!--使用dubbo协议在20880端口暴露服务--> <dubbo:protocol name="dubbo" port="20880"/> <!--声明需要暴露的服务的接口--> <dubbo:service interface="com.scu.service.GoodsService" ref="goodsService"/> <bean id="goodsService" class="com.scu.dubbo.service.GoodsServiceImpl"/> </beans>
服务实现类:
public class GoodsServiceImpl implements GoodsService {
@Override
public Goods getGoods(int id) {
System.out.println(id);
return new Goods(id,"mi 9 pro",3600.0,"小米首款5G手机");
}
}
启动类:读取配置文件,注册服务
public class DubboMain {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
System.in.read();
}
}
目录结构如下
运行main方法启动服务(先得把zookeeper服务启动起来),查看dubbo admin
服务消费者:goods-service-consumer
其中pom.xml同服务提供者
applicationContext.xml内容:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
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="goods-service-provider"/>
<!--使用zookeeper广播注册中心暴露服务地址-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!--生成远程代理服务,可以和本地bean一样使用GoodsService-->
<dubbo:reference id="goodsService" interface="com.scu.service.GoodsService" />
</beans>
主程序,启动Spring容器调用服务
public class ConsumerMain {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
GoodsService goodsService = applicationContext.getBean("goodsService",GoodsService.class);
Goods goods = goodsService.getGoods(1);
System.out.println("id为1的商品信息为:" + goods);
System.in.read();
}
}
目录结构如下:
启动服务,查看两个控制台的输出分别为:
1
id为1的商品信息为:Goods{id=1, name='mi 9 pro', price=3600.0, desc='小米首款5G手机'}
到这一个简单的使用dubbo的RPC调用demo就完成了。
tips:maven如果使用3.6.2会报绑定错误。换回3.6.1
三个点:
1.导包不同
2.配置文件使用application.yml(或者application.properties),照着applicationContext.xml填即可
3.添加注解的时候注意导入的是哪个类(@Service,@Reference)。
服务提供者依赖如下
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <dependencies> <dependency> <groupId>com.scu</groupId> <artifactId>service-interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency> </dependencies>
编写配置文件application.yml:
dubbo:
application:
name: goods-service-provider
registry:
protocol: zookeeper
address: localhost:2181
protocol:
name: dubbo
port: 20880
application.name就是服务名,不能跟别的dubbo提供端重复
registry.protocol 是指定注册中心协议
registry.address 是注册中心的地址加端口号
protocol.name 是分布式固定是dubbo,不要改。
base-package 注解方式要扫描的包,可以在启动类上使用注解代替
服务实现:
package com.scu.dubbo.service;
import com.alibaba.dubbo.config.annotation.Service;
import com.scu.bean.Goods;
import com.scu.service.GoodsService;
@Service // 注意导入的是alibaba的Service而不是Spring的
public class GoodsServiceImpl implements GoodsService {
@Override
public Goods getGoods(int id) {
System.out.println(id);
return new Goods(id,"mi 9 pro",3600.0,"小米首款5G手机");
}
}
启动类:
package com.scu.dubbo;
import com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@DubboComponentScan(basePackages = "com.scu.dubbo.service") // 扫描的包
public class ServiceStarter {
public static void main(String[] args) {
SpringApplication.run(ServiceStarter.class,args);
}
}
再写消费者
导入依赖:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <dependencies> <dependency> <groupId>com.scu</groupId> <artifactId>service-interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency> </dependencies>
编写配置文件application.yml:
server:
port: 8081 #使用了81端口 前面用了80端口
dubbo:
application:
name: goods-service-consumer
registry:
protocol: zookeeper
address: localhost:2181
controller类:
package com.scu.dubbo.controller; import com.alibaba.dubbo.config.annotation.Reference; import com.scu.bean.Goods; import com.scu.service.GoodsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class GoodsController { @Reference private GoodsService goodsService; @GetMapping("/goods") public Goods getGoodsById(int id){ System.out.println(123); return goodsService.getGoods(id); } }
启动类:
@SpringBootApplication
public class ConsumerStarter {
public static void main(String[] args) {
SpringApplication.run(ConsumerStarter.class,args);
}
}
整个的目录结构如下
先启动生产者,再启动消费者
页面访问 http://localhost:8081/goods?id=1
,返回如下结果说明调用成功。
{"id":1,"name":"mi 9 pro","price":3600.0,"desc":"小米首款5G手机"}
之前的demo要求先启动服务提供方,后启动服务消费方。如果先启动服务消费方,那么会报错,找不到服务提供方。这是因为启动的时候会检查容器,找不到服务提供方,抛出异常服务就没有启动起来了。必要的时候我们需要关闭这个启动时检查。使用配置check=false
即可。
public class ConsumerMain {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// GoodsService goodsService = applicationContext.getBean("goodsService", GoodsService.class);
// Goods goods = goodsService.getGoods(10086);
// System.out.println(goods);
System.in.read();
}
}
<dubbo:reference id="goodsService" interface="com.scu.service.GoodsService" check="false"/>
或者使用全局配置
<dubbo:consumer check="false" timeout="4000"/>
默认情况下,服务消费方调用服务提供方提供的服务,如果1000ms内没有收到响应,抛出超时异常。比如我在服务消费方,设置服务超时时间为3秒,服务提供方提供的服务在4秒后才会返回结果。
服务消费方设置超时时间:timeout=3000
<dubbo:reference id="goodsService" interface="com.scu.service.GoodsService" check="false"
timeout="3000"/>
服务提供方: 4秒后返回结果
public class GoodsServiceImpl implements GoodsService {
@Override
public Goods getGoods(int id) {
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("服务被调用:" + id);
return new Goods(id,"xiaomi 9 pro",3699.0,"小米的第一款5G手机");
}
}
所以最后会抛出超时异常
Caused by: com.alibaba.dubbo.remoting.TimeoutException: Waiting server-side response timeout.
此外,超时设置还可以加到方法上,全局配置上。优先级是方法>接口>全局配置
<dubbo:reference id="goodsService" interface="com.scu.service.GoodsService" check="false"
timeout="3000">
<dubbo:method name="getGoods" timeout="1000"/>
</dubbo:reference>
<!--消费者全局配置:所有的服务都不检查-->
<dubbo:consumer check="false" timeout="4000"/>
甚至可以配置到服务提供的那一方,而且如果级别是一样的话那么就是消费者优先。但是,如果服务提供者的级别更高,那么仍然是服务提供者邮箱。具体的顺序,参考官网。
由于网络问题可能导致超时,所以有时需要设置超时重试,使用retries=2
即可,2表示重试的次数,不包括最初的调用,所以最终会调用三次,但是只要有一次返回则不会继续重新调用。
服务消费方,设置重试次数 2次。
<dubbo:reference id="goodsService" interface="com.scu.service.GoodsService" check="false"
timeout="3000" retries="2">
<dubbo:method name="getGoods" timeout="1000"/>
</dubbo:reference>
服务提供方代码如下: 每次调用会答应id,同时休眠2秒,调用方超时时间设置为1秒,所以肯定会超时。
public class GoodsServiceImpl implements GoodsService {
@Override
public Goods getGoods(int id) {
System.out.println("服务被调用:" + id);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Goods(id,"xiaomi 9 pro",3699.0,"小米的第一款5G手机");
}
}
想起的服务提供方再启动服务消费方,服务方控制台输出三次:服务被调用:10086
服务被调用:10086
服务被调用:10086
服务被调用:10086
此外,如果该服务在多台机器上都有,比如三台机器都有该服务,那么默认每次重试会调用不同的服务。为了做个试验,启动三次服务提供方(三个进程当作三台机器),每次都需要修改服务暴露的端口(20880,20881,20882),同时修改getGoods
方法,比如启动三次,3次只修改如下
System.out.println("服务1被调用:" + id);
System.out.println("服务2被调用:" + id);
System.out.println("服务3被调用:" + id);
public class ProviderMain { @Test // 端口20880 System.out.println("服务1被调用:" + id); public void test1() throws IOException { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); System.in.read(); } @Test // 端口20881 System.out.println("服务2被调用:" + id); public void test2() throws IOException { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); System.in.read(); } @Test // 端口20882 System.out.println("服务3被调用:" + id); public void test3() throws IOException { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); System.in.read(); } }
启动服务消费方后,对应的test1-test3控制台分别打印
服务1被调用:10086
服务2被调用:10086
服务3被调用:10086
tips: 如果是修改/删除/查询等操作需要设计成幂等操作(多次相同的操作不会带来额外的影响,比如多次相同的查询得到的会是同一个结果,多次相同的删除操作,第一次成功,后面虽然不能删除带上不会带来额外的影响),同时设置重试次数;新增操作则设计为非幂等操作,并且不要加重试次数。
有时候会遇到接口的升级,那么就有新版本和旧版本,我们先让服务消费方调用部分的新版本,等新版本稳定会再全部换成新版本。比如GoodsService接口,现在又了新版本实现如下
旧版本:
public class GoodsServiceImpl implements GoodsService {
@Override
public Goods getGoods(int id) {
System.out.println("服务1被调用:" + id);// 新旧版本区别在这
return new Goods(id,"xiaomi 9 pro",3699.0,"小米的第一款5G手机");
}
}
新版本:
public class GoodsServiceImpl2 implements GoodsService {
@Override
public Goods getGoods(int id) {
System.out.println("新的服务被调用:" + id);
return new Goods(id,"xiaomi 9 pro",3699.0,"小米的第一款5G手机");
}
}
提供方修改的配置文件
<dubbo:service interface="com.scu.service.GoodsService" ref="goodsService" version="0.0.1"/>
<bean id="goodsService" class="com.scu.dubbo.service.GoodsServiceImpl"/>
<dubbo:service interface="com.scu.service.GoodsService" ref="goodsService2" version="0.0.2"/>
<bean id="goodsService2" class="com.scu.dubbo.service.GoodsServiceImpl2"/>
消费方的配置文件: 使用version="0.0.1"
使用旧版本
<dubbo:reference id="goodsService" interface="com.scu.service.GoodsService" check="false"
timeout="3000" retries="2" version="0.0.1">
<dubbo:method name="getGoods" timeout="1000"/>
</dubbo:reference>
输出为: 服务1被调用:10086
假设新服务稳定了,此时修改调用方配置文件,设置version="0.0.2"
,
那么这次输出的就是:新的服务被调用:10086
现象:注册中心Zookeeper宕机后,还可以调用Dubbo暴露的服务,这是因为注册中心虽然全部宕机,但是服务提供者和服务消费者可以通过本地缓存进行通讯,即服务消费者,如果调用过服务,那么会本地记录服务地址,但Zookeeper宕机后,可以绕过Zookeeper直接和Dubbo提供的服务连接。此外,还有种方案,即Dubbo直连可以使用url=127.0.0.1:20880
标签(如果是使用@Reference
注解就是``@Reference(url=xxx)
)与Dubbo直接进行直连,即告诉消费者服务提供者地址。
假设将GoodsService
服务部署到了3台机器,那么则需要使用负载均衡策略来决定每次服务调用者调用的是哪台机器。
下面的介绍来自Dubbo官网,因为确实没啥好说的了。
<dubbo:service interface="..." loadbalance="roundrobin" />
<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>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。