在較長的一段時間裏,LAMP框架是Web網站的常見方式:Linux+ Apache+ PHP+ MySQL,或者另外一套MVC規範,java裏比較常見的選擇通常是Spring +Struts +MyBatis +Tomcat,有時候也會選擇重量級的EJB來實現,儘管在技術細節上的處理各不相同,但是都有一個共性:垂直應用架構。
垂直應用架構
一、傳統的垂直應用架構
1.MVC架構
以經典的MVC垂直應用架構爲栗子,通常分爲三層:
- View展示層,是用戶看到並與之交互的界面。
- Control層,用於前端Web請求的分發,調度後臺的業務處理。
- Model模型層,包含業務數據和執行邏輯。
標準的MVC模式並不包括數據訪問層,所以通常還需要專門的ORM框架,可以屏蔽對底層數據庫連接池和數據源的實現,提供對上層JDBC的訪問,提升開發效率,常見的一般都是Hibernate和Mybatis。通常基於MVC框架的應用都會打成一個war包,部署在Tomcat等Web容器中。
業務組網也不復雜,通常做好雙熱機即可,可通過watchDog來檢測應用,判斷應用進程是否異常,如果一個出現問題可以立即啓動到備機,如果考慮到更復雜的併發場景,可在後端做集羣部署,還有前端F5等負載均衡處理。
2.垂直應用架構的缺陷
1.難以應付複雜的業務場景,且開發和維護的成本會增高。
2.團隊協作效率差,公共功能重複開發,重複率高。
3.系統的可靠性變差,某個節點的故障會導致整個系統的“雪崩效應”。
4.維護和定製困難,複雜應用的業務拆分困難,代碼修改牽一髮而動全身。
當垂直應用越來越多,應用之間的交互不可避免,將核心業務抽取出來,作爲獨立的服務,逐漸形成穩定的服務中心,使得前端能夠更快的相應市場需求,同時將公共的API抽取出來,可以作爲獨立的公共服務給其他調用者消費,實現服務的共享和重用,於是有了RPC框架的需求。
二、RPC框架
RPC的全稱爲(Remote Procedure Call),遠程過程調用,是一種進程間的通信方式,在2006年後的移動互聯網時代開始興起,出現了各種各樣的開源RPC框架。
RPC的框架屏蔽了底層的傳輸方式(TCP/UDP),序列化方式(XML / JASON / ProtoBuf)和通信細節,使用者只需要知道who(誰)在where(哪裏)提供了what(什麼)服務即可。
一個最簡單的RPC框架只需要考慮如下三個部分的實現:
- 服務提供者,運行在服務端,負責提供服務接口定義和實現。
- 服務發佈者,運行在RPC服務端,負責將本地服務發佈成遠程服務,供其他消費者調用;
- 本地服務代理,運行在RPC客戶端,通過代理調用遠程服務提供者,然後將結果進行封裝返回給本地消費者;
在這裏根據思路來簡單提供一段代碼實現,首先是服務的接口定義和實現:
/**
* HelloService 服務接口
*/
public interface HelloService {
String hello(String name);
}
/**
* HelloServiceImpl 服務接口的實現
*/
public class HelloServiceImpl implements HelloService {
public String hello(String name) {
return "Hello " + name;
}
}
服務的發佈:
/**
* RpcFramework
**/
public class RpcFramework {
/**
* 暴露服務
*
* @param service 服務實現
* @param port 服務端口
*/
public static void export(final Object service, int port) throws Exception {
if (service == null) {
throw new IllegalArgumentException("service instance == null");
}
if (port <= 0 || port > 65535) {
throw new IllegalArgumentException("Invalid port " + port);
}
System.out.println("Export service " + service.getClass().getName() + " on port " + port);
ServerSocket server = new ServerSocket(port);
for(;;) {
try {
final Socket socket = server.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
try {
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
String methodName = input.readUTF();
Class<?>[] parameterTypes = (Class<?>[])input.readObject();
Object[] arguments = (Object[])input.readObject();
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
try {
Method method = service.getClass().getMethod(methodName, parameterTypes);
Object result = method.invoke(service, arguments);
output.writeObject(result);
} catch (Throwable t) {
output.writeObject(t);
} finally {
output.close();
}
} finally {
input.close();
}
} finally {
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
服務的引用
/**
* 引用服務
* @param interfaceClass 接口類型
* @param host 服務器主機名
* @param port 服務器端口
* @return 遠程服務
*/
@SuppressWarnings("unchecked")
public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {
if (interfaceClass == null)
throw new IllegalArgumentException("Interface class == null");
if (! interfaceClass.isInterface())
throw new IllegalArgumentException("The " + interfaceClass.getName() + " must be interface class!");
if (host == null || host.length() == 0)
throw new IllegalArgumentException("Host == null!");
if (port <= 0 || port > 65535)
throw new IllegalArgumentException("Invalid port " + port);
System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] {interfaceClass}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
Socket socket = new Socket(host, port);
try {
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
try {
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(arguments);
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
Object result = input.readObject();
if (result instanceof Throwable) {
throw (Throwable) result;
}
return result;
} finally {
input.close();
}
} finally {
output.close();
}
} finally {
socket.close();
}
}
});
}
}
public class RpcProvider {
public static void main(String[] args) throws Exception {
HelloService service = new HelloServiceImpl();
RpcFramework.export(service, 1234);
}
}
服務的調用:
public class RpcConsumer {
public static void main(String[] args) throws Exception {
HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 1234);
for (int i = 0; i < Integer.MAX_VALUE; i ++) {
String hello = service.hello("World" + i);
System.out.println(hello);
Thread.sleep(1000);
}
}
}
2.RPC的不足
在大規模服務化以前,應用以前只能通過暴露接口和應用遠程服務的方式去調用,服務越來越多的時候會有以下情況:
- 服務URL的配置管理變的困難
- 服務間的依賴關係變成錯綜複雜,難以分清哪個應用要在哪個應用前啓動
- 服務的調用量越來越大,服務的容量問題出現問題,某個服務需要多少機器、什麼時候該加機器
- 缺乏一個服務註冊中心,動態的註冊和發現服務。
服務化之後,隨之而來的就是服務治理問題,現在的RPC框架在這方面都有所欠缺,要解決這些問題必須通過服務框架+服務治理來完成,單憑RPC框架無法解決服務治理的問題。
三、SOA服務化架構
SOA,Service-Oriented Architecture,面向服務的架構(SOA)是一個組件模型,是一種粗粒度、鬆耦合的以服務爲中心的架構,接口之間通過定義明確的協議和接口進行通信。
面向服務的核心是對傳統的垂直架構進行改造,其中的核心技術就是分佈式服務框架,應用也從集中式走向了分佈式,大規模系統的架構設計原則就是儘可能的拆分,以達到更好的獨立擴展與伸縮,更靈活的部署、更好的隔離和容錯,更高的開發效率,具體的拆分策略是:橫向拆分和縱向拆分。
業務縱向拆分:
根據業務的特性把應用拆開,不同的業務模塊獨立部署,將複雜的業務線拆分成相對獨立的、靈活的具體能力域,由大到小分而治之。
業務橫向拆分:
將核心的、公共的業務拆分出來,通過分佈式服務框架對業務進行服務化,消費者通過標準的契約來消費這些服務,服務提供者獨立打包、部署,與消費者解耦。
服務治理
拆分了之後,隨着服務數的增多,亟需一個服務治理框架,有效管理服務,提升服務的運行質量,服務治理需要滿足:服務生命週期管理,服務容量規劃,運行期治理和服務安全等。目前較爲成熟的商用服務框架有Spring cloud,阿里巴巴提供的開源的Dubbo框架,非開源的HSF框架,
至於Dubbo和HSF這兩者的差別,抄一段來展示:阿里巴巴第一代RPC框架Dubbo是國內第一款成熟的商用級RPC框架,已於2011年正式對外開源,目前已發展成爲國內開源價值最高、用戶使用規模最大的開源軟件之一。2016年度中國開源軟件Top10。最新一代RPC框架HSF,全稱High Speed Framework,也叫"好舒服","很舒服"框架,是阿里內部對這一款高性能服務框架的暱稱,是一款面向企業級互聯網架構量身定製的分佈式服務框架。HSF以高性能網絡通信框架爲基礎,提供了諸如服務發佈與註冊,服務調用,服務路由,服務鑑權,服務限流,服務降級和服務調用鏈路跟蹤等一系列久經考驗的功能特性。
架構原理
分佈式服務的架構可以抽象爲三層:
1、RPC層:底層通信框架(例如NIO框架的封裝),序列化和反序列化框架等。
2、FilterChain層:服務調用職責鏈,例如負載均衡,服務調用性能統計,服務調用完成通知,失敗重發等等。
3、Service層:java動態代理,將服務提供者的接口封裝成遠程服務調用;java反射,服務提供者使用,根據消費者請求消息中的接口名、方法名、參數列表反射調用服務提供者的接口本地實現類。
分佈式服務框架的兩個核心功能:服務治理和服務註冊中心,服務中心中dubbo默認使用的是ZooKeeper,HSF默認使用的爲ConfigServer。
四、微服務
SOA解決了應用服務化的問題,隨着服務化實踐的深入,服務的規模也越來越大,服務治理的問題也越來越多,這時候出現了微服務的思想。微服務架構由多個微小服務構成,每個服務就是一個獨立的可部署單元或組件,它們是分佈式的,相互解耦的,通過輕量級遠程通信協議(比如REST)來交互,每個服務可以使用不同的數據庫,而且是語言無關性的。它的特徵是彼此獨立、微小、輕量、鬆耦合,又能方便的組合和重構,猶如《超能陸戰隊》中的微型機器人,個體簡單,但組合起來威力強大。
微服務之所以這麼火,另一個原因是因爲 Docker 的出現,它讓微服務有一個非常完美的運行環境,Docker 的獨立性和細粒度非常匹配微服務的理念,Docker的優秀性能和豐富的管理工具,讓大家對微服務有了一定的信息,概括來說 Docker 有如下四點適合微服務:
- 獨立性:一個容器就是一個完整的執行環境,不依賴外部任何的東西。
- 細粒度:一臺物理機器可以同時運行成百上千個容器。其計算粒度足夠的小。
- 快速創建和銷燬:容器可以在秒級進行創建和銷燬,非常適合服務的快速構建和重組。
- 完善的管理工具:數量衆多的容器編排管理工具,能夠快速的實現服務的組合和調度。
作者:YitaiCloud
鏈接:https://www.jianshu.com/p/62398fa9bc64
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。