微信公众号:bugstack虫洞栈 | bugstack.cn
沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。目前已完成的专题有;Netty4.x实战专题案例、用Java实现JVM、基于JavaAgent的全链路监控、手写RPC框架、架构设计专题案例[Ing]等。
你用剑 、我用刀:hocho:,好的代码都很烧:smirk:,望你不吝出招:dash:!
随着项目需求的变化,或者说从小公司跳槽了互联网。需求变化了、承载的用户体量增多了,整体系统的架构也随着改变了。就像你做毕业设计的时候,可能只为了完成功能即可,一个单体的MVC结构足可以让你毕业。但!现在你长大了,为了可以承载几百、几千、几亿的用户体量,你开始发现原来还有这么多套路在里面。对于爱学习的人,肯定蠢蠢欲动的想研究研究了(不研究也写不了代码,能抗住揍不)!
在我们的技术栈中RPC框架有;Dubbo、Motan、Tars、gRPC等等,而且每个公司可能还有自己的RPC,如果想深入了解那么可以参照《 手写RPC框架第三章《RPC中间件》 》。对于一个程序猿来说还是要从多家的框架中吸取营养,精进技术。
本章节我们主要将Dubbo技术与DDD的架构融合,搭建出分布式架构体系。随着一点点的深入,本案例没有引入过多的过技术栈,比如;Mq、ES、分库分表等,这些会随着后续的章节陆续完善。当前章节尽可能简单的体现核心内容;
好!那么,最后在开始之前,再问一个小问题。
答:其实在Idea中已经提供了这样自动生成功能,只需要配置上即可;File -> Settings -> Editor -> Inspections -> 搜索 Serialization issues ,找到 Serializable class without 'serialVersionUID' ->打上勾,Apply->OK 效果如图;
itstack-demo-frame-parent 父类工程
itstack-demo-frame-parent ├── itstack-demo-frame-parent │ ├── src │ │ └── main │ │ └── java │ │ └── org.itstack.demo.frame.common │ │ ├── constants │ │ │ └── Constants.java │ │ └── domain │ │ ├── PageRequest.java │ │ └── Result.java │ └── pom.xml └── pom.xml 复制代码
itstack-demo-frame-dcs 分布式框架
itstack-demo-frame-dcs ├── itstack-demo-frame-dcs-ddd │ └── src │ ├── main │ │ ├── java │ │ │ └── org.itstack.demo │ │ │ ├── application │ │ │ │ └── UserService.java │ │ │ ├── domain │ │ │ │ ├── model │ │ │ │ │ ├── aggregates │ │ │ │ │ │ └── UserInfoCollect.java │ │ │ │ │ ├── req │ │ │ │ │ │ └── UserReq.java │ │ │ │ │ └── vo │ │ │ │ │ └── UserInfo.java │ │ │ │ ├── repository │ │ │ │ │ └── IUserRepository.java │ │ │ │ └── service │ │ │ │ └── UserServiceImpl.java │ │ │ ├── infrastructure │ │ │ │ ├── common │ │ │ │ │ ├── EasyResult.java │ │ │ │ │ └── PageRequest.java │ │ │ │ ├── dao │ │ │ │ │ └── IUserDao.java │ │ │ │ ├── po │ │ │ │ │ └── User.java │ │ │ │ └── repository │ │ │ │ └── UserRepository.java │ │ │ └── interfaces │ │ │ └── UserController.java │ │ ├── resources │ │ │ ├── mapper │ │ │ ├── props │ │ │ ├── spring │ │ │ ├── logback.xml │ │ │ ├── mybatis-config.xml │ │ │ └── spring-config.xml │ │ └── webapp │ │ ├── page │ │ ├── res │ │ ├── WEB-INF │ │ ├── index.html │ │ └── res_layui.html │ └── test │ └── java │ └── org.itstack.demo.test │ └── ApiTest.java │ └── itstack-demo-frame-dcs-rpc └── src └── main └── java └── org.itstack.demo.rpc ├── dto │ └── UserDto.java ├── req │ └── UserReq.java ├── res │ └── UserRes.java └── IUserRpc.java 复制代码
itstack-demo-frame-dcs-test RPC测试工程
itstack-demo-frame-dcs-test └── src ├── main │ ├── java │ │ └── org.itstack.demo.interfaces │ │ └── UserController.java │ ├── resources │ │ ├── spring │ │ ├── logback.xml │ │ └── spring-config.xml │ └── webapp │ ├── page │ ├── res │ ├── WEB-INF │ ├── index.html │ └── res_layui.html └── test └── java └── org.itstack.demo.test └── ApiTest.java 复制代码
<groupId>org.itstack.demo</groupId> <artifactId>itstack-demo-frame-parent</artifactId> <version>1.0.0-RELEASE</version> <modules> <module>itstack-demo-frame-common</module> </modules> <packaging>pom</packaging> <name>itstack-demo-frame-parent</name> <description>itstack Demo Project Dependencies</description> <properties> <!-- Base --> <jdk.version>1.8</jdk.version> <sourceEncoding>UTF-8</sourceEncoding> <!-- Spring --> <spring.version>4.3.24.RELEASE</spring.version> <servlet-api.version>2.5</servlet-api.version> <spring.redis.version>1.8.4.RELEASE</spring.redis.version> <!-- DB:mysql、mybatis--> <mysql.version>5.1.20</mysql.version> <mybatis.version>3.3.0</mybatis.version> <mybatis_spring.version>1.2.3</mybatis_spring.version> <!-- JSON --> <fastjson.version>1.2.60</fastjson.version> <jackson.version>2.5.4</jackson.version> <!-- Junit --> <junit.version>4.12</junit.version> <!-- Common --> <commons-dbcp2.version>2.6.0</commons-dbcp2.version> <commons-lang3.version>3.8.1</commons-lang3.version> <!-- 日志 --> <slf4j.version>1.7.7</slf4j.version> <logback.version>1.0.9</logback.version> <!-- 其他服务 --> <dubbo.version>2.6.6</dubbo.version> <zookeeper.version>3.4.14</zookeeper.version> <netty.version>4.1.36.Final</netty.version> <redis.version>2.9.0</redis.version> <scheduler.version>2.3.2</scheduler.version> </properties> 复制代码
应用层是比较薄的一层,不做具体逻辑开发。本工程里只包括服务的定义,具体逻辑有领域层实现。
UserService.java & 服务定义
public interface UserService { UserInfoCollect queryUserInfoList(UserReq req); } 复制代码
领域层是整个工程的核心服务层,这里负责处理具体的核心功能,完成领域服务。domain下可以有多个领域,每个领域里包括;聚合、请求对象、业务对象、仓储、服务。
UserServiceImpl.java & 服务实现
@Service("userService") public class UserServiceImpl implements UserService { @Resource(name = "userRepository") private IUserRepository userRepository; @Override public UserInfoCollect queryUserInfoList(UserReq req) { return userRepository.queryUserInfoList(req); } } 复制代码
IUserRepository.java & 仓库定义
public interface IUserRepository { UserInfoCollect queryUserInfoList(UserReq req); } 复制代码
实现领域层仓储定义,数据库操作为非业务属性的功能操作,在仓储实现层进行组合装配DAO&Redis&Cache等。
UserDBRepository.java & 仓库实现
@Repository("userDBRepository") public class UserDBRepository implements IUserRepository { @Resource private IUserDao userDao; @Resource private Redis redis; @Override public UserInfoCollect queryUserInfoList(UserReq req) { Long count = userDao.queryUserInfoCount(req); List<User> userList = userDao.queryUserInfoList(req); List<UserInfo> userInfoList = new ArrayList<>(); userList.forEach(user -> { UserInfo userInfo = new UserInfo(); userInfo.setUserId(user.getId()); userInfo.setName(user.getName()); userInfo.setAge(user.getAge()); userInfo.setAddress(user.getAddress()); userInfo.setEntryTime(user.getEntryTime()); userInfo.setStatus(user.getStatus()); userInfoList.add(userInfo); }); UserInfoCollect userInfoCollect = new UserInfoCollect(count, userInfoList); if (StringUtils.isNoneBlank(req.getName())) { redis.set(req.getName(), JSON.toJSONString(userInfoCollect)); } return userInfoCollect; } } 复制代码
UserRpc.java & RPC接口实现
@Service("userRpc") public class UserRpc implements IUserRpc { @Resource private UserService userService; @Override public UserRes queryUserInfoList(UserReq req) { UserInfoCollect userInfoCollect = userService.queryUserInfoList(UserAssembler.buildUserReq(req)); return UserAssembler.buildUserInfoCollect(userInfoCollect); } } 复制代码
服务接口定义,rpc框架需要对外提供接口描述jar包,因此单独提取出来是最方面处理的。不要让这一层引用其他层的逻辑代码。
IUserRpc.java & 接口定义
public interface IUserRpc { UserRes queryUserInfoList(UserReq req); } 复制代码
这一层是整个工程的最外层POM文件,引入父类的定义配置
<?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> <!-- 框架统一服务定义 --> <parent> <groupId>org.itstack.demo</groupId> <artifactId>itstack-demo-frame-parent</artifactId> <version>1.0.0-RELEASE</version> </parent> <artifactId>itstack-demo-frame-dcs</artifactId> <packaging>pom</packaging> <version>1.0.0-SNAPSHOT</version> <modules> <module>itstack-demo-frame-dcs-ddd</module> <module>itstack-demo-frame-dcs-rpc</module> </modules> ... </project> 复制代码
<?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://dubbo.apache.org/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <!-- 提供方应用信息,用于计算依赖关系 --> <dubbo:application name="itstack-demo-frame-dcs"/> <!-- 使用multicast广播注册中心暴露服务地址 --> <dubbo:registry address="multicast://224.5.6.7:1234"/> <!-- 用dubbo协议在20880端口暴露服务 --> <dubbo:protocol name="dubbo" port="20880"/> <!-- 声明需要暴露的服务接口 --> <dubbo:service interface="org.itstack.demo.rpc.IUserRpc" ref="userRpc"/> </beans> 复制代码
DROP TABLE user; CREATE TABLE user ( id bigint(11) NOT NULL AUTO_INCREMENT, name varchar(32), age int(4), address varchar(128), entryTime datetime, remark varchar(64), createTime datetime, updateTime datetime, status int(4) DEFAULT '0', PRIMARY KEY (id), INDEX idx_name (name) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into user (id, name, age, address, entryTime, remark, createTime, updateTime, status) values (1, '水水', 18, '吉林省榆树市黑林镇尹家村5组', '2019-12-22 00:00:00', '无', '2019-12-22 00:00:00', '2019-12-22 00:00:00', 0); insert into user (id, name, age, address, entryTime, remark, createTime, updateTime, status) values (2, '豆豆', 18, '辽宁省大连市清河湾司马道407路', '2019-12-22 00:00:00', '无', '2019-12-22 00:00:00', '2019-12-22 00:00:00', 1); insert into user (id, name, age, address, entryTime, remark, createTime, updateTime, status) values (3, '花花', 19, '辽宁省大连市清河湾司马道407路', '2019-12-22 00:00:00', '无', '2019-12-22 00:00:00', '2019-12-22 00:00:00', 0); 复制代码
这一层就很简单了,添加好dubbo配置,引用RPC接口定义POM,调用服务端接口返回数据即可
pom.xml 引用RPC定义接口
<dependency> <groupId>org.itstack.demo</groupId> <artifactId>itstack-demo-frame-dcs-rpc</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency> 复制代码
spring-config-dubbo-consumer.xml & dubbo配置
<?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://dubbo.apache.org/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 --> <dubbo:application name="itstack-demo-frame-dcs-test" /> <!-- 使用multicast广播注册中心暴露发现服务地址 --> <dubbo:registry address="multicast://224.5.6.7:1234" /> <!-- 生成远程服务代理,可以和本地bean一样使用demoService --> <dubbo:reference id="userRpc" interface="org.itstack.demo.rpc.IUserRpc" /> </beans> 复制代码
ApiTest.java & 单元测试类
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring-config.xml") public class ApiTest { private Logger logger = LoggerFactory.getLogger(ApiTest.class); @Resource private IUserRpc userRpc; @Test public void test_queryUserInfoList() { UserReq req = new UserReq(); req.setName("豆豆"); req.setPage("1", "5"); UserRes res = userRpc.queryUserInfoList(req); logger.info("/r/n测试结果 req:{} res:{}", JSON.toJSONString(req), JSON.toJSONString(res)); } } 复制代码
2019-12-29 09:20:43.268 [DubboMulticastRegistryReceiver] INFO com.alibaba.dubbo.registry.multicast.MulticastRegistry[387] - [DUBBO] Notify urls for subscribe url consumer://127.0.0.1/org.itstack.demo.rpc.IUserRpc?application=itstack-demo-frame-dcs-test&category=providers,configurators,routers&dubbo=2.0.2&interface=org.itstack.demo.rpc.IUserRpc&methods=queryUserInfoList&pid=14416&revision=1.0.0-SNAPSHOT&side=consumer×tamp=1577582442523, urls: [dubbo://127.0.0.1:20880/org.itstack.demo.rpc.IUserRpc?anyhost=true&application=itstack-demo-frame-dcs&bean.name=org.itstack.demo.rpc.IUserRpc&dubbo=2.0.2&generic=false&interface=org.itstack.demo.rpc.IUserRpc&methods=queryUserInfoList&pid=15048&revision=1.0.0-SNAPSHOT&side=provider×tamp=1577582403854], dubbo version: 2.6.6, current host: 127.0.0.1 2019-12-29 09:20:43.397 [DubboMulticastRegistryReceiver] INFO com.alibaba.dubbo.remoting.transport.AbstractClient[282] - [DUBBO] Successed connect to server /127.0.0.1:20880 from NettyClient 127.0.0.1 using dubbo version 2.6.6, channel is NettyChannel [channel=[id: 0x82d694ae, L:/127.0.0.1:65193 - R:/127.0.0.1:20880]], dubbo version: 2.6.6, current host: 127.0.0.1 2019-12-29 09:20:43.398 [DubboMulticastRegistryReceiver] INFO com.alibaba.dubbo.remoting.transport.AbstractClient[91] - [DUBBO] Start NettyClient JRA1W11T0247/127.0.0.1 connect to the server /127.0.0.1:20880, dubbo version: 2.6.6, current host: 127.0.0.1 2019-12-29 09:20:43.449 [main] INFO com.alibaba.dubbo.registry.multicast.MulticastRegistry[387] - [DUBBO] Notify urls for subscribe url consumer://127.0.0.1/org.itstack.demo.rpc.IUserRpc?application=itstack-demo-frame-dcs-test&category=providers,configurators,routers&dubbo=2.0.2&interface=org.itstack.demo.rpc.IUserRpc&methods=queryUserInfoList&pid=14416&revision=1.0.0-SNAPSHOT&side=consumer×tamp=1577582442523, urls: [dubbo://127.0.0.1:20880/org.itstack.demo.rpc.IUserRpc?anyhost=true&application=itstack-demo-frame-dcs&bean.name=org.itstack.demo.rpc.IUserRpc&dubbo=2.0.2&generic=false&interface=org.itstack.demo.rpc.IUserRpc&methods=queryUserInfoList&pid=15048&revision=1.0.0-SNAPSHOT&side=provider×tamp=1577582403854], dubbo version: 2.6.6, current host: 127.0.0.1 2019-12-29 09:20:43.454 [main] INFO com.alibaba.dubbo.config.AbstractConfig[429] - [DUBBO] Refer dubbo service org.itstack.demo.rpc.IUserRpc from url multicast://224.5.6.7:1234/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=itstack-demo-frame-dcs-test&bean.name=org.itstack.demo.rpc.IUserRpc✓=false&dubbo=2.0.2&generic=false&interface=org.itstack.demo.rpc.IUserRpc&methods=queryUserInfoList&pid=14416®ister.ip=127.0.0.1&remote.timestamp=1577582403854&revision=1.0.0-SNAPSHOT&side=consumer×tamp=1577582442523, dubbo version: 2.6.6, current host: 127.0.0.1 十二月 29, 2019 9:20:43 上午 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping register 信息: Mapped "{[/api/user/queryUserInfoList],methods=[GET]}" onto public org.itstack.demo.rpc.res.UserRes org.itstack.demo.controller.UserController.queryUserInfoList(java.lang.String,java.lang.String,java.lang.String) 十二月 29, 2019 9:20:43 上午 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter initControllerAdviceCache 信息: Looking for @ControllerAdvice: org.springframework.context.support.GenericApplicationContext@4f51b3e0: startup date [Sun Dec 29 09:20:41 CST 2019]; root of context hierarchy 十二月 29, 2019 9:20:43 上午 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter initControllerAdviceCache 信息: Looking for @ControllerAdvice: org.springframework.context.support.GenericApplicationContext@4f51b3e0: startup date [Sun Dec 29 09:20:41 CST 2019]; root of context hierarchy 十二月 29, 2019 9:20:44 上午 org.springframework.web.servlet.handler.SimpleUrlHandlerMapping registerHandler 信息: Mapped URL path [/**] onto handler 'org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler#0' 2019-12-29 09:20:45.157 [main] INFO org.itstack.demo.test.ApiTest[31] - 测试结果 req:{"name":"豆豆","pageEnd":5,"pageStart":0} res:{"count":1,"list":[{"name":"豆豆","status":1}],"result":{"code":"0000","info":"成功"}} 十二月 29, 2019 9:20:45 上午 org.springframework.context.support.GenericApplicationContext doClose 信息: Closing org.springframework.context.support.GenericApplicationContext@4f51b3e0: startup date [Sun Dec 29 09:20:41 CST 2019]; root of context hierarchy 2019-12-29 09:20:45.159 [DubboShutdownHook] INFO com.alibaba.dubbo.config.DubboShutdownHook[56] - [DUBBO] Run shutdown hook now., dubbo version: 2.6.6, current host: 127.0.0.1 2019-12-29 09:20:45.160 [Thread-1] INFO com.alibaba.dubbo.registry.support.AbstractRegistryFactory[64] - [DUBBO] Close all registries [multicast://224.5.6.7:1234/com.alibaba.dubbo.registry.RegistryService?application=itstack-demo-frame-dcs-test&dubbo=2.0.2&interface=com.alibaba.dubbo.registry.RegistryService&pid=14416×tamp=1577582442547], dubbo version: 2.6.6, current host: 127.0.0.1 复制代码