提及soa遠程調用基礎組件,最著名的莫過於淘寶的dubbo了,目前不少的大型互聯網公司都有一套本身的遠程服務調用分佈式框架,或者是使用開源的(例如dubbo),或者是本身基於某種協議(例如hessian,http等)進行開發,整體來講,使用遠程服務調用框架最大的好處莫過於如下三點:java
1.透明化的遠程方法調用,就像調用本地方法同樣調用遠程方法,只需簡單配置,沒有任何API侵入。
2.軟負載均衡及容錯機制,可在內網替代F5等硬件負載均衡器,下降成本,減小單點。
3. 服務自動註冊與發現,再也不須要寫死服務提供方地址,註冊中心基於接口名查詢服務提供者的IP地址,而且可以平滑添加或刪除服務提供者。web
那麼咱們如何從零開始開發出本身的soa遠程調用服務基礎組件呢?算法
soa基礎組件的主流架構圖以下所示:spring
Provider: 暴露服務的服務提供方。服務器
Consumer: 調用遠程服務的服務消費方。架構
Registry: 服務註冊與發現的註冊中心,通常都使用zookeeper做爲註冊中心。app
Monitor: 統計服務的調用次調和調用時間的監控中心。負載均衡
Container: 服務運行容器。框架
0. 服務容器負責啓動,加載,運行服務提供者。dom
1. 服務提供者在啓動時,向註冊中心註冊本身提供的服務。
2. 服務消費者在啓動時,向註冊中心訂閱本身所需的服務。
3. 註冊中心返回服務提供者地址列表給消費者,若是有變動,註冊中心將基於長鏈接推送變動數據給消費者。
4. 服務消費者,從提供者地址列表中,基於軟負載均衡算法,選一臺提供者進行調用,若是調用失敗,再選另外一臺調用。
5. 服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鐘發送一次統計數據到監控中心。
說白了,就是消費端和提供端都經過長鏈接連到zookeeper,消費端經過zk獲取到服務提供者的地址列表,存在內存中,當請求調用的時候直接從內存中選擇一個服務提供端的地址去訪問某一臺機器的服務(具體選擇哪一臺服務器根據特定的負載均衡算法肯定),當服務提供端有節點動態增長或者減小時,zk上也會相應的增長或者減小(經過心跳的方式實現),這個時候,服務節點的變化也會經過zk通知到消費端,消費端再把最新的zk中的服務提供端的地址列表刷新到本地內存中。這樣就能夠動態的進行服務提供端節點的增長和刪除。
瞭解了soa組件的基本架構以後,接下來咱們就來一步步實現本身的soa組件啦。
首先,咱們須要選擇一種協議,做爲組件的底層通訊協議,常見的好比hessian,http,rmi,WebService,Thrift等,在本例中咱們選擇hessian做爲咱們組件的底層通訊協議。爲了簡便起見,咱們直接使用spring 框架自帶的對於hessian協議的實現(由於僅僅是示例,因此沒有考慮非spring框架項目的狀況),spring發佈hessian服務的方式以下:
web.xml中配置:
<!-- hessian服務相關配置 --> <servlet> <servlet-name>hessianProvider</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-config-hessianservice.xml</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>hessianProvider</servlet-name> <url-pattern>/hessianProvider/*</url-pattern> </servlet-mapping>
spring-config-hessianservice.xml配置:
<bean name ="hessianTest" class="com.zhaogang.pricesoa.service.hessianservice.impl.HessianTestImpl"> </bean> <bean name ="/hessianTestService" class="org.springframework.remoting.caucho.HessianServiceExporter"> <property name="service" ref="hessianTest" /> <property name="serviceInterface" value="com.zhaogang.pricesoa.client.HessianTest" /> </bean>
HessianTest.java:
package com.zhaogang.pricesoa.client; import com.zhaogang.pricesoa.clientdomain.HessianTestDto; import com.zhaogang.pricesoa.clientdomain.HessianTestVo; public interface HessianTest{ public HessianTestVo getHessianTestVo(HessianTestDto hessianTestDto); }
HessianTest的具體實現HessianTestImpl.java:
package com.zhaogang.pricesoa.service.hessianservice.impl; import com.zhaogang.pricesoa.client.HessianTest; import com.zhaogang.pricesoa.clientdomain.HessianTestDto; import com.zhaogang.pricesoa.clientdomain.HessianTestVo; public class HessianTestImpl implements HessianTest{ public HessianTestVo getHessianTestVo(HessianTestDto hessianTestDto) { HessianTestVo hessianTestVo = new HessianTestVo(); hessianTestVo.setFieldA("a"); hessianTestVo.setFieldB(1); return hessianTestVo; } }
HessianTestVo.java:
package com.zhaogang.pricesoa.clientdomain; import java.io.Serializable; public class HessianTestVo implements Serializable{ private static final long serialVersionUID = -3489449530072196795L; private String fieldA; private Integer fieldB; public String getFieldA() { return fieldA; } public void setFieldA(String fieldA) { this.fieldA = fieldA; } public Integer getFieldB() { return fieldB; } public void setFieldB(Integer fieldB) { this.fieldB = fieldB; } }
HessianTestDto.java:
package com.zhaogang.pricesoa.clientdomain; import java.io.Serializable; public class HessianTestDto implements Serializable{ private static final long serialVersionUID = 5736809480913645948L; private Integer type; public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } }
很是簡單,一個hessian服務就發佈成功了。
客戶端調用上述發佈的hessian服務的方式以下:
在spring配置文件中配置以下配置
<!-- hessian服務 -->
<bean id="hessianTestService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<property name="serviceInterface" value="com.zhaogang.pricesoa.client.HessianTest" /> <!-- hessian服務接口類 -->
<property name="servicePathName" value="/hessianProvider/hessianTestService" /><!-- hessian服務路徑 -->
</bean>
這樣在代碼中咱們就能夠經過HessianTestVo hessianTestVo = hessianTestService.getHessianTestVo(hessianTestDto)的方式調用hessian服務了。
未完待續。。。。。。