背景
隨着互聯網的發展,網站應用的規模不斷擴大,常規的垂直應用架構已無法應對,分布式服務架構以及流動計算架構勢在必行,亟需一個治理系統確保架構有條不紊的演進。
單一應用架構
當網站流量很小時,只需一個應用,將所有功能都部署在一起,以減少部署節點和成本。此時,用於簡化增刪改查工作量的數據訪問框架(ORM)是關鍵。
- Dubbo是Alibaba開源的分布式服務框架,它最大的特點是按照分層的方式來架構,使用這種方式可以使各個層之間解耦合(或者最大限度地松耦合)。從服務模型的角度來看,Dubbo采用的是一種非常簡單的模型,要么是提供方提供服務,要么是消費方消費服務,所以基於這一點可以抽象出服務提供方(Provider)和服務消費方(Consumer)兩個角色。關於注冊中心、協議支持、服務監控等內容
- Zookeeper 分布式服務框架是 Apache Hadoop 的一個子項目,它主要是用來解決分布式應用中經常遇到的一些數據管理問題,如:統一命名服務、狀態同步服務、集群管理、分布式應用配置項的管理等
使用案例:
先前由於業務需求,要出很多新的功能,考慮到服務器的壓力(用戶正常訪問的業務+不斷迭代的新功能明顯感覺得到系統緩慢,響應延遲),單一的webservice或者httpclient互相調用不同的服務也感覺比較笨拙,加上某個提供者掛掉導致部分業務不能正常訪問,所以就有采用了dubbo框架作為不同的服務之間相互調用。
-----------------------------------------------------------------分割線--------------------------------------------------------------
- 整合dubbo之前我先貼上幾段spring代碼(如何搭建dubbo和Zookeeper環境這里就不介紹了,網上有很多教程, 我的dubbo版本是2.5.4,zookeeper是3.3.6),主要是模擬spring mvc的一個簡單過程,但是沒用到數據庫和spring 事務等, 用來對比dubbo和spring整合后到底哪里發生了改變
- 首先是實體類,我們經常說的pojo
package com.zdd.dubbo.provider; /** * * @ClassName: Goods * @Description: 商品表 * @author: kevin * @date: 2016-10-21 上午10:23:03 */ public class Goods { private int id; private String name; private double price; private int num; private String color; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } @Override public String toString() { return "Goods [id=" + id + ", name=" + name + ", price=" + price + ", num=" + num + ", color=" + color + "]"; } }
查詢商品接口以及實現類
package com.zdd.dubbo.provider; import java.util.List; public interface GoodsService { List<Goods> findAllGoodsByParams(); }
package com.zdd.dubbo.provider; import java.util.ArrayList; import java.util.List; /** * * @ClassName: GoodsServiceImpl * @Description: 實現類,模擬從數據庫查出數據 * @author: kevin * @date: 2016-10-21 上午10:28:36 */ public class GoodsServiceImpl implements GoodsService { @Override public List<Goods> findAllGoodsByParams() { List<Goods> glist = new ArrayList<>(); Goods g = new Goods(); g.setColor("藍色"); g.setId(1); g.setName("耐克長袖"); g.setPrice(268); Goods g1 = new Goods(); g1.setColor("黑色"); g1.setId(1); g1.setName("阿迪達斯板鞋"); g1.setPrice(499); glist.add(g); glist.add(g1); return glist; } }
控制層代碼,在springmvc又稱handler
package com.zdd.dubbo.consumer; import javax.xml.bind.annotation.XmlAccessOrder; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.zdd.dubbo.provider.GoodsService; public class GoodsAction { private static GoodsService goodsService; /** * * @Title: main * @Description: 模擬spring mvc的 請求 類似於 @Controller * @param args * @return: void */ public static void main(String[] args) { //spring 注入 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("file:D:/workspace/GZDTL_TRUNK/zdd-web-consumer/spring/applicationContext.xml"); goodsService = (GoodsService) applicationContext.getBean("getGoodService"); System.out.println(goodsService.findAllGoodsByParams()); } }
spring的配置文件
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "> <!-- <import resource="dubbo-provider.xml"></import> --> <!-- spring注入 --> <bean id="getGoodService" class="com.zdd.dubbo.provider.GoodsServiceImpl"></bean> </beans>
通過spring注入然后執行main方法調用本地的service獲取一個商品list,輸出結果如下圖。
#這時候我打算用dubbo框架改變上面的代碼,通過dubbo把需要提供的服務暴露出去,然后消費者再進行調用,使得2個系統之間耦合度變低,我的理解是消費者只關心他要調用什么服務,提供者只關心他提供什么服務, 它們之間通過rpc協議來進行傳輸。因為當垂直應用越來越多,應用之間交互不可避免,將核心業務抽取出來,作為獨立的服務,逐漸形成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。此時,用於提高業務復用及整合的分布式服務框架(RPC)是關鍵。
- dubbo-provider.xml 提供者配置(注意:注冊中心暴露服務地址是zookeeper的地址,例如你本地安裝了zookeeper並啟動成功,那么你的地址就是zookeeper://127.0.0.1:2181,必須要啟動zookeeper,不然本地服務無法注冊上去,無法注冊的話就沒辦法實現遠程調用)
<?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="find_all_goods" />
<!-- 使用multicast廣播注冊中心暴露服務地址 -->
<!-- <dubbo:registry address="multicast://224.5.6.7:1234" /> -->
<!-- 使用zookeeper注冊中心暴露服務地址 -->
<dubbo:registry address="zookeeper://xx.xxxx.xx:2181" />
<!-- 用dubbo協議在20880端口暴露服務 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 聲明需要暴露的服務接口(注意是接口,不是實現類) -->
<dubbo:service interface="com.zdd.dubbo.provider.GoodsService" ref="goodsService" />
<!-- 這里是具體實現類,id和上面的暴露的服務接口ref要一致,dubbo就是通過這個來注冊對應的服務 -->
<bean id="goodsService" class="com.zdd.dubbo.provider.GoodsServiceImpl"></bean>
</beans>
<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="find_all_goods" />
<!-- 使用multicast廣播注冊中心暴露服務地址 -->
<!-- <dubbo:registry address="multicast://224.5.6.7:1234" /> -->
<!-- 使用zookeeper注冊中心暴露服務地址 -->
<dubbo:registry address="zookeeper://xx.xxxx.xx:2181" />
<!-- 用dubbo協議在20880端口暴露服務 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 聲明需要暴露的服務接口(注意是接口,不是實現類) -->
<dubbo:service interface="com.zdd.dubbo.provider.GoodsService" ref="goodsService" />
<!-- 這里是具體實現類,id和上面的暴露的服務接口ref要一致,dubbo就是通過這個來注冊對應的服務 -->
<bean id="goodsService" class="com.zdd.dubbo.provider.GoodsServiceImpl"></bean>
</beans>
- consumer.xml 消費者的xml配置(注意:對比上面的提供者xml配置可以清楚的發現消費者通過 <dubbo:reference id id="goodsService"> 標簽指向了 goodsService, 假設我們隨便寫一個mallService 肯定是不行的,因為我們提供的只有goodService服務)
<?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-consumer"/> <!-- 使用multicast廣播注冊中心暴露發現服務地址 --> <!-- <dubbo:registry address="multicast://224.5.6.7:1234" /> --> <dubbo:registry address="zookeeper://xxx.xx.xx:2181" /> <!-- 生成遠程服務代理,可以和本地bean一樣調用 --> <dubbo:reference id="goodsService" interface="com.zdd.dubbo.provider.GoodsService" /> </beans>
- 最后整合完測試一下代碼 (注意:這段代碼先初始化的是提供者的xml,然后初始化消費者的xml。必須按順序執行,否則的話服務提供的都沒起來,消費方是拿不到zk上面的服務的)
package com.zdd.dubbo.consumer; import java.io.IOException; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.zdd.dubbo.provider.GoodsService; public class GoodsAction { private static GoodsService goodsService; /** * * @Title: main * @Description: 模擬spring mvc的 請求 類似於 @Controller * @param args * @return: void * @throws IOException */ public static void main(String[] args) throws IOException { /*//spring 注入 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("file:D:/workspace/GZDTL_TRUNK/zdd-web-consumer/spring/applicationContext.xml"); goodsService = (GoodsService) applicationContext.getBean("getGoodService"); System.out.println(goodsService.findAllGoodsByParams());*/ ApplicationContext dubbo_provider = new ClassPathXmlApplicationContext("file:D:/workspace/GZDTL_TRUNK/zdd-service-provider/spring_dubbo/dubbo-provider.xml"); ApplicationContext dubbo_cusumer = new ClassPathXmlApplicationContext("file:D:/workspace/GZDTL_TRUNK/zdd-web-consumer/spring/dubbo-consumer.xml"); GoodsService service = (GoodsService) dubbo_cusumer.getBean("goodsService"); System.out.println(service.findAllGoodsByParams()); System.in.read(); } }
- 輸出結果如下。