微信公衆號:bugstack蟲洞棧 | bugstack.cn
沉澱、分享、成長,專一於原創專題案例,以最易學習編程的方式分享知識,讓本身和他人都能有所收穫。目前已完成的專題有;Netty4.x實戰專題案例、用Java實現JVM、基於JavaAgent的全鏈路監控、手寫RPC框架、架構設計專題案例[Ing]等。
你用劍🗡、我用刀🔪,好的代碼都很燒😏,望你不吝出招💨!html
隨着項目需求的變化,或者說從小公司跳槽了互聯網。需求變化了、承載的用戶體量增多了,總體系統的架構也隨着改變了。就像你作畢業設計的時候,可能只爲了完成功能便可,一個單體的MVC結構足可讓你畢業。但!如今你長大了,爲了能夠承載幾百、幾千、幾億的用戶體量,你開始發現原來還有這麼多套路在裏面。對於愛學習的人,確定蠢蠢欲動的想研究研究了(不研究也寫不了代碼,能抗住揍不)!java
在咱們的技術棧中RPC框架有;Dubbo、Motan、Tars、gRPC等等,並且每一個公司可能還有本身的RPC,若是想深刻了解那麼能夠參照《手寫RPC框架第三章《RPC中間件》》。對於一個程序猿來講仍是要從多家的框架中吸收養分,精進技術。mysql
本章節咱們主要將Dubbo技術與DDD的架構融合,搭建出分佈式架構體系。隨着一點點的深刻,本案例沒有引入過多的過技術棧,好比;Mq、ES、分庫分表等,這些會隨着後續的章節陸續完善。當前章節儘量簡單的體現核心內容;web
好! 那麼,最後在開始以前,再問一個小問題。redis
實現了Serializable接口的類,怎麼自動生成serialVersionUID(總不能本身亂編呀)spring
答:其實在Idea中已經提供了這樣自動生成功能,只須要配置上便可;File -> Settings -> Editor -> Inspections -> 搜索 Serialization issues ,找到 Serializable class without 'serialVersionUID' ->打上勾,Apply->OK 效果如圖;sql
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 分佈式框架apache
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
複製代碼
如下對工程模塊進行介紹,總體源碼獲取,能夠關注公衆號:bugstack蟲洞棧,回覆:框架搭建
<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&check=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 複製代碼