在企業級軟件的架構模型上,咱們主要討論下SOA與微服務架構。html
SOA的全稱是Service-Oriented Architecture,可譯爲「面向服務的架構」,它是一個組件模型,將應用程序的不一樣功能單元(稱爲服務)經過這些服務之間定義良好的接口和契約聯繫起來。接口是採用中立的方式進行定義的,它應該獨立於實現服務的硬件平臺、操做系統和編程語言。這使得構建在各類各樣的系統中的服務能夠以一種統一和通用的方式進行交互。前端
SOA是一種粗粒度、鬆耦合服務架構,服務之間經過簡單、精肯定義接口進行通信,不涉及底層編程接口和通信模型。SOA能夠看做是B/S模型、XML(標準通用標記語言的子集)/Web Service技術以後的天然延伸。java
SOA服務具備平臺獨立的自我描述XML文檔。Web服務描述語言(WSDL, Web Services Description Language)是用於描述服務的標準語言。
SOA 服務用消息進行通訊,該消息一般使用XML Schema來定義(也叫作XSD, XML Schema Definition)。消費者和提供者或消費者和服務之間的通訊多見於不知道提供者的環境中。服務間的通信也能夠看做企業內部處理的關鍵商業文檔。
在一個企業內部,SOA服務經過一個扮演目錄列表(directory listing)角色的登記處(Registry)來進行維護。應用程序在登記處(Registry)尋找並調用某項服務。統一描述,定義和集成(UDDI, Universal Description, Definition, and Integration)是服務登記的標準。
每項SOA服務都有一個與之相關的服務品質(QoS, quality of service)。QoS的一些關鍵元素有安全需求(例如認證和受權),可靠通訊(譯註:可靠消息是指,確保消息「僅且僅僅」發送一次,從而過濾重複信息。),以及誰能調用服務的策略。mysql
要運行,管理SOA應用程序,企業須要SOA基礎,這是SOA平臺的一個部分。SOA基礎必須支持全部的相關標準,和須要的運行時容器。SOA基礎結構示意圖以下:web
上圖中,WSDL,UDDI和SOAP是SOA基礎的基礎部件。WSDL用來描述服務;UDDI用來註冊和查找服務;而SOAP,做爲傳輸層,用來在消費者和服務提供者之間傳送消息。SOAP是Web服務的默認機制。一個消費者能夠在UDDI註冊表(registry)查找服務,取得服務的WSDL描述,而後經過SOAP來調用服務。spring
WS-I Basic Profile,由Web服務互用性組織(Web Services Interoperability Organization)提供,是SOA服務測試與互用性所須要的核心構件。服務提供者可使用Basic Profile測試程序來測試服務在不一樣平臺和技術上的互用性。sql
儘管J2EE和.NET平臺是開發SOA應用程序經常使用的平臺,但SOA不只限於此。像J2EE這類平臺,不只爲開發者天然而然地參與到SOA中來提供了一個平臺,還經過他們內在的特性,將可擴展性,可靠性,可用性以及性能引入了SOA世界。新的規範,例如 JAXB(Java API for XML Binding),用於將XML文檔定位到Java類;JAXR(Java API for XML Registry)用來規範對UDDI註冊表(registry)的操做;XML-RPC(Java API for XML-based Remote Procedure Call)在J2EE1.4中用來調用遠程服務,這使得開發和部署可移植於標準J2EE容器的Web服務變得容易,與此同時,實現了跨平臺(如.NET)的服務互用。數據庫
在企業中,關鍵任務系統(mission-critical system,是指若是一個系統的可靠性對於一個組織是相當重要的,那麼該系統就是該企業的關鍵任務系統)用來解決高級需求,例如安全性,可靠性,事物。當一個企業開始採用服務架構做爲工具來進行開發和部署應用的時候,基本的Web服務規範,像WSDL,SOAP,以及UDDI就不能知足這些高級需求。這些需求也稱做服務品質(QoS,quality of services)。與QoS相關的衆多規範已經由一些標準化組織(standards bodies)提出,像W3C(World Wide Web Consortium)和OASIS(the Organization for the Advancement of Structured Information Standards)。apache
在瞭解SOA後,什麼是微服務(micro service)呢?微服務能夠在「本身的程序」中運行,並經過「輕量級設備與HTTP型API進行溝通」。微服務不須要像普通服務那樣成爲一種獨立的功能或者獨立的資源。微服務主要圍繞業務領域組件來建立應用,這些應用可獨立地進行開發、管理和迭代。微服務的本質是用一些功能比較明確、業務比較精練的服務去解決更大、更實際的問題。編程
微服務(Microservice)這個概念是2012年出現的,做爲加快Web和移動應用程序開發進程的一種方法,2014年開始受到各方的關注,而2015年,能夠說是微服務的元年,愈來愈多的論壇、社區、blog以及互聯網行業巨頭開始對微服務進行討論、實踐,能夠說這樣更近一步推進了微服務的發展和創新。
咱們接下來以Java web項目爲例,比較傳統開發模式與微服務開發模式的區別。
在傳統開發模式裏,全部的功能打包在一個 WAR包裏,基本沒有外部依賴(除了容器),部署在一個JEE容器(Tomcat,JBoss,WebLogic)裏,包含了 DO/DAO,Service,UI等全部邏輯,以下圖所示:
上述傳統開發模式的優勢是:
①開發簡單,集中式管理;
②基本不會重複開發;
③功能都在本地,沒有分佈式的管理和調用消耗。
缺點是:
①效率低:開發都在同一個項目改代碼,相互等待,衝突不斷;
②維護難:代碼功功能耦合在一塊兒,新人不知道何從下手;
③不靈活:構建時間長,任何小修改都要重構整個項目,耗時;
④穩定性差:一個微小的問題,均可能致使整個應用掛掉;
⑤擴展性不夠:沒法知足高併發下的業務需求。
通常來講,常見的系統架構遵循的三個標準和業務驅動力有:
①提升敏捷性:及時響應業務需求,促進企業發展;
②提高用戶體驗:提高用戶體驗,減小用戶流失;
③下降成本:下降增長產品、客戶或業務方案的成本。
相對來講,基於微服務的設計能夠有效的拆分應用,實現敏捷開發和部署。示意圖以下:
微服務的具體特徵有:①由一些獨立的服務共同組成系統;②單獨部署,跑在本身的進程中;③每一個服務爲獨立的業務開發;④分佈式管理;⑤很是強調隔離性。
那麼,SOA和微服務有哪些區別呢?
①SOA喜歡重用,微服務喜歡重寫。
SOA的主要目的是爲了企業各個系統更加容易地融合在一塊兒。SOA裏有一個重要的ESB(Enterprise Service Bus,即企業服務總線)機制,ESB是傳統中間件技術與XML、Web服務等技術結合的產物。ESB提供了網絡中最基本的鏈接中樞,是構築企業神經系統的必要元素。咱們能夠把ESB想象成一個鏈接全部企業級服務的腳手架。SOA還能夠把一個服務路由到另外一個服務上,也能夠集中化管理業務邏輯,規則和驗證等。它還有一個重要功能是消息隊列和事件驅動的消息傳遞,好比把JMS服務轉化成SOAP協議。各服務間可能有複雜的依賴關係。
微服務一般由重寫一個模塊開始。咱們向微服務遷移的時候一般從耦合度最低的模塊或對擴展性要求最高的模塊開始,把它們一個一個剝離出來敏捷地重寫,能夠嘗試最新的技術、語言和框架,然 後單獨佈署。它一般不依賴其餘服務。微服務中經常使用的API Gateway的模式主要目的也不是重用代碼,而是減小客戶端和服務間的往來。
②SOA喜歡水平服務,微服務喜歡垂直服務。
SOA設計喜歡給服務分層(如Service Layers模式)。如對於一個Entity服務層的設計,可能美其名曰Data Access Layer。 這種設計要求全部的服務都經過這個Entity服務層來獲取數據。這種設計很是不靈活,好比每次數據層的改動均可能影響到全部業務層的服務。而每一個微服務一般有它本身獨立的data store。 咱們在拆分數據庫時能夠適當的作些去範式化(denormalization),讓它不須要依賴其餘服務的數據。
微服務一般是直接面對用戶的,每一個微服務一般直接爲用戶提供某個功能。
③SOA喜歡自上而下,微服務喜歡自下而上。
SOA架構在設計開始時會先定義好服務合同(service contract)。 它喜歡集中管理全部的服務,包括集中管理業務邏輯,數據,流程,schema等。 它使用Enterprise Inventory和Service Composition等方法來集中管理服務。 SOA架構一般會預先把每一個模塊服務接口都定義好。 模塊系統間的通信必須遵照這些接口,各服務是針對他們的調用者。
微服務則敏捷得多。只要用戶用獲得,就先把這個服務挖出來。而後針對性的,快速確認業務需求,快速開發迭代。
全部的微服務都是獨立的Java進程跑在獨立的虛擬機上,因此服務間的通訊就是IPC(inter process communication)。
相比較而言,SOA更關注企業規模範圍,微服務架構則更關注應用規模範圍。
總的來講,微服務架構繼承了面向服務架構(SOA)的總體思路,將應用分割爲一系列細小的服務,每一個服務專一於單一的功能,運行在獨立的進程中,服務之間的邊界清晰,採用輕量級通訊機制相互溝通、配合來實現完整的應用,知足業務和用戶的需求。微服務架構更可能是屬於應用技術架構。咱們接下來看一個微服務架構的案例SpringBoot。
Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。它默認配置了不少框架的使用方式,就像maven整合了全部的jar包,spring boot幾乎整合了全部的框架,
通常來講或,咱們平時搭建一個spring web項目,每每要這樣作:
①配置web.xml,加載spring和spring mvc;
②配置數據庫鏈接、配置spring事務;
③配置加載配置文件的讀取,開啓註解;
④配置日誌文件
...
配置完成以後再部署tomcat,調試等。
好了,下面我用Spring Boot建立一個案例。
pom.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <!--模塊版本--> <modelVersion>4.0.0</modelVersion> <!--組,項目id,版本,打包類型--> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <!--項目名稱,描述--> <name>demo</name> <description>Demo project for Spring Boot</description> <!--父模塊--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <!--屬性配置--> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <!--依賴--> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--配置mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--c3p0配置--> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> <!--添加對tomcat的支持--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <!--添加對jsp的支持;tomcat插件,默認端口8080--> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <!--此處的<scope></scope>必定不要加上,做用域爲provided,能夠爲compile或缺省--> </dependency> </dependencies> <build> <!--插件配置--> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.properties文件:
#jsp視圖配置,若是報錯,可修改成:spring.view.prefix和spring.view.suffix,這個和spring boot的版本有關 spring.mvc.view.prefix=/WEB-INF/ spring.mvc.view.suffix=.jsp c3p0.jdbcUrl=jdbc:mysql://localhost:3306/itszt4 c3p0.user=root c3p0.password=2018 c3p0.driverClass=com.mysql.jdbc.Driver c3p0.minPoolSize=2 c3p0.maxPoolSize=10 c3p0.maxIdleTime=1800000 c3p0.acquireIncrement=3 c3p0.maxStatements=1000 c3p0.initialPoolSize=3
DemoApplication.java文件:
package com.example.demo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.web.servlet.DispatcherServlet; import javax.sql.DataSource; @SpringBootApplication @Configuration @MapperScan("com.example.demo.dao") public class DemoApplication { @Bean(name = "dataSource") @Qualifier(value = "dataSource") @Primary @ConfigurationProperties(prefix = "c3p0") public DataSource dataSource() { return DataSourceBuilder.create().type(com.mchange.v2.c3p0.ComboPooledDataSource.class).build(); } //分發中心配置返回視圖的規則 @Bean public ServletRegistrationBean dispatcherRegistration(DispatcherServlet dispatcherServlet) { ServletRegistrationBean reg = new ServletRegistrationBean(dispatcherServlet); reg.getUrlMappings().clear(); reg.addUrlMappings("*.html"); reg.addUrlMappings("*.json"); return reg; } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
接下來是domain,dao,service,controller層的文件:
//domain實體類 package com.example.demo.domain; /** * 實體類,映射數據庫裏的表 */ public class User { private String username, userpwd; public User() { } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUserpwd() { return userpwd; } public void setUserpwd(String userpwd) { this.userpwd = userpwd; } public User(String username, String userpwd) { this.username = username; this.userpwd = userpwd; } } ---------------------------------------------------------------- dao層: package com.example.demo.dao; import com.example.demo.domain.User; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.springframework.stereotype.Repository; /** * 訪問,操做數據庫 */ @Repository public interface UserDao { @Select("select * from user where username=#{username} and userpwd=#{userpwd}") public User queryUser(@Param("username") String username,@Param("userpwd") String userpwd); } ---------------------------------------------------------------- service層: package com.example.demo.service; /** * 業務接口 */ public interface UserService { boolean doLogin(String username,String userpwd); } package com.example.demo.service; import com.example.demo.dao.UserDao; import com.example.demo.domain.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * 引入userDao */ @Service public class UserServiceImpl implements UserService{ @Autowired private UserDao userDao; @Override public boolean doLogin(String username, String userpwd) { User user = userDao.queryUser(username, userpwd); if(user==null){ return false; } return true; } } --------------------------------------------------------------- controller層: package com.example.demo.controller; import com.example.demo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; /** * 引入業務實現類,判斷是否登陸成功 * 傳遞用戶名和密碼兩個參數 */ @RequestMapping("/userCenter") @Controller public class UserController { @Autowired private UserService userService; @RequestMapping("/login.html") public String doLogin(@RequestParam("username") String username,@RequestParam("userpwd") String userpwd){ boolean boo = userService.doLogin(username, userpwd); if(boo){ return "usercenter"; } return "redirect:/index.jsp?errorInfo=登陸錯誤!"; } }
web.xml文件:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>demo</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
兩個前端頁面:
index.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <form action="/userCenter/login.html"> <input type="text" name="username" value="admin"> <hr> <input type="text" name="userpwd" value="admin"> <hr> <input type="submit" value="登陸"> <hr> </form> <span style="color: red;font-weight: bold">${param.errorInfo}</span> </body> </html> ------------------------------------------------------- usercenter.jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> 我是usercenter123456789。。 </body> </html>
啓動DemoApplication.java中的main()函數,在瀏覽器中訪問http://localhost:8080/index.jsp便可。