文章目录
1.确定接口
新建一个springboot项目,什么模块都不用选,然后里面新建entity实体类和service接口。
如下图:
User.java如下,这里需要注意的是要实现序列化接口。
private String email;
private String nickName;
private String password;
private String regTime;
private String userName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email == null ? null : email.trim();
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName == null ? null : nickName.trim();
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password == null ? null : password.trim();
}
public String getRegTime() {
return regTime;
}
public void setRegTime(String regTime) {
this.regTime = regTime == null ? null : regTime.trim();
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName == null ? null : userName.trim();
}
}
UserService.java如下:
public interface UserService {int deleteByPrimaryKey(Long id);
int insert(User record);
User selectByPrimaryKey(Long id);
List<User> selectAll();
int updateByPrimaryKey(User record);
Map<String, Object> addMethod();
}
2.创建提供者
因为要实现负载均衡,这里就需要创建两个提供者。这里使用的是dubbo传统的配置方式:xml。现在dubbo已经支持注解方式,不过个人更偏向于xml配置。
创建两个Springboot项目:Provider和ProviderB。Provider和ProviderB基本配置和代码都一样,除了一个协议端口不一样。这里就记录Provider的配置方式。
2.1 pom配置
因为是用xml方式,在springboot项目里就引入最干净的dubbo依赖库。如果你是用注解的方式,需要引入springboot-dubbo的依赖库,该库在dubbo基础上做了springboot适配。
org.springframework.boot spring-boot-starter <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.4</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.12</version>
</dependency>
<!--<dependency>-->
<!--<groupId>com.github.sgroschupf</groupId>-->
<!--<artifactId>zkclient</artifactId>-->
<!--<version>0.1</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
</dependencies>
这里不需要使用引入web依赖,因为只提供接口服务。然后需要引入mybatis、mysql模块(这两个在初始化springboot时勾选引入)。再加上dubbo,排除掉它自带的sping库,防止冲突。引入zookeeper库,zookeeper客户端依赖库curator-recipes(排除冲突,去除zookeeper库),再引入druid数据库连接池。
2.2dubbo配置文件
通过xml方式配置,在resources下新建一个dubbo文件夹,然后创建两个文件:dubbo.properties和dubbo-provider.xml,如下图:
dubbo.properties写一些配置参数,
dubbo.application.name=dubbo-provider
dubbo.registry.protocol=zookeeper
dubbo.registry.address1=172.16.30.100:2181
dubbo.registry.address2=172.16.30.101:2181
dubbo.registry.address3=172.16.30.101:2181
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
- 1
- 2
- 3
- 4
- 5
- 6
- 7
dubbo-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.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 提供方应用信息,用于计算依赖关系 --> <dubbo:application name="${dubbo.application.name}"/> <!-- 注册中心暴露服务地址 --> <dubbo:registry protocol="${dubbo.registry.protocol}" address="${dubbo.registry.address1},${dubbo.registry.address2},${dubbo.registry.address3}"/> <!-- 暴露服务 --> <dubbo:protocol name="${dubbo.protocol.name}" port="${dubbo.protocol.port}"/> <dubbo:service interface="com.steven.xmldubbo.service.UserService" ref="userServiceImpl" retries="0" timeout="6000" loadbalance="random"/>
</beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
这里需要配置一个参数loadbalance="random"
,用来设定负载均衡策略。这里设置是Random,随机的。
还有其他三种策略,参考官网:http://dubbo.apache.org/zh-cn/docs/user/demos/loadbalance.html
2.3 application.properties
这里只要配置数据库访问相关的参数就行。这里也贴一下吧
spring.datasource.name=datasource
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#监控统计拦截的filters
spring.datasource.druid.filters=stat
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=true&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
#配置初始化大小/最小/最大
spring.datasource.druid.initial-size=1
spring.datasource.druid.min-idle=1
spring.datasource.druid.max-active=20
#获取连接等待超时时间
spring.datasource.druid.max-wait=60000
#间隔多久进行一次检测,检测需要关闭的空闲连接
spring.datasource.druid.time-between-eviction-runs-millis=60000
#一个连接在池中最小生存的时间
spring.datasource.druid.min-evictable-idle-time-millis=300000
spring.datasource.druid.validation-query=SELECT 1
spring.datasource.druid.test-while-idle=true
spring.datasource.druid.test-on-borrow=false
spring.datasource.druid.test-on-return=false
#打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
spring.datasource.druid.pool-prepared-statements=false
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20
#映射方式 配置下面这个就行了
#pojo类所在包路径
mybatis.type-aliases-package=com.steven.xmldubbo.entity
#xml方式
#xml文件所在路径
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
mybatis.config-location=classpath:mybatis/mybatis-config.xml
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
2.4 mybatis相关
2.4.1 配置UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.steven.xmldubbo.dao.UserMapper">
<resultMap id="BaseResultMap" type="com.steven.xmldubbo.entity.User">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="email" jdbcType="VARCHAR" property="email" />
<result column="nick_name" jdbcType="VARCHAR" property="nickName" />
<result column="password" jdbcType="VARCHAR" property="password" />
<result column="reg_time" jdbcType="VARCHAR" property="regTime" />
<result column="user_name" jdbcType="VARCHAR" property="userName" />
</resultMap>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
delete from user
where id = #{id,jdbcType=BIGINT}
</delete>
<insert id="insert" parameterType="com.steven.xmldubbo.entity.User">
insert into user (id, email, nick_name,
password, reg_time, user_name
)
values (#{id,jdbcType=BIGINT}, #{email,jdbcType=VARCHAR}, #{nickName,jdbcType=VARCHAR},
#{password,jdbcType=VARCHAR}, #{regTime,jdbcType=VARCHAR}, #{userName,jdbcType=VARCHAR}
)
</insert>
<update id="updateByPrimaryKey" parameterType="com.steven.xmldubbo.entity.User">
update user
set email = #{email,jdbcType=VARCHAR},
nick_name = #{nickName,jdbcType=VARCHAR},
password = #{password,jdbcType=VARCHAR},
reg_time = #{regTime,jdbcType=VARCHAR},
user_name = #{userName,jdbcType=VARCHAR}
where id = #{id,jdbcType=BIGINT}
</update>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
select id, email, nick_name, password, reg_time, user_name
from user
where id = #{id,jdbcType=BIGINT}
</select>
<select id="selectAll" resultMap="BaseResultMap">
select id, email, nick_name, password, reg_time, user_name
from user
</select>
</mapper>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
2.4.2 配置UserMapper接口
在项目包下创建dao文件夹,然后创建UserMapper.java
@Repository public interface UserMapper { int deleteByPrimaryKey(Long id);
int insert(User record); User selectByPrimaryKey(Long id); List<User> selectAll(); int updateByPrimaryKey(User record);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
2.4.3 实现UserService接口
包下创建service文件夹,再创建impl文件夹,然后创建UserServiceImpl,实现UserService接口。
这里需要配置项目Provider依赖Interface,然后就找得到Interface里的UserService了
@Service public class UserServiceImpl implements UserService{
private Logger logger = LoggerFactory.getLogger(UserServiceImpl.class); @Autowired private UserMapper userMapper; @Override public int deleteByPrimaryKey(Long id) { return userMapper.deleteByPrimaryKey(id); } @Override public int insert(User record) { return userMapper.insert(record); } /** * 增加调用方基本信息 * @param id * @return */ @Override public User selectByPrimaryKey(Long id) { // 本端是否为提供端,这里会返回true boolean isProviderSide = RpcContext.getContext().isProviderSide(); // 获取调用方IP地址 String clientIp = RpcContext.getContext().getRemoteHost(); // 获取当前服务配置信息,所有配置信息都将转换为URL的参数 String url = RpcContext.getContext().getUrl().toFullString(); logger.info("{} {} {}", isProviderSide, clientIp, url); return userMapper.selectByPrimaryKey(id); } @Override public List<User> selectAll() { return userMapper.selectAll(); } @Override public int updateByPrimaryKey(User record) { return userMapper.updateByPrimaryKey(record); } /** * 接口新增一个方法测试 * @return */ @Override public Map<String, Object> addMethod() { Map<String,Object> result = Maps.newHashMap(); result.put("attachment", true); String count = RpcContext.getContext().getAttachment("count"); result.put("count", count); return result; }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
2.5 让dubbo配置生效
在项目包下新建一个config文件夹,创建DubboConfig.java
/**
* com.steven.xmldubbo.config
* Created by ZhiLiSteven
* Date 2018/10/30
*/
@Configuration
@PropertySource("classpath:dubbo/dubbo.properties")
@ImportResource({"classpath:dubbo/*.xml"})
public class DubboConfig {
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
2.6 实现Springboot application
因为Provider不是web项目,所以启动方式要调整。并且为防止它启动main之后,因为不是以web方式启动,直接退出,需要增加一个阻塞:
@SpringBootApplication @MapperScan("com.steven.xmldubbo.dao") public class XmldubboApplication implements CommandLineRunner{
private Logger logger = LoggerFactory.getLogger(XmldubboApplication.class); @Bean public CountDownLatch countDownLatch() { return new CountDownLatch(1); } public static void main(String[] args) throws InterruptedException { ApplicationContext ctx = new SpringApplicationBuilder(XmldubboApplication.class) .web(WebApplicationType.NONE)//非web项目 .run(args);
// SpringApplication.run(XmldubboApplication.class, args);
CountDownLatch countDownLatch = ctx.getBean(CountDownLatch.class);
countDownLatch.await();
}
@Override
public void run(String... args) throws Exception {
logger.info("Dubbo Provider start now.................");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
至此,提供方搭建完成。
然后再新建一个ProviderB,其他都一样,唯一区别就是项目名不一样和dubbo.properties一个参数不一样。
dubbo.protocol.port=20881
这里ProviderB改成20881
然后,在接口实现的一个方法里增加一个参数
/**
* 接口新增一个方法测试
* @return
*/
@Override
public Map<String, Object> addMethod() {
Map<String,Object> result = Maps.newHashMap();
result.put("attachment", true);
String count = RpcContext.getContext().getAttachment("count");
result.put("count", count);
//区别Provider和ProviderB,这里增加一个参数
result.put("more", "providerB");
return result;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
3.创建消费者
新建spingboot项目Consume,初始化时,勾选web就可以了。
3.1 pom依赖
然后项目依赖dubbo库,zookeeper库,curator-recipes这几个,和Provider一样。
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.4</version> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.12</version> </dependency> <!--<dependency>--> <!--<groupId>com.github.sgroschupf</groupId>--> <!--<artifactId>zkclient</artifactId>--> <!--<version>0.1</version>--> <!--</dependency>--> <!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>4.0.0</version> <exclusions> <exclusion> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
项目结构如下图:
3.2 dubbo配置
配置和Provider差不多,在resources下新建dubbo文件夹,然后新建dubbo.properties和dubbo-consumer.xml
3.2.1 dubbo.properties
dubbo.application.name=dubbo-consumer
dubbo.registry.protocol=zookeeper
dubbo.registry.address1=172.16.30.100:2181
dubbo.registry.address2=172.16.30.101:2181
dubbo.registry.address3=172.16.30.102:2181
- 1
- 2
- 3
- 4
- 5
3.2.2 dubbo-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.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 消费方应用信息,用于计算依赖关系 --> <dubbo:application name="${dubbo.application.name}"/> <!-- 注册中心暴露服务地址 --> <!-- <dubbo:registry address="multicast://224.5.6.7:1234" /> --> <dubbo:registry protocol="${dubbo.registry.protocol}" address="${dubbo.registry.address1},${dubbo.registry.address2},${dubbo.registry.address3}"/> <dubbo:reference interface="com.steven.xmldubbo.service.UserService" id="userService" retries="0" timeout="6000"/>
</beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
3.2.3 dubbo配置生效
项目包下创建config文件夹,创建DubboConfig.java
/**
* com.steven.xmldubbo.config
* Created by ZhiLiSteven
* Date 2018/10/30
*/
@Configuration
@PropertySource("classpath:dubbo/dubbo.properties")
@ImportResource({"classpath:dubbo/*.xml"})
public class DubboConfig {
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
3.3 web创建
创建一个UserController.java
/** * com.steven.xmldubbo.web * Created by ZhiLiSteven * Date 2018/10/30 */ @RestController public class UserController {
@Autowired private UserService userService; @GetMapping("user") public Map<String, Object> user(String id) { Map<String, Object> result = new HashMap<>(); try { result.put("user", userService.selectByPrimaryKey(Long.parseLong(id))); result.put("type", "200"); result.put("content", "success"); } catch (Exception e) { e.printStackTrace(); result.put("type", "500"); result.put("content", e.getMessage()); } return result; } @GetMapping("allUsers") public Map<String, Object> allUsers() { Map<String, Object> result = new HashMap<>(); try { result.put("users", userService.selectAll()); result.put("type", "200"); result.put("content", "success"); } catch (Exception e) { e.printStackTrace(); result.put("type", "500"); result.put("content", e.getMessage()); } return result; } /** * 回声测试 * @return */ @GetMapping("echoTest") public Map<String, Object> echoTest() { // 所有服务自动实现 EchoService 接口 EchoService echoService = (EchoService) userService; // 回声测试可用性 String status = (String) echoService.$echo("OK"); Map<String, Object> result = Maps.newHashMap(); result.put("status", status); return result; } /** * 获取上下文信息 */ @GetMapping("rpcContext") public Map<String, Object> rpcContext() { // 要先请求一次 userService.selectAll(); // 本端是否为消费端 boolean isConsumerSide = RpcContext.getContext().isConsumerSide(); // 获取最后一次调用的提供方IP地址 String serverIp = RpcContext.getContext().getRemoteHost(); // 获取当前服务配置信息,所有配置信息都将转换为URL的参数 String application = RpcContext.getContext().getUrl().getParameter("application"); Map<String, Object> result = Maps.newHashMap(); result.put("isConsumerSide", isConsumerSide); result.put("serverIp", serverIp); result.put("application", application); return result; } private int count = 0; /** * 隐式参数 * */ @GetMapping("attachment") public Map<String, Object> attachment() { if (++count%2 != 0) { // 隐式传参,后面的远程调用都会隐式将这些参数发送到服务器端,类似cookie,用于框架集成,不建议常规业务使用 RpcContext.getContext().setAttachment("count", count+""); } return userService.addMethod(); }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
然后application.properties 设定一个端口
server.port=8882
至此,消费方也创建好了。
4.启动测试
启动两个提供者Provider,ProviderB项目,然后启动Consume项目
调用http://localhost:8882/allUsers
{“type”:“200”,“users”:[{“id”:9,“email”:“bb”,“nickName”:“bb123456”,“password”:“bb@126.com”,“regTime”:“2018年10月24日 下午02时03分53秒”,“userName”:“bb2”},{“id”:10,“email”:“cc”,“nickName”:“cc123456”,“password”:“cc@126.com”,“regTime”:“2018年10月24日 下午02时03分53秒”,“userName”:“cc3”},{“id”:11,“email”:“steven@126.com”,“nickName”:“steven”,“password”:“123456”,“regTime”:“2018年10月24日 下午02时06分57秒”,“userName”:“steven”},{“id”:12,“email”:“Liz@126.com”,“nickName”:“Liz”,“password”:“123321”,“regTime”:“2018年10月24日 下午02时06分57秒”,“userName”:“Liz”},{“id”:13,“email”:“HanHan@126.com”,“nickName”:“HanHan”,“password”:“654321”,“regTime”:“2018年10月24日 下午02时06分57秒”,“userName”:“HanHan”},{“id”:15,“email”:“YaoYao@126.com”,“nickName”:“YaoYao”,“password”:“654321”,“regTime”:“2018-01-01”,“userName”:“YaoYao”}],“content”:“success”}
调用http://localhost:8882/attachment(该接口测试负载均衡)
分别得到两种类型结果:
{“attachment”:true,“count”:“1”}
{“attachment”:true,“more”:“providerB”,“count”:null}
这两个结果随机出现。
至此,搭建测试完成!
文章出处:https://blog.youkuaiyun.com/sinat_30276961/article/details/83589748#2_86