前言
現在越來越多的互聯網公司還是將自己公司的項目進行服務化,這確實是今後項目開發的一個趨勢,就這個點再憑藉之前的 SSM 項目來讓第一次接觸的同學能快速上手。
淺談分佈式架構
分佈式架構 單看這個名字給人的感覺就是高逼格,但其實從歷史的角度來分析一下就比較明瞭了。
我們拿一個電商系統來說:
單系統
對於一個剛起步的創業公司項目肯定是追求越快完成功能越好,並且用戶量也不大。
這時候所有的業務邏輯都是在一個項目中就可以滿足。
垂直拆分-多應用
當業務量和用戶量發展到一定地步的時候,這時一般會將應用同時部署到幾臺服務器上,在用戶訪問的時候使用 Nginx 進行反向代理和簡單的負載均衡。
SOA服務化
當整個系統以及發展的足夠大的時候,比如一個電商系統中存在有:
- 用戶系統
- 訂單系統
- 支付系統
- 物流系統
等系統。
如果每次修改了其中一個系統就要重新發布上線的話那麼耦合就太嚴重了。
所以需要將整個項目拆分成若干個獨立的應用,可以進行獨立的開發上線實現快速迭代。
如上圖所示每個應用之間相互獨立,每個應用可以消費其他應用暴露出來的服務,同時也對外提供服務。
從架構的層面簡單的理解了,接下來看看如何編碼實現。
基於dubbo的實現
dubbo 應該算是國內使用最多的分佈式服務框架,基於此來實現對新入門的同學應該很有幫助。
其中有涉及到安裝dubbo服務的註冊中心zookeeper等相關知識點可以自行查看 官方文檔 ,這裡就不單獨講了。
對外提供服務
首先第一步需要在 SSM-API 模塊中定義一個接口,這裡就搞了一個用戶查詢的接口
/**
* Function:用戶API
* @author chenjiec
* Date: 2017/4/4 下午9:46
* @since JDK 1.7
*/
public interface UserInfoApi {
/**
* 獲取用戶信息
* @param userId
* @return
* @throws Exception
*/
public UserInfoRsp getUserInfo(int userId) throws Exception;
}
接著在 SSM-SERVICE 模塊中進行實現:
import com.alibaba.dubbo.config.annotation.Service;
/**
* Function:
* @author chenjiec
* Date: 2017/4/4 下午9:51
* @since JDK 1.7
*/
@Service
public class UserInfoApiImpl implements UserInfoApi {
private static Logger logger = LoggerFactory.getLogger(UserInfoApiImpl.class);
@Autowired
private T_userService t_userService ;
/**
* 獲取用戶信息
*
* @param userId
* @return
* @throws Exception
*/
@Override
public UserInfoRsp getUserInfo(int userId) throws Exception {
logger.info("用戶查詢Id="+userId);
//返回對象
UserInfoRsp userInfoRsp = new UserInfoRsp() ;
T_user t_user = t_userService.selectByPrimaryKey(userId) ;
//構建
buildUserInfoRsp(userInfoRsp,t_user) ;
return userInfoRsp;
}
/**
* 構建返回
* @param userInfoRsp
* @param t_user
*/
private void buildUserInfoRsp(UserInfoRsp userInfoRsp, T_user t_user) {
if (t_user == null){
t_user = new T_user() ;
}
CommonUtil.setLogValueModelToModel(t_user,userInfoRsp);
}
}
這些都是通用的代碼,但值得注意的一點是這裡使用的 dubbo 框架所提供的 @service 註解。作用是聲明需要暴露的服務接口。
再之後就是幾個dubbo相關的配置文件了。
spring-dubbo-config.xml
<dubbo:application name="ssm-service" owner="crossoverJie"
organization="ssm-crossoverJie" logger="slf4j"/>
<dubbo:registry id="dubbo-registry" address="zookeeper://192.168.0.188:2181"
file="/tmp/dubbo.cachr" />
<dubbo:monitor protocol="registry" />
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:provider timeout="15000" retries="0" delay="-1" />
<dubbo:consumer check="false" timeout="15000" />
其實就是配置我們服務註冊的zk地址,以及服務名稱、超時時間等配置。
spring-dubbo-provider.xml
<dubbo>annotation package="com.crossoverJie.api.impl" />/<dubbo>
這個配置掃描註解包的位置,一般配置到接口實現包即可。
spring-dubbo-consumer.xml
這個是消費者配置項,表明我們需要依賴的其他應用。
這裡我們在 SSM-BOOT 項目中進行配置:
<reference>
interface="com.crossoverJie.api.UserInfoApi" />
直接就是配置的剛才我們提供的那個用戶查詢的接口,這樣當我們自己的內部項目需要使用到這個服務只需要依賴 SSM-BOOT 即可,不需要單獨的再去配置 consumer 。這個我有在上一篇 SSM(十) 項目重構-互聯網項目的Maven結構 中也有提到。
安裝管理控制檯
還有一個需要做的就是安裝管理控制檯,這裡可以看到我們有多少服務、調用情況是怎麼樣等作用。
這裡我們可以將dubbo的官方源碼下載下來,對其中的 dubbo-admin 模塊進行打包,將生成的 WAR包 放到 Tomcat 中運行起來即可。
但是需要注意一點的是:
需要將其中的 dubbo.properties 的zk地址修改為自己的即可。
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.admin.root.password=root
dubbo.admin.guest.password=guest
到時候登陸的話使用root,密碼也是root。
使用guest,密碼也是guest。
登陸界面如下圖:
其中我們可以看到有兩個服務以及註冊上去了,但是沒有消費者。
消費服務
為了能夠更直觀的體驗到消費服務,我新建了一個項目:
https://github.com/crossoverJie/SSM-CONSUMER 。
其中在 SSM-CONSUMER-API 中我也定義了一個接口:
/**
* Function:薪資API
* @author chenjiec
* Date: 2017/4/4 下午9:46
* @since JDK 1.7
*/
public interface SalaryInfoApi {
/**
* 獲取薪資
* @param userId
* @return
* @throws Exception
*/
public SalaryInfoRsp getSalaryInfo(int userId) throws Exception;
}
因為作為消費者的同時我們也對外提供了一個獲取薪資的一個服務。
在 SSM-CONSUMER-SERVICE 模塊中進行了實現:
/**
* Function:
* @author chenjiec
* Date: 2017/4/4 下午9:51
* @since JDK 1.7
*/
@Service
public class SalaryInfoApiImpl implements SalaryInfoApi {
private static Logger logger = LoggerFactory.getLogger(SalaryInfoApiImpl.class);
@Autowired
UserInfoApi userInfoApi ;
/**
* 獲取用戶信息
*
* @param userId
* @return
* @throws Exception
*/
@Override
public SalaryInfoRsp getSalaryInfo(int userId) throws Exception {
logger.info("薪資查詢Id="+userId);
//返回對象
SalaryInfoRsp salaryInfoRsp = new SalaryInfoRsp() ;
//調用遠程服務
UserInfoRsp userInfo = userInfoApi.getUserInfo(userId);
salaryInfoRsp.setUsername(userInfo.getUserName());
return salaryInfoRsp;
}
}
其中就可以直接使用 userInfoApi 調用之前的個人信息服務。
再調用之前需要注意的有點是,我們只需要依賴 SSM-BOOT 這個模塊即可進行調用,因為 SSM-BOOT 模塊已經為我們配置了消費者之類的操作了:
<dependency>
<groupId>com.crossoverJiegroupId>
<artifactId>SSM-BOOTartifactId>
dependency>
還有一點是在配置 SSM-BOOT 中的 spring-dubbo-cosumer.xml 配置文件的時候,路徑要和我們初始化spring配置文件時的路徑一致:
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath*:spring/*.xmlparam-value>
context-param>
接下來跑個單測試一下能否調通:
/**
* Function:
*
* @author chenjiec
* Date: 2017/4/5 下午10:41
* @since JDK 1.7
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:/spring/*.xml" })
public class SalaryInfoApiImplTest {
@Autowired
private SalaryInfoApi salaryInfoApi ;
@Test
public void getSalaryInfo() throws Exception {
SalaryInfoRsp salaryInfo = salaryInfoApi.getSalaryInfo(1);
System.out.println(JSON.toJSONString(salaryInfo));
}
}
消費者
提供者
可以看到確實是調用成功了的。
接下來將消費者項目也同時啟動在來觀察管理控制檯有什麼不一樣:
會看到多了一個消費者所提供的服務 com.crossoverjie.consumer.api.SalaryInfoApi ,同時
com.crossoverJie.api.UserInfoApi 服務已經正常,說明已經有消費者了。
點進去便可查看具體的消費者。
總結
這樣一個基於dubbo的分佈式服務已經講的差不多了,在實際的開發中我們便會開發一個大系統中的某一個子應用,這樣就算一個子應用出問題了也不會影響到整個大的項目。
再提一點:
在實際的生產環境一般同一個服務我們都會有一個 master , slave 的主從服務,這樣在上線的過程中不至於整個應用出現無法使用的尷尬情況。
談到了 SOA 的好處,那麼自然也有相對於傳統模式的不方便之處:
Jenkins
elk
以上就是我理解的,如有差錯歡迎指正。
/<reference>閱讀更多 歪脖攻城獅 的文章