分散式系統架構實戰demo:SSM+Dubbo
對於傳統的單一構架,也就是打成的war或者ear包部署在同一個Web容器裡的構架,它雖然開發、測試、部署簡單,但隨著業務的不斷髮展,維護成本增加,可擴充套件性差,而且一臺Tomcat的併發數在500左右,系統性能存在瓶頸。雖然網際網路應用的系統性能瓶頸存在於每一個地方(資料庫,網路等),先不考慮其他系統瓶頸,本文采用多Tomcat來解決一個Tomcat帶來的瓶頸問題,那麼多個Tomcat之間如何通訊?答案是Dubbo。
為什麼要使用Dubbo?兩臺伺服器A、B,分別部署不同的應用a,b。當A伺服器想要呼叫B伺服器上應用b提供的函式或方法的時候,由於不在一個記憶體空間,不能直接呼叫,需要通過網路來表達呼叫的語義傳達呼叫的資料。--
Dubbo也是一個被國內網際網路公司廣泛使用的SOA(核心理念:對外提供服務)基礎框架,說的直白點就是:一次開發出來的服務介面,相關服務都能呼叫。很像Java經典的特性:一次編譯,到處執行。
開發環境:
Window 7 64;Eclipse-Jee-mars-2-win32-x86_64 ;Apache-Maven-3.3.9;Spring 4.1.3;Mybatis 3.2.7;Springmvc 4.1.3;Dubbo 2.5.3,開發工具和原始碼:http://pan.baidu.com/s/1pLdoe0r
系統構架如下:
1.配置環境,建立專案工程
需要注意的是本文所用JDK是1.7,Tomcat是7.0。
1.1 mysql安裝
資料庫環境搭建吐血推薦,博主在踩了很多坑之後總結的高效安裝方法。一分鐘快速安裝MySQL,超簡單。
Mysql連結工具Navicat安裝後,開啟Navicat,新建連線-->新建資料庫Mysql-->雙擊進入資料庫-->查詢-->新建查詢:
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `book`;
CREATE TABLE `book` (
`book_id` varchar(100) NOT NULL COMMENT '圖書ID',
`book_name` varchar(100) DEFAULT NULL COMMENT '圖書名稱',
`number` int(11) DEFAULT NULL COMMENT '館藏數量',
PRIMARY KEY (`book_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='圖書表';
INSERT INTO `book` VALUES ('1000', 'Java程式設計', '10');
INSERT INTO `book` VALUES ('1001', '資料結構', '10');
INSERT INTO `book` VALUES ('1002', '設計模式', '10');
INSERT INTO `book` VALUES ('1003', '編譯原理', '10');
1.3 建立父工程和子工程,目錄結構如下:
其中,parent是pom檔案的父工程,common是jar包,裡面存放interface,bean,dao等公用內容。
controller控制層和service業務層是war包,也就是各分為一個Tomcat。
建立父工程:
建立common工程,在Packaging時選擇jar:
建立controller和service的步驟和common一樣,不過Packaging選擇war,完成後會出現以下報錯:
原因是webapp下沒有WEB_INF資料夾和web.xml檔案,自己建立web.xml,它內容在下文2.1中,複製貼上即可。注意,此時的執行環境是jdk1.5,需要永久切換成jdk1.7(點buildPath切換1.7的執行環境,下回開啟還是jdk1.5).所以在所有的pom檔案中加入
<!-- jdk1.7 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
加入後會有以下報錯,根據如下圖片中步驟處理即可:
1.4 匯入jar包。
parent專案pom.xml匯入所有jar包的座標,service和controller會依賴common,所以在common的pom檔案中加入要引入的jar包。因為太長,此處省略原始碼,讀者可以下載原始碼檢視。
因為公用jar放在了common裡,所以在controller和service的pom,xml檔案中都加入common的依賴:
<!-- 依賴管理 -->
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>test-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
2.整合Spring+Mybatis
2.1 配置Spring監聽器
一般會整合Spring+Mybatis放在業務邏輯層service。在service的web.xml中加入:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 上下文配置檔案 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-context.xml</param-value>
</context-param>
<!-- 監聽器 載入配置上下文配置檔案-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
2.2 建立上下文配置檔案: spring-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.0.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 資料來源 、事務 、掃描、MYbatis、ActiveMQ、Freemarker、Redis、Solr。。。。。 -->
<import resource="classpath:config/*.xml"/>
</beans>
2.3建立資料來源和配置檔案jdbc.xml:
jdbc.xml中加入讀取配置檔案-->資料來源-->事務管理-->Spring宣告式事務管理配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.0.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 讀取配置檔案,可以讀取多個 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
<!-- 資料來源 阿里巴巴出品 druid -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="initialSize" value="${jdbc.initialSize}"></property>
<property name="maxActive" value="${jdbc.maxActive}"></property>
<property name="minIdle" value="1"></property>
<property name="maxWait" value="30000"></property>
<property name="timeBetweenEvictionRunsMillis" value="60000"></property>
<property name="minEvictableIdleTimeMillis" value="300000"></property>
<property name="validationQuery" value="SELECT 1 "></property>
<property name="testWhileIdle" value="true"></property>
<property name="poolPreparedStatements" value="true"></property>
<property name="maxPoolPreparedStatementPerConnectionSize" value="20"></property>
<property name="filters" value="stat,slf4j" />
<property name="proxyFilters">
<list>
<ref bean="stat-filter" />
<ref bean="log-filter" />
</list>
</property>
</bean>
<bean id="log-filter" class="com.alibaba.druid.filter.logging.Slf4jLogFilter">
<property name="resultSetLogEnabled" value="true" />
<property name="statementExecutableSqlLogEnable" value="true" />
</bean>
<bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter">
<property name="slowSqlMillis" value="30000" />
<property name="logSlowSql" value="true" />
<property name="mergeSql" value="true" />
</bean>
<!-- 事務管理-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 攔截器方式配置事物,方法命名記得要規範 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="select*" read-only="true" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true" propagation="REQUIRED"/>
<tx:method name="query*" read-only="true" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true" propagation="REQUIRED"/>
<tx:method name="search*" read-only="true" propagation="REQUIRED"/>
<tx:method name="is*" read-only="true" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="create*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="add*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="execute*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="remove*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="put*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="import*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="submit*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="cancel*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="send*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="edit*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="handle*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="close*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
<tx:method name="export*" propagation="REQUIRED" rollback-for="Exception,RuntimeException"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="transactionPointcut"
expression="execution(* com.test..*ServiceImpl.*(..))" />
<aop:advisor pointcut-ref="transactionPointcut" advice-ref="txAdvice" />
</aop:config>
</beans>
此處的Spring宣告式事務管理配置是上一篇具體應用,有興趣的可以深入理解下AOP: Spring AOP是什麼?你都拿它做什麼?
2.4 建立spring-common-context.xml自動掃描